Shalloway explica que al abordar un diseño hay básicamente dos opciones: diseñar estríctamente para cubrir los requerimientos (con lo que el código resultante irá siendo progresivamente más difícil de mantener) o diseñar pensando en el futuro (con lo que frecuentemente introducimos más complejidad de lo necesario). Aquí introduce Shalloway una tercera opción que denominamos Diseño Emergente y que resulta de la combinación de tres disciplinas (ojo, dice disciplinas):
- basar el diseño en patrones para crear arquitecturas resistentes y flexibles a la vez, abaratando así el coste de mantenimiento del código
- limitar el diseño sólo a los requisitos actuales (no adelantarse a requisitos futuros), consiguiendo así reducir el tamaño y complejidad del código
- escribir pruebas de aceptación y unitarias automatizadas antes de escribir el propio código, favoreciendo así la construcción de un mejor diseño y haciendo que sea más seguro aplicar cambios
Shalloway también dice que el Diseño Emergente nos ayuda a añadir código nuevo desacoplado del viejo pero sin incorporar complejidades innecesarias. Esto, dicho así, puede resultar un poco difícil de entender, pero en el contexto de desarrollos iterativos tiene mucho sentido.
Trabajar sin un diseño completo desde el principio es algo incómodo por el nivel de incertidumbre que se maneja, pero si lo pensamos fríamente, es muchor mejor postergar las decisiones de diseño mientras tengamos dudas sobre los requisitos. En cualquier caso, me gustaría añadir que trabajar con un Diseño Emergente NO significa trabajar SIN diseño o con un diseño IMPROVISADO. Me remito a las tres disciplinas que enumera Shalloway:
Patrones de diseño
Emplear patrones de diseño contribuye definitivamente a que tengamos un diseño fácil de transimitir (incluso fácil de documentar). (Nota para los arquitectos: es una buena práctica documentar con gran detalle los patrones empleados -aunque no sean del GoF y nos los hayamos “inventado”- y pedir a los desarrolladores que documenten en su código qué piezas de los patrones están implementando.)
Por otro lado, los patrones de diseño nos permiten construir soluciones extensibles puesto que no resuelven nuestro problema sino un problema más general.
Me gustaría añadir que también podemos añadir patrones de análisis (ver libro de Fowler “Analysis Patterns“, p.ej.) a nuestra caja de herramientas con el objetivo de ser capaces de plantear el diseño más adecuado a nuestro problema, pero con la “puerta abierta” a futuras necesidades.
No adelantarse al futuro
Nuestro pequeño gran ego de programador nos empuja indefectiblemente a demostrarle al mundo entero lo listos que somos al ser capaces de ver el futuro y adelantarnos a todos prediciendo las necesidades de los clientes. Para ello solemos diseñar soluciones tan virtuosas como inútiles para el 80% de las necesidades reales de los clientes. Frecuentemente nos encontramos con diseños que transforman datos entre capas una y otra vez hasta llevarlas a la base de datos, validando una y otra vez los datos (antes y después de persistirlos)… aunque esos datos en realidad el cliente nunca los ha necesitado guardar. Alguien decidió en algún momento que era mejor guardarlo todo “por si acaso” y la consecuencia es un código tremendamente difícil de explicar, con muchos defectos de desarrollo y un rendimiento muy mejorable. Muchas veces somos nosotros mismos los que diseñamos así: “por si acaso”.
Pues bien, está demostrado que es mejor no diseñar “por si acaso”. En la mayoría de las ocasiones no estamos haciendo una inversión sino introduciendo complejidad que nunca se verá compensada. En términos económicos eso no es rentable.
Propongo que hagáis el ejercicio de identificar algunas de esas “características avanzadas” que se incorporan “por un pequeño esfuerzo más” y que al final del proyecto nunca se han utilizado. Si además sois capaces de saber cuánto ha sido ese “pequeño esfuerzo más”, quizás lo podáis sumar a los defectos y retrasos provocados por esas “características avanzadas”. Claro, un diseño sin “características avanzadas” es menos “cool”. 🙂
Pruebas automatizadas
Muchísimas veces me he encontrado con que teníamos tanto miedo de hacer cambios en el diseño que retorcíamos el diseño existente hasta límites insospechados. En ocasiones, cambiar aquí hacía que allí dejara de funcionar. Lo más terrible era cuando nos enterábamos de ese impacto cuando nos llamaba el cliente para decirnos que les había salido un mensaje muy extraño en la pantalla…. Ay, si hubieramos contado con un conjunto de pruebas automatizadas que cubrieran los requisitos de nuestros clientes…
Hoy día, este problema está claramente superado, si no tenemos un motor de integración continua y desarrollamos nuestras pruebas unitarias y de aceptación es o porque no sabemos o porque no queremos, pero no nos podemos escudar en que no es posible o que es muy costoso.
Instalar Hudson, Continuum o CruiseControl se hace en dos patadas y desarrollar pruebas unitarias con JUnit o TestNG está ampliamente documentado. Si tenéis un frontal web podéis usar Selenium, HttpUnit o algún otro framework similar. Si tenéis web services podéis usar SoapUI o haceros vuestro propio framework “ad hoc” basándolo en HttpUnit, por ejemplo. Y finalmente, para escribir las pruebas de aceptación tenéis desde el viejo Fit (o Fitnesse) hasta Concordion (lógicamente mi recomendación) o incluso easyb o jBehave. Para cada clavo seguro que tenéis vuestro martillo.
Pero Shalloway va un poco más allá y pide escribir las pruebas ANTES que el código. Hay mucho escrito sobre TDD (test-driven development), ATDD (acceptance test-driven development) o BDD (behaviour-driven development), pero os puedo asegurar que practicar el “red–green-refactor” termina dando como resultado diseños y código de mucha mejor calidad, con el beneficio añadido de que tenemos una estupenda red de seguridad formada por las pruebas que hemos ido escribiendo para explicar qué queríamos que hiciera el software y no para explicar lo que ya hacía el software. Además, reducimos el número de defectos y, yo no sé a vosotros, pero a mi no me gusta nada estar resolviendo defectos… a mis jefes tampoco. 🙂
Proceso de descubrimiento
Otra afirmación de Shalloway con la que estoy muy de acuerdo es que:
El proceso de desarrollo de software es más un proceso de descubrimiento que de construcción.
Podría dedicar un artículo entero a esta afirmación pero sólo quiero señalar que parte de lo que se va
descubriendo durante este proceso es justamente el Diseño Emergente.
Viendo el desarrollo de software dentro del proceso mayor que lo engloba, el desarrollo del producto, podemos decir (simplificando mucho) que hay 3 fases para desarrollar un producto:
- descubrir las necesidades del cliente
- imaginar cómo construir los artefactos que cubrirán esas necesidades
- construir los artefactos
Shalloway explica muy bien en su artículo cómo empleamos un gran esfuerzo en las dos primeras fases a pesar de que normalmente no somos casi ni conscientes (¿inconscientes?) de ello. Si de repente tuvieramos la oportunidad de reconstruir nuestro sistema tardaríamos digamos un 50%-80% menos del tiempo empleado la primera vez. Ese tiempo “perdido” es el correspondiente a las dos primeras fases: descubrir necesidades y diseñar los artefactos.
El artículo de Shalloway del que he extraído las principales ideas que os he comentado es mucho más rico porque recorre los principios fundamentales de “Lean” y va mostrando qué prácticas ágiles surgen a partir de ellos. Esta muy bien porque así es posible tener un fundamento teórico para unas prácticas y, cuando en ocasiones no es posible aplicarlas, podemos sustituirlas por otras pero respetando los principios, es decir, buscando los mismos beneficios. También compara los procesos de producción JIT (just-in-time) con los procesos ágiles de desarrollo de software, pero esto casi merece otro artículo sólo para él.
Estaría encantado de conocer vuestras opiniones.