Cómo migrar de AngularJS a Angular sin downtime
La forma segura de salir de AngularJS es hacer convivir el código viejo con el nuevo y mover una pieza a la vez. Así funciona en la práctica.
El principio: strangler fig
En vez de construir una app nueva al lado de la vieja y cambiar todo en un solo día, haces crecer el código nuevo dentro de la app existente. Cada pantalla que pasa a Angular moderno se publica por separado. La parte en AngularJS se achica hasta desaparecer. Los usuarios siguen usando el producto todo el tiempo y el equipo sigue entregando funcionalidades.
Angular soporta esto directamente con sus herramientas de upgrade, que permiten que una aplicación Angular aloje módulos AngularJS, comparta servicios en ambas direcciones y enrute entre los dos.
Paso 0: Diagnóstico (1–2 semanas)
Antes de escribir código de migración, arma un inventario:
- Módulos y pantallas, con un ranking aproximado de uso. Las pantallas que nadie usa simplemente se borran.
- Rutas (normalmente estados de ui-router) y enlaces profundos que los clientes usan.
- Servicios: wrappers de
$http, autenticación y sesión, estado compartido en$rootScope. - Directivas y componentes, y cuánta lógica vive en controladores versus templates.
- Dependencias de terceros: librerías de AngularJS, plugins de jQuery, widgets de fechas y grillas. Son los bloqueos habituales.
- Pruebas y cadena de build: qué existe, qué corre, qué versión de Node necesita todo.
De ahí sale el piloto: un módulo lo bastante valioso como para importar y lo bastante aislado como para ser de bajo riesgo. Reportes, configuración y dashboards son candidatos típicos. El login y la pantalla principal de listado, normalmente no.
Paso 1: Preparar el código legacy para la migración
Migrar es mucho más fácil cuando el código AngularJS ya se parece a código moderno. Sin cambiar comportamiento:
- Pasa al estilo de componentes de AngularJS 1.5 (
.component()en vez de controladores con scope). - Reemplaza
$scopeporcontrollerAsdonde sea barato. - Encapsula la manipulación directa del DOM con jQuery en directivas propias, para poder reemplazarlas después.
- Pasa el código por un build moderno (TypeScript, módulos ES) para que ambos frameworks compartan una sola cadena de herramientas.
Este paso por sí solo reduce el esfuerzo de migración, porque cada componente que se migra después sigue la misma forma.
Paso 2: Arrancar la aplicación híbrida
La aplicación Angular arranca primero y levanta la app AngularJS adentro. Aquí hay que resolver dos cosas, y son las mismas en toda migración:
- Una sola sesión. Autenticación, tokens y usuario actual viven en un solo servicio, expuesto a ambos frameworks. Los usuarios nunca deben iniciar sesión dos veces ni perder la sesión porque una pantalla cambió de framework.
- Convivencia de rutas. El router de Angular es dueño de las rutas nuevas; el router legacy conserva las viejas. Una ruta comodín entrega todo lo desconocido al lado legacy, así cada enlace existente sigue funcionando.
Al final de este paso los usuarios no ven nada distinto. Ese es el punto: el piloto se publica sin cambios visibles y demuestra que la "plomería" funciona.
Paso 3: Migrar servicios antes que pantallas
Los servicios son las hojas del árbol de dependencias. Reescríbelos en Angular primero y luego degrádalos (downgrade) para que las pantallas legacy los sigan usando. Así las pantallas nuevas y las viejas comparten la misma capa de datos, y nunca duplicas estado durante la migración.
Orden típico: capa HTTP con interceptores, autenticación, configuración, y después los servicios de dominio.
Paso 4: Mover pantallas de a una, detrás de flags
Cada pantalla se reconstruye en Angular con el mismo diseño y el mismo comportamiento. Que sea el mismo diseño importa: una migración no es un rediseño, y mezclar ambos duplica el riesgo y el esfuerzo de pruebas.
Publica cada pantalla detrás de un feature flag o un cambio de ruta. Si algo sale mal, vuelves atrás en minutos. Cuando una pantalla lleva un tiempo estable en producción, borra la versión AngularJS. No mantengas las dos.
Prioriza por riesgo y valor: primero pantallas hoja, luego las más usadas cuando los patrones ya estén probados, y el shell compartido (navegación, layout) cuando sea conveniente.
Paso 5: Probar lo que importa
- Pruebas end-to-end de los flujos críticos (login, la transacción principal, exportaciones). Escríbelas primero contra la app legacy; son tu verificación de paridad para cada pantalla migrada.
- Pruebas unitarias a medida que migras. Las reglas de negocio que vivían en controladores AngularJS pasan a ser servicios y formularios tipados, y se pueden probar.
- Revisión de paridad por pantalla: mismos campos, mismas validaciones, mismos casos borde.
Paso 6: Eliminar AngularJS
Cuando se mueve la última pantalla, borra la capa de upgrade, el router legacy y toda dependencia de AngularJS del package.json. Después elimina el código de compatibilidad que escribiste en el paso 2. El estado final es una aplicación Angular moderna y limpia.
Errores comunes
- Widgets de plugins jQuery (grillas, selectores de fecha, editores). Encapsúlalos si deben quedarse, reemplázalos si existe un equivalente moderno. Decide widget por widget en el diagnóstico.
- Eventos de
$rootScopeusados como bus global. Reemplázalos por servicios tipados y observables antes de migrar las pantallas que dependen de ellos. - Diferencias de detección de cambios. El ciclo de digest de AngularJS y la detección de cambios de Angular son distintos; el código que dependía de "en algún momento se actualiza" necesita signals u observables explícitos.
- Dos copias del mismo estado. Si cada framework mantiene su propio estado de usuario o configuración, aparecen errores muy difíciles de reproducir. Comparte el servicio.
- Tamaño del bundle en la fase híbrida. Por un tiempo se envían los dos frameworks. Carga el bundle legacy de forma diferida y mide.
- Estilos que se filtran entre componentes viejos y nuevos. Aísla los estilos nuevos y mantén una capa compartida de design tokens.
Qué exige de verdad "sin downtime"
El framework hace su parte, pero el "sin downtime" también depende de cosas fuera del código: un pipeline de despliegue que pueda publicar una sola pantalla, monitoreo que avise en minutos si suben los errores, y feature flags que permitan volver atrás sin volver a desplegar. Si todavía no existen, móntalos durante el piloto. Se pagan solos en cada fase siguiente.
Un cronograma realista
| Fase | Duración | Resultado |
|---|---|---|
| Diagnóstico | 1–2 semanas | Inventario, elección del piloto, plan con precio fijo por fase |
| Piloto | 2–4 semanas | App híbrida arrancando, sesión compartida, primera pantalla en producción |
| Fases | Por fase | Pantallas migradas por grupos, cada uno aprobado por separado |
| Limpieza | 1–2 semanas | AngularJS eliminado, capa de upgrade borrada |
El tiempo total depende de la cantidad de pantallas y de cuánta interfaz de terceros haya que reemplazar. El diagnóstico es lo que convierte eso en un número con el que se puede planificar.
¿Planeas una migración o una plataforma de microfrontends?
En una llamada de 20 minutos me cuentas tu situación y te digo cómo la abordaría. Si todavía no vale la pena, también te lo digo.
Escríbeme por LinkedIn