Ontem eu falei de Arquitetura Evolutiva, então nada melhor que dar um exemplo de como isso já ocorreu comigo na prática. Já que nos últimos anos estive trabalhando no Qual Canal, vou usá-lo como exemplo, mas isso já aconteceu comigo em diversos outros sitemas e serviços. Para quem não conhece, o QC é uma ferramenta que fornece inteligência em cima dos comentários presentes nas redes socias sobre programas de televisão.
Quando comecei no projeto ele já possuía um código existente. Consistia em um protótipo feito pelo Farinha e Anderson para experimentar como tratar os dados do twitter. Este protótipo já realizava a coleta de dados, inseria em uma base MySQL e gerava um ranking, usando Django. Para quem conhece um pouco de processamento de Big Data sabe que essa não é uma arquitetura ideal esse tipo de serviço. Talvez para alguém mais versado no meio, a primeira decisão seria de jogar esse protótipo fora e começar algo mais robusto. Porém, esse protótipo atendia perfeitamente a nossa necessidade na época. Essa era de validar como agregação em ranking influenciava o público, então eu decidi usar aquele código como base para continuar os trabalhos.
Essa decisão permitiu que tivéssemos um foco maior na formatação dos dados e como o usuário interagia com ele. Essa era a decisão mais barata naquele momento, pois esse era nosso foco de aprendizado na época. Apesar disso, eu não estava disposto a deixar o sistema com jeitão de protótipo. Minha experiência mostra que esse tipo de dívida cobra juros caros no futuro. Por isso, decidi também investir em dois ajustes que considerei cruciais. Primeiro, em separar o consumo [e agregação] de dados sociais da interface web. Dessa forma permitindo que o back-end fosse independente de como nosso usuário fosse interagir com os dados. Segundo, criar um conjunto de testes para me proteger de mim mesmo de besteiras que eu pudesse fazer, e possibilitar nosso deploy contínuo. Nesse ponto, comecei criando os casos para as funcionalidades mais importantes já existente, enquanto garantisse que as novas fossem sempre cobertas.
Esta estrutura permitiu que o aprendizado evoluisse de forma gradativa por diversos meses, com a inclusão de diversas funcionalidades. Até que sofremos a primeira cobrança por não seguir uma arquitetura de Big Data. Naquele momento, nós tínhamos passado de alguns milhares de registros por dia, para esporádicos milhares por minuto. O que fazia nossa estrutura "congelar" por diversas horas. Converter nossa arquitetura para se adequar parecia muito caro. Nossa decisão foi de quebrar o processo de tratamento em vaŕias fases, além de multiprocessá-lo (além de alguns truques na base de dados). Com isso, conseguimos manter a mesma base de código e chegar no patamar que desejávamos. Além disso, com uma estrutura multiprocessada podíamos distribuir - mesmo que rudimentarmente - em diversas máquinas, conforme a necessidade.
Essa estrutura durou mais de um ano sem apresentar problemas. Tempo esse que permitiu que muito aprendizado ocorresse. Inclusive tendo uma nova troca no mercado atendido pela ferramenta, além das formas agregação, interface e fontes de dados coletados. Neste tempo, a maior parte do esforço foi gasto no front-end e no auxílio da administração da ferramenta. Tarefas que a estrutura com Django/MySQL atendia perfeitamente bem.
Mas nem tudo são flores, como era esperado, chegou o momento em que nossa escala de comentários tornou-se um gargalo novamente. Dessa vez, precisávamos que nosso ritmo de processamento saltasse para dezenas de milhares por minuto, em tempo real. Realizamos diversos testes e vimos que não tinha mais "truques baratos" a nossa disposição, era a hora de ir para a famigerada arquitetura de Big Data. Fizemos alguns experimentos e decidimos por usar o Apache Storm¹ com Cassandra. Já era esperado que migração saísse caro, e para minimizar esse custo ela foi realizada em passos, migrando um elemento por vez (comentários, contagens, agregações). Isso permitiu que mantivéssemos ambas as estruturas rodando juntas, avaliando o desempenho e resiliêncoa. Isso durou seis meses, até que o processamento em Python fosse completamente desligado. Apesar dessa mudança, a interface e administração permaneceu em Django/MySQL onde esta tecnologia brilha.
Hoje a produto passa por mais um ajuste de mercado, porém esta estrutura se mantém. Mas o que fica dessa história é que, apesar da arquitetura de Big Data ser a resposta certa desde o início, ela não era a mais adequada para o caso. Ser capaz de adiar essa decisão (e custo) por alguns anos permitiu que muito aprendizado ocorresse, antes que ela se provasse realmente necessária. Aprendizado este que foi muito importante não apenas para o negócio, mas para se amadurecer em como a tecnolgia podia realmente contribuir para ele.
Bem, até aqui eu contei como entregar a solução mais barata foi interessante. Mais ainda por que o pessoal que cuida do negócio adora receber funcionalidades baratas e rápdas. Mas ontem eu também falei que deletar código era igualmente importante. E aí que entra um novo desafio. Se essa tarefa já é difícil pra desenvolvedores, imagina pra quem está de olho no mercado. A primeira vez que eu sugeri que a gente retirasse uma funcionalidade da nossa base de código, ocorreu o drama já esperado. Um medo de que isso representasse uma perda de difícil remediassão. Bem como o velho questionamento "Não dá só pra desabilitar? Deixar comentado?".
Pois bem, eu tinha que educar essa prática e isso veio claro quando estávamos na primeira mudança de mercado. A sugestão foi de se livrar de tudo de interação com usuário que não estava relacionado com o novo cliente. Pra deixar os demais mais tranquilos, assumi a responsabilidade de voltar com as funcionalidades de maneira rápida, se elas se provassem úteis no futuro. Mesmo asism ouve receio, o que levou aceitar a remoção completa, menos o ranking (filho histórico do sistema). Passou-se um tempo e a aposta se pagou, nenhuma das funcionalidades foi ao menos cogitada de retornar. No fim, até mesmo o ranking acabou conhecendo seu destino no /dev/null. Com isso, a empresa ganhou aprendizado, e eu (e gerações futuras) me livrei de um legado pra cuidar.
¹ Hoje eu trocaria a escolha do Storm pelo Akka devido a sua maior flexibilidade de deploy. Mas a decisão se mantém funcional e estável. Quem sabe esse custo se justifica em no futuro.