La Programación Extrema –término habitualmente abreviado como XP– es un método de desarrollo de software ideado por Kent Beck y recogido en el libro Extreme Programming Explained [1], cuya primera edición se publicó en el año 1999.
XP nació dentro del proyecto Comprehensive Compensation System, comúnmente conocido como C3, de la compañía automovilística estadounidense Chrysler. C3 fue un sistema de pago de nóminas iniciado en 1994 y desarrollado en Smalltalk que tenía como objetivo el tratamiento de todas las nóminas de los 87 000 empleados de la compañía. Beck fue contratado en 1996, dos años después del lanzamiento durante los cuales el sistema había sido incapaz de imprimir una sola nómina, prometiendo tenerlo operativo un año después, como así fue.
Las principales características de XP son las siguientes:
- El desarrollo es incremental con ciclos de entrega cortos –de unos pocos meses como máximo– que buscan la crítica temprana y continua. Cada ciclo se divide en iteraciones de una o dos semanas donde se llevan a cabo los requisitos solicitados por el cliente. Dentro de cada iteración, como no puede ser de otra forma, se planifican tareas de muy corta duración.
- Se solicita a la parte del equipo orientada al negocio –miembros de pleno derecho del mismo– que seleccione qué requisitos debería cumplir la versión más pequeña de la aplicación, pero con más valor desde el punto de vista comercial.
- Busca la adaptación a los cambios de los requisitos, es decir, está preparado para trabajar con requisitos poco claros o que cambian rápidamente.
- Se crea un conjunto de pruebas automáticas de todo el código como base para garantizar la calidad. Las escriben tanto los programadores como los clientes, los segundos en forma de pruebas de aceptación.
- La base documental es el código, las pruebas y la comunicación verbal.
- El sistema debe estar siempre a punto para su despliegue.
- Se pide a los programadores que estimen la duración de su propio trabajo y que acepten la responsabilidad de hacerlo en el tiempo dado. Los datos se guardan para que sirvan de orientación en el futuro.
- El diseño es evolutivo e incremental.
- Se exige la estrecha colaboración de todos los miembros del equipo.
Beck reconoce que XP busca la excelencia técnica y, por tanto, exige que los participantes deban alcanzar un alta capacitación para ponerla al servicio de los objetivos del equipo.
La Programación Extrema no sólo se ocupa de cómo debe hacerse el software, sino de cómo debe trabajar el equipo. Lo hace asentándose en cinco valores, veintiuna prácticas y once principios que trasladan los valores a las prácticas.
Trece de las veintiuna prácticas son las denominadas prácticas principales, de las que cabe destacar: las historias de usuario, la programación en parejas, la programación dirigida por pruebas, el diseño incremental y la integración continua.
Las historias de usuario
Beck acuña el término «historia» como sustituto de «requisito» para eliminar el carácter obligatorio de todo aquello que debe hacer el sistema. Los clientes que trabajan con enormes documentos de requisitos a menudo incluyen un porcentaje significativo de los mismos que los usuarios jamás utilizarán. Por esta razón, las historias pretenden describir características del sistema que tengan verdadero valor para el cliente.
Normalmente una historia se divide en tres partes:
- Una breve descripción.
- Los detalles de la historia recogidos a partir de las conversaciones con el cliente.
- Un conjunto de pruebas de aceptación que complementan los detalles y sirven para determinar si la historia está terminada. Las pruebas de aceptación verifican que la historia se ha realizado de la manera esperada por el cliente.
Cuando comienza una iteración, el cliente prioriza las historias basándose en aquellas que le aporten mayor valor. La parte técnica del equipo puede sugerir cambiar la selección realizada basándose en riesgos técnicos. Sea como fuere, una historia no puede priorizarse sin tener en cuenta su coste, algo que determina el equipo técnico asignándoles puntos que indican su tamaño y complejidad.
Seleccionadas las historias, el personal técnico comienza con el desarrollo mientras que el equipo del cliente lo hace especificando las pruebas. El equipo del cliente puede estar constituido, además de por usuarios finales y el gestor del producto, por ingenieros de pruebas y diseñadores de interacción. Un diseñador de interacción es un profesional encargado de crear interfaces de usuario atractivas y con un comportamiento bien pensado surgidas del diálogo con el cliente. En resumen, el equipo del cliente lo forman todas aquellas personas encargadas de asegurar que el sistema satisfará las necesidades de los usuarios a los que va dirigido.
Se habrá percatado de que la implicación del cliente es esencial durante todo el proceso de desarrollo del sistema. «Todo» significa desde que el proceso empieza hasta que acaba, no intensamente los primeros meses y esporádicamente luego. Si el cliente no está dispuesto a colaborar activamente, olvídese de XP y de cualquier proceso ágil. No funcionará.
La programación en parejas
La programación en parejas es un diálogo constante entre dos personas que analizan, diseñan, programan y prueban conjuntamente con el objetivo de mejorar continuamente el código de producción, mediante el intercambio de ideas o tomando la iniciativa cuando una de ellas se bloquea.
Trabajar en parejas no implica que no se pueda pensar a solas. Si en algún momento es necesario trabajar en una idea y se considera que es mejor hacerlo en solitario, se puede posponer la sesión y retomarla exponiendo el resultado ─la idea, no el código─ y realizarla con el compañero. Se hará más rápido y los resultados se entenderán mejor.
Aunque trabajar es parejas es muy gratificante, también es agotador. No deberían dedicarse más de cinco horas diarias a tal actividad. También es importante que las parejas roten.
La programación basada en pruebas
La idea básica tras esta práctica ─también llamada desarrollo dirigido por pruebas y abreviada habitualmente como TDD por sus siglas en inglés─ es escribir una prueba que falle antes de escribir el código de producción. Seguidamente, se escribe el código para hacerla funcionar. Se vuelve a escribir otra prueba fallida para añadir más código y así sucesivamente. Este enfoque permite establecer objetivamente lo que debe hacer el programa y no perderse en disquisiciones relativas a aquello que sería bueno que hiciera o tuviera.
El código de producción, una vez ha pasado la prueba, debe refactorizarse para alcanzar la máxima calidad posible. La refactorización es una técnica para cambiar poco a poco la estructura interna del software con el objetivo de mejorar su diseño ─hacerlo más flexible, robusto y claro─ sin que ello implique alterar su comportamiento observable. El ciclo es siempre el mismo: probar, programar y refactorizar.
La fortaleza de un código totalmente probado de forma automática da al equipo una sensación de seguridad y confianza impagables. Sin embargo, programar de esta manera tiene el riesgo de perder la visión global del sistema ─a no ser que se dedique mucho tiempo al diseño─ porque las pruebas tienen un alcance muy limitado.
El diseño incremental
El diseño está omnipresente en la Programación Extrema. Como suele decir Jon Reffries, en XP se diseña «a todas horas» [2]. Sin embargo, el diseño incremental difiere bastante del diseño clásico, aquel que se realiza de forma directa como en el Proceso Unificado. En lugar de pensar en el diseño a priori ─antes de ponernos a programar─ en XP se piensa en el diseño mientras se hace TDD y a posteriori.
Se empieza creando el diseño más simple posible para llevar la historia a cabo. El diseño evoluciona con el producto y se somete continuamente a su mejora. Más concretamente: se crea un elemento de diseño (un método, una clase, un paquete o algo mayor) específicamente para solucionar el problema con el que se está trabajando; la segunda vez que nos encontremos con ese elemento, modificaremos su diseño para hacerlo más general; la tercera vez, se generalizará aún mas; y así sucesivamente. Llegará un momento en que dicho elemento alcanzará un grado de abstracción suficiente para el sistema.
TDD, en cierto modo, es un ejemplo de diseño incremental pero trabaja a nivel de métodos y clases. El diseño incremental va más allá y lo hace a un nivel de relaciones entre clases, paquetes e incluso a nivel arquitectónico. Mientras que la evolución de un método ─a través de la refactorización─ puede llevar minutos, una arquitectura puede tardar meses. Haciendo un buen TDD se pueden conseguir unos métodos y unas clases a nivel individual de calidad (simples y legibles). Pero esto no es suficiente. Si no se presta atención a las relaciones entre las clases, el diseño del sistema se tornará rígido, inmóvil, frágil y opaco.
Un sistema en XP debe estructurarse y organizarse de alguna manera cuando empieza a crecer, prestando atención a las relaciones entre sus elementos constitutivos (clases y paquetes). Es aquí donde entra en escena la arquitectura del software.
Existen diferentes estilos arquitectónicos, siendo uno de los más comunes el modelo de tres capas: interfaz de usuario, lógica de negocio y persistencia. También se suelen denominar patrones a los estilos arquitectónicos pero, dado que no son patrones en el sentido de los patrones de diseño de Gamma y compañía, me referiré a ellos simplemente como estilos.
En XP se comienza probando el nuevo estilo en una sola parte de su diseño. Por ejemplo, sea una bonita y elegante clase que almacena unos datos que manipula y presenta. Dado que la clase no es cohesiva se debería emplear el estilo arquitectónico mencionado para dividirla en tres: una clase para la presentación, otra para el tratamiento de los datos y otra para su gestión. El cambio se realiza en esa clase y en otras clases relacionadas y se deja estar durante un par de semanas para ver si tiene buen encaje. Si es así, se va refactorizando el resto del código poco a poco sin que ello implique dejar de completar y entregar nuevas historias. La labor de refactorización a este nivel corresponde principalmente al arquitecto, que es uno de los miembros del equipo de XP.
La clave del diseño incremental es mantenerlo lo más simple posible y enfocarlo a las funcionalidad que el sistema soporte en un momento dado. Sin embargo, no es incompatible con pensar en cómo cambiará cuando se añadan más historias, aunque no se deberían hacer esos cambios hasta que llegue el momento de incorporarlas.
Si el equipo entero (y esto incluye especialmente a la parte del cliente) no se compromete a buscar la mejora continua diariamente, no use XP. El gran peligro de TDD es que a ojos del cliente puede parecer que una historia está complemente terminada porque la ve funcionar. Sin embargo, las clases que la llevan a cabo tendrá que refactorizarse aplicando los principios del diseño. Este trabajo adicional es absolutamente necesario, pero el cliente quizás no sea capaz de verlo. ¿Para qué hay que cambiarlo si ya funciona? Cuidado con preguntas así. La respuesta es clara: si no lo cambiamos, la velocidad del equipo irá reduciéndose hasta, quizás, la parálisis total.
Por otra parte, el diseño incremental es tremendamente exigente a nivel técnico. No solo hay que dominar todos los principios del diseño orientado a objetos, sino saber cómo aplicarlos cuando se parte de un conjunto de clases que deben refactorizarse para cumplir con ellos.
En mi opinión, el diseño incremental es mucho más complicado que el diseño directo, aunque los principios aplicables son exactamente los mismos. Recomiendo que si usa XP y está aprendiendo diseño, combine el diseño directo con el incremental. Al principio más directo que incremental, con el tiempo (y pueden ser años) los porcentajes irán variando hasta poder llegar a hacer solo incremental. Lo que sí debe tener presente es que debe centrarse en lo que hace el sistema hoy y que su diseño sea acorde. No se adelante a diseñar cosas que están por venir.
El diseño directo no es incompatible con la visión de Beck: «El diseño incremental no dice que el diseño directo sea horrible, dice que el diseño realizado cuando hace falta es más eficiente».
La integración continua
Probar la integración de todo el código debe ser una práctica a realizar varias veces al día. Cuanto más se demore la integración, más costará hacerlo. El resultado de la integración debe ser el producto completo: un programa con su instalador, un conjunto de paquetes Linux para el despliegue en las máquinas involucradas, etc.
La integración puede delegarse en una máquina que la realice automáticamente tras recibir los cambios. La máquina, esencialmente, compilará todo el código, pasará las pruebas y empaquetará el software. En caso de error, avisará al autor por algún medio, normalmente, por correo electrónico.
Una vez enviados los cambios, podemos esperar a que la máquina termine o pasar a otra tarea. En el primer caso, si el programador y su pareja esperan y el resultado de las pruebas devuelve algún error, se pueden poner inmediatamente a corregirlo. Esto es siempre mucho mejor que tenerlo que hacer veinte minutos después de haber dado comienzo a otra tarea. El tiempo que tome la máquina en hacer su trabajo puede aprovecharse para que la pareja discuta qué podrían haber hecho mejor.
Referencias
[1] Kent Beck y Cynthia Andres. Extreme Programming Explained: Embrace Change. Second Edition. Addison Wesley, 2004.
[2] Ron Jeffries. Essential XP: Emergent Design. 2001. url: https://ronjeffries.com/xprog/classics/expemergentdesign/
Fotografía de la entrada por Blake Connally en Unsplash