Como implementar um analisador de código estático em um projeto legado sem desmotivar a equipe

Como implementar um analisador de código estático em um projeto legado sem desmotivar a equipe
É fácil experimentar um analisador de código estático. Mas implementá-lo, especialmente no desenvolvimento de um grande projeto antigo, requer habilidade. Se feito incorretamente, o analisador pode agregar trabalho, retardar o desenvolvimento e desmotivar a equipe. Vamos falar brevemente sobre como abordar adequadamente a integração da análise estática no processo de desenvolvimento e começar a usá-la como parte do CI/CD.

Introdução

Recentemente minha atenção foi atraída para a publicação "Introdução à análise estática sem sobrecarregar a equipe". Por um lado, este é um bom artigo que vale a pena conhecer. Por outro lado, parece-me que ainda não fornece uma resposta completa sobre como implementar sem dor a análise estática em um projeto com muito do código legado. O artigo diz que você pode aceitar dívida técnica e trabalhar apenas em novo código, mas não há resposta sobre o que fazer com essa dívida técnica posteriormente.

Nossa equipe PVS-Studio oferece sua visão sobre este tema. Vejamos como surge o problema de implementar um analisador de código estático, como superar esse problema e como eliminar gradualmente a dívida técnica de maneira indolor.

Problemas

Geralmente não é difícil iniciar e ver como funciona um analisador estático [1]. Você pode ver erros interessantes ou até mesmo vulnerabilidades potenciais assustadoras no código. Você pode até consertar alguma coisa, mas muitos programadores desistem.

Todos os analisadores estáticos produzem falsos positivos. Esse é um recurso da metodologia de análise estática de código e nada pode ser feito a respeito. No caso geral, este é um problema insolúvel, como confirmado pelo teorema de Rice [2]. Algoritmos de aprendizado de máquina também não ajudarão [3]. Mesmo que uma pessoa nem sempre saiba se este ou aquele código está errado, você não deve esperar isso do programa :).

Os falsos positivos não são um problema se o analisador estático já estiver configurado:

  • Conjuntos de regras irrelevantes desativados;
  • Alguns diagnósticos irrelevantes foram desativados;
  • Se estamos falando de C ou C++, então são marcadas macros que contêm construções específicas que fazem com que avisos inúteis apareçam em todos os locais onde tais macros são usadas;
  • São marcadas funções próprias que executam ações semelhantes às funções do sistema (seu próprio analógico memcpy ou printf) [4];
  • Os falsos positivos são especificamente desativados por meio de comentários;
  • E assim por diante.

Neste caso, podemos esperar uma baixa taxa de falsos positivos de cerca de 10-15% [5]. Em outras palavras, 9 em cada 10 avisos do analisador indicarão um problema real no código, ou pelo menos “código com cheiro forte”. Concordo, este cenário é extremamente agradável e o analisador é um verdadeiro amigo do programador.

Como implementar um analisador de código estático em um projeto legado sem desmotivar a equipe
Na verdade, em um projeto grande, o quadro inicial será completamente diferente. O analisador emite centenas ou milhares de avisos para código legado. É impossível compreender rapidamente quais destes avisos são relevantes e quais não são. É irracional sentar e começar a lidar com todos esses avisos, pois o trabalho principal neste caso ficará parado por dias ou semanas. Normalmente, uma equipe não pode permitir tal cenário. Haverá também um grande número de diferenças que estragam o histórico de alterações. E a rápida edição em massa de tantos fragmentos no código resultará inevitavelmente em novos erros de digitação e erros.

E o mais importante, tal feito na luta contra os avisos faz pouco sentido. Concorde que, como o projeto está sendo executado com sucesso há muitos anos, a maioria dos erros críticos já foram corrigidos. Sim, essas correções eram muito caras, precisavam ser depuradas, recebiam feedback negativo dos usuários sobre bugs e assim por diante. Um analisador estático ajudaria a corrigir muitos desses erros na fase de codificação, de forma rápida e barata. Mas no momento, de uma forma ou de outra, esses erros foram corrigidos e o analisador detecta principalmente erros não críticos no código antigo. Este código não pode ser usado, pode ser usado muito raramente e um erro nele pode não levar a consequências visíveis. Talvez em algum lugar a sombra do botão tenha a cor errada, mas isso não atrapalha o uso do produto por ninguém.

É claro que mesmo os pequenos erros ainda são erros. E às vezes um erro pode esconder uma vulnerabilidade real. Porém, desistir de tudo e passar dias/semanas lidando com defeitos que mal se manifestam parece uma ideia duvidosa.

Os programadores olham, olham, olham todos esses avisos sobre o antigo código funcional... E eles pensam: podemos passar sem análise estática. Vamos escrever algumas novas funcionalidades úteis.

À sua maneira, eles estão certos. Eles imaginam que primeiro precisam, de alguma forma, se livrar de todos esses avisos. Só então eles poderão se beneficiar do uso regular do analisador de código. Caso contrário, os novos avisos simplesmente se afogarão nos antigos e ninguém prestará atenção a eles.

