Disciplínate: Aplica prácticas de programación ágil

Publicado en

Nota del Editor: Este texto es una versión traducida y condensada del artículo “Programming Practices” disponible en http://www.versionone.com/Agile101/Programmer_Practices.asp. Ha sido traducido y reproducido por SG con el permiso de VersionOne, Inc.

En un proyecto ágil, los programadores tienen una misión: entregar periódicamente (cada 2, 3 o 4 semanas dependiendo de la duración de las iteraciones) software que funcione y esté probado. Para lograr esto, los programadores requieren mantenerse ágiles tanto a sí mismos, como al código que generan. Esto requiere mucha disciplina. El código ágil es aquel que solamente implementa los requerimientos actuales, tiene pocos defectos y se basa en un diseño robusto que permite extenderlo fácilmente para incorporar requerimientos adicionales en futuras iteraciones. Un código ágil está factorizado adecuadamente, y bien protegido por pruebas unitarias. Entre los métodos ágiles, Extreme Programming (XP) es posiblemente el que entre en mayor detalle acerca de como los programadores pueden hacer código ágil. Cada vez es más común encontrar equipos ágiles que aunque no apliquen XP en su totalidad, sí apliquen al menos algunas prácticas de XP que tienen que ver con la forma de trabajar de los programadores.

Dichas prácticas son:
• Desarrollo dirigido por pruebas
• Refactorización rigurosa y constante
• Integración continua
• Diseño simple
• Programación en pares
• Compartir una base de código entre los programadores
• Adherirse a un estándar de programación
• Un área de trabajo común

Estudiémoslas en mayor detalle.

Desarrollo dirigido por pruebas

