«No puedo corregir este código porque no sé dónde fallará si lo toco».
Todos hemos dicho esto alguna vez frente a código heredado.
Vayamos al grano. Antes de refactorizar, lo único que hay que hacer es crear «una prueba que capture exactamente el comportamiento actual».
Antes de embellecer el código, fija qué hace realmente ahora.
Hoy explicaré el orden exacto que seguí al añadir pruebas a código heredado real. Respetar la secuencia reduce mucho los accidentes al romper algo mientras lo corriges.
¿Por qué las pruebas van antes que la refactorización?
Empecemos por definir la refactorización.
La refactorización mejora la estructura interna sin cambiar el comportamiento observable.
La clave es la promesa de «no cambiar el comportamiento».
¿Cómo verificamos esa promesa? Con pruebas.
Sin pruebas, desplegamos basándonos en la sensación de que «parece que no ha cambiado». Desplegar código de pagos por intuición… da escalofríos solo de imaginarlo.
Por eso Michael Feathers lo define así en Working Effectively with Legacy Code: «el código heredado es código sin pruebas».
El criterio no es si es antiguo, sino si cuenta con una red de seguridad.
Lista de comprobación antes de refactorizar (resumen clave)
Para quienes tienen prisa, este es el orden.
- Definir primero el alcance (límite) del código que se tocará
- Añadir pruebas de caracterización que registren el comportamiento actual
- Comprobar que las pruebas están en verde (correctas)
- Solo entonces refactorizar en unidades pequeñas
- Volver a ejecutar las pruebas en cada paso
Con hacer estas cinco cosas en orden ya tendrás medio trabajo hecho. Vamos a desglosarlas.
¿Cómo se añaden pruebas de caracterización?
Es la pregunta más frecuente: «No conozco el comportamiento, ¿qué prueba escribo?»
Hay que invertir la perspectiva. No escribes la prueba conociendo la respuesta correcta: fijas como respuesta el resultado actual que devuelve el código.
Esto se llama prueba de caracterización (Characterization Test).
El método es sorprendentemente sencillo. Introduce cualquier valor aproximado y ejecuta la prueba. Fallará y te dirá «este era el valor real»; copia ese valor tal cual y listo.
// 1) Introducir deliberadamente un valor incorrecto porque se desconoce el valor de retorno real
@Test func cálculo del descuento_comportamiento actual() {
let result = calcDiscount(user: user, cart: cart)
#expect(result == 0) // Fallar y mostrar el valor real
}
// 2) Fijar el valor real mostrado en el mensaje de error (p. ej.: 1500))
// #expect(result == 1500)
Ahora esta prueba se convierte en el guardián del hecho de que «este código originalmente devuelve 1500».
Si más tarde un error de refactorización produce 1200, la prueba lo detectará de inmediato con una luz roja.
Todavía no juzgamos si el código es bueno o malo. El objetivo es capturar su estado actual.
¿Qué hacer con el código al que no se pueden añadir pruebas?
Este es el verdadero muro del código heredado. Si elementos intocables como una DB, una API externa o la hora actual están incrustados en mitad de una función, la prueba no puede ejecutarse.
Aquí ayuda el concepto de «seam» de Feathers: interrumpir ligeramente el flujo para crear un punto donde insertar valores falsos.
La opción más segura es extraer como parámetro de la función solo la línea estrictamente necesaria.
Por ejemplo, si la función llama directamente a 현재시간(), basta con cambiarla para recibirlo como argumento. Así puedes introducir la hora que quieras en la prueba.
Una advertencia: incluso este cambio mínimo para añadir pruebas debe hacerse de forma mecánica y cuidadosa. Esa zona aún no tiene red de seguridad.
Preguntas frecuentes (Q&A)
P. ¿Qué porcentaje de cobertura debo alcanzar antes de empezar?
No necesitas cubrirlo todo. Basta con envolver la parte que vas a tocar ahora, ese límite. El 100 % suele ser una trampa, no un objetivo.
P. ¿Y si no tengo tiempo para añadir pruebas?
Conozco bien esa presión. En ese caso, cubre solo la función que vas a corregir con una prueba de caracterización antes de empezar. Cinco minutos pueden evitar el peor incidente.
P. ¿Está bien que las pruebas sean desordenadas?
Sí. Las pruebas de caracterización son como un andamio provisional. Cuando termine la refactorización y el código esté limpio, puedes limpiar también las pruebas.
Al final, todo se reduce a una secuencia: capturar (prueba) → cambiar (refactorización) → verificar.
El código heredado da miedo no porque sea malo, sino porque no tiene red de seguridad. Elige hoy una sola función y añádele una prueba de caracterización. A partir de ahí, todo resulta mucho más fácil. ¡Ánimo!

