Quando o Jurídico Certo se tornou Jusbrasil — Parte 1

A experiência de unificar a infraestrutura de duas empresas de tecnologia: desafios tecnológicos e cultura de desenvolvimento.

Osvaldo Matos Júnior - Tupy
Jusbrasil Tech
10 min readAug 2, 2019

--

No início de 2018, o Jusbrasil realizou a aquisição da plataforma Jurídico Certo (JC), o maior site do Brasil para contratação de advogados correspondentes — advogados que prestam serviço advocacia de apoio para advogados e escritórios em outras cidades. Após o aperto de mãos, foi iniciada a integração entre as duas plataformas.

Na primeira etapa foi feita a integração das contas: assinantes do JC poderiam acessar os serviços prestados no Escritório Online (EO) do Jusbrasil e vice-versa. As próximas etapas seriam:

a) unificar base de usuários;

b) refazer o frontend do antigo painel do JC dentro do EO;

c) mover infraestrutura da AWS para o Google Cloud;

d) re-implementação das aplicações mobile.

Devido às diferenças de tecnologia, cultura de desenvolvimento dos times, além das diretrizes de prioridade na camada de negócios e produto, o cronograma se estendeu por mais de 1 ano, quando a integração das contas estava finalizada e iniciada a reescrita do painel dos advogados. No entanto, esse período foi muito importante para compreender a qualidade tecnológica que desejamos ter e como podemos melhorar a cultura de desenvolvimento.

Nesse artigo iremos abordar os desafios de tecnologia e cultura encontrados ao mover a infraestrutura da AWS para o Google Cloud.

Migração da Infra: AWS to GCloud

A stack do Jurídico Certo foi construída em cima da Amazon Web Services (AWS): aplicações PHP rodam em máquinas virtuais (EC2) com autoscaling, são acessadas via ELB (Elastic Load Balancing) e os dados armazenados no RDS (Relational Database Services)e S3 (Simple Storage Service). O Jusbrasil já foi assim. No entanto, hoje o Jusbrasil é formado por centenas de microsserviços, alguns com seu próprio banco de dados, rodando no Kubernetes do Google Cloud (cabe um artigo, estamos devendo).

Operar em dois clouds distintos não é algo trivial. Isso tem impacto diretamente na complexidade da estrutura de deployment, monitoramento e manutenção dos serviços existentes e, consequentemente, financeiro. Por exemplo, existem engenheiros que ficam de plantão (chamamos de oncall) acompanhando a saúde dos serviços. Dessa forma, esses engenheiros precisam conhecer, acompanhar e ter acesso a ambas plataformas para conter qualquer tipo de incidente.

A seguir, iremos listar as melhorias na cultura de desenvolvimento e os principais desafios técnicos encontrados durante a migração — alguns listados em Top 10 GitHub best practices for developers.

GitHub Projects

Não existe uma ferramenta obrigatória para gerenciamento de projetos no Jusbrasil. A maioria dos times utiliza o Trello, uma excelente ferramenta para gerenciar o desenvolvimento de produtos. No entanto, quando a tarefa é muito muito técnica, as Issues do GitHub conseguem conectar melhor o desenvolvedor com o código-fonte. Por exemplo, atualizar a versão de uma dependência faz mais sentido estar no GitHub e abrir modal ao clicar no botão estar no Trello. Por isso, utilizamos o GitHub Projects para organizar a migração. Seguem algumas das tarefas, ou issues, criadas no GitHub:

  • Atualizar PHP para versão 7.3
  • Atualizar CakePHP para 2.10.17
  • Construir imagem Docker
  • Executar testes com docker-compose
  • Migrar configurações para variáveis de ambiente
  • Executar build no Google Cloud Build
  • Configurar deploy no Google Kubernetes

