Me prometí que esta semana escribiría sobre temas más ligeros. Pero el lunes terminó oficialmente mi aventura con JSNation, y me di cuenta de que no había escrito ni un solo artículo sobre mi charla. Así que aquí vamos.
Sobre JSNation: tengo sentimientos encontrados. Me seleccionaron para la pista virtual y, a pesar del increíble profesionalismo de los organizadores, no fue lo mismo que estar ahí en persona. Podría haber estado en Ámsterdam tomando cerveza con Wes Bos, pero en lugar de eso veía mi propia charla desde la cocina mientras respondía mensajes de Teams. 😄 Aun así, fue una gran experiencia.
Mi charla se llamaba “¿Reescribir o Refactorizar? Cómo migrar aplicaciones legacy a frameworks modernos de forma segura”. Y créanme, soy la persona indicada para hablar de esto. He migrado montones de aplicaciones frontend: Angular viejo a moderno, ASP.NET MVC 5 a Vue, React con componentes de clase a React moderno, etc.
La razón es simple: cuando una empresa necesita una migración urgentemente y recibe el CV de alguien que ya ha hecho una, suele contratarlo felizmente — incluso por un salario ligeramente superior al del mercado. 😄 Así fue como me convertí en experta en migraciones frontend.
¿Pero por qué migrar?
Antes de hablar de estrategias, hay que responder una pregunta más fundamental: ¿por qué migrar?
He escuchado esta pregunta muchas veces de stakeholders no técnicos. “Si la aplicación funciona, ¿para qué tocarla? Los desarrolladores solo persiguen tendencias y quieren tecnologías brillantes en su CV, ¿cierto?”
Mi respuesta es simple y honesta.
No soy fanática de ningún framework en particular. Para mí son herramientas. Como martillos. Pero cuando construyo algo, prefiero usar un martillo sólido y confiable que uno oxidado al que le faltan piezas. 😉
Pero la migración no es solo sobre experiencia de desarrollo. En realidad, se trata de la supervivencia de tu producto. Hoy que la tecnología evoluciona más rápido que nunca y la IA acelera todo aún más.
La seguridad, por ejemplo. Cuando una librería queda obsoleta y nadie la mantiene, los parches de seguridad dejan de llegar. Los reportes de vulnerabilidades empiezan a ponerse rojos. Ningún product owner ha dicho jamás: “Claro, sacrifiquemos seguridad por unas cuantas features extra”.
Y sin mencionar las herramientas modernas. Solo Vite puede marcar una diferencia enorme. Las aplicaciones modernas son simplemente más rápidas, y las aplicaciones más rápidas son mejores aplicaciones.
“Cuanto más postergues una migración, más difícil y costosa se vuelve.”
OK, quiero migrar. ¿Pero cómo?
Curiosamente, la llegada de los LLMs no ha cambiado las estrategias de migración. Son básicamente las mismas que antes de que la IA se volviera mainstream. A veces el trabajo es más rápido, a veces no, pero los enfoques subyacentes siguen siendo los mismos.
Hay tantas estrategias de migración como ingenieros seniors, pero en la práctica caen en dos categorías:
Rewrite (Estrategia Big Bang)
Reescribir toda la aplicación — o, como suele pasar en frontend, actualizar un stack antiguo directamente a la última versión del framework.
Refactor (Patrón Strangler)
Reescribir la aplicación pieza por pieza mientras se siguen entregando funcionalidades.
¿Cuál enfoque es mejor?
Honestamente, no vale la pena iniciar una guerra santa. En la mayoría de los casos, las realidades de tu proyecto deciden por ti.
Si tu aplicación es relativamente pequeña, tu equipo tiene experiencia, la documentación es decente y puedes pausar el desarrollo de funcionalidades por algunas semanas o meses, un rewrite puede ser una opción perfectamente razonable.
En cambio, si trabajas en un proyecto enorme con muchos desarrolladores juniors o personas que no conocen la tecnología, y la documentación es prácticamente inexistente — un refactor incremental es probablemente tu única opción realista.
| Factor | Rewrite (Big Bang) | Refactor (Strangler) |
|---|---|---|
| Tamaño de la app | Pequeño o mediano | Grande |
| Desarrollo de features | Se puede pausar | Debe continuar |
| Documentación | Buena | Pobre |
| Experiencia del equipo | Alta | Mixta |
| Tolerancia al riesgo | Alta | Baja |
| Tiempo a primeros resultados | Corto | Largo |
| Complejidad durante migración | Baja | Alta |
| Riesgo de migración infinita | Bajo | Alto |
Big Bang — No da tanto miedo como parece
Tengo que admitirlo: me gusta esta estrategia. Claro, los libros y artículos suelen describirla como riesgosa, a veces incluso como un anti-patrón. Y tienen razón… si hablamos de un monolito Java de veinte años que nadie se atreve a tocar.
Pero muchas aplicaciones frontend son relativamente jóvenes y pequeñas. En esos casos, un enfoque Big Bang puede ser simplemente más rápido y más barato.
Las reescrituras completas son raras hoy en día. Pero las actualizaciones grandes son bastante comunes: por ejemplo, migrar de Angular 7 a Angular moderno con Signals, o de React antiguo con componentes de clase a React moderno con hooks.
Ejemplo real: Migración Big Bang de Angular 7 a Angular moderno
A veces nos preguntamos cómo un equipo pudo dejar una aplicación años sin actualizaciones. En realidad, es sorprendentemente fácil, especialmente cuando nadie trabaja activamente en ella porque el producto está en modo mantenimiento.
Ese fue exactamente el caso.
Un día, los stakeholders decidieron expandir la aplicación significativamente. Los convencí de actualizar a la última versión de Angular, principalmente por razones de seguridad — el sistema almacenaba datos críticos.
Cuatro desarrolladores la actualizaron a la última versión en cuatro meses. Y resultó ser mucho más difícil de lo que esperaba.
“Si tienes una librería que no se ha actualizado en años, cuyo autor parece haberla olvidado, y ni siquiera compila en Node 18, eso no es ‘código que funciona’. Es una bomba de tiempo.”
El framework rara vez es el problema real. El ecosistema a su alrededor sí lo es.
Por ejemplo, nuestra librería de componentes principal introdujo cambios importantes de sintaxis entre las versiones 12 y 13. ¡Imagina cuántos lugares necesitaban actualizarse! Claro, la IA puede ayudar, pero si un ingeniero de UI decide renombrar clases CSS y cambiar estructuras de componentes, la IA no te salvará.
Otra lección importante: ten siempre una estrategia sólida de branching para hotfixes. Asume que algo saldrá mal.
Afortunadamente, la migración fue un éxito. Los tiempos de build mejoraron drásticamente. El bundle se redujo significativamente. El nuevo layout se veía mucho mejor que el anterior. Y lo más importante: la aplicación quedó lista para futuras actualizaciones.
Migración incremental — Tener el pastel y comérselo también
A veces tu aplicación es demasiado grande para un enfoque Big Bang, o no puedes permitirte dejar de entregar funcionalidades. En ese caso, la migración incremental es la única opción realista.
Este tipo de refactorización paso a paso se implementa usualmente usando el Patrón Strangler. Es elegante, relativamente simple y probado por algunas de las empresas tecnológicas más grandes del mundo.
El concepto es simple: comienzas con tu aplicación legacy. Construyes una nueva aplicación a su lado. Colocas un reverse proxy al frente que decide qué rutas van a qué aplicación. Luego migras pantalla por pantalla, ruta por ruta. Con el tiempo, la aplicación legacy se vuelve más pequeña hasta que solo queda la nueva.
En este escenario, ambas aplicaciones deben compartir autenticación y backend, pero no código frontend.
“Todos sabemos lo que significa ‘temporal’ en ingeniería de software. Para siempre.” 😅
Ejemplo real: Migración de Backbone a Vue con Strangler Pattern
Esta era una aplicación antigua escrita en Backbone — y tengo que admitir que estaba muy bien escrita. Pero seguía siendo Backbone. 😄
La aplicación no era enorme, así que un Big Bang era teóricamente posible. Pero éramos una startup, y un día escuchamos las famosas palabras: “Chicos, acabamos de vender una funcionalidad que aún no existe. Tienen tres meses para entregarla. ¡Diviértanse!”
Mi equipo consistía en mí y… dos desarrolladores Java junior que, lo juro, escucharon la palabra “TypeScript” por primera vez en sus vidas.
Afortunadamente, eran ambiciosos y aprendieron rápido. Esa es otra ventaja de esta estrategia: los desarrolladores juniors pueden aprender mientras tanto.
Creé una nueva aplicación Vue en paralelo. Migré la pantalla de login como prueba de concepto. Luego pasamos a la nueva funcionalidad que el cliente ya había pagado.
Durante el año siguiente, migramos la aplicación pantalla por pantalla. Al final, Backbone desapareció por completo. Durante todo el proceso, tuvimos cero downtime. Los clientes ni siquiera notaron que estaba ocurriendo una migración.
El enfoque Strangler no es todo color de rosas. El proceso toma mucho tiempo — en nuestro caso, con una aplicación relativamente pequeña, tomó un año entero. Y existe el peligro de la migración interminable: los deadlines se acumulan, las features siguen llegando, y terminas suplicando a tu product owner que libere algunos story points para la migración.
Palabras finales
Migrar aplicaciones legacy es como comprar una casa vieja. No es tu culpa que se esté cayendo a pedazos, pero es tu responsabilidad ponerla en forma. Puedes tomarte unas semanas y renovarlo todo de una vez, o mudarte y arreglar un cuarto a la vez.
La única estrategia realmente mala es no hacer nada. Tarde o temprano tendrás que migrar de todos modos — solo que lo harás por un incidente en producción. 😉
Así que, ¿cuál es tu enfoque para renovar aplicaciones legacy?
¿Te gustó este artículo? Si trabajas con código legacy y estás considerando una migración, en DojoFullStack exploramos estrategias prácticas de arquitectura de software. Y si necesitas transcribir tus reuniones técnicas para no perder detalle, prueba meettalky — 300 minutos gratis al mes sin tarjeta de crédito.