Los sistemas antiguos tienen dos cosas en común: funcionan, y ponen nervioso a todo el mundo. Funcionan porque años de uso real los moldearon en torno al negocio de verdad. Ponen nerviosa a la gente porque nadie quiere tocarlos, la documentación es escasa, y uno de los desarrolladores originales se marchó hace tres trabajos.
Qué cuenta realmente como "legado"
No todo sistema antiguo es legado. Una aplicación de 10 años funcionando sobre un stack con soporte, bien probada, con mantenimiento activo, es simplemente madura. Legado normalmente significa:
- Versiones de plataforma o framework que ya no reciben actualizaciones de seguridad.
- Conocimiento concentrado en una o dos personas.
- Miedo a cambiar cosas porque pequeños cambios rompen funciones lejanas.
- Despliegues manuales, pruebas manuales, todo manual.
- Una brecha creciente entre lo que el negocio necesita y lo que el sistema puede hacer.
Los dos enfoques que fracasan
- No hacer nada. El riesgo se acumula. Tarde o temprano algo crítico se rompe en un mal momento.
- Reescribir desde cero. Una reescritura de golpe casi siempre lleva más tiempo del planeado, descubre requisitos ocultos demasiado tarde, y corre en paralelo con el sistema antiguo durante tanto tiempo que el equipo se rinde.
Los enfoques que realmente funcionan se sitúan entre estos dos extremos.
Estrategia 1: estrangular el sistema antiguo
Mantén el sistema legado funcionando, pero enruta gradualmente la nueva funcionalidad a través de una nueva capa. Con el tiempo, cada vez más funciones viven en el nuevo sistema; el antiguo se va reduciendo hasta que finalmente se apaga.
Esto funciona para sitios web (mover página a página), herramientas internas (mover módulo a módulo) e integraciones (introducir una nueva pasarela de API y luego redirigir a los llamadores hacia ella).
Estrategia 2: modernizar in situ
A veces la arquitectura está bien, pero el stack es demasiado antiguo. En ese caso, actualiza una capa cada vez: lenguaje/runtime, luego librerías, luego framework, luego despliegue. Añade pruebas automatizadas sobre la marcha, para que cada paso sea verificable. No es glamuroso, pero es muy eficaz.
Estrategia 3: dividir y luego modernizar
Los grandes sistemas monolíticos suelen ser más fáciles de modernizar pieza a pieza una vez que las costuras están claras. Eso significa primero definir límites dentro del sistema existente (módulos, contextos delimitados), y luego reemplazar módulos de forma independiente. Los límites importan más que la elección de tecnología.
Qué hacer antes de escribir una sola línea de código
- Anota lo que el sistema hace hoy en realidad, no lo que se suponía que debía hacer.
- Identifica las funciones de las que la gente depende a diario, esas deben seguir funcionando durante la transición.
- Identifica funciones muertas, a menudo son una parte significativa del código y deberían retirarse en silencio.
- Haz inventario de datos, integraciones, permisos y exportaciones. Aquí es donde los proyectos de modernización suelen meterse en problemas.
- Decide qué significa "hecho" en términos medibles.
Controles de riesgo que marcan una verdadera diferencia
- Copias de seguridad y restauraciones probadas, antes de cualquier cambio.
- Pruebas automatizadas para el comportamiento que te importa, introducidas gradualmente.
- Feature flags para que los cambios puedan revertirse en producción sin un despliegue.
- Observabilidad, registros, seguimiento de errores, métricas de negocio clave, para que descubras rápido si algo ha retrocedido.
- Un despliegue por fases: el nuevo código se ejecuta primero para usuarios internos, luego un pequeño porcentaje de usuarios reales, luego todos.
Patrón strangler fig. En lugar de una arriesgada reescritura de golpe, enrutas el tráfico a través de una fachada fina que reenvía gradualmente cada vez más peticiones a nuevos servicios, mientras el sistema legado sigue atendiendo el resto hasta que queda vacío.
Ejemplo: una ruta mínima de fachada
Una pequeña pasarela puede decidir por ruta si una petición va al monolito legado o al nuevo servicio. Escrito de forma neutra respecto al framework:
app.use((req, res, next) => {
if (req.path.startsWith('/api/v2/invoices')) {
return proxy(req, res, NEW_SERVICE_URL);
}
return proxy(req, res, LEGACY_URL);
});
Cada módulo migrado mueve un prefijo de LEGACY_URL a NEW_SERVICE_URL. Los usuarios nunca ven un corte de golpe; el equipo entrega pasos pequeños y reversibles.
Trampa habitual: escribir el nuevo sistema y el antiguo como si tuvieran que ser equivalentes en funciones desde el primer día. No tienen por qué. Empieza con la pieza más pequeña que tenga valor de negocio real, demuestra la ruta de migración, y luego repite.
Errores habituales
- Convertir la modernización en una cuestión de elección de tecnología. Los stacks nuevos y vistosos no ayudan si el modelo de datos y las integraciones siguen siendo un misterio.
- Esperar a un rediseño completo antes de arreglar los problemas visibles. Los usuarios pagan el coste; el impulso muere.
- Apagar el sistema antiguo demasiado pronto. Haz funcionar ambos en paralelo el tiempo suficiente para confiar en el nuevo.
- Perder el conocimiento institucional. Empareja con las personas que conocen el sistema. Su conocimiento es el verdadero activo.
Un cronograma realista
- Estabiliza: copias de seguridad, monitorización, parches de seguridad, pruebas mínimas.
- Mapea: documenta qué existe y qué se usa realmente.
- Prioriza: elige la pieza que duele ahora y es valiosa de modernizar primero.
- Reemplaza: construye la nueva versión de esa pieza junto a la antigua.
- Cambia: enruta el tráfico, monitoriza, corrige problemas.
- Retira: da de baja la pieza antigua solo después de que haya estado tranquila un tiempo.
- Repite para la siguiente pieza.