Esta é a mesma analogia dos avisos do compilador. Não é à toa que eles recomendam manter o número de avisos do compilador em 0. Se houver 1000 avisos, quando houver 1001, ninguém prestará atenção a isso e não está claro onde procurar esse aviso mais recente.

Como implementar um analisador de código estático em um projeto legado sem desmotivar a equipe
A pior coisa nesta história é se alguém de cima neste momento forçar você a usar a análise estática de código. Isso só vai desmotivar a equipe, pois do ponto de vista deles haverá uma complexidade burocrática adicional que só atrapalha. Ninguém olhará os relatórios do analisador e todo o uso será apenas “no papel”. Aqueles. Formalmente, a análise está integrada ao processo DevOps, mas na prática isso não beneficia ninguém. Ouvimos histórias detalhadas nos estandes dos participantes da conferência. Tal experiência pode desencorajar os programadores de usar ferramentas de análise estática por um longo tempo, se não para sempre.

Implementando e eliminando dívida técnica

Na verdade, não há nada difícil ou assustador em introduzir a análise estática, mesmo em um grande projeto antigo.

CI / CD

Além disso, o analisador pode imediatamente fazer parte do processo de desenvolvimento contínuo. Por exemplo, a distribuição PVS-Studio contém utilitários para visualizar convenientemente o relatório no formato que você precisa e notificações para desenvolvedores que escreveram seções problemáticas do código. Para aqueles que estão mais interessados ​​em lançar o PVS-Studio a partir de sistemas CI/CD, recomendo que você se familiarize com o correspondente seção documentação e uma série de artigos:

Mas voltemos à questão do grande número de falsos positivos nos primeiros estágios da implementação de ferramentas de análise de código.

Corrigindo a dívida técnica existente e lidando com novos avisos

Os analisadores estáticos comerciais modernos permitem estudar apenas novos avisos que aparecem em códigos novos ou alterados. A implementação deste mecanismo varia, mas a essência é a mesma. No analisador estático PVS-Studio, esta funcionalidade é implementada da seguinte forma.

Para começar a usar a análise estática rapidamente, sugerimos que os usuários do PVS-Studio usem o mecanismo de supressão em massa de avisos [6]. A ideia geral é a seguinte. O usuário iniciou o analisador e recebeu muitos avisos. Como um projeto que está em desenvolvimento há muitos anos está vivo, em desenvolvimento e ganhando dinheiro, provavelmente não haverá muitos avisos no relatório indicando defeitos críticos. Em outras palavras, bugs críticos já foram corrigidos de uma forma ou de outra, usando métodos mais caros ou graças ao feedback dos clientes. Assim, tudo o que o analisador encontra atualmente pode ser considerado dívida técnica, o que é impraticável de tentar eliminar imediatamente.

Você pode dizer ao PVS-Studio para considerar esses avisos irrelevantes por enquanto (guardar dívidas técnicas para mais tarde), e ele não os mostrará mais. O analisador cria um arquivo especial onde salva informações sobre erros que ainda não são interessantes. E agora o PVS-Studio emitirá avisos apenas para códigos novos ou alterados. Além disso, tudo isso é implementado de forma inteligente. Se, por exemplo, uma linha vazia for adicionada ao início do arquivo de código-fonte, o analisador entenderá que, de fato, nada mudou e continuará silencioso. Este arquivo de marcação pode ser colocado em um sistema de controle de versão. O arquivo é grande, mas isso não é problema, pois não adianta armazená-lo com frequência.

Agora todos os programadores verão avisos relacionados apenas a códigos novos ou alterados. Assim, você pode começar a usar o analisador, como dizem, a partir do dia seguinte. E você pode retornar ao débito técnico mais tarde, corrigir gradualmente os erros e configurar o analisador.

Assim, o primeiro problema com a implementação do analisador em um grande projeto antigo foi resolvido. Agora vamos descobrir o que fazer com a dívida técnica.

Correções de bugs e refatorações

A coisa mais simples e natural é reservar algum tempo para analisar os avisos do analisador massivamente suprimidos e lidar gradualmente com eles. Em algum lugar você deve corrigir erros no código, em algum lugar você deve refatorar para informar ao analisador que o código não é problemático. Exemplo simples:

if (a = b)

A maioria dos compiladores e analisadores C++ reclamam desse tipo de código, pois há uma grande probabilidade de que eles realmente quisessem escrever (uma == b). Mas há um acordo tácito, e isso geralmente é observado na documentação, de que, se houver parênteses adicionais, considera-se que o programador escreveu deliberadamente esse código e não há necessidade de xingar. Por exemplo, na documentação do PVS-Studio para diagnóstico V559 (CWE-481) está claramente escrito que a seguinte linha será considerada correta e segura:

if ((a = b))

Outro exemplo. Está esquecido neste código C++? quebrar ou não?

case A:
  foo();
case B:
  bar();
  break;

O analisador PVS-Studio emitirá um aviso aqui V796 (CWE-484). Isso pode não ser um erro; nesse caso, você deve dar uma dica ao analisador adicionando o atributo [[Cair em]] ou por exemplo __attribute__((fallthrough)):

case A:
  foo();
  [[fallthrough]];
case B:
  bar();
  break;

