O Guia Completo do Code Review Parte 1 — Antes da Revisão de Código

Sergio Fiorotti
10 min readMar 31, 2023

--

Fala galera! Tudo bom com vocês? Depois de um artigo meu falando de Code Review ter chegado a mais de 4k visualizações, eu resolvi criar uma série falando desse processo desafiador que temos como desenvolvedores e líderes de software, já que estamos falando aqui de um processo semiautomático (será que um dia teremos uma IA capaz de revisar código? ou já temos?), aqui eu separei dicas em alguns momentos que acredito ser relevante antes e durante esse processo.

Quero que esse guia como tantos outros seja algo que possamos construir juntos, então se você quiser comentar aqui no Medium e vamos evoluindo essa série dramática, se tomar forma jogarei no Github pra facilitar, mas vamos de MVP!

Parte 1 — Antes da Revisão de Código

Fiz essa seção anterior aos papéis de autor e revisor, justamente para reafirmar algumas práticas que não damos o devido valor e cuidado, e que no fim das contas impactam em todo o processo de desenvolvimento, chegamos na hora da revisão e ficamos com dúvidas, geramos atrasos, ciclos de revisão, entregas mal feitas, impactando negativamente o resultado de negócio.

Arquitetura

Gostaria de começar esse texto com o seguinte questionamento que vejo acontecer em muitos times de desenvolvimento, uma tarefa para entrar para desenvolvimento deveria estar bem definida e entendida por todos os membros do time, correto?

O time por sua vez, já quebrou a história ou o épico e essa é uma mini tarefa que deveria ser concluída em no máximo X dias, de acordo com cada time, eu diria em no máximo 2 dias, por ser um tempo mais razoável, mas pode ser mais dependendo do escopo, time e outras possibilidades.

Dito isso, a arquitetura está definida, o que será feito está definido, se tiver tela ou for uma API, o que é esperado está definido, e o time tem claro qual a definição de pronto da história.

Sem essas certezas fica difícil ter um processo de revisão eficiente, por que para mim, revisão de código não é só code smell, lint e verificar se o código tem os testes necessários, mas vamos falar do papel do revisor mais pra frente.

O primeiro passo anterior ao processo de revisão de código é ter em mente antes da execução o que é para ser feito tanto do ponto de vista de arquitetura como de função em si.

Claro que não precisamos transformar esse processo num “cascatágil”, mas temos que ser propositivos no sentido de não cometer os mesmos erros e aprender rápido com nossas entregas, vou dar um exemplo.

Aprendizados do Sergião

Nosso time precisava melhorar a importação de planilha, ela funciona de um jeito muito ruim, basicamente estávamos fazendo stream do arquivo direto para o servidor e lá depois de ter o arquivo em memória, consumíamos linha a linha até ler todos registros. Você já pode imaginar a porcaria desse serviço e o quanto ele afetava a performance geral da aplicação.

O time não era tão sênior, houve algumas mudanças de estrutura e repasse dessa tarefa, o resumo é que não houve um alinhamento mínimo de arquitetura, o time que assumiu presumiu que precisava fazer o upload direto do front para um storage (acertaram nessa tarefa) e colocaram uma fila com o mesmo servidor como consumidor (erraram no que esperávamos de arquitetura), não lembro de termos definido bem a arquitetura, mas estava claro para mim o que era esperado na minha cabeça.

Se tivéssemos uma descrição mínima do que era esperado de arquitetura, alguns desenhos, especificações, teríamos bloqueado essas alterações na revisão e acertado de primeira essa melhoria. Ter colocado os mais experientes do time para revisar essa tarefa em específico também poderia ter garantido a entrega esperada.

Se tivermos claro qual a solução que iremos usar para resolver um determinado problema, tudo fica mais fácil, na hora de descrever o que precisa ser testado, se o ambiente onde vai ser testado está pronto e preparado para os testes e homologação e as decisões ou tarefas futuras que iremos tomar em conjunto, por que nem tudo precisa nascer automatizado, com 100% de cobertura de testes, com nota A de segurança, e tendo isso conseguimos também mapear os débitos, um projeto em que o time não tem mapeado os débitos técnicos está fadado ao fracasso.

Tenha em mente que você precisa ter um passo para validação da arquitetura a ser executada, pode ser durante o refinamento, pode ser mais próximo da execução, mas tenha isso bem alinhado com o time.

Dicas de Leitura e Cursos
- Upstream e Downstream no Kanban e Big Design Up Front por Raphael Batagini
- Arquitetura Limpa por Otávio Lemos
- Clean Code e Clean Architecture por Rodrigo Branas
- Assine a newsletter do Serverless Land

