02. Código Fuente
Este es uno de los activos más preciados de cualquier equipo de desarrollo, porque en el código fuente reside todo el trabajo. De hecho, es el único entregable que cada proyecto siempre necesitará construir. Muchos equipos no lo documentan, ni le hacen pruebas, pero sencillamente sin código no hay software. El código es lo único que los equipos de desarrollo de software siempre tendrán. Esto significa que es un área fundamental a la que prestar atención para controlar la calidad y minimizar los riesgos y costos asociados, sin importar cuáles sean los objetivos y el contexto de la empresa.
Al hablar de código fuente, nos referimos a varias cosas, pero principalmente a la gestión y la calidad del código. Se deben tener en cuenta ambos aspectos para lograr un buen enfoque de testing continuo.
Gestión de código
Dado que el código es un activo tan importante, un riesgo crucial a tener controlado es asegurarse de gestionarlo adecuadamente, así como cualquier modificación que se efectúe. Existen herramientas específicas para esto, creadas para trabajar con un repositorio centralizado donde todos los miembros del equipo pueden almacenarlo de manera confiable.
Las herramientas actuales han evolucionado en términos de enfoque de tareas, pasando de un archivo de texto simple como CVS a otros más sofisticados y flexibles como un sistema Git. Lo que permiten, algunos en mayor o menor medida, es tener el código centralizado. Cada desarrollador puede enviar código, consultarlo, recuperar versiones anteriores (lo cual es útil cuando su código no funciona correctamente y necesita volver a la versión de trabajo anterior), agregar comentarios a cada commit realizado (por ejemplo, comentarios que indican por qué se decidió enviar un código específico al repositorio) y mucho más.
Estas herramientas también van acompañadas de una metodología. En particular, esto se refleja en cómo se administran las branches (ramas) de las versiones (aprobadas, eliminadas, fusionadas, etc.), lo que permite que diferentes personas trabajen en distintas versiones del código. Hay quienes mantienen una rama para cada cliente (si cada uno tiene un código diferente), o una rama para versiones más grandes de código, o en un nivel más fino, donde se abre una nueva rama para cada funcionalidad o corrección que alguien hace.
Calidad del código
Por otro lado, es importante prestar atención a la calidad del código para promover la mantenibilidad. La calidad del código suele ser un atributo interno de la calidad, ya que no es visible para el usuario. Es posible que, aunque el usuario perciba que un software es de alta calidad (fácil de usar, rápido, sin errores críticos, etc.), en realidad puede tener graves problemas de calidad de código. Pero como los usuarios no pueden ver ni analizar el código por sí mismos, nunca lo sabrán. Por el contrario, los factores de calidad como el rendimiento, la confiabilidad y la usabilidad, entre otros, son externos, es decir, los usuarios sí los perciben.
Pero, puede suceder que los factores de calidad internos se transformen en externos. Por ejemplo, cuando se realiza un cambio en el sistema en respuesta a una solicitud del usuario. Si la calidad del código es tan mala que requiere varias semanas modificar el sistema, entonces hay un gran problema de mantenimiento que no solo afecta al equipo de desarrollo, sino también al usuario. Cuanto más tarde en ejecutarse el cambio debido al código incorrecto, la insatisfacción y frustración del usuario aumentará.
La mantenibilidad depende de muchos aspectos. Hay una frase popular que dice: "El código se escribe una vez y se lee muchas veces", lo que significa que debe ser fácil de comprender. Pero esto no solo hace alusión a seguir las conocidas convenciones como cuándo usar mayúsculas y cuándo usar minúsculas, o usar la sangría correcta, sino que también es necesario mantener la complejidad del código, el acoplamiento, el tamaño de las clases, la cantidad de parámetros, entre otros factores.
Hace años surgió el término "deuda técnica" para explicar fácilmente el problema de la mantenibilidad del código a alguien sin conocimientos de programación. Hace una analogía con el concepto más conocido de deuda financiera. Básicamente, señala que si uno programa rápidamente una determinada funcionalidad por falta de tiempo, sin cumplir con el "definition of done" del equipo (por ejemplo, pasar por alto la documentación poniendo comentarios en el código, no cumplir con los estándares y convenciones de programación ni implementar pruebas unitarias) entonces en ese momento "la deuda" se acumula, se le debe al sistema lo que sea que se pasó por alto, por ejemplo, las pruebas unitarias, posponiéndolas para cuando el tiempo lo permita.
¿Cuál es el problema con eso? Además de pagar la deuda, se agregarán "intereses", por así decirlo, ya que es probable que tome más tiempo y más recursos solucionar las cosas después de que haya pasado un tiempo, en vez de hacerlo justo en el momento.
Cada problema de mantenibilidad genera una deuda técnica y siempre alguien la paga: a veces solo el desarrollador, o peor aún, el usuario que acaba sufriendo la mala calidad del sistema.
Algunas fallas comunes en la calidad del código son:
- Código duplicado.
- Falta de pruebas unitarias, falta de comentarios.
- Código espagueti, complejidad ciclomática, alto acoplamiento.
- Métodos demasiado largos.
- No adaptación a los estándares y a las convenciones de programación.
- Vulnerabilidades conocidas de seguridad.
Los entornos de desarrollo modernos ayudan en gran medida con algunos de estos problemas, como Visual Studio o Eclipse (por nombrar algunos populares), hasta el punto de que si no se cumplen algunas de las restricciones, el código no compilará. En cualquier caso, hay muchas cosas que se pueden analizar con herramientas específicas para analizar la calidad del código. Hoy en día, la herramienta más popular para este propósito es SonarQube, pero hay varias otras como PMD, Kiuwan y CheckStyle.
Estas herramientas realizan un análisis estático del código y son de caja blanca, es decir, analizan el código sin ejecutarlo. Comprueban las fallas comunes mencionadas anteriormente, como el código duplicado o la complejidad ciclomática.
Algo muy sorprendente es que SonarQube y otras herramientas indican cuál es exactamente la deuda técnica, y calculan cuántos minutos tardarían en resolver cada problema que detecta. Por ejemplo, la siguiente imagen se muestra públicamente en el sitio de SonarQube como resultado de MySQL. Se puede observar que el software tiene una deuda de 36.000 días, por lo que se necesitan 36.000 días para resolver todas las incidencias detectadas.
¡Tanta deuda no es algo que alguien quiera para su software!
Si bien es genial que SonarQube pueda proporcionar información sobre dónde está la deuda técnica, también es importante saber cómo pagarla. Pero clasificar estos problemas por prioridad no es suficiente. La clave aquí es encontrar el código que dará el mejor ROI. Es necesario cruzar la información de los problemas y su nivel de criticidad, con la cantidad de commits que hay en cada clase. Al hacerlo, los esfuerzos pueden centrarse en mejorar la mantenibilidad de aquellas clases que se modifican con más frecuencia. Sin embargo, si hay problemas de mantenibilidad de las clases que rara vez se abordan, pero funcionan correctamente, es mejor no abordarlos. Como se dice popularmente, "si no está roto, no lo arregles".
Para lograr la práctica de testing continuo, es clave construir un entorno de integración continua (CI). Para hacerlo, el código debe ser accesible en un repositorio bien organizado con varias validaciones de calidad del código, utilizando una herramienta como SonarQube.