blog conviso

Code Comprehension e seu papel na revisão de código

February 3, 2022

By

Conviso

Application Security

Revisão de Código

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

  1. Expectations, Outcomes, and Challenges Of Modern Code Review
  2. Helping Developers Help Themselves: Automatic Decomposition of Code Review Changesets
  3. Why Security Defects Go Unnoticed during Code Reviews? A Case-Control Study of the Chromium OS Project
  4. Associating Working Memory Capacity and Code Change Ordering with Code Review Performance
  5. Cognitive-Support Code Review Tools
  6. Code Comprehension: O que é?

Autores deste artigo:
Gabriel Quadros – Sênior Information Security Analyst
Ricardo Silva – Information Security Analyst

Nova call to action

Sobre o autor

Conviso
Application Security

Bachelor's degree in Computer Science from the Federal University of Alagoas. He has spoken at events such as the NullByte Security Conference and Hackers to Hackers Conference, and presented research projects at national and international events. He has experience in application security, exploit development, and vulnerability research in both userland and kernel land, having discovered vulnerabilities for companies like Microsoft, AMD, and Intel. His interests include operating system internals, compilers, malware development, vulnerability research, and EDR evasion.

Saiba mais