Evita hundirte en las arenas movedizas del Legacy Code.

¿Alguna vez has empezado a refactorizar legacy code y después de unas horas te has dado cuenta de que habías editado más del doble de los archivos que esperabas al principio? Seguramente quisiste hacer un commit mucho antes pero no eras capaz de que tu código compilase. Si alguna vez te has enfrentado a esta situación, este artículo puede ayudarte la próxima vez que estés refactorizando código.

Contexto

Desarrollar nuevas funciones sobre código legacy puede ser complicado. Es fácil sentirse abrumado por un montón de dependencias entre clases y código no testeado cuya funcionalidad debe conservarse, pero también adaptarse para satisfacer a los nuevos requerimientos.

Hace un mes en el equipo de Android apps de Habitissimo nos encontramos este desafío. Esta publicación es una descripción general y rápida de una técnica que encontramos útil para evitar commits demasiado grandes y pipelines rotos mientras íbamos progresando en la refactorización con cada commit que realizamos.

El objetivo de nuestra tarea era crear la pantalla para el nuevo dashboard de la aplicación. Esta funcionalidad iba a estar disponible solo en unos pocos mercados, por lo que necesitábamos mantener el dashboard anterior (a.k.a viejo dashboard) hasta que todos los mercados adoptaran el nuevo.

En base a eso, decidimos crear un nuevo módulo feature para el nuevo dashboard. También decidimos que el nuevo módulo feature no tendría dependencias con el módulo anterior. Esto nos obligó a crear un módulo base adicional para compartir el código y las dependencias necesarias para los dos módulos feature. Debido a esto, necesitábamos mover las dependencias necesarias del antiguo módulo feature al nuevo módulo base compartido.

Idealmente, después de terminar la refactorización, el módulo antiguo contendría únicamente el código que solo necesita el dashboard antiguo. Esto significa que, una vez que se lance el nuevo dashboard en todos los mercados, el módulo antiguo podría eliminarse de manera segura.

Nuestra primera aproximación

Una vez que tuvimos listo el nuevo módulo feature, comenzamos a crear la Activity, Fragments, ViewModels, etc. necesarios para manejar la nueva funcionalidad en nuestro nuevo módulo. En este punto, nos dimos cuenta de que necesitábamos mover algunas clases del módulo del viejo dashboard para reutilizar algunos comportamientos existentes: interactors, repositorios, mapeadores…

Elegimos nuestra primera víctima (la que está en rojo en el diagrama) y la arrastramos al nuevo módulo. Y eso fue todo, así conseguimos tener nuestra primera clase refactorizada y pudimos usarla en el nuevo dashboard.

Pero como dice uno de nuestros compañeros “La vida no es un picnic” y la cruda realidad pronto empezó a golpearnos en la cara. Después de mover esta clase al nuevo módulo, el proyecto ya compilaba. Nos dimos cuenta de que esta clase tenía algunas dependencias con otras clases del módulo antiguo, por lo que también procedimos a mover esas clases al nuevo módulo.

Recordando lo que definimos al principio, las clases del nuevo módulo feature no pueden tener dependencias con el módulo antiguo. Unas horas después de iniciar la refactorización, nos dimos cuenta de que habíamos estado moviendo clases siguiendo el gráfico de dependencias y el proyecto aún estaba muy lejos de compilar. Nos hundíamos en arenas movedizas: cada vez que solucionábamos un problema, surgían otros dos más.

El Método Mikado

Para pequeños cambios, se pueden mantener las cosas en la cabeza, pero para cambios más grandes, las posibilidades de perderse en una jungla de dependencias o en un desierto de código roto aumentan drásticamente. El Método Mikado es el mapa para guiar en reestructuraciones de tareas que llevan días o semanas, ayudándote a dividir casi cualquier problema en pequeñas porciones conquistables.

Hay cuatro conceptos básicos y bien conocidos que resumen el “proceso” que sigue el Método Mikado:

  • Fijar un objetivo: piensa lo que quieres conseguir. (E.g. compartir un interactor entre dos dashboards). El objetivo tiene dos propósitos:
    1. Representa un punto de inicio para los cambios.
    2. Determina si el método ha tenido éxito o no..
  • Experimentos: Un experimento es un procedimiento que hace un descubrimiento o establece la validez de una hipótesis, es decir, los cambios aplicados al código. (Por ejemplo, mover un método de una clase a otra, extraer una clase o reducir el scope de una variable…)
  • Visualización: Anotar el objetivo y los requisitos previos para ese objetivo.
  • Deshacer: cuando un experimento para implementar un objetivo o un prerrequisito ha roto tu sistema y has visualizado lo que necesita cambiar en el sistema para evitar que se rompa, restaura los cambios que hiciste a un estado de trabajo anterior.

El método Mikado propone una solución sencilla. Para cada cambio, cuando encuentras las dependencias que generan errores una vez que realizas este cambio, crea un gráfico que muestra estos errores, junto con lo que debe hacerse para corregirlos antes de realizar el cambio. Luego revierte su cambio y comienza a mirar una hoja en ese gráfico. Soluciona ese error, comprueba si eso causa más problemas; si lo hace, repite el proceso, continúa dibujando más hojas en el gráfico con detalles de qué más debe cambiarse, deshaz todos los cambios en el código y comienza a trabajar en la hoja nuevamente.

En cada punto en el que reviertes el código, es posible que sientas que has vuelto al punto inicial, pero no es así; en realidad, tienes más información que cuando empezaste. Además, siempre estás trabajando con código que se compila (¡y pasa las tests!), en lugar de tener mucho código que no se está compilando, por lo que es posible utilizar las herramientas de refactorización del IDE.

Cada vez que se soluciona un problema de un nodo hoja y no genera más errores, el estado se puede verificar muy fácilmente y la hoja se puede marcar en verde; una vez que todas las hojas de un nodo son verdes, puede comenzar a trabajar en ese nodo y así sucesivamente hasta que termine el cambio original.

Nuestra segunda aproximación

Siguiendo los pasos definidos por el Método Mikado fijamos nuestras metas y movimos las clases de la misma forma que lo hicimos en la primera aproximación. Pero esta vez, utilizando el gráfico Mikado, identificamos los elementos más internos (los nodos hoja que se muestran en verde en la imagen a continuación). Después de deshacer los cambios, comenzamos la refactorización moviendo esas clases al nuevo paquete.

Después de aplicar algunas iteraciones, pudimos refactorizar todas las clases que tenían dependencias con nuestro nuevo código. Además, con cada iteración pudimos hacer commits pequeños, simples y claros mientras manteníamos nuestros tests actualizados y ejecutándose con éxito todo el tiempo.

Por qué se llama “Mikado”

Es una referencia al juego de seleccionar palitos de Mikado.

Cada palo está enredado con docenas de otros palos: dependencias molestas y ajustes que necesita hacer,para que el código siga funcionando. La estrategia es eliminar primero los palos fáciles. Los que no están enredados. Progresivamente, tu palo objetivo se va desenredando hasta que puedas alcanzarlo sin romper nada.

Incluso hay un libro que profundiza en los detalles de este proceso: The Mikado Method.

Con un poco de práctica cada vez se te dará mejor y te convertirás en un desarrollador mucho más eficiente.

Podéis encontrar el artículo original publicado en Medium en el siguiente enlace:

https://mreigosa.medium.com/smooth-code-refactors-using-the-mikado-method-a69095988718

One thought on “Refactorizar código de forma sencilla con el Método Mikado

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.