Desarrollando para el corto o el largo plazo (o cómo no acumular deuda técnica)
Un artículo de Rafa G. Blanes
No hace mucho he tenido que retomar un desarrollo que comencé hace año y medio y que después continuó uno de mis compañeros. A veces programamos pensando que nos vamos a acordar de todos y cada uno de los detalles de lo que hacemos cuando la realidad es que a los pocos días se nos olvidan sin poder evitarlo y a los meses casi ni reconocemos que esto o aquello los hiciste tú mismo.
Para cada clase incluimos una sección de comentarios indicando el autor de la misma y su propósito, como manera rápida de identificar esa información; en ocasiones me cuesta mucho recordar que una clase concreta la comencé yo mismo... No es mala memoria (que también, quién sabe), aunque me consuelo diciéndome que es el efecto de haber escrito miles de líneas de código en los últimos años.
Con frecuencia se cae en el error de programar para solucionar algo ahora y sin tener en cuenta que hay que solucionarlo para siempre. Pero, ¿cómo medir más o menos objetivamente que algo está mejor resuelto para el largo plazo? O nos interesamos por los resultados a corto plazo o a largo plazo, el resultado en uno y otro caso no tienen nada que ver.
Al retomar aquel proyecto pude comprobar una vez más cómo escribir software de manera limpia, clara, sencilla y con una buena cobertura de pruebas te puede salvar de malgastar días y semanas de trabajo sencillamente retomando aquello que hiciste hace mucho tiempo. Lo contrario es acumular deuda técnica que te pasará factura tarde o temprano, o lo que es peor, tendrás a un compañero acordándose de tus propias chapuzas; sí, seguramente sea ese que cuando te lo cruzas viniendo del office te mira con mala cara, quién sabe.
Del mismo modo no siempre he podido trabajar tanto en el detalle, en encontrar la mejor solución y sé de sobra el resultado: malos trabajos que se entregan y que terminan empeorando con el tiempo.
Llamamos deuda técnica a todos esos detalles que vamos dejando de lado sin resolver del todo bien pero que cuando se acumulan, terminan en una solución difícil de evolucionar y mantener. Es algo malo, muy malo, como eso de acumular karma negativo... Es la pesadilla de cualquier desarrollador: el tener que asumir un proyecto que ha hecho otro y que no hay quien lo entienda, que está cogido con pinzas.
Lo habitual es que se trabaje dejando muchísima deuda técnica, y esto es así porque es muy difícil evaluar el impacto en tiempo y falta de productividad que produce meses o años después cuando el gestor de un proyecto (tu jefe, vaya), lo que quiere son resultados inmediatos (o sea, pan para hoy y hambre para mañana).
Como el jefe soy yo mismo (aunque suene mal decirlo), una de las máximas que sigo en los proyectos que desarrollamos es que las cosas se tienen que terminar limpias y claras; si de lo que se trata es de reducir riesgos (cosa que se entiende mejor en otros contextos de la compañía) entonces esto es así como una garantía para el futuro: reducimos riesgos refactorizando diseños, limpiando código y simplificando. Curiosamente, el intentar trabajar así no nos ha hecho fallar en ninguna fecha de entrega acordada, lo cual es buena señal.
Puesto que en esencia un proyecto se paga por tiempo dedicado a él, no siempre podemos pararnos todo lo que nos gustaría a hacerlo lo mejor posible. No obstante, mi opinión es que si te paras a tratar de reducir o eliminar esa deuda técnica, todo el tiempo que en ese momento pierdas lo recuperarás multiplicado más adelante.
También es habitual encontrarte con gente que resuelve rápido, pero la pregunta no es cómo de rápido trabajas en software, sino cómo de limpio y mantenible haces el trabajo que entregas. Todo esto suele ser muy sutil, subjetivo, difícil de evaluar, me temo.
En esta ocasión, a las dos horas de revisar lo que se hizo en su día, ya estaba en condiciones de estimar el tiempo que podríamos tardar en añadir la nueva funcionalidad requerida por uno de nuestros clientes.
No hay una varita mágica para llegar a crear un software que te permita asumirlo de nuevo con comodidad al cabo de algunos meses o años incluso, depende del tipo de proyecto y claro está, tu propia experiencia acumulada. Sin embargo, cuando se siguen unos simples principios durante toda la solución es mucho más fácil (y rentable) volver a trabajar en ella como en el primer día.
En este caso que comento, la varita mágica era básicamente lo siguiente:
- Ese módulo a evolucionar seguía la arquitectura de diseño general de la solución. Nada de excepciones en el diseño alejado del general, las cosas estaban ahí donde un esperaba encontrarlas.
- Las clases son suficientemente pequeñas como para percibir claramente qué hace cada una, qué propósito único resuelve y cómo encaja con el resto.
- Las pruebas te permiten ver cómo se usa cada clase; los tests no sólo te sirven para saber si lo que has escrito funciona, también te permiten documentar el uso del código que generas.
- La inyección de dependencias te permite localizar rápidamente dónde están definidas y cómo se ensamblan las partes inyectables de la solución. Si está bien planteada, una gran parte funcional del sistema será inyectable.
- Ausencia total de código obsoleto o muerto (que nunca se ejecuta pero que a alguien le ha dado pena eliminar). Es decir, no hay basura que te distraiga de captar lo esencial.
- Estructura de la aplicación extraordinariamente ordenada y entendible.
- Nombre de métodos y clases claros y bien elegidos.
En ocasiones nos surge la pregunta de qué hacer cuando se está en una fase de definición o se tienen huecos de tiempo por llenar y que podrías usar productivamente. La respuesta está clara: siempre puede buscar aplicar todos los puntos anteriores a la solución en la que trabajas.
Para casi cualquier sección del código final, su calidad no surgió a la primera cuando se desarrolló hace algunos años, sino que es el producto de muchos refactorings y muchas fases de trabajo, algunas de las cuales tenían únicamente el propósito de mejorar la estructura interna y el diseño del sistema.
Quizá el mejor desarrollador no es aquel que rápidamente encuentra y resuelve cómo hacer algo; también es un magnífico desarrollador el que se para un momento para mejorar lo que ya funciona en algún aspecto, el que presta mucha atención a los detalles que nos permiten años después poder retomar fácil y cómodamente lo que hacemos ahora. Es algo así como dar un paso atrás para dar después un gran salto adelante.
Es ya muy antigua la polémica por la que un desarrollo software se debe considerar horroroso, decente, regular o magnífico, hay tantos puntos de vista como esas críticas contradictorias de cualquier película o la descripción de una misma noticia por varios periódicos con distintas líneas editoriales; es un factor también tremendamente subjetivo.
No obstante, de lo que sí podemos estar seguros es de que una solución que acumula mucha deuda técnica es bastante peor que aquella en la que no existe. La pregunta no es sólo ¿funciona o no funciona?, también ¿será fácil retomar esta aplicación en el futuro por alguien que ha participado en ella?
Como ocurre en muchos temas y como decía Benedetti: "Cuando creíamos que teníamos todas las respuestas, de pronto, cambiaron todas las preguntas". Según las preguntas que te hagas acerca del tipo de calidad que esperas en el software que desarrollas (¿es rápido, eficiente, usable, mantenible, de diseño limpio, traza bien los errores, etc.?) así será este.
¿Desarrollas pensando en el corto plazo o un poco más allá?