Cómo implementar un analizador de código estático en un proyecto heredado sin desmotivar al equipo

Cómo implementar un analizador de código estático en un proyecto heredado sin desmotivar al equipo
Es fácil probar un analizador de código estático. Pero implementarlo, especialmente en el desarrollo de un gran proyecto antiguo, requiere habilidad. Si se hace incorrectamente, el analizador puede agregar trabajo, ralentizar el desarrollo y desmotivar al equipo. Hablemos brevemente sobre cómo abordar adecuadamente la integración del análisis estático en el proceso de desarrollo y comenzar a usarlo como parte de CI/CD.

introducción

Recientemente me llamó la atención la publicación "Introducción al análisis estático sin abrumar al equipo". Por un lado, este es un buen artículo con el que vale la pena familiarizarse. Por otro lado, me parece que todavía no proporciona una respuesta completa sobre cómo implementar sin problemas el análisis estático en un proyecto con mucho del código heredado. El artículo dice que puede aceptar deuda técnica y trabajar solo en código nuevo, pero no hay respuesta sobre qué hacer con esta deuda técnica más adelante.

Nuestro equipo de PVS-Studio ofrece su opinión sobre este tema. Veamos en primer lugar cómo surge el problema de implementar un analizador de código estático, cómo superar este problema y cómo eliminar gradualmente y sin problemas la deuda técnica.

Problemas

Por lo general, no es difícil iniciar y ver cómo funciona un analizador estático [1]. Es posible que vea errores interesantes o incluso vulnerabilidades potenciales aterradoras en el código. Incluso puedes arreglar algo, pero luego muchos programadores se dan por vencidos.

Todos los analizadores estáticos producen falsos positivos. Esta es una característica de la metodología de análisis de código estático y no se puede hacer nada al respecto. En el caso general, este es un problema irresoluble, como lo confirma el teorema de Rice [2]. Los algoritmos de aprendizaje automático tampoco ayudarán [3]. Incluso si una persona no siempre puede saber si este o aquel código es incorrecto, entonces no debe esperar esto del programa :).

Los falsos positivos no son un problema si el analizador estático ya está configurado:

  • Conjuntos de reglas irrelevantes deshabilitados;
  • Se han desactivado algunos diagnósticos irrelevantes;
  • Si hablamos de C o C++, entonces las macros están marcadas y contienen construcciones específicas que provocan que aparezcan advertencias inútiles en cada lugar donde se utilizan dichas macros;
  • Están marcadas funciones propias que realizan acciones similares a las funciones del sistema (su propio análogo memcpy o Printf) [4];
  • Los falsos positivos se desactivan específicamente mediante comentarios;
  • Y así sucesivamente.

En este caso, podemos esperar una tasa baja de falsos positivos, de alrededor del 10-15% [5]. En otras palabras, 9 de cada 10 advertencias del analizador indicarán un problema real en el código, o al menos "código con olor fuerte". De acuerdo, este escenario es extremadamente agradable y el analizador es un verdadero amigo del programador.

Cómo implementar un analizador de código estático en un proyecto heredado sin desmotivar al equipo
En realidad, en un proyecto grande, la imagen inicial será completamente diferente. El analizador emite cientos o miles de advertencias para código heredado. Es imposible entender rápidamente cuáles de estas advertencias son relevantes y cuáles no. Es irracional sentarse y empezar a afrontar todas estas advertencias, ya que el trabajo principal en este caso se detendrá durante días o semanas. Normalmente, un equipo no puede permitirse semejante escenario. También habrá una gran cantidad de diferencias que arruinarán el historial de cambios. Y la rápida edición masiva de tantos fragmentos del código inevitablemente dará como resultado nuevos errores tipográficos y errores.

