Refactorizar antes de reescribir

¿Por qué refactorizar es importante?

Refactorizar código tiene una serie de ventajas. Desde asegurar que el código sea limpio y, por ende, más fácil de entender y sustentar, hasta ahorrar tiempo y dinero, ya que reduce la probabilidad de errores futuros y facilita la implementación de nuevo software.

En este post, explico las principales ventajas de la refactorización y su uso como un primer paso a la modernización de aplicaciones legacy antes de emprender un proyecto de modernización total de software.

También examino una de las distintas maneras de empezar a refactorizar código legacy y comparto una kata que servirá como ejemplo práctico.

¿Cómo las empresas acaban con deuda técnica no gestionada?

Imagínate una empresa pequeña que en los últimos 10 años ha crecido rápidamente en su mercado. Su producto de software ha desempeñado un papel importante en su éxito. Otros en el mercado crean productos similares, productos con una una marca trendy y una UI elegante, pero para los clientes de esta empresa nada puede sustituir el valor que aporta su software. A primera vista suena bien: muchos clientes satisfechos, crecimiento estable y un producto de gran valor que lo respalda.

Pero hay un problema, y este problema también es la razón del éxito de la empresa. En los últimos 10 años el producto se ha iterado rápidamente y la deuda técnica se ha acumulado sin ninguna intención de ser pagada. Nuevos miembros del equipo han ido y venido, y este rápido crecimiento ha supuesto la inclusión de una función tras otra sin prospectiva de su mantenimiento futuro.

Esto ha empezado a notarse: los problemas de rendimiento abundan, la productividad se está viendo afectada, las funciones tardan cada vez más en añadirse, la teoría de las ventanas rotas se ha  difundido y la calidad del producto se deteriora con cada iteración.

El management de la empresa no está ciega ante la situación y reconocen estos problemas. Se han contratado nuevos empleados para ayudar a apagar el incendio, se está dedicando tiempo a conseguir resultados rápidos y, sobre todo, se ha decidido reescribir todo el sistema en el futuro.

¿Existen pasos hacia la modernización antes generar la alineación empresarial?

A lo largo de nuestras carreras, la mayoría de los desarrolladores habremos trabajado al menos en una empresa como esta, y el enfoque suele variar. En algunos casos han aplicado la famosa reescritura, en otros el producto se ha dividido en servicios más pequeños creando un monolito distribuido. Con mucha frecuencia, la reescritura se pospone año tras año por limitaciones financieras o la falta de confianza en el equipo de desarrollo. 

Creo que estas estrategias son realmente válidas. Cada una tiene méritos específicos para el negocio. Sin embargo, pienso que antes de considerar los pros y los contras de cualquiera de estos enfoques se debería hacer una refactorización del codebase. 

Por dónde empezar la refactorización de aplicaciones legacy 

Las aplicaciones que la mayoría de las personas considera como legacy suelen tener ciertas características en común: utilizan frameworks y patrones obsoletos, tienen poca cobertura de testing, alta complejidad ciclomática, mucho acoplamiento y baja cohesión. 

Lo más destacable es que la lógica de dominio está dispersa por todo el sistema. Esto es lo que impide que los desarrolladores reescriban eficazmente el software desde cero o que incluso reescriban grandes partes a la vez utilizando herramientas como el patrón Fig Strangler. El valor reside en la lógica dispersa por el codebase. Pero si no somos capaces de entender realmente de dónde viene este valor, no podemos pretender reescribir el software con eficacia. 

Entonces, ¿cómo empezamos a refactorizar una aplicación legacy cuando no sabemos dónde está la lógica valiosa?

Realiza mejoras incrementales al refactorizar código legacy

Creo que la respuesta está en refactorizar pequeñas partes del sistema a la vez, en lugar de intentar hacer cambios arquitectónicos radicales que inevitablemente romperán alguna parte de la lógica. Si nos enfocamos en algo tan pequeño como una clase, un método o una función a la vez, podremos hacer pequeños cambios incrementales que mejorarán la calidad general del codebase sin arriesgar perder la valiosa lógica empresarial.

