Migración de plataforma
Migración de PHP heredado a Laravel
CodeIgniter, Zend, Symfony 2 o ningún framework - movidos a Laravel de forma incremental, detrás de un router que manda cada ruta al sistema que hoy es su dueño.
Una aplicación escrita en CodeIgniter en 2014, o en Zend, o en Symfony 2, o sin framework ninguno: sigue funcionando, sigue ganando dinero, y ahora es cara de una manera concreta. Nadie la toca, la gente que la escribió se fue, y la versión de PHP que necesita está fuera de soporte.
La propuesta que suele llegar es una reescritura. Tiene la forma equivocada para este problema y es como fracasan estos proyectos.
Por qué la reescritura es el riesgo
Reescribir significa construir un segundo sistema al lado del primero y cambiar cuando esté listo. De ahí se siguen tres cosas, y las tres son predecibles.
El negocio deja de recibir cambios durante todo ese tiempo, porque cada hora dedicada al sistema antiguo se tira. La línea de meta se mueve, porque los requisitos nunca se escribieron y se van redescubriendo desde el código según se tropieza con ellos. Y el cambio es un único día en el que todas las reglas que nadie recordaba se prueban en producción a la vez.
Las reglas son el problema. Un sistema que lleva una década funcionando codifica decisiones que no existen en ningún otro sitio: por qué ese grupo de clientes está exento, por qué la exportación corre a las 03:40, por qué se ignora un campo de precio. Una reescritura las encuentra a razón de una queja cada vez.
Mover ruta por ruta en su lugar
Ponga un router delante de los dos sistemas. Manda cada petición a la aplicación que ahora mismo es dueña de esa ruta: Laravel para lo que se ha movido, la aplicación heredada para todo lo demás.
location /account/ { proxy_pass http://laravel; }
location / { proxy_pass http://legacy; }Ahora una migración es una secuencia de despliegues pequeños en lugar de uno grande. Cada ruta que se mueve está viva la misma semana en que se escribe, cada una se puede devolver revirtiendo una línea, y el negocio sigue entregando todo el tiempo.
Lo que hace que funcione en la práctica es el orden:
- Primero el inventario. Cada ruta, cada tarea programada, cada integración, y qué toca cada una. Construido desde el código y la base de datos, no desde lo que alguien recuerda.
- Sesiones y autenticación compartidas. Los dos sistemas tienen que estar de acuerdo sobre quién ha iniciado sesión desde el primer día, o un usuario que cruce la frontera se queda fuera. Normalmente un almacén de sesiones compartido y un sistema dueño del login.
- La base de datos se queda. Las dos aplicaciones leen las mismas tablas. El esquema se mejora después, por su cuenta, para que una regresión sea atribuible a un cambio y no a dos.
- Primero las rutas hoja. Algo autocontenido y de poco tráfico, para demostrar el enrutado, el despliegue y la vuelta atrás antes de que algo importante dependa de ellos.
- Después por valor. Las rutas que más se cambian, porque ahí es donde se está pagando de verdad el coste del sistema antiguo.
- El trabajo programado y las integraciones al final. No tienen usuarios mirando y son las que más estado oculto guardan.
Las partes que son genuinamente difíciles
Las sesiones. Dos frameworks, dos formatos de sesión. O un sistema lee el formato del otro, o los dos se mudan a un almacén compartido con una serialización común. Esto se decide antes de mover la primera ruta, no lo descubre un cliente al que han echado la sesión.
Los hashes de contraseña antiguos. No puede convertir lo que no puede leer. Laravel verifica contra el algoritmo antiguo en el login y vuelve a hashear al tener éxito, de modo que la base de datos se convierte sola según la gente entra, en lugar de mediante un correo de restablecimiento enviado a todos a la vez.
Tablas que dio forma el ORM del sistema antiguo. Claves compuestas, sin
marcas de tiempo, una clave primaria con otro nombre, un booleano guardado como
'Y'. Los modelos de Eloquent se configuran a esas tablas en lugar de cambiar
las tablas para que le encajen a Eloquent; eso viene después, si es que viene.
Dos bases de código escribiendo las mismas filas. La regla es que un solo sistema es dueño de cada camino de escritura en cada momento. Que los dos escriban la misma tabla es de donde sale la corrupción de datos en estos proyectos.
Lo que no hacemos
No movemos todo. Algunos de estos proyectos terminan con una pequeña aplicación heredada sirviendo todavía un puñado de rutas que nadie necesita cambiar, y eso es un final legítimo: el objetivo era detener el coste, no llegar a un número.
No mejoramos el comportamiento mientras lo movemos. Una ruta se mueve para hacer exactamente lo que hacía, fallos incluidos, y se mejora en un commit aparte después. Cambiar comportamiento y ubicación a la vez significa que una regresión tiene dos causas posibles y ninguna forma barata de distinguirlas.
Cómo transcurre un encargo
Lo primero que necesitamos es la tabla de rutas de lo que hay ahora, y una respuesta honesta sobre qué partes ya no entiende nadie.
De ahí sale un inventario de rutas y un alcance por fases con un precio para cada una. Dice qué rutas se mueven primero, qué sigue funcionando al lado durante el movimiento, y qué partes le recomendamos no migrar en absoluto. Ese documento es a lo que se engancha el contrato.
Después las fases. Las dos aplicaciones corren juntas hasta que se mueve la última ruta, y cada fase termina con algo en producción.
Qué recibe
Una capa de enrutado con un mapa explícito de qué sistema es dueño de qué, el inventario del que salió ese mapa, las rutas movidas con las pruebas escritas al moverlas, y una secuencia para el resto con el razonamiento adjunto.
Si la aplicación ya es Laravel y solo está varias versiones por detrás, este no es el trabajo que necesita: actualizaciones y rescate lo es, y es un encargo más pequeño.
