- A revisão de código está mais próxima de um processo para revelar com antecedência código que será difícil de manter no futuro do que de um procedimento para encontrar bugs ou garantir integridade
- Se até o revisor tem dificuldade para entender o código ao lê-lo, há grande chance de que o mesmo problema afete futuros mantenedores
- É melhor corrigir isso agora, enquanto o autor original ainda se lembra do contexto, e não é realista esperar que a inspeção de código encontre bugs de forma confiável
- Para o revisor, uma tarefa mais executável do que “encontre bugs” é “tente entender e marque as partes que não fizerem sentido”
- Uma boa revisão não começa tentando provar tudo com perfeição, mas sim deixando notas nos pontos que não forem compreendidos e pedindo melhorias
Mudando o foco da revisão de código
- O objetivo central da revisão de código não é que o revisor encontre bugs, nem garantir que o código não tenha bugs
- Na prática, é fraca a expectativa de conseguir encontrar bugs de forma geral apenas passando os olhos pelo código
- Por isso, o centro da revisão está menos em “o código está correto?” e mais em “outra pessoa conseguirá ler e corrigir isso depois?”
Código difícil de entender é sinal de risco de manutenção
- O revisor lê o código tentando entender o que ele faz e como funciona
- Partes que não são compreendidas se tornam um sinal de risco de que futuros mantenedores podem ficar bloqueados
- É melhor corrigir esse tipo de código imediatamente, enquanto o autor original ainda se lembra do contexto
Tarefas de revisão mais executáveis do que caçar bugs
- O pedido “tente encontrar bugs neste código” é uma tarefa cujo sucesso é difícil de avaliar
- Mesmo que alguns bugs sejam encontrados, outros podem continuar escondidos, o que para o revisor torna o fracasso mais fácil de ficar evidente
- Em contraste, o pedido “tente entender este código e aponte o que não conseguir entender” tem um critério mais claro
- Não é necessário entender tudo perfeitamente
- Basta registrar as partes que não foram compreendidas
- Tentar entender o todo e deixar notas nos pontos em que travou já cumpre o papel da revisão
Critérios de revisão na prática
- Código que o revisor não consegue entender pode, por si só, se tornar alvo de correção
- Comentários de revisão não servem apenas para relatar bugs, mas também para revelar falta de explicação, problemas de estrutura e fluxos difíceis de ler
- Nesse critério, mais importante do que provar a validade do código é deixá-lo em um estado que outros membros da equipe consigam ler e trabalhar depois
Encontrar bugs é um efeito colateral
- Isso não significa que a revisão de código nunca encontre bugs
- Bugs podem ser descobertos durante a revisão, mas é difícil esperar que ela encontre todos os bugs ou a maioria deles
- Uma condição de sucesso mais realista é verificar a compreensibilidade e melhorar imediatamente, junto com o autor original, as partes que serão difíceis de manter
1 comentários
Opiniões do Hacker News
Revisão de código não tem um único objetivo. Encontrar código difícil de manter é importante, mas pode não ser o único objetivo, nem talvez o mais importante
Ela funciona como uma salvaguarda que dificulta o merge de código malicioso, mesmo que um desenvolvedor ou uma IA vá para uma direção estranha, e alguém que não esteja tão próximo do problema pode enxergar uma abordagem melhor ou problemas que passaram despercebidos
Alguém que conhece melhor outras partes do sistema também pode detectar problemas de interação, faz com que pelo menos uma pessoa se familiarize com aquele código, e vira uma oportunidade de aprendizado tanto para o autor quanto para o revisor
Isso é especialmente importante quando há diferença de senioridade: ao mentorar novos funcionários, adiciono-os como revisores em todos os meus PRs para que vejam como trabalho, e também reviso os PRs deles para ajudar a dar direção. Às vezes eu também aprendo com eles
Encontrar bugs também faz parte, mas não deveria ser o mecanismo principal, e é especialmente importante para bugs de segurança e desempenho, que são difíceis de capturar com testes automatizados
Isso vale especialmente para modularização e decomposição; depois de entender um PR enorme por completo, você forma um modelo mental e começa a enxergar se ele será sustentável, se um dia virará um pesadelo completo, ou algo intermediário
Acho que talvez a parte mais importante da revisão de código seja a transferência de conhecimento
Na nossa equipe pequena, salvo casos urgentes, a equipe inteira marca aprovação no PR antes do merge, e graças a isso todos os membros têm uma noção geral do estado atual da base de código. Isso evita surpresas como “o sistema inteiro do qual eu dependia desapareceu”, que já vivi em empresas mais isoladas em silos
Também vira um espaço para fazer perguntas sobre como as coisas funcionam e aumentar o entendimento. Em uma equipe que funciona bem, todos os desenvolvedores deveriam entender em algum grau o sistema inteiro, incluindo partes em que não mexem diretamente
Outra função importante é a checagem do conhecimento organizacional. Recentemente alterei um pouco uma tabela, e um colega avisou que um microsserviço que eu não tinha considerado escrevia nela e quebraria. Eu nem sabia que esse microsserviço existia, nem que acessava aquela tabela, mas graças a essa checagem conseguimos evitar um problema maior e uma situação de limpeza de dados
No fim, todo mundo está ocupado com seu próprio trabalho e não quer ser a pessoa que bloqueia um PR importante, então apenas aprova, e na prática ninguém está revisando o código
Para problemas como “o sistema inteiro do qual eu dependia desapareceu”, acho que testes automatizados capazes de detectar isso mesmo sem a presença de quem depende daquele sistema são muito importantes
Também sou forte defensor de propriedade compartilhada sobre tudo. É natural que as pessoas acabem praticamente donas de certas partes da base de código, especialmente componentes que criaram, mas isso leva a silos e a um baixo bus factor. Não deve existir uma estrutura em que uma pessoa é dona de um sistema, e esse sistema depende de outro componente pertencente a uma única pessoa
Quando há coisa demais para ler, ninguém consegue acompanhar; por isso se delega, se cria documentação e se realizam sessões de visão geral
Sempre achei melhor encarar a revisão de código como o portão pelo qual o código que era de propriedade do autor passa a ser propriedade da equipe ou do projeto
O código que estou revisando não é o seu código; é um código que em breve será nosso. Naturalmente, manutenibilidade é um fator importante nisso
Minha equipe começou a usar IA, então mudei para uma abordagem simples. Não deixo comentários; faço apenas um julgamento binário de aprovação: “isso está em um nível insano ou dá para passar?”
Estou preservando meu tempo e minha saúde mental
Fazer isso só torna o revisor e o autor mais preguiçosos
O objetivo da revisão de código é multifacetado. O código é difícil de manter? Pode conter bugs? Dá para torná-lo mais simples e limpo? Está de acordo com o estilo de código do projeto? Faz outras pessoas entenderem o código? Ajuda a integrar membros juniores da equipe? Faz um sanity check das decisões de design? Tudo isso conta
Textos leves assim, em geral, estão mais perto de uma justificativa que revisores de código preguiçosos dão a si mesmos
É preciso verificar se, funcionalmente, ela atinge o objetivo descrito na issue ou no PR; se há código desnecessário, como saídas de debug restantes ou chaves de API privadas; e se há defeitos evidentes, como vazamentos de memória, casos de borda não tratados, falhas de segurança ou chamadas a APIs obsoletas
Também é preciso ver se dá para tornar o código mais compreensível, se convém adicionar ou remover abstrações, se os nomes de variáveis e métodos são melhores, e se seria melhor usar mais ou menos estilo funcional
Também é preciso confirmar se está consistente com a base de código ou o guia de estilo, se há melhorias óbvias de desempenho, como usar um conjunto hash em vez de uma lista ou aplicar avaliação preguiçosa, e se os testes são suficientes
Também não concordo totalmente com a tese de que, se você não consegue entender o código, ele não deve passar. Alguns códigos são simplesmente muito difíceis de entender, e o objetivo é torná-los funcionalmente corretos e tão compreensíveis quanto possível
Mas este texto parece mais uma isca, parecido com dizer “as pessoas acham que jantar é comer comida, mas na verdade não é comer, é se conectar com família e amigos”. É um tipo específico de lógica reducionista frágil que funciona bem no HN
Senti que revisão e depuração consomem muito mais tempo do que escrever e produzir código, e simplesmente “rezar para funcionar” nunca termina bem
A afirmação de que “em geral, não é possível encontrar bugs olhando o código” não está certa de forma alguma. É possível encontrar muitos em cada nível de abstração, e chamamos isso de code smell
Descritores de arquivo não fechados, corrotinas sem await, blocos try/catch enormes que retornam algum valor sem registrar o erro, casts de tipo incorretos etc. entram nisso
Como princípio geral, não se deve tratar o type checker, o compilador e o runtime como meras etapas pelas quais o código precisa passar. É preciso trabalhar com essas etapas, reconhecer que são ferramentas valiosas e não trabalhar contra elas
Dá para definir “em geral” de algum modo que torne a frase tecnicamente verdadeira, mas aí ela perde quase todo o sentido
Para fazer afirmações amplas sobre revisão de código, é importante definir de que tipo de revisão de código se está falando
Hoje, a revisão assíncrona de PRs no estilo GitHub, com comentários inline, é o padrão, mas revisão não é só isso. Antigamente havia revisões presenciais com processos mais próximos de uma defesa de tese ou apresentação em conferência
A literatura que diz que revisão de código é uma prática de qualidade útil — na verdade, uma das poucas práticas de qualidade úteis — vem, em geral, de processos de revisão muito mais estruturados do que os de hoje
Pessoalmente, antes dos LLMs, eu achava que as revisões de PR no estilo GitHub serviam para fazer o processo parecer bom ou para preencher uma caixinha de governança; na era dos LLMs, acho que o custo-benefício ficou muito pior e elas devem desaparecer
Forçando uma analogia técnica, a comunicação assíncrona por texto tem mais perda e menor throughput do que a fala em termos da informação que consegue codificar com sucesso. Por isso, quando é preciso trocar muita informação, vale a pena pagar o custo de sincronização
Era mais próximo da engenharia tradicional, e todos tinham que pensar no software como algo mais permanente
Garantir a manutenibilidade é, sim, um dos benefícios da revisão de código, mas dizer que esse é seu único objetivo é uma afirmação bastante ousada
A revisão de código também é uma ferramenta para a equipe acompanhar mudanças no código e compartilhar a responsabilidade coletiva por toda a base de código
Revisão de código não é uma coisa única. Há vários motivos, como compartilhamento de conhecimento, lavagem de responsabilidade, qualidade de código, conformidade regulatória etc.; e, como sempre, o objetivo depende do contexto de uso
Se o autor é matemático, a frase “em geral, não é possível encontrar bugs olhando o código” provavelmente não quer dizer que encontrar bugs seja completamente impossível
Deve estar mais perto de dizer que não é possível encontrar todos os bugs, ou encontrar um bug específico
Ligando isso ao ponto da manutenibilidade, um objetivo da revisão também é tornar o código o mais simples possível para aumentar a probabilidade de que ele seja depurável por revisão. Isso não impede bugs em sentido absoluto, mas aumenta a probabilidade
A primeira interpretação é relevante, mas falsa; a segunda é verdadeira, mas irrelevante
Talvez o autor quisesse dizer “em geral, não é possível encontrar um determinado bug olhando o código”, ou seja, “para todo bug B, não é possível encontrar B”, mas isso também é verdadeiro e distante do ponto central
Já trabalhei tanto com pessoas que frequentemente recusam sugestões em PRs quanto com pessoas que as aceitam
Quem aceita sugestões me faz pensar que, em certa medida, está disposto a compartilhar comigo a propriedade do código. Dá a sensação de que ambos mantemos e entendemos o código, olhando na mesma direção
Por outro lado, em PRs de pessoas que recusam sugestões, a vontade de participar diminui. Se vai ser recusado de qualquer forma, por que gastar tempo fazendo uma revisão cuidadosa?
thought,change,nit,fix,chatPor exemplo,
thoughtseria algo como “talvez foo fique mais comum no futuro, então vamos refatorar quando isso acontecer”;change, “isso é uma abstração vazada, eu gostaria que fosse modelado como bar”;nit, “o nome não é muito intuitivo, então considere Baz ou Boo”;fix, “este teste unitário valida o campo errado”;chat, “esta é uma decisão grande que vai definir o formato das soluções dessa categoria daqui para frente, então vamos discutir primeiro com a equipe”Alguns prefixos significam que o PR fica bloqueado até a correção, enquanto outros indicam que o comentário pode ser aceito ou não. Para o autor, fica claro o que é algo em que “precisamos chegar ao mesmo entendimento” e o que é apenas uma “preferência de expressão” ou uma “observação”
Porém, se você deixou um
nite a outra pessoa não concordou e ignorou, não deve se sentir ofendido. Se você se sentia fortemente sobre aquilo, então não deveria ter sido umnit