Definición
Refactorizar es cambiar cómo está escrito el código sin cambiar lo que hace. Desde fuera, el programa se comporta exactamente igual antes y después. Por dentro, es más fácil de leer, de cambiar y de entender.
La palabra clave es sin cambiar lo que hace. Si al reorganizar añades una funcionalidad o arreglas un fallo, eso ya no es refactorizar: son dos cosas mezcladas, y cuando algo se rompa no sabrás cuál fue.
Tampoco es reescribir. Reescribir es tirar y empezar de nuevo, con todo el riesgo de perder por el camino los cientos de casos particulares que el código viejo ya contemplaba. Refactorizar es una serie de pasos pequeños, cada uno de los cuales deja el programa funcionando.
Las señales de que hace falta
Se les llama code smells: no son errores, son síntomas.
- Código duplicado. Lo mismo escrito en tres sitios. Cuando cambie la regla, habrá que acordarse de los tres.
- Funciones larguísimas. Si no cabe en una pantalla, probablemente hace más de una cosa.
- Nombres que mienten. Una función que se llama
validary además guarda en la base de datos. - Demasiados parámetros. Siete argumentos suelen ser un objeto que no se ha creado.
- Condicionales anidados. Cuatro niveles de
ifdentro deif. - Comentarios que explican qué hace el código. Si hace falta explicarlo, casi siempre se puede reescribir para que se explique solo.
- Miedo. El síntoma definitivo: si te da miedo tocar un fichero, ese fichero necesita atención.
Cómo hacerlo sin romper nada
1. Asegura el comportamiento actual. Con tests si los hay. Si no, al menos una comprobación manual escrita de qué tiene que seguir funcionando.
2. Pasos pequeños. Extraer una función, renombrar una variable, sacar una condición a su propia función. Cada paso deja el programa entero.
3. Un commit por paso. Si algo se tuerce, vuelves un paso atrás y no un día entero.
4. No mezcles. Refactorizar y añadir funcionalidad son dos commits distintos, y a ser posible dos ratos distintos.
5. Aprovecha las herramientas. Renombrar con el editor es seguro; hacerlo con buscar y reemplazar, no. Con TypeScript, el compilador te enseña todos los sitios afectados.
Ejemplo práctico
Un caso de esta misma web que enseña el orden correcto.
Al reescribir la página de habilidades, dos cosas estaban mezcladas: el contenido era falso —listaba WordPress, Sanity y Vercel, que no uso— y la maqueta tenía un problema, cinco tarjetas en una rejilla que dejaba una huérfana.
La tentación era arreglarlo todo de una vez. Lo hice en pasos separados, y menos mal:
Primero cambié el contenido. Compilé, comprobé que la página seguía funcionando.
Después toqué la rejilla. Y al medirla apareció algo que no esperaba: el contenedor medía 884 píxeles cuando debía medir 1280. Ese fallo ya estaba antes, no lo había causado yo. Como el paso anterior estaba cerrado y verificado, supe inmediatamente que no era culpa del cambio de contenido, y pude buscar la causa real: mx-auto dentro de un contenedor flex encoge la caja al contenido en vez de estirarla.
Si hubiera cambiado las dos cosas a la vez, habría pasado un buen rato pensando que había roto la maqueta al tocar los textos.
La lección: refactorizar en pasos no es lentitud, es lo que permite saber qué causó qué.
Errores comunes
- La gran reescritura. "Esto está tan mal que lo hago de cero." Casi siempre acaba peor: el código viejo, por feo que sea, contiene años de casos particulares que nadie recuerda.
- Refactorizar sin red. Sin tests ni comprobación, estás cambiando a ciegas.
- Mezclar con funcionalidad. El commit que dice "refactor y arreglo del cálculo" es imposible de revisar y de revertir.
- Refactorizar código que nadie toca. Si un módulo lleva tres años funcionando y nadie lo abre, déjalo. El código feo que no se toca no cuesta dinero.
- Hacerlo por gusto estético. El objetivo es reducir el coste de los cambios futuros, no que quede bonito.
- Cambiar el nombre de todo a la vez. Un commit con doscientos ficheros hace ilegible el historial.
Cuándo hacerlo
El mejor momento es justo antes de tocar algo. Si vas a añadir una funcionalidad a un módulo enrevesado, ordénalo primero y añade después. Ordenar es más fácil sin la presión de la funcionalidad nueva, y añadir es más fácil sobre código ordenado.
El peor momento es "cuando tengamos tiempo". Ese momento no llega.
Y la pregunta que decide si merece la pena: ¿voy a volver a tocar esto? Si la respuesta es no, déjalo como está.