Deberíamos mitigar aún más este riesgo adoptando un enfoque Green, Refactor, Green de TDD. Esto se puede lograr siguiendo los siguientes pasos:

  • Escribe un test unitario superable 
  • Refactoriza el código
  • Asegúrate de que el nuevo código pase el test 
  • Refactoriza el test si es necesario  

Al seguir estos pasos podemos descubrir cuál es la funcionalidad prevista y mejorar las partes valiosas del código a un nivel básico. 

Los largos y complejos métodos y clases pueden dividirse en partes más pequeñas. Se puede eliminar la lógica superflua y aclarar la intención. Todo ello con la seguridad de que el sistema seguirá comportándose como se espera en producción. 

Si simplemente hubiéramos reescrito esta parte del sistema desde cero, no hubiésemos podido estar seguros de que se cubrieran todos los caminos del código y de que el nuevo sistema continuase a comportarse de la misma manera que el anterior. 

Practica la refactorización utilizando un enfoque incremental con la kata Gilded Rose.

Recomiendo encarecidamente trabajar en la kata Gilded Rose, ya sea en Katalyst o en github, utilizando el enfoque descrito anteriormente y asegurándote de seguir estrictamente las reglas definidas. Es un gran ejemplo, aunque ligeramente artificial, de refactorización de código legacy.

No vas a arreglar todo lo que está mal con el código y eso está bien. Después de que la parte más compleja del sistema se vea mejor, el resto se vuelve fácil. Si después de trabajar a través de la kata, todavía no estás convencido del enfoque, intenta hacerlo de nuevo, pero añade primero la función de "objetos conjurados". ¿Qué enfoque prefieres?

¿Cuándo utilizar un enfoque de refactorización incremental?

El enfoque Green, Refactor, Green funciona bien si puedes dedicarle tiempo a cada iteración, sprint o semana y concentrarte en un área concreta del sistema. Dicho esto, también funciona si las limitaciones de la empresa no permiten dedicar tiempo a la refactorización. Si practicas esto cada vez que soluciones un error o añadas una nueva funcionalidad, verás beneficios muy rápidamente.

Como habrás visto en la kata de Gilded Rose, a veces es más rápido refactorizar el código que es difícil de entender a añadirle directamente una lógica nueva. No sólo eso, sino que hacer esto puede animar a otros miembros de tu equipo a empezar a hacer lo mismo y, antes de que te des cuenta, tendrás a todo un equipo de personas trabajando conscientemente para mejorar un producto que antes se consideraba irreparable desde el punto de vista económico. 

A este punto, empiezan a surgir patrones, el acoplamiento disminuye y la cohesión aumenta. El sistema se vuelve más estable. La valiosa lógica empresarial se hace más visible y fácil de mantener. Las cosas que no pertenecen al codebase se hacen más evidentes y fáciles de eliminar o de manejar en otro lugar. 

Ventajas de refactorizar antes de empezar a modernizar

Este enfoque permite realizar cambios de forma segura para proteger el valor existente del código. Evita romper cosas mientras se separan grandes partes del sistema e, incluso, precipitarse para alcanzar la paridad de características en una nueva y reluciente versión del producto y volver a cometer los mismos errores.

Este enfoque permite realizar cambios de forma segura para proteger el valor existente del código.

Para que quede claro, no digo que que el software no deba reescribirse y ser sustituido. Tampoco digo que la refactorización sea una solución mágica que arregle todos los errores. Solo comparto cómo crear un punto de partida mejor para realizar una modernización de software más agresiva dedicando una pequeña cantidad de tiempo y esfuerzo a estabilizar y mejorar pequeñas partes de un sistema. La ventaja de este enfoque es que puede ayudar a aumentar la confianza del equipo y de la empresa en ti, en el producto y en sí mismos.

cuual_es_la_madurez_de_tus_sistemas