Pode-se dizer que tais alterações de código não corrigem o bug. Sim, isso é verdade, mas faz duas coisas úteis. Em primeiro lugar, o relatório do analisador elimina falsos positivos. Em segundo lugar, o código torna-se mais compreensível para as pessoas envolvidas na sua manutenção. E isso é muito importante! Só por isso, vale a pena realizar pequenas refatorações para tornar o código mais claro e fácil de manter. Como o analisador não entende se a “pausa” é necessária ou não, também não ficará claro para outros programadores.

Além de correções de bugs e refatorações, você pode suprimir especificamente avisos obviamente falsos do analisador. Alguns diagnósticos irrelevantes podem ser desativados. Por exemplo, alguém pensa que os avisos são inúteis V550 sobre como comparar valores float/double. E alguns os classificam como importantes e dignos de estudo [7]. Quais avisos são considerados relevantes e quais não são, cabe à equipe de desenvolvimento decidir.

Existem outras maneiras de suprimir alertas falsos. Por exemplo, a marcação macro foi mencionada anteriormente. Tudo isso é descrito com mais detalhes na documentação. O mais importante é entender que se você abordar gradual e sistematicamente o trabalho com falsos positivos, não há nada de errado com eles. A grande maioria dos avisos desinteressantes desaparecem após a configuração, permanecendo apenas os locais que realmente exigem um estudo cuidadoso e algumas alterações no código.

Além disso, sempre ajudamos nossos clientes a montar o PVS-Studio caso surja alguma dificuldade. Além disso, houve casos em que nós mesmos eliminamos avisos falsos e corrigimos erros [8]. Por precaução, decidi mencionar que esta opção de cooperação alargada também é possível :).

Método de catraca

Existe outra abordagem interessante para melhorar gradualmente a qualidade do código, eliminando o aviso do analisador estático. O resultado final é que o número de avisos só pode diminuir.

Como implementar um analisador de código estático em um projeto legado sem desmotivar a equipe

O número de avisos emitidos pelo analisador estático é registrado. O portão de qualidade está configurado de tal forma que agora você só pode inserir um código que não aumente o número de operações. Como resultado, o processo de redução gradual do número de alarmes começa com o ajuste do analisador e a correção de erros.

Mesmo que uma pessoa queira trapacear um pouco e decida passar pelo portão de qualidade não eliminando avisos em seu novo código, mas melhorando o antigo código de terceiros, isso não é assustador. Mesmo assim, a catraca gira em uma direção e gradualmente o número de defeitos diminuirá. Mesmo que uma pessoa não queira consertar seus próprios novos defeitos, ela ainda terá que melhorar algo no código vizinho. Em algum momento, as maneiras fáceis de reduzir o número de avisos terminam e chega um ponto em que bugs reais serão corrigidos.

Esta metodologia é descrita com mais detalhes em um artigo muito interessante de Ivan Ponomarev "Implemente análise estática no processo, em vez de usá-la para encontrar bugs", que recomendo a leitura para qualquer pessoa interessada em melhorar a qualidade do código.

O autor do artigo também traz uma reportagem sobre o tema: “Análise estática contínua".

Conclusão

Espero que, após este artigo, os leitores aceitem mais as ferramentas de análise estática e queiram implementá-las no processo de desenvolvimento. Se você tiver alguma dúvida, estamos sempre prontos conselho usuários do nosso analisador estático PVS-Studio e ajudar na sua implementação.

Existem outras dúvidas típicas sobre se a análise estática pode realmente ser conveniente e útil. Tentei dissipar a maioria dessas dúvidas na publicação “Razões para introduzir o analisador de código estático PVS-Studio no processo de desenvolvimento” [9].

Obrigado pela atenção e venha baixar e experimente o analisador PVS-Studio.

Links adicionais

  1. Andrei Karpov. Como posso ver rapidamente avisos interessantes que o analisador PVS-Studio produz para código C e C++?
  2. Wikipedia. Teorema de arroz.
  3. Andrey Karpov, Victoria Khanieva. Usando aprendizado de máquina na análise estática do código-fonte do programa.
  4. Estúdio PVS. Documentação. Configurações adicionais de diagnóstico.
  5. Andrei Karpov. Características do analisador PVS-Studio usando o exemplo de EFL Core Libraries, 10-15% de falsos positivos.
  6. Estúdio PVS. Documentação. Supressão em massa de mensagens do analisador.
  7. Ivan Andryashin. Sobre como testamos a análise estática em nosso projeto de simulador educacional de cirurgia endovascular de raios X.
  8. Pavel Eremeev, Svyatoslav Razmyslov. Como a equipe do PVS-Studio melhorou o código do Unreal Engine.
  9. Andrei Karpov. Razões para introduzir o analisador de código estático PVS-Studio no processo de desenvolvimento.

Como implementar um analisador de código estático em um projeto legado sem desmotivar a equipe

Se você quiser compartilhar este artigo com um público que fala inglês, use o link de tradução: Andrey Karpov. Como introduzir um analisador de código estático em um projeto legado e não desanimar a equipe.

Fonte: habr.com

Adicionar um comentário