Migración de aplicaciones
Migración y rescate de Next.js
De Pages Router a App Router, de otro framework a Next.js, o una base de código heredada que nadie quiere tocar - migrada de forma incremental, en producción y sin congelar el desarrollo.
Dos situaciones traen aquí a la mayoría de los equipos. O una aplicación Next.js existente se ha quedado pequeña en Pages Router y el camino incremental no es evidente, o una aplicación construida por otros se ha convertido en algo que el equipo actual tiene miedo de desplegar.
Ambas tienen solución. Ninguna requiere una reescritura.
De Pages Router a App Router
Los dos routers interoperan. Esa es toda la estrategia: una aplicación
compartida donde app/ y pages/ coexisten, migrada desde las hojas, con cada
ruta moviéndose tras su propio pull request y su propia verificación.
Empezamos por rutas de poco tráfico y bajo riesgo para construir los patrones
compartidos -layouts, acceso a datos, límites de error- y luego subimos hasta
las rutas que importan. La obtención de datos pasa de getServerSideProps al
árbol de componentes. El estado de cliente que solo existía para compensar el
modelo de renderizado antiguo suele desaparecer por completo.
El orden es deliberado, no alfabético. Las primeras rutas en moverse son las que enseñan al equipo los patrones nuevos al menor coste de equivocarse, y las últimas son aquellas donde equivocarse sale caro. Para cuando se mueve un checkout o un panel, las convenciones que necesita ya se han probado sobre algo que no factura.
Lo que suele llevar más tiempo no es el enrutado. Es el código compartido que
daba por supuesto el modelo anterior: un hook de datos que esperaba ejecutarse
en el navegador, un proveedor de contexto envolviendo toda la aplicación porque
un componente tres niveles más abajo necesitaba un valor, una comprobación de
autenticación escrita para getServerSideProps. Cada uno de ellos tiene un
reemplazo más pequeño en el modelo nuevo, y encontrarlos es el trabajo de
verdad. La mecánica, en detalle.
De otro framework a Next.js
Create React App, SPAs con Vite, Gatsby y una larga cola de configuraciones de webpack a medida. La parte mecánica es el enrutado y la configuración de build; la parte que exige criterio es decidir cuáles de las suposiciones de la aplicación eran artefactos del framework y cuáles eran requisitos reales.
Una aplicación de página única que se mueve a renderizado en servidor es el
caso con más sorpresas dentro, porque en una SPA todo puede dar por supuesto un
navegador. window en el cuerpo de un módulo, una fecha formateada en hora
local, un guardia de autenticación en cliente que era lo único que impedía el
acceso a una página: todo funciona perfectamente hasta que el mismo código se
ejecuta en un servidor. Esa migración tiene
página propia, porque es trabajo
suficiente para ser su propio encargo.
Bases de código heredadas
Antes de cambiar nada, lo leemos todo. Dos semanas de orientación producen un mapa de dependencias, un inventario de renderizado de cada ruta, una lista de lo que está muerto y un registro de riesgos priorizado. Ese documento es tuyo independientemente de lo que decidas después: es lo que vuelve gobernable la base de código, y varios clientes se lo han llevado a casa y han hecho el trabajo ellos mismos.
Nos parece un resultado razonable.
El registro de riesgos es la parte que cambia las conversaciones. «La base de código es un desastre» es una sensación y no se puede planificar. «Cuatro rutas no tienen error boundary, la integración de pagos no tiene ningún test, y dos dependencias tienen avisos de seguridad conocidos sin ruta de actualización» es una lista, y una lista se puede recorrer por orden de prioridad con quien esté disponible. Lo que produce esa lectura.
Qué sostiene la migración
Las URLs no se mueven. La migración es interna. Cada dirección que posicionaba ayer responde igual mañana, y donde una tenga que cambiar de verdad, redirige permanentemente y la redirección se comprueba en CI.
Cada paso es desplegable. Nada de ramas de migración eternas. Si el trabajo se detiene un trimestre porque tus prioridades cambiaron, lo fusionado es coherente y lo que queda es una lista en lugar de un conflicto.
Lo viejo y lo nuevo no pueden tener razón a la vez. Una vez elegido un patrón, sustituye al anterior en las rutas que ya se han movido, en lugar de acumularse a su lado. Una base de código que arrastra dos convenciones indefinidamente es el estado que la migración debía terminar.
Verificación por ruta, no por release. Cada ruta movida se comprueba en modo de renderizado, tamaño de bundle y comportamiento antes de empezar la siguiente.
Cuándo una auditoría es el mejor primer paso
Si lo que tienes es una sospecha y no una decisión - la aplicación parece lenta, o frágil, o cara de cambiar, y nadie sabe decir exactamente por qué - una auditoría lo responde en dos semanas y te dice si migrar es siquiera la respuesta correcta. A veces no lo es: el router está bien y el problema es una decisión de renderizado en cuatro rutas, que es un trabajo mucho más pequeño.
Cómo se acota
El primer entregable es un mapa y no una migración: qué rutas existen, de qué depende cada una, y cuáles ya no sabe explicar nadie.
De ahí sale un alcance por fases con un número de rutas y un precio para cada una. El contrato se refiere a esa lista, así que la discusión sobre qué entra en el alcance ocurre mientras todavía es barata. Puede parar después de cualquier fase y quedarse con una aplicación que funciona, y esa propiedad es la que hace esto sobrevivible.
Después las fases, ruta a ruta, con los dos routers funcionando hasta que se mueve el último.
Qué recibes
La aplicación en el App Router, migrada ruta a ruta, con cada fase en su propio pull request contra una lista de rutas acordada de antemano. Nada se mueve en un único cambio demasiado grande para revisarlo.
También recibe el mapa que se hace al principio: qué rutas existen, de qué depende cada una y cuáles no ha sabido explicar nadie. Ese documento sobrevive a la migración y suele ser lo primero que se le da a alguien que entra nuevo.