Tener pruebas unitarias para solamente algunas partes de nuestro código puede resultar en código de baja calidad. Por otro lado, cubrir el 100% no es muy práctico y resulta en baja productividad. Una cobertura de pruebas unitarias de entre 75% y 85% del código es típicamente considerada como óptima. Una excelente forma de lograr tener estos niveles de cobertura es aplicar el Test-First programming. Esta técnica consiste en que antes de que creemos el código para satisfacer un requerimiento, primero generemos pruebas unitarias automatizadas que validen dicho código. Tal acercamiento puede sentirse extraño en un principio, sin embargo es de gran utilidad. Pensemos en los escaladores de montañas que crean caminos al ir dejando anclas; hacer esto es mucho más tardado que simplemente escalar, pero será de gran utilidad para los próximas veces que se tome este camino. Para implementar esta técnica, típicamente se utiliza algun framework de la familia xUnit (Junit para Java, Nunit para C#, etcétera). Dichos frameworks facilitan la creación, ejecución y organización de pruebas unitarias. Otra ventaja es que la mayoría de los IDEs soportan estos frameworks. El desarrollo dirigido por pruebas (test driven development, TDD) es un caso especial de test-first programming que agrega el elemento del diseño continuo. Con TDD, el diseño del sistema no es restringido por un diseño estático en papel, sino que el diseño es continuamente retroalimentado por el proceso de creación de pruebas y código de producción. Conforme el código se refactoriza para simplificarlo y clarificarlo, es posible encontrar mejores métodos, clases y modelos que los que establece el diseño original. TDD se basa en la premisa de que no puedes determinar cual será el mejor diseño, hasta que estés implementando el código. Conforme te vas dando cuenta de qué funciona, y qué no, estás en el mejor momento para ajustar el diseño, mientras tienes las ideas frescas. Y para protegerte de que pudieras realizar cambios que afecten elementos ya implementados, estás protegido por tu conjunto de pruebas unitarias automatizadas.

Refactorización

La refactorización (refactoring) es el proceso de clarificar y simplificar el diseño de un código existente, sin cambiar su comportamiento. Esta actividad se realiza de forma continua. El código que no es continuamente refactorizado, se pudre. Dicha putrefacción se puede notar de distintas formas: demasiadas dependencias entre clases o paquetes, asignación incorrecta de responsabilidades en clases, duplicación de código, tan solo por nombrar algunas. Cada vez que cambiamos un código existente y no intentamos refactorizar, la putrefacción aumenta. Sin embargo, hay que estar conscientes de que la única forma de que sea sano y viable refactorizar de forma continua, es que contemos con una buena base de pruebas unitarias automatizadas. Si no somos capaces de ejecutar dichas pruebas cada que refactorizamos código, corremos el riesgo de estar introduciendo defectos sin darnos cuenta. El libro “Refactoring: Improving the Design of Existing Code” de Martin Fowler es una excelente guía que describe oportunidades de refactorización comunes, y distintas formas de resolverlas. Pero la refactorización no solo ocurre a nivel de
código, sino que se puede llevar al nivel de patrones de diseño. En su libro “Refactoring to Patterns”, Joshua Kerievsky explica cómo hacer esto, así como su utilidad.


Integración continua

Los métodos tradicionales de desarrollo de software no indican cada cuando se debe integrar todo el código fuente de un proyecto y generar una versión ejecutable. Los programadores pueden trabajar por separado por horas, días o incluso semanas en un código sin darse cuenta de cuantos conflictos y defectos están generando. Como comentamos en un principio, los equipos ágiles requieren producir código ejecutable y robusto en cada iteración, por lo que si dejan la integración del código hasta el final de la iteración, se encuentran con un arduo proceso de resolución de conflictos y depuración de código. Ante esto, los equipos ágiles comúnmente escogen aplicar la integración continua. La integración continua involucra construir un ejecutable (build) del sistema varias veces al día, apoyándose en herramientas de gestión de la configuración (software configuration management). Los equipos ágiles típicamente configuran la integración continua para incluir compilación automática, ejecución de pruebas unitarias e integración de código fuente. Una regla popular de la integración continua establece que los programadores no dejen elementos sin integrar al final del día, es decir que no se pueden dejar builds rotos para el siguiente día. Adicionalmente se puede combinar esta regla con la de que el programador que “rompe” un build al hacer check-in es al que le toca arreglarlo, lo cual genera un incentivo natural para que los programadores hagan check-in frecuentemente durante el día.
 
Diseño simple
Los equipos ágiles ponen gran valor en la extensibilidad de su código. Esto se refiere a qué tan fácil es mantenerlo y extenderlo. Ya discutimos lo importante que es la refactorización continua para tener código extendible. El otro elemento clave para esto es la simplicidad del diseño. La extensibilidad del código es inversamente proporcional a la complejidad del diseño. Parafraseando al poeta Wallace Stevens, un diseño simple significa: “el arte de lo que es suficiente”. Esto se traduce en programar para los requerimientos de hoy, y nada más. Sin embargo, esta no es una inclinación natural de nosotros los programadores. Es común que por querer utilizar las herramientas y tecnologías más novedosas, terminemos complicando nuestro diseño más de lo que deberíamos. Cualquier elemento extra que agregamos, es un lastre que debemos cargar a lo largo del proyecto. In Extreme Programming, se utiliza el término “You aren’t gonna need it” (YAGNI) para detectar esta situación. Las empresas que trabajan de forma ágil tienden a preferir contratar gente con experiencia en el arte de la extensibilidad y el diseño simple, por encima de aquellas con mayor conocimiento de las tecnologías.


Programación en pares

La programación en pares es claramente la más controversial de las técnicas de programación ágil. Esta técnica consiste en tener dos programadores trabajando en una sola computadora. Un programador “conduce” operando el teclado, mientras el otro “navega” observerando, aprendiendo, preguntando, y haciendo sugerencias. En teoría, el conductor se enfoca en el código que está haciendo (la sintáxis, semántica y algoritmos), y el navegador se enfoca en mayores niveles de abstracción (la prueba que están tratando de hacer pasar, los próximos pasos, la calidad del diseño en general). La teoría es que la programación en pares resulta en un mejor diseño, menos defectos, y una mayor difusión del conocimiento a través del equipo de trabajo. Los resultados de investigación reportan que al usar programación en pares la productividad puede disminuir en un 15% al corto plazo, pero como el código es mejor, la productividad al largo plazo se incrementa. Todo depende de cómo se mida la productividad, y en qué marco de tiempo. En un contexto ágil, la productividad típicamente se mide en base a características implementadas (y probadas) por iteración. Por otro lado, alguien que mida la productividad en base a líneas de código por semana, seguramente encontrará que se disminuye drásticamente con la programación en pares.Los beneficios de la programación en pares se incrementan si se considera un plazo
suficientemente largo como para incluir la rotación de staff. Esto es porque la programación en pares mitiga el problema de que haya personas que sean “islas de conocimiento”, y que al abandonar un proyecto lo afecten significativamente. Al estar continuamente
compartiendo el conocimiento dentro de un equipo, se reduce el impacto de la rotación. En Extreme Programming se utiliza el término “número de camiones” (truck number) para referirse al número de personas que tendrían que ser atropellados por un camión
para que el proyecto tuviera que cancelarse, y XP busca que dicho número sea lo más cercano al número total de personas en el equipo. No es que no haya especialización entre los miembros del equipo, es solo que todos están enterados de qué es lo que está
pasando, y por qué se hizo así.

 

Adherirse a un estándar de programación
Si los programadores se adhieren a un mismo estándar de programación (incluyendo detalles como tabuladores vs. espacios, posicionamiento de corchetes, y nomenclatura de elementos), todo funciona mejor. Es más sencillo mantener y extender código, refactorizarlo, y reconciliar conflictos de integración, cuando se utiliza un mismo estándar a través de todo el código de un proyecto. No importa cual estándar se utilice, lo importante es adherirse a él.


Base de código común

Esta técnica se refiere a que todos, o al menos la mayoría, de los miembros del equipo compartan la misma base de código fuente. Esto solo es viable si se están aplicando otras técnicas como la integración continua y la adherencia a un estándar de programación. También sirve de mucho aplicar programación en pares. La principal ventaja de utilizar una base de código común es minimizar el impacto de la rotación de personal. Si todo el equipo utiliza una misma base de código y tiene una buena idea de en qué está trabajando cada quién, entonces es más fácil tanto quitar como agregar programadores al equipo. Esta práctica también facilita el tener un diseño consistente a lo largo de todo el proyecto.

 

Un área de trabajo común
Durante la segunda guerra mundial, cuando Inglaterra se convenció de que necesitaba poder romper los complicados algoritmos criptográficos de los Nazis, ensamblaron un grupo con los mejores matemáticos y criptógrafos. Al hacer esto, no los pusieron en oficinas separadas para que trabajaran aislados, sino que los juntaron lo más posible, fomentando la interacción entre ellos. Este grupo estaba aislado del resto del mundo. Trabajaban, comían y dormían juntos. Fue de esta tumultuosa convergencia que surgieron ideas brillantes que los llevaron a construir las máquinas (computadoras) que hicieron posible descifrar los códigos Nazis. Se ha demostrado en repetidas ocasiones que los espacios de trabajo abiertos facilitan la comunicación en el equipo. Cuando un programador tiene una pregunta técnica, duda sobre algún requerimiento, o encuentra un conflicto de integración, cuenta con ayuda a la mano. Cuando las personas hablan entre sí exactamente en el momento que lo necesitan (en lugar de esperar a la próxima junta), los problemas se resuelven más rápido y mejor.


Conclusión
Invitamos a los equipos de desarrollo de software a adoptar las prácticas descritas en este artículo. Sin duda encontrarán que les permiten desarrollar mejor software de forma consistente.