No post anterior, cobrimos o que é Code Comprehension e falamos sobre alguns exemplos de sua presença no ciclo de vida de desenvolvimento de software. Agora vamos falar sobre o processo de revisão de código, suas motivações, desafios e como a pesquisa de Code Comprehension pode ser útil.
Revisão de código moderna
A revisão de código por pares é uma prática difundida de engenharia de software que é realizada com diferentes motivações. Normalmente é usada para localizar defeitos no código, mas também pode ser usada para outros fins, como transferência de conhecimento e refatoração.
Ela pode ser executada em vários estágios de um projeto de software e de várias formas. Por exemplo, pode-se escolher revisar apenas uma parte do código em que está interessado ou fazer uma revisão completa do código-fonte. Pode-se esperar para revisar quando o software ou algum componente estiver concluído ou revisá-lo continuamente, a cada commit ou pull request.
O artigo de Bacchelli e Bird [1] descreve revisão de código moderna como “(1) informal (em contraste com o estilo Fagan), (2) baseado em ferramentas e que (3) ocorre regularmente na prática hoje em dia, por exemplo, em empresas como Microsoft, Google, Facebook e em outras empresas e projetos de software open-source”. Este tipo de revisão é comumente praticada hoje em dia, seja pelos próprios desenvolvedores, seja por profissionais de garantia de qualidade ou de segurança. Uma vez que uma nova mudança é enviada para o repositório de origem, ela se torna disponível para revisões e este processo geralmente é apoiado por uma ferramenta que pode exibir as mudanças em um formato agradável e permitir que o revisor insira comentários.
Motivações e obstáculos em revisão de códig
No artigo “Expectations, Outcomes, and Challenges Of Modern Code Review” [1], os autores realizaram um estudo usando a análise de estudos anteriores, observações, entrevistas e questionários. Desenvolvedores, revisores de código e gerentes forneceram alguns dados que ajudaram os autores a mapear as motivações, expectativas e os desafios da revisão de código moderna.
De acordo com este estudo, as motivações mais importantes para revisão de código são:
- Encontro de defeitos - para achar bugs;
- Melhoria de código - para melhorar a consistência do código, legibilidade, etc.;
- Soluções alternativas - para encontrar uma melhor implementação;
- Transferência de conhecimento - para fins de aprendizagem;
- Conscientização e transparência da equipe - para tornar a equipe ciente da evolução do código e tornar as alterações do código transparentes.
- Compartilhamento de posse do código - relacionado a “Conscientização e Transparência da Equipe”, mas com conotação de colaboração.
Os resultados mostraram que nem sempre as expectativas são atendidas. Comentários sobre bugs (encontrar bugs foi uma das principais motivações) foram poucos e superficiais. Os desenvolvedores e gerentes esperavam comentários mais aprofundados sobre questões conceituais e de design.
Durante este estudo, muitos comentários apareceram sobre os desafios na revisão do código. O principal fator para uma revisão de código eficaz é a compreensão. Mesmo quando há pequenas mudanças na base do código, o revisor precisa ler outros trechos para obter todo o contexto necessário para entender o que está acontecendo. Leva tempo para entender o código com o qual você não está familiarizado, mas uma vez que o entendimento é adquirido, acreditamos que o resultado da revisão pode ser mais produtivo em termos de tempo gasto no processo e na qualidade do feedback.
Outro estudo [6] investigou questões relacionadas à compreensão das mudanças no código. Os autores queriam saber em quais cenários a compreensão do código é necessária, a frequência desse processo, quais informações são necessárias e o que pode ser feito para melhorar a eficácia e eficiência para entender as alterações do código.
Este artigo listou que a compreensão do código é necessária para:
- Revisar as mudanças de outras pessoas;
- Corrigir bugs;
- Desenvolver nova funcionalidade;
- Revisar suas próprias mudanças;
- Escrever / atualizar casos de teste;
- Refatoração;
- Resolver conflitos de "merge".
Sobre a frequência, demonstrou que a maioria dos participantes precisava entender as mudanças de código diariamente.
De acordo com os dados coletados, as 3 principais informações para compreender as mudanças no código são:
- Justificativa - Qual é a razão por trás dessa mudança de código?
- Exatidão - Esta alteração está correta? Funciona como esperado?
- Risco - Essa mudança quebra algum código em outro lugar? Como?
E as informações mais difíceis de adquirir também foram coletadas. As 3 principais são:
- Consistência - Existem outros lugares que precisam de mudanças semelhantes?
- Risco - Essa mudança quebra algum código em outro lugar? Como?
- Comportamento - Como essa mudança altera o comportamento dinâmico do programa?
Sobre o que poderia ser feito para melhorar a eficácia e eficiência para entender as mudanças de código, os autores mapearam duas abordagens:
- Determinar o risco de uma mudança - De acordo com a maioria dos participantes, testes (por exemplo, testes unitários) e revisão do código são as duas práticas principais para determinar o risco de uma mudança. Duas funcionalidades foram propostas para melhorar estas práticas: 1) uma funcionalidade para detectar o código e os casos de teste afetados pela mudança e também notificar o responsável pelo teste sobre a necessidade de um reteste; 2) uma funcionalidade para permitir análise estática (exemplo, ir para definição) na tela de diff.
- Decompor uma mudança - Às vezes uma mudança é grande e afeta muitos arquivos; em outras, a mudança é a implementação de muitas funcionalidades ou correções de bugs que não necessariamente relacionados. Isso torna o entendimento do código mais difícil. Para mitigar isso, uma funcionalidade foi proposta para decompor uma mudança em outras menores, agrupadas por seu relacionamento.
Desafios
Em ambos os artigos [1] e [6], podemos extrair comentários que ilustram alguns dos desafios enfrentados por quem está fazendo revisão de código:
“A coisa mais difícil ao fazer uma revisão de código é entender o motivo da mudança” [1]
“A análise de impacto global requer compreensão contextual. Ao revisar uma mudança pequena e desconhecida, muitas vezes é necessário ler muito mais código do que o que está sendo revisado. ” [1]
“Leva muito mais tempo para entender código desconhecido, mas mesmo assim o entendimento não é muito profundo.” [1]
“Normalmente, eu precisaria encontrar manualmente as partes do código que estão usando a parte alterada e descobrir como essa mudança afeta as chamadas. Às vezes não é óbvio pelo próprio código, eu tenho que realmente percorrer o código com o depurador para entendê-la. ” [6]
“É difícil avaliar os impactos em outros componentes, a menos que haja uma interface clara entre este e outros. Muito frequentemente, outros componentes têm algumas suposições sobre este componente, embora essas suposições não estejam documentadas. ” [6]
A revisão de código é uma tarefa demorada e requer algum contexto sobre o código que está sendo revisado. Mesmo para pequenas alterações, o revisor precisa ler mais código, que geralmente estão distribuídos em muitos arquivos. Às vezes, uma mudança de código é grande e não relacionada (por exemplo, diferentes correções de bugs no mesmo commit). Além dos principais desafios extraídos dos artigos, adicionamos a falta de documentação, comentários pouco claros em mensagens de commit e estilo de código inconsistente como outros obstáculos para uma revisão de código mais produtiva.
Como Code Comprehension ajuda?
Como vimos na seção anterior, o entendimento do código é considerado o aspecto mais importante para revisões de código bem-sucedidas. Quando consideramos o processo moderno de revisão de código, isso significa principalmente compreensão da mudança e do contexto. Em um estudo recente [3] sobre por que os defeitos de segurança não são detectados por revisões de código, os pesquisadores descobriram que as vulnerabilidades que não requerem o entendimento de muitas linhas do contexto do código têm uma chance maior de serem detectadas, enquanto as vulnerabilidades que requerem o entendimento de um contexto de código maior têm maiores chances de permanecerem indetectadas.
Conforme descrito no primeiro post desta série [7], os ambientes de desenvolvimento integrado (IDEs) modernos já incorporam e fornecem muitas funcionalidades de suporte à compreensão de código, como visualizações de hierarquia de chamadas, navegação de código, localizador de referências, localizador de definições e visualização dividida. Esse tipo de ferramenta às vezes oferece suporte a atividades de revisão de código e, portanto, essas funcionalidades podem ser usadas para melhorar a compreensão do código. No entanto, é comum que as revisões de código sejam conduzidas em ferramentas Web que podem carecer dessas funcionalidades e fornecer apenas o highlight da sintaxe do diff contendo a alteração do código, o que pode tornar mais difícil para o revisor alcançar a compreensão da mudança e do contexto.
Outras pesquisas vão um passo além e exploram tópicos mais avançados, como decomposição de conjuntos de mudanças para revisão de código [2], relação da carga mental e a capacidade de memória de trabalho com a eficácia da revisão de código [4], influência da ordem em que as mudanças de código são apresentadas ao revisor [4] e redução da carga cognitiva do revisor [5].
Se beneficiando como revisor
Um conselho para os revisores é dar preferência a ferramentas que suportem pelo menos funcionalidades básicas de compreensão de código. Isso ajudará no processo de compreensão do contexto do código e como as alterações afetam a base de código existente. Isso é ainda mais importante em situações em que o revisor tem pouco ou nenhum conhecimento prévio sobre o código envolvido nas mudanças, como é o caso de desenvolvedores revisando o código de um componente no qual não trabalharam ou profissionais de segurança terceirizados revisando as mudanças de código para encontrar vulnerabilidades, por exemplo.
Outro ponto é sobre como preservar o conhecimento obtido durante uma revisão de código e torná-lo acessível para revisões subsequentes. Hoje em dia é comum os revisores apenas deixarem comentários nas telas de revisão de código, mas isso poderia ser melhorado? O que poderia tornar mais fácil para o revisor compreender algumas alterações no código? Isso é algo para se pensar.
Conclusão
Neste post, cobrimos o processo moderno de revisão de código, seus desafios e principais requisitos para revisões bem-sucedidas. Vimos algumas pesquisas [1, 6] que descrevem a compreensão do código como o elemento mais importante nas revisões. Também vimos como os estudos de Code Comprehension se relacionam com essa área.
Este é o segundo post no blog de uma série sobre Code Comprehension. Fique ligado para mais conteúdo sobre o assunto!
Referências
- Expectations, Outcomes, and Challenges Of Modern Code Review
- Helping Developers Help Themselves: Automatic Decomposition of Code Review Changesets
- Why Security Defects Go Unnoticed during Code Reviews? A Case-Control Study of the Chromium OS Project
- Associating Working Memory Capacity and Code Change Ordering with Code Review Performance
- Cognitive-Support Code Review Tools
- Code Comprehension: O que é?
Autores deste artigo:
Gabriel Quadros – Sênior Information Security Analyst
Ricardo Silva – Information Security Analyst
