En la gran mayoría de los proyectos de software necesarios para desarrolladores internos (del departamento de TI) o externos (de consultoría y fábricas de software), podemos notar que los principales puntos de medición del funcionamiento son los plazos de entrega y costo. Como Robert Austin nos muestra perfectamente, en su libro "La medición y la gestión del rendimiento en las Organizaciones", la medición de un sistema complejo a través de unos pocos parámetros (métricas) hace que sea disfuncional y genera el efecto contrario a los fines previstos.
Calidad interna y externa de un software y su código
Cuando se mide la ejecución de un proyecto de software sólo por su costo y tiempo, se nota un clásico comportamiento disfuncional: como al director del proyecto y los miembros del equipo sólo se los medirá por la velocidad de entrega, entonces todo se hace y por lo general con prisa sin la debida atención a un buen diseño, las pruebas unitarias, las pruebas funcionales y los refators constantes. Sin esa atención, el sistema generalmente viene con una serie de defectos por encima de lo aconsejado y se convierte en código legado desde su entrada en producción. Esta medición del rendimiento también hace que los niveles gerenciales exijan más de 40 horas semanales de los profesionales (a menudo sin el debido pago de horas extras), lo que genera una degradación aún peor de la moral del equipo y un volumen de negocio más alto en la empresa .
Entonces, ¿cómo se puede conseguir un buen código, que no se haga legado y que sea entregado al equipo que lo mantendrá? En primer lugar vamos a definir qué es un código legado, que es un buen código y que significa el requisito no funcional de mantenible.
El código legado
Al examinar la palabra código legado, en la cabeza de los desarrolladores viene una visión de código imposible de comprender, estructuralmente complejo, lleno de condiciones encadenadas, código macarrónico y métodos con cientos e incluso miles de líneas de código! Según Michael Feathers (en su libro "Trabajar de manera efectiva con Código Legado") el código legado es aquel que es difícil de entender y, por tanto, difícil de cambiar para aplicar las nuevas funciones o corregir defectos.
Pero además dió la definición que es más importante: el código legado es el código sin unidad de pruebas unitarias automatizadas. Esa definición radical e inusual nos muestra la importancia de contar con una suite de pruebas de unidad para mantener la salud del código y nuestro coraje de hacer cambios cuando sea necesario. Sin pruebas los desarrolladores adoptan la postura de no juguetear con el código que no "es de ellos", por temor a generar defectos. Tampoco hacen cambios estructurales para mantener la cohesión del código y con bajo acoplamiento, en especial cuando incluyen nuevas funcionalidades. Entonces, sin pruebas unitarias es difícil dar mantenimiento evolutivo y correctivo en el código y mantenerlo bien, evitando que se convierta en legado y complejo.
Malos olores en el código
Nuestra siguiente definición es buen código. Esta es una definición más compleja. Me gusta el término inventado por Kent Beck: "Code Smell". El "olor" en el código es un síntoma de que algo está mal. Indica que el código debe ser refactorizado o el diseño debería revisarse. Algunos "olores" son clásicos (visita la taxonomía de malos olores, si quieres más detalles):
- Métodos largos - que pueden variar en tamaño, pero algunas personas creen que un método de más de 50 líneas de código es un método de largo (algunas llegan a ser más puristas deciendo que un método no debe tener más de 10 líneas!). Veremos más adelante en el artículo que la complejidad ciclomática puede ayudarnos a obtener un indicador más exacto
- Clases grandes y complejas - por lo general los que no siguen el principio de alta cohesión
- Clases con mucha relación con otras clases - los que no siguen el principio de bajo acoplamiento
- Clases perezosas - que no hacen muchas cosas o son ejecutadas con poca frecuencia
- Código duplicado - que es una de las principales plagas que deben combatirse
Y los requisitos no funcionales de mantenimiento? Bueno, eso es, como cualquier requisito no funcional, un elemento clave para definir la arquitectura de software. Es la facilidad de mantenimiento incluso para las nuevas características, corrección de los defectos y los cambios a las reglas de negocio. Este requisito es fundamental para cualquier sistema que tendrá una larga vida en producción y que estará sufriendo continuos cambios. Los clientes y otras partes interesadas (stakeholders) no suelen hablar con claridad "este sistema debe tener una duración de 10 años en nuestra empresa, es crítico y va a tener un gran número de nuevas necesidades mensuales." Por lo tanto, el mantenimiento se debe cuestionar claramente a las partes interesadas (stakeholders). Algunos dicen que no hay que preocuparse del mantenimiento si el código va estar poco tiempo en producción. Tenga cuidado, porque la tendencia de todo código creado es seguir siendo utilizado por un plazo de tiempo razonable. Por lo tanto, sospeche si dicen que el sistema estará poco tiempo en producción. No siempre el cliente lo sabe todo (véase el libro "El dilema de la Innovación" por Clayton Christensen para más detalles de este fenómeno en el mundo de los negocios).
Medir al proveedor
Ahora el problema: teniendo en vista que sabemos la importancia de obtener un buen código, no legado y de alta mantenibilidad, como medir eso con un proveedor (interno y/o externo) y la forma de garantizar que la calidad existe? Estos son algunos consejos.
La primera parte es la más fácil: mediciones de código. Hoy tenemos varias herramientas (en este artículo me centrare en herramientas para Java, pero sabemos que también hay similares en .NET y otras plataformas) que ayudan en el 80% del esfuerzo para detectar problemas. Vamos a algunas de ellas para ejemplificar:
- Utilice JUnit para hacer pruebas unitarias automatizadas y, por tanto, no dejar que el código se torne legado conforme la deficinición de Featherts. Como un cliente puede medir si las pruebas unitarias se están realizando y si son pertinentes? Una muy buena manera es medir la cobertura de código (de líneas y desviaciones), realizada por las pruebas unitarias. Para ello puede utilizar una herramienta de código abierto como Cobertura o una herramienta embutida como Rational Application Developer.
- Utilizar una herramienta como JavaNCSS contar el número de líneas de código y la complejidad ciclomática por método. Con estas métricas se puede analizar si el código de cada método es demasiado complejo y debe ser partido para mantener la cohesión. Puede utilizar una herramienta como crap4J paraa dar métricas combinadas de cobertura de código y la complejidad ciclomática.
- Utilizar una herramienta de análisis estático de código como PMD o incrustados en Rational Application Developer para evaluar las normas básicas de código. Esta es una herramienta esencial y debe ser utilizada antes de cualquier inspección oficial realizada por los desarrolladores. Ella puede obtener errores básicos que pueden ser configurados. Detecta fallas como:
- Métodos largos
- Nombres cortos en las variables y los métodos
- Nombres de variables que no empiecen con minúsculas
- Indentación de llaves
- Demasiados parámetros
- Acoplamiento entre objetos
- Anidamientos grandes de ifs y elses
- Densidad de Switch
- Código no utilizado (variables, parámetros, métodos, etc)
- Use la herramienta CPD (Copiar / Pegar Detector) para identificar el código duplicado y susceptible de refatorización.
- Utilizar una herramienta de análisis de las dependencias de código orientado a objetos como JDepend o Rational Software Architect (con su capacidad de descubrimiento arquitectónico y la detección de antipatrones arquitectónicos).
Todas las herramientas mencionadas anteriormente pueden realizarse a través de un simple comando de Ant, después de realizada la configuración correcta. Esto también puede ser colocado a correr dentro de un servidor de integración contínua, que puede enviar informes diarios por correo de la situación de las métricas del código.
Además de medir el código usando herramientas, siempre hacer las auditorías en cada una de las versiones entregadas (recuerde que las herramientas garantizan el 80% de los casos, normalmente los más típicos). Las herramientas que no excluyen el uso de buenos desarrolladores haciendo revisiones de código formales. Según McConnell, en el libro "Code Complete", de 45 a 70% de los defectos de su código se eliminan cuando se pasa por una inspección formal de código realizada por desarrolladores experimentados.
Establecer un SLA
Ahora, vayamos a la parte que dará más trabajo (en términos de negociación y posibles conflictos): como garantizar la calidad de su proveedor? La respuesta: establecer un SLA (Service Level Agreement) para cada parámetro importante discutido en la primera parte (métricas). Usted puede definir, por ejemplo:
- Una complejidad ciclomática de hasta 15 como aceptable, del 16 al 30 como aceptable si el proveedor justifica de forma admisible o inaceptables cuando es más de 30.
- Una cobertura de líneas de código en un 80% como aceptable. Por debajo de esto es inaceptable y está por encima del 80% se podría hablar de un bonus.
- Reglas definidas por el cliente en las herramientas de análisis estático de código que se deben seguir en su totalidad. No debe haber ningún error.
Sobre esa base podemos establecer sanciones en caso de que el proveedor no se encuentra dentro de los SLA's y una bonificación por sobrepasar los límites aceptables del SLA.
También es posible elaborar SLA's para que el proveedor realice pruebas funcionales automatizadas y pruebas de rendimiento (performance) automatizadas. Las pruebas automatizadas son una muy buena garantía porque se puede ejecutar para confirmar si se hicieron (a diferencia de los casos de pruebas manuales) y además se convertirán en un activo de su empresa, porque se podrán utilizar en el futuro como pruebas de regresión. Un mínimo de pruebas funcionales automatizadas (aunque sea apenas las llamadas "pruebas de humo") deben ser requeridas.
Con estas técnicas se puede salir de la situación de la figura que sigue:
Para entrar en una situación mejor como:
Y una sugerencia: Si su proveedor cuestiona o dice que tendrá que aumentar el costo por esto, le podría preguntar: ¿cómo pensaba entonces garantizar la calidad y facilidad de mantenimiento de su código sin estas métricas? ¿O no lo hacía? Es probable que el proveedor no tenga una buena respuesta real.
Otro consejo: Si su proveedor se dice "ágil", no acepte sólo que haga las entregas durante iteraciones cortas. Esta es sólo una parte (importante). Exija una alta calidad para realmente demostrar la agilidad del proveedor. En los procesos ágiles priman la calidad interna y externa del código. El anuncio es importante porque ya se observó en el mercado que tienden a señalar a las empresas "pseudo-ágil". Son aquellos que dicen que práctican Scrum y XP, pero de hecho practican una codificación caótica e indisciplinada, todo lo que los procesos ágiles no son.
¿Por qué estoy dando esta información valiosa? Porque creo que si los clientes son más exigentes con sus proveedores de software, entonces todo el país terminará ganando. Formaremos profesionales con mayor calidad, que generan un producto (código) de mayor calidad y así podremos competir en los mercados extranjeros con otros profesionales, no sólo en términos de coste, pero sin la calidad del trabajo. Cambiaremos una estrategia de océano rojo de costo por una estrategia de océano azul la diferenciación.
Por lo tanto, recordando al cliente: elabore contratos que demanden entregas cortas (desarrollo iterativo) con calidad interna y externa medida por herramientas, las personas y los SLAs. Vamos a mejorar juntos el nivel de calidad de nuestros productos!
Una sugerencia a los proveedores: Sean pro-activos y ofrezcan este tipo de contratos para sus clientes. Cambien sus estrategias de desarrollo y ofrezcan calidad con agilidad. Generar beneficios de valor agregado, antes que sus competidores lo hagan!
Espero comentarios de ustedes sobre este artículo. Siempre es bueno el diálogo con los lectores!
Traducción libre del original