Y lo más importante es que tal hazaña en la lucha contra las advertencias no tiene mucho sentido. Esté de acuerdo en que, dado que el proyecto ha estado funcionando con éxito durante muchos años, la mayoría de los errores críticos ya se han corregido. Sí, estas correcciones eran muy costosas, había que depurarlas, recibían comentarios negativos de los usuarios sobre los errores, etc. Un analizador estático ayudaría a corregir muchos de estos errores en la etapa de codificación, de forma rápida y económica. Pero por el momento, de una forma u otra, estos errores se han solucionado y el analizador detecta principalmente errores no críticos en el código antiguo. Este código no se puede utilizar, se puede utilizar muy raramente y un error en él puede no tener consecuencias perceptibles. Quizás en algún lugar la sombra del botón sea del color incorrecto, pero esto no interfiere con el uso del producto por parte de nadie.

Por supuesto, incluso los errores menores siguen siendo errores. Y a veces un error puede ocultar una vulnerabilidad real. Sin embargo, renunciar a todo y pasar días o semanas lidiando con defectos que apenas se manifiestan parece una idea dudosa.

Los programadores miran, miran, miran todas estas advertencias sobre el antiguo código de trabajo... Y piensan: podemos prescindir del análisis estático. Vamos a escribir alguna nueva funcionalidad útil.

A su manera, tienen razón. Se dan cuenta de que primero tienen que deshacerse de alguna manera de todas estas advertencias. Sólo entonces podrán beneficiarse del uso regular del analizador de código. De lo contrario, las nuevas advertencias simplemente se ahogarán en las antiguas y nadie les prestará atención.

Esta es la misma analogía que con las advertencias del compilador. No en vano recomiendan mantener el número de advertencias del compilador en 0. Si hay 1000 advertencias, cuando haya 1001, nadie le prestará atención y no está claro dónde buscar esta advertencia más nueva.

Cómo implementar un analizador de código estático en un proyecto heredado sin desmotivar al equipo
Lo peor de esta historia es si alguien de arriba en este momento te obliga a utilizar el análisis de código estático. Esto sólo desmotivará al equipo, ya que desde su punto de vista habrá una complejidad burocrática adicional que sólo estorbará. Nadie mirará los informes del analizador y todo su uso se realizará únicamente "en papel". Aquellos. Formalmente, el análisis está integrado en el proceso DevOps, pero en la práctica esto no beneficia a nadie. Escuchamos historias detalladas en los stands de los asistentes a la conferencia. Una experiencia así puede disuadir a los programadores de utilizar herramientas de análisis estático durante mucho tiempo, si no para siempre.

Implementación y eliminación de deuda técnica.

De hecho, no hay nada difícil ni aterrador en introducir el análisis estático incluso en un proyecto antiguo de gran tamaño.

CI / CD

Además, el analizador puede formar parte inmediatamente del proceso de desarrollo continuo. Por ejemplo, la distribución PVS-Studio contiene utilidades para ver cómodamente el informe en el formato que necesita y notificaciones a los desarrolladores que escribieron secciones problemáticas del código. Para aquellos que estén más interesados ​​en ejecutar PVS-Studio desde sistemas CI/CD, les recomiendo que se familiaricen con el correspondiente sección documentación y una serie de artículos:

Pero volvamos a la cuestión de la gran cantidad de falsos positivos en las primeras etapas de implementación de herramientas de análisis de código.

Arreglar la deuda técnica existente y afrontar nuevas advertencias

Los analizadores estáticos comerciales modernos le permiten estudiar solo las advertencias nuevas que aparecen en código nuevo o modificado. La implementación de este mecanismo varía, pero la esencia es la misma. En el analizador estático PVS-Studio, esta funcionalidad se implementa de la siguiente manera.

Para comenzar a utilizar rápidamente el análisis estático, sugerimos a los usuarios de PVS-Studio que utilicen el mecanismo de supresión masiva de advertencias [6]. La idea general es la siguiente. El usuario inició el analizador y recibió muchas advertencias. Dado que un proyecto que ha estado en desarrollo durante muchos años está vivo, desarrollándose y generando dinero, lo más probable es que no haya muchas advertencias en el informe que indiquen defectos críticos. En otras palabras, los errores críticos ya se han solucionado de una forma u otra mediante métodos más costosos o gracias a los comentarios de los clientes. En consecuencia, todo lo que el analizador encuentra actualmente puede considerarse deuda técnica, que no es práctico intentar eliminar de inmediato.