Trunk Based Development

Que cacete de agulha é isso, Sergião? Bom se seu dia a dia costuma parecer com a imagem abaixo, sinto muito em te dizer mas você está gastando tempo do seu time e dinheiro do seu negócio. E segundo a documentação de DevOps do Google que está apoiada em cima das pesquisas do DORA Metrics, o desenvolvimento baseado em troncos é uma prática necessária para implementação da integração contínua.

Diagrama de branches, demonstrando um time que não usa Trunk Based Development. Fonte: Tecnologia de Devops: desenvolvimento baseado em troncos

O que o time que usa o diagrama acima está fazendo exatamente. Ao iniciar uma feature nova, abrem uma branch da funcionalidade e vão trabalhando em cima dessa branch até que a funcionalidade esteja 100% pronta para ir pra produção. E o que pode ir acontecendo durante os dias, semanas e meses desse desenvolvimento? Se o time for um pouco mais esperto, ele irá fazer um merge com a branch principal de tempos em tempos, senão um belo dia vai ocorrer algo parecido com o meme abaixo.

Meme clássico ao executar um merge com a branch principal no repositório

Beleza, então você quer que todo o time trabalhe direto na branch principal? (main, master, seja lá o que você usa por aí) Exatamente!

Claro que se seu time for pequeno e você não tiver uma quantidade enorme de alterações na branch principal isso não será um problema de imediato.

Mas é isso que vocês precisam adotar, fazer mudanças pequenas diretas em produção, é um paradigma gigantesco na cabeça de qualquer pessoa, e você já deve estar pensando em n cenários para isso ser uma incoerência e você continuar usando algum outro artifício para o cenário x ou y da sua empresa.

E o que isso tem haver com revisão de código?

Quando temos mudanças grandes de código, a revisão de código é falha, complexa, trabalhosa e demorada. Você irá criar diversos mecanismos e um processo extremamente burocrático de revisão de código para mitigar as falhas na entrega e não irá resolver porque revisão de código não é 100% automatizada.

Há vários passos a serem feitos pra vocês conseguirem chegar nesse nível processual, também irá haver resistência sobre isso. Geralmente usamos branches associadas aos ambientes mesclou com a main vai para produção mesclou com develop vai pro ambiente de desenvolvimento e assim por diante, esse paradigma não ajuda nessa implementação, o correto são todas as solicitações de alteração, vulgo PR, serem feitas a uma branch única, a main.

Hoje o Github Actions e tantos outros pipelines de automação de software tem acionamentos dos mais diversos possíveis e você consegue literalmente gerar uma release de qualquer branch. Há até possibilidade de ter um pipeline automatizado de rollback e muitas outras coisas que poderíamos fazer um artigo só disso. (Alô Github patrocina nóis!)

A conclusão desse tópico se define por aprender literalmente a trabalhar com pequenas entregas para conseguirmos ter um processo de revisão de código eficaz e o próximo tópico é exatamente uma sacada para te ajudar a entregar software em produção todos os dias.

Throughput em produção do time da Matchbox em Q1 de 2023 via Velocity da CodeClimate

Dicas de Leitura
- O tamanho do Pull Request é mais importante do que você pensa por Victor Hugo
- SourceLevel — Ferramenta de Métricas de Engenharia criada por George Guimarães
- Recursos técnicos de DevOps por DORA Team

Feature Flag

Bandeira de funcionalidade, que troço é esse? Você já deve ter implementado um sistema de permissionamento, digamos que temos dois perfis, um administrador que aparece todas as funções e outro um usuário padrão, que aparecem menos itens no menu no frontend e são feitas algumas validações no backend quando executada determinada requisição, certo?

Feature Flag é basicamente isso, fazer um código dinâmico em que consigamos ativar e desativar uma funcionalidade para um determinado público ou contexto.

O Martin Fowler, o pai da refatoração, escreveu um artigo muito interessante em seu blog sobre feature toggle (que é a mesma coisa que feature flag) e lá ele usou uma imagem muito interessante que mostra os diversos tipos de toggles em um software.

Gráfico da relação de dinamismo e longevidade dos tipos de toggles retirada do artigo do Martin Fowler

E ele cita um determinado tipo de toggle que é o que quero focar nesse momento, que ele chama de Release Toggles, e ele fala que esse tipo de toggle dura por dias ou até semanas e geralmente só muda com um novo deploy ou mudando alguma configuração em tempo de execução. Ele começa sua explicação com essa introdução abaixo.

These are feature flags used to enable trunk-based development for teams practicing Continuous Delivery. They allow in-progress features to be checked into a shared integration branch (e.g. master or trunk) while still allowing that branch to be deployed to production at any time. Release Toggles allow incomplete and un-tested codepaths to be shipped to production as latent code which may never be turned on.

O papai Fowler (é o mesmo que papai Lebron só que para programadores) resume exatamente o ponto aonde quero chegar, feature flags são usadas para habilitar o desenvolvimento trunk-based para termos entrega contínua.

Mas Sergião, não entendi como aplicar isso na prática? Como que faz? Há diversas maneiras de se usar isso, algumas talvez você já faça sem perceber.

Por exemplo, quando vamos criando no frontend uma rota nova que só quem sabe a url acessa, isso pode ser uma saída pro seu time. No backend a mesma coisa, construir uma nova rota para ir testando e validando e depois, ou publicar uma nova versão ou substituir a rota quando tudo estiver realmente homologado.

Para os desenvolvedores raiz colocar uma verificação de ambiente pode ser uma saída, aquele IF bonito!

if (process.env === 'development') {
// FAZ ALGO QUE SÓ VAI FUNCIONAR EM DEV
}

Você pode usar algo mais sofisticado como um Redis na Upstash ou usar o módulo de Experimentation da Optimizely que é grátis e usamos na Matchbox.

Percebeu como há várias maneiras de você colocar código em produção e na branch principal, sem que isso impacte seu cliente final?

Claro que há cenários muito mais críticos a serem avaliados de infraestrutura e mudanças em banco de dados, mas a intenção principal é pense sempre em como desbloquear seu time para que ele entregue software em produção.

Dicas de Leitura
- Feature Toggles (aka Feature Flags) por Martin Fowler
- Feature Flags with Optimizely por Vercel

Testes

Eu sei que talvez essa cultura da revisão de código seja nova para muita gente, passei anos sem fazer isso e diversos problemas aconteciam no meio do caminho que devem estar acontecendo onde você trabalha, por desconhecimento profundo nas práticas, muito pouco se falava sobre o assunto.

Passei muito tempo também ignorando testes, e não estou aqui querendo ser um cara mau e crítico, estou há dois anos liderando o time da Matchbox, quando cheguei tínhamos pouquíssimos testes, quase nenhum, aumentamos muito a nossa base de código e há poucos meses atingimos um patamar aceitável em poucos projetos, não é fácil convencer as pessoas sobre testes, sobre uma revisão boa de código, é árduo demais.

Mas está fazendo a diferença em nosso dia a dia. Recentemente, o Otávio Lemos lançou a braba no LinkedIn e postou assim:

Recorte do post do Otávio Lemos no Linkedin sobre agilidade e testes, “Não há agilidade sem teste automatizado.”

E realmente concordo com a frase, pois se queremos impactar o negócio todos os dias com entrega contínua, como podemos garantir as mudanças que estamos fazendo sem algo para nos apoiar?

Testes tem tudo haver com revisão de código também, e não só testes automatizados, unitários, de integração e end to end, estou falando de testar o PR de verdade, baixar a branch local, fazer o que o PR se propõe e ver o que realmente trás de resultado. E vamos falar disso mais pra frente no papel do revisor, mas tanto o teste automatizado como o teste manual são fundamentais para o sucesso da entrega.

Saber o quão crítico é aquela tarefa que você está desenvolvendo e entender a importância de ter testes automatizados garantindo aquele resultado esperado é fundamental para todo o processo. Por isso coloquei esse item como anterior a revisão, ele deve ser um checklist do revisor, mas estar na mente de todos do time como fundamental para entrega.

Dicas de Leitura e Vídeo
- Livro do Pai do TDD por Kent Beck
-
Live sobre TDD com Diego Fernandes e Rodrigo Manguinho por Rocketseat

Fechando aqui a Parte 1 do Guia do Code Review, destaquei alguns pontos extremamente relevantes que irão ajudar a diminuir o impacto das revisões, melhorar também a percepção do time de forma geral, tanto em velocidade quanto de resultado de negócio, vocês vão conseguir experimentar mais rápido, fazer mudanças mais rápidas, por que processos burocráticos e as famosas subidas críticas irão diminuir ou acabar.

Dito isso podemos focar em dicas para o autor e o revisor de PR’s! Curta, comente e compartilhe para eu escrever mais rápido as outras duas partes (risos!)

Guia Completo do Code Review
Parte 1 — Antes da Revisão de Código
Parte 2 — O Papel do Autor
Parte 3 — Em construção

--

--