Essas issues foram criadas diretamente nos repositórios dos 9 projetos que seriam migrados. Era possível acompanhar o andamento da migração de cada projeto usando Milestone para agrupar issues e, com o GitHub Projects, foi possível ter uma visibilidade global das tarefas em aberto, o que estava em progresso, aguardando revisão, aprovado e finalmente concluído. Muitas vezes, essas tarefas iniciais se desmembravam em mais tarefas (e bugs), ou mais de um Pull Request e, graças às referências permitidas no GitHub, a equipe de desenvolvimento conseguia acompanhar de perto toda evolução — leia-se linhas de código modificadas), até a conclusão.

Essa decisão de ferramenta nos fez observar outras melhorias no fluxo de desenvolvimento: o tempo todo novas issues eram criadas propondo melhorias ou com bugs identificados no código; mensagens de commits antes pobres passaram a ser concisas; Pull Requests gigantes foram quebrados em blocos menores e já sobem em produção, isso facilita a revisão e minimiza o risco; qualquer alteração passa por revisão e com liberdade a comentários e reprovação, além de branches protegidas que forçam ao menos uma aprovação. E essas melhorias influenciaram diretamente na performance e felicidade do time.

Documentação

Documentação é um saco. Porém, é importante. Poucos desenvolvedores gostam de documentar as coisas que fazem, a maioria prefere codificar sem parar e entregar sua obra de arte funcionando lindamente! #fail
A documentação mais importante é o código-fonte. É preciso internalizar que essa obra de arte não está sendo escrita para você, e sim para os outros leitores. Um código bem escrito dispensa javadocs inúteis, que sempre ficam desatualizados com o passar do tempo. Esse é um assunto bem amplo, melhor parar por aqui.

Acreditamos que deve existir o mínimo de documentação em um projeto. Então, um README explicando o essencial de um projeto é indispensável, como: objetivo, instalação e configuração. No Jusbrasil haviam dezenas de projetos sem essa documentação mínima: README inexistente ou vazio. Pouco tempo atrás sanamos essa deficiência. Adotamos um README template e alguns documentos ficaram bem completos, que ajudaram bastante no onboarding de novos engenheiros. Essa ausência de documentação também existia nos projetos envolvidos na migração. Aproveitamos esse momento para entender a responsabilidade e documentar o mínimo sobre cada um deles.

Configuração

A primeira melhoria técnica foi realizada na configuração das aplicações: maior parte da configuração estava escrita diretamente no código. Essa prática não é segura, pois credenciais de acesso a banco de dados podem facilmente ser descobertas por pessoas mal intencionadas. Adicionalmente, muitas dessas configurações variam com ambiente de execução (e.g. development, test, production), por exemplo, endereços de servidores e remetente de e-mails.

Praticamente todas as configurações foram migradas para variáveis de ambiente, passadas apenas no momento do deploy, de acordo com o ambiente de execução. No novo cloud, ConfigMaps foram usados para armazenar e compartilhar configurações da aplicação, já as credenciais (i.e. senhas e API keys) foram armazenadas com Secrets e Cloud KMS.

Isolamento em containers

Durante o desenvolvimento, o desenvolvedor precisa ter acesso às principais tecnologias usadas no projeto. As aplicações do JC foram desenvolvidas em PHP, banco de dados MySQL e MongoDB. Logo, era necessário instalar o interpretador do PHP (nginx + php-fpm) e banco de dados na própria máquina para reduzir acesso a serviços e APIs externas.

Além disso, o ideal é que ambiente de desenvolvimento seja o mais próximo de produção, reduzindo assim a possibilidade de integrar erros desconhecidos. A fim de acelerar o desenvolvimento local, cada projeto passou a ser executado dentro de container docker que embutia todos requisitos de sistema, e demais serviços (i.e. MySQL, MongoDB e RabbitMQ) atachados com ajuda do docker-compose. Com ambiente mais enxuto e isolado, agora era possível executar localmente as APIs dependentes que antes eram acessadas via Internet.

Poliglotas

Praticamente todas as aplicações do JC eram feitas em PHP, exceto o TempoReal que usava Socket.io (Node.js). O time de engenharia não possuía domínio em outras linguagens de programação, monolíngues, então sempre seria a plataforma escolhida na concepção de novos produtos. Entretanto, a linguagem de programação é a caixa de ferramenta do desenvolvedor. É preciso analisar os principais requisitos do produto para escolher a linguagem adequada.

O Jusbrasil era apenas Java, uma limitação semelhante. Em algum momento essa luz veio, abriu-se novos horizontes e, graças à entrada de novas pessoas, hoje contamos com aplicações escritas em Java, Scala, Python, JavaScript (Node.js, TypeScript), Go e Ruby. Aqui fica o desafio de incentivar os engenheiros a buscar conhecimento em novas linguagens, novos paradigmas, serem mais flexíveis e dinâmicos, verdadeiros poliglotas.

Gerenciamento de Dependências

Reuso de código é um conceito amplamente consolidado na Engenharia de Software. Reutilizar código escrito pela comunidade, ou internamente, acelera o desenvolvimento das aplicações. No entanto, colocar o código de dependências junto ao projeto é totalmente abolido, isso aumenta o tamanho do repositório e dificulta a atualização dessas dependências.

Linguagens de programação modernas utilizam gerenciadores de pacotes e repositórios centralizados para compartilhar código público, ou privado, como: o Java com Maven Central, Node com NPM, Python com Pypi e PHP com Composer. Nessa etapa, foram apagadas todas as dependências de terceiros embutidas no código de cada projeto (ou baixadas via Git Submodule) e passaram a ser instaladas via Composer. Bibliotecas e plugins desenvolvidos internamente foram publicados repositórios privados e distribuídos usando Satis. A partir daí foi possível ter maior controle das dependências, permitindo a atualização de versão mais segura e protegida, pelo arquivo de lock e obedecendo o Semantic Versioning.

Integração Contínua

Nosso primeiro servidor de integração contínua (CI - Continuous Integration) foi o Hudson. Após a aquisição pela Oracle, veio o medo de se tornar um software pago, então seguimos o movimento dos core commiters e migramos para um fork, o Jenkins. Esse processo de migração foi penoso, pois toda configuração de build precisou ser feita novamente, algo semelhante aconteceria caso corrompesse o disco, por exemplo. Esse risco foi reduzido após o uso do JobDSL Plugin, que permitiu escrever tais configurações em arquivo e salvar junto ao projeto. O Wercker foi outro CI que passou a ser usado em projetos menores no Jusbrasil, principalmente projetos web, por ser gratuito para repositórios privados, ter uma interface amigável, workflows e configuração simples via YAML.

Entretanto, após algum tempo, o custo de manutenção do Jenkins se tornou caro: sem time para manter, plugins desatualizados era algo comum e representava um risco alto na quebra de compatibilidade. A Oracle adquiriu também o Wercker e começou cobrar seus novos assinantes. Era hora de buscar uma alternativa no mercado e escolhemos o Google Cloud Build para novos projetos, demais seriam migrados gradativamente.

O Jurídico Certo também usava o Jenkins, com builds configurados manualmente, como fazíamos no passado. Manter um Jenkins já era problema, imagine dois. Como já seria necessário migrar toda configuração dos projetos do Jurídico Certo para arquivo, optamos por escrever o pipeline de build, test e deploy já usando o Google Cloud Build. Mais precisamente, essas etapas finalmente seriam:

  1. Construção da imagem docker;
  2. Execução dos testes no ambiente isolado com docker-compose;
  3. Envio da imagem docker para Google Cloud Registry;
  4. Deploy da aplicação no Kubernetes nos diferentes namespaces.

Testes e Deployment

Não ficamos mais surpresos quando conversamos com outras empresas de tecnologia e simplesmente dizem que não fazem testes. Testes são essenciais quando deseja-se garantir funcionamento da aplicação, ainda mais quando o código passa por grande alterações. Algumas das aplicações migradas não possuíam testes, outras possuíam alguns poucos e já ajudaram bastante na identificação de bugs introduzidos. Com o tempo, novos testes serão adicionados para aumentar a cobertura e confiabilidade no código. Os testes unitários dependiam de serviços externos (e.g. banco de dados no RDS, S3, memcached), agora eram executados com docker-compose com serviços dependentes embutidos, tornando-se mais enxutos e isolados.

Para esse tipo de migração, o ideal seria um teste de integração, que iria garantir o funcionamento entre as partes do sistema. Optamos por escrever testes end-to-end dos principais fluxos usando o Cypress. Esses últimos testes são executados já em produção, e funcionam mais como monitoramento.

Os testes unitários executam durante o build no CI, em seguida é feito o deploy. Existem várias estratégias de deployment, anteriormente era feito pull deployment: máquinas virtuais verificavam junto ao CI (Jenkins) se estavam na última versão e realizavam sua própria atualização automaticamente. Quando migramos para o Kubernetes, passamos a realizar push deployment: o CI informa explicitamente qual a versão deve ir para produção.

Durante a migração, uma branch legacy foi mantida com antigo fluxo de deployment ativo para possíveis alterações (e.g. bug fixes). Já a branch master seguia o novo fluxo descrito anteriormente.

Diferentes Ambientes

Uma boa prática bem enraizada na cultura de desenvolvimento do Jurídico Certo era a execução em múltiplos ambientes, a seguir: staging, onde novas features são testadas antes de ir para produção; demo, usado pelo setor de vendas para prospectar novos clientes; production, usadas pelos usuários finais.

Essa prática não era utilizada no Jusbrasil até então, devido ao custo de replicar e manter toda stack. Sempre utilizamos de outros recursos, como feature flags que habilitam novas funcionalidades escondidas a serem validadas em produção. Retroceder não era uma opção. Entendemos que era algo que deveria ser incorporado à nossa cultura. Então, utilizamos namespaces do Kubernetes para replicar toda stack do Jurídico Certo nos três ambientes listados acima e, de forma bem simples, bastou configurar as variáveis em cada ambiente/namespace corretamente para tudo funcionar conforme esperado e totalmente isolado.

Monitoramento

Os serviços do Jurídico Certo eram monitorados com o New Relic. Bem no comecinho, o Jusbrasil também, saímos apenas devido ao custo. Hoje o monitoramento é feito por meio do Prometheus e Grafana. Os serviços migrados continuaram com o New Relic e ganharam monitoramento adicional do Prometheus e Grafana.

Considerações Finais

Essa seria uma release interna, porém optamos por compartilhar com a comunidade para compreenderem os desafios que existem no mundo real.

Não foi apenas uma migração de aplicações entre datacenters. Talvez o maior desafio tenha sido cultural. Pessoas que trabalhavam de forma distinta tiveram que se conhecer melhor, ganhar mais confiança e aceitar os desafios. A estimativa inicial de 3 semanas viraram 11, no entanto, todos compreenderam que esse era o momento para esse salto tecnológico, unificar o conhecimento, ver o time amadurecer. Grande parte dos nossos engenheiros são fullstack, poliglotas, e assim queremos continuar.

Claro que isso só foi possível porque tivemos um time 🔝 de engenheiros super dedicados e competentes que aceitaram o desafio e executaram esta missão com excelência, #tamojunto: Diego, Sonba, Takeda, Roger, Jotapê, Davi e Padil. 👊🏻💪🏻

Este artigo é a Parte 1, que conta a migração na parte de infraestrutura. A próxima etapa será a migração da aplicação, a qual iremos compartilhar a trajetória por meio da Parte 2.

Gostou do artigo? Bate palminhas!! 👏🏻👏🏻👏🏻
Gostaria de trabalhar no Jusbrasil? https://sobre.jusbrasil.com.br/carreiras
Acompanhem nossas contribuições em: https://twitter.com/JusbrasilTech

--

--