Puede decirle a PVS-Studio que considere estas advertencias irrelevantes por ahora (guarde la deuda técnica para más adelante) y ya no las mostrará. El analizador crea un archivo especial donde guarda información sobre errores que aún no son interesantes. Y ahora PVS-Studio emitirá advertencias sólo para códigos nuevos o modificados. Además, todo esto se implementa de forma inteligente. Si, por ejemplo, se agrega una línea vacía al principio del archivo de código fuente, entonces el analizador comprende que, de hecho, nada ha cambiado y seguirá en silencio. Este archivo de marcado se puede colocar en un sistema de control de versiones. El archivo es grande, pero esto no es un problema, ya que no tiene sentido almacenarlo con frecuencia.

Ahora todos los programadores verán advertencias relacionadas únicamente con el código nuevo o modificado. Así, podrás empezar a utilizar el analizador, como suele decirse, a partir del día siguiente. Y podrá volver a la deuda técnica más adelante y corregir errores y configurar gradualmente el analizador.

Así, se ha resuelto el primer problema con la implementación del analizador en un gran proyecto antiguo. Ahora averigüemos qué hacer con la deuda técnica.

Corrección de errores y refactorizaciones

Lo más sencillo y natural es dedicar algo de tiempo a analizar las advertencias del analizador suprimidas masivamente y abordarlas gradualmente. En algún lugar deberías corregir los errores en el código, en algún lugar deberías refactorizar para decirle al analizador que el código no es problemático. Ejemplo sencillo:

if (a = b)

La mayoría de los compiladores y analizadores de C++ se quejan de este tipo de código, ya que existe una alta probabilidad de que realmente quisieran escribirlo. (a == b). Pero existe un acuerdo tácito, y esto generalmente se indica en la documentación, de que si hay paréntesis adicionales, entonces se considera que el programador escribió deliberadamente dicho código y no hay necesidad de jurar. Por ejemplo, en la documentación de diagnóstico de PVS-Studio V559 (CWE-481) está claramente escrito que la siguiente línea se considerará correcta y segura:

if ((a = b))

Otro ejemplo. ¿Se olvida en este código C++? romper o no?

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

El analizador PVS-Studio emitirá una advertencia aquí V796 (CWE-484). Es posible que esto no sea un error, en cuyo caso debe darle una pista al analizador agregando el atributo [[caer a través]] o por ejemplo __atributo__((fallthrough)):

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

Se puede decir que dichos cambios de código no solucionan el error. Sí, esto es cierto, pero hace dos cosas útiles. En primer lugar, el informe del analizador elimina los falsos positivos. En segundo lugar, el código se vuelve más comprensible para las personas involucradas en su mantenimiento. ¡Y esto es muy importante! Sólo por esto, vale la pena realizar refactorizaciones menores para que el código sea más claro y más fácil de mantener. Dado que el analizador no comprende si es necesaria una "descanso" o no, tampoco quedará claro para los compañeros programadores.

Además de las correcciones de errores y las refactorizaciones, puede suprimir específicamente las advertencias del analizador obviamente falsas. Se pueden desactivar algunos diagnósticos irrelevantes. Por ejemplo, alguien piensa que las advertencias no tienen sentido. V550 sobre comparar valores flotantes/dobles. Y algunos los clasifican como importantes y dignos de estudio [7]. Qué advertencias se consideran relevantes y cuáles no, depende del equipo de desarrollo decidir.

Existen otras formas de suprimir las alertas falsas. Por ejemplo, el marcado macro se mencionó anteriormente. Todo esto se describe con más detalle en la documentación. Lo más importante es comprender que si se aborda gradual y sistemáticamente el trabajo con falsos positivos, no tienen nada de malo. La gran mayoría de las advertencias poco interesantes desaparecen después de la configuración, y solo quedan aquellas que realmente requieren un estudio cuidadoso y algunos cambios en el código.

Además, siempre ayudamos a nuestros clientes a configurar PVS-Studio si surge alguna dificultad. Además, hubo casos en los que nosotros mismos eliminamos las advertencias falsas y corregimos errores [8]. Por si acaso, decidí mencionar que esta opción de cooperación ampliada también es posible :).

método de trinquete

Existe otro enfoque interesante para mejorar gradualmente la calidad del código eliminando la advertencia del analizador estático. La conclusión es que el número de advertencias sólo puede disminuir.

Cómo implementar un analizador de código estático en un proyecto heredado sin desmotivar al equipo

Se registra el número de advertencias emitidas por el analizador estático. Quality Gate está configurado de tal manera que ahora solo se puede ingresar un código que no aumenta el número de operaciones. Como resultado, el proceso de reducción gradual del número de alarmas comienza ajustando el analizador y corrigiendo errores.

Incluso si una persona quiere hacer un poco de trampa y decide pasar la puerta de calidad no eliminando las advertencias en su nuevo código, sino mejorando el antiguo código de terceros, esto no da miedo. De todos modos, el trinquete gira en una dirección y gradualmente el número de defectos irá disminuyendo. Incluso si una persona no quiere corregir sus propios defectos nuevos, todavía tendrá que mejorar algo en el código vecino. En algún momento, las formas sencillas de reducir la cantidad de advertencias terminan y llega un momento en el que se solucionan errores reales.

Esta metodología se describe con más detalle en un artículo muy interesante de Ivan Ponomarev "Implemente el análisis estático en el proceso, en lugar de buscar errores con él", que recomiendo leer a cualquiera interesado en mejorar la calidad del código.

El autor del artículo también tiene un informe sobre este tema: "Análisis estático continuo".

Conclusión

Espero que después de este artículo, los lectores acepten mejor las herramientas de análisis estático y quieran implementarlas en el proceso de desarrollo. Si tienes alguna pregunta, siempre estamos listos. consultar usuarios de nuestro analizador estático PVS-Studio y ayudar con su implementación.

Existen otras dudas típicas sobre si el análisis estático puede ser realmente conveniente y útil. Intenté disipar la mayoría de estas dudas en la publicación “Razones para introducir el analizador de código estático PVS-Studio en el proceso de desarrollo” [9].

Gracias por su atención y venga. descargar y pruebe el analizador PVS-Studio.

Enlaces adicionales

  1. Andréi Kárpov. ¿Cómo puedo ver rápidamente las advertencias interesantes que produce el analizador PVS-Studio para código C y C++?
  2. Wikipedia. teorema de arroz.
  3. Andrey Karpov, Victoria Khanieva. Uso del aprendizaje automático en el análisis estático del código fuente del programa.
  4. Estudio PVS. Documentación. Configuraciones de diagnóstico adicionales.
  5. Andréi Kárpov. Características del analizador PVS-Studio usando el ejemplo de EFL Core Libraries, 10-15% de falsos positivos.
  6. Estudio PVS. Documentación. Supresión masiva de mensajes del analizador..
  7. Iván Andriashin. Acerca de cómo probamos el análisis estático en nuestro proyecto de un simulador educativo de cirugía endovascular de rayos X.
  8. Pavel Eremeev, Sviatoslav Razmyslov. Cómo el equipo de PVS-Studio mejoró el código de Unreal Engine.
  9. Andréi Kárpov. Razones para introducir el analizador de código estático PVS-Studio en el proceso de desarrollo.

Cómo implementar un analizador de código estático en un proyecto heredado sin desmotivar al equipo

Si desea compartir este artículo con una audiencia de habla inglesa, utilice el enlace de traducción: Andrey Karpov. Cómo introducir un analizador de código estático en un proyecto heredado y no desanimar al equipo.

Fuente: habr.com

Añadir un comentario