Por qué el software se corrompe
Un artículo de Rafa G. Blanes
Lo he visto en demasiadas ocasiones: una aplicación es concebida inicialmente para realizar un conjunto reducido de tareas, pero, con el tiempo, se va tratando de incluir más y más funcionalidad. Lo que debería ser un éxito (se pide nueva funcionalidad porque lo anterior funciona y es de utilidad) se termina convirtiendo en una auténtica patata caliente imposible de mantener y para la que cualquier nuevo cambio cuesta más y más esfuerzo (y dinero) en introducirse.
También lo he visto (y sufrido) demasiadas veces: los responsables de esas aplicaciones son reacios a reconocer el problema y suelen echar balones fuera y mantenerse dentro de cierto caos antes de asumir profesionalmente que se ha alcanzado un punto crítico en el que hay que tomar decisiones desagradables.
El resultado es siempre el mismo: después de mucha frustración, se llega inevitablemente a la conclusión de que hay que rehacer partes completas de la aplicación cuando no tirarla a la basura completamente y comenzar desde cero.
No podemos perder la perspectiva: si se trata de un proyecto con cierto carácter lúdico, un repositorio que apenas mantienes en Github o Codeplex, bien, no pasa nada, salvo el tiempo que se haya podido perder, pero si se trata de un proyecto empresarial, todo lo anterior se traduce en costes dolorosos y, seguramente, un equipo frustrado por trabajar en una aplicación difícil.
Este es un mantra que conviene no olvidar nunca: todo software requerirá de cambios inevitablemente (y el fundamento del enfoque ágil). Si no se diseña para admitirlos, entonces nos encontraremos tarde o temprano con una aplicación corrupta y el escenario descrito párrafos más arriba.
La buena noticia es que se puede evitar, trabajando desde el inicio del proyecto en la línea correcta.
Pero, ¿por qué llega a corromperse una aplicación con el paso del tiempo a medida que la hacemos cambiar? A continuación enumero una lista de algunas de la razones:
- Una aplicación es concebida inicialmente como simple o pequeña y se le obliga a crecer de forma muy rápida y descontrolada.
- No existe metodología a la hora de ir introduciendo nuevos cambios: los requisitos llegan de un día para otro, se vuelve atrás con frecuencia, no hay planificación y cultura de testing o validación de la aplicación antes de ser entregada, abonando el terreno para el caos.
- Los requisitos no son claros: la traducción de los requisitos de negocio a los requisitos técnicos no se realiza con exactitud y la suficiente comprensión. El resultado es que se implementa funcionalidad que no es exactamente la que se quería, produciendo una cascada de cambios y, posiblemente, dejando código muerto y obsoleto a medida que eso ocurre, ensuciando la solución.
- Se utiliza una dispersión de tecnologías que dificulta la evolución de la aplicación. Para programar bien y de forma mantenible, hay que conocer bien las librerías y frameworks que se utilizan; un exceso de estos en la misma aplicación provoca confusión y es más difícil de llevar. Además, si se usan librerías complejas, esto ofuscará la solución.
- No se dedica tiempo a mejorar lo que ya existe, aunque funcione. El desarrollo de software es en cierta medida incremental: construimos nueva funcionalidad sobre lo ya existente o del mismo modo que lo que ya existe. Hay que tener siempre la actitud de mejorar continuamente lo que ya hay.
- Se intenta mantener código obsoleto, mal diseñado, mal probado, a toda costa. A veces cuesta trabajo eliminar partes de la aplicación porque recordamos el esfuerzo que nos llevó hacerlas, o por simple pereza, pero arrastrar algo que no está del todo bien es perjudicial: podemos decir que lo que eliminamos, en realidad, nos sirvió para aprender a hacerlo mejor.
- No se tiene en cuenta que no solo se mejora el código en sí, también el diseño y la organización del proyecto.
- No se siguen prácticas de código limpio y de refactoring de forma continuada.
- El proyecto no tiene tests o la batería de tests es insuficiente con una cobertura de código baja. Esto provoca que no sepamos cuanto antes qué bugs introducimos al hacer cambios: cuanto antes se detecte un bug, más fácil y sencillo será corregirlo. Además, incluir tests obliga a diseñar la aplicación con mejor estructura (software testeable), alejándonos de enfoques monolíticos y rígidos.
- El diseño (si es que existe) es pobre.
- Y, por supuesto, ocurre parte o todo lo que describro en El Libro Negro del Programador
Lo bien o lo mal que hagamos todo lo anterior, hará que lleguemos antes o después a la barrera de inmantenibilidad, tal y como describe la curva o Ley de Boehm, que viene a indicar el coste incremental de corregir errores desde una fase temprana del proyecto o en una fase tardía (cuando ya el software es... demasiado corrupto):
Nota: este artículo es es un resumen y forma parte del proyecto en desarrollo El Libro Práctico del Programador Ágil. Lanzamiento en julio 2018.