De qué hablo cuando hablo de refactorizar
Un artículo de Rafa G. Blanes
Me propongo aclarar algunos términos.
Cuando comenzamos un nuevo proyecto software, cualquier tipo de aplicación, raramente sabemos con exactitud cómo lo vamos a implementar; quiero decir, qué estructura de clases habrá, cómo será la mejor forma de organizar los assets del proyecto, qué interfaces surgirán y hasta qué decisiones de diseño y arquitectura deberemos tomar. Como digo, tendremos si acaso una aproximación mental pero no la forma completa y exacta que tomará la solución.
Es cierto que con el tiempo y la experiencia, ya vas teniendo una idea clara antes de comenzar según el tipo de proyecto y de aplicación.
Por tanto, en realidad hasta que no se va implementando funcionalidad, no sabremos la mejor forma de hacer las cosas, lo que no es más que una versión de aquello de "caminando no hay camino: se hace camino al andar".
Puedo asegurar esta ley universal: la primera aproximación nunca es la mejor.
Lo peor que le puede ocurrir a una aplicación es dejarla exactamente tal y como surgió en su implementación inicial, sin refinar ni mejorar absolutamente nada de su estructura y diseño. Cuando hablo de aplicación, podemos bajar de nivel a librería, módulos, clases, métodos, etc. que el principio se sigue cumpliendo.
La construcción de una aplicación de calidad es un proceso de mejora incremental y continuo y, en realidad, siempre quedará algo por mejorar.
Todo se complica cuando vamos añadiendo más y más funcionalidad, alguna que ni siquiera esperábamos ni imaginábamos, algo totalmente natural en el desarrollo de software, de modo que el peor error que podemos cometer es añadir esa funcionalidad en el esqueleto anterior ya realizado, de cualquier modo. De ahí, con el tiempo, tendremos el terreno abonado de una aplicación inmatenible y difícil de entener y evolucionar.
Refactorizar es esencial para evitar la situación anterior, y cuando hablamos de refactorizar nos refererimos exclusivamente a la aplicación de ciertas técnicas que permiten mejorar el microdiseño del código que hemos escrito. Son pequeños pasos en los que cada uno mejora un diminuto aspecto de la solución.
Por extensión, también hablamos de refactorizar como sinónimo de mejorar cualquier elemento de la aplicación, y está bien que extendamos ese concepto aunque en origen se refiere exclusivamente a la aplicación de técnicas muy concretas y bien documentadas para mejorar el código. De ahí que aunque no sea demasiado correcto, se pueda entender también qué se quiere decir con refactorizar el diseño de una base de datos, un script para esto o lo otro, etc.
Ahora bien, ¿para qué refactorizar si un trozo de codigo o la misma solución en conjunto "ya funciona"?
Toda solución software debe ser diseñada para admitir cambios con facilidad, algo que le va a ocurrir con el 99% de probabilidad. Por tanto, refactorizar nos permite tener una aplicación con mejor diseño y mejor estructura, más entendible y más mantenible.
Pero también hay aspectos sutiles que solo se aprenden con la experiencia: refactorizando, conseguimos que nuestro código sea más testeable, pero también al revés: nuestro interés por hacer tests nos lleva a la obligación de refactorizar (consciente o inconscientemente). Este es uno de los aspectos del desarrollo de software que más me gustan, porque muchos elementos de la naturaleza del código son sutiles de asumir.
Refactorizar va de la mano de dos conceptos y está tan intrincados en ellos que es imposible hablar de una cosa sin la otra: la orientación a objetos y el testing.
Una aplicación programada en un lenguaje orientado a objetos permite una mejor estructura y una mejor solución porque nos permite abstraer un problema en entidades mentales más comprensibles a nuestro modo de pensar (objetos), mientras que el testing es un requisito ineludible para garantizar la calidad y el correcto funcionamiento de la misma.
Si cambiamos algo, por mínimo que sea en el código, ¿cómo sabemos que todo sigue funcionando? Exacto, con tests.
Por tanto, es imposible o incluso peligroso aplicar técnicas de refactoring si la aplicación no está suficientemente cubierta y respaldada por tests.
En software no es una buena práctica mejorar lo que sea al final, sino que es más productivo insertar toda mejora en el día a día, como parte misma de la actividad de desarrollo. Esto lo dice la teoría, pero es que lo he comprobado yo mismo a lo largo de todos estos años. Mejorar el código continuamente permite alcanzar más velocidad de desarrollo sin ninguna duda.
Esto es, las técnicas de refactoring se aplican continuamente y de forma incremental. Es sorprendente cómo pequeñísimos cambios en una aplicación pueden tener un efecto enorme y acumulativo en la aplicación final.
Existen muchas técnicas bien documentadas desde que apareció este concepto en el libro de Martin Fowler y Kent Beck, y muchas otras que diferentes autores han propuesto desde que en nuestra industria se asumió que el código se debe mejorar continuamente a pequeños pasos incrementales.
Incluso una aplicación que no ha sido refactorizada en este sentido nunca y que incluso no tiene buena cobertura de tests, se puede enriquecer aunque haya que comenzar desde cero su refactorización, aunque sea poco a poco.
Si quieres saber más sobre refactoring, detallo las técnicas más comunes en mi último trabajo El Libro Práctico del Programador Ágil, junto con infinidad de prácticas esenciales para crear código de calidad.