🔄 Migraciones de tienda

PHP legacy → Symfony: caso de estudio de una migración tras doce años de código

Doce años de PHP 5.4 a medida, sin documentación y con un bus factor igual a uno. Cómo reescribimos la tienda a Symfony 6.4 con el patrón Strangler Fig, sin paradas y sin perder posiciones.

14 min de lectura

El propietario de una tienda de muebles llega con un problema que muchos conocen: la tienda funciona, vende, tiene buenas posiciones en Google y se apoya en un código que nadie se atreve a tocar. Doce años de desarrollo a medida sobre PHP 5.4, un programador que todavía recuerda por qué ciertas cosas funcionan así y no de otro modo, y una lista cada vez más larga de funciones que «mejor no tocar». No es una historia sobre tecnología obsoleta. Es una historia sobre riesgo de negocio congelado en el código.

A continuación desmontamos la migración de Bogart Meble, una tienda que pasó de PHP legacy a Symfony 6.4 sin un solo día de parada y sin perder posiciones. Mostraremos el punto de partida (la deuda técnica), el motivo por el que descartamos reescribir «de una vez», la elección del stack, un calendario de dieciséis semanas en cuatro fases y lo que realmente cambió. Sin marketing, con la mecánica que se puede trasladar a tu propia tienda.

El punto de partida: doce años de deuda técnica

Empezamos por una auditoría de lo que había. La imagen era típica de una tienda que durante años creció más rápido que su cimiento técnico. Cinco problemas de los que cada uno por separado se puede sobrevivir, pero que juntos crean un estado de riesgo elevado.

  • PHP 5.4 tras el fin del soporte. La falta de parches de seguridad significa una puerta abierta y un problema creciente de compatibilidad: las pasarelas de pago y las integraciones exigen versiones más nuevas, y cada mes profundiza la deuda.

  • Framework a medida sin documentación. En la práctica lo entendía una sola persona. Es el clásico bus factor igual a uno: una enfermedad, unas vacaciones o la salida de una persona deja la tienda sin soporte real.

  • Falta de tests. Cada cambio era una ruleta, porque el coste de una regresión era desconocido. El efecto es siempre el mismo: el equipo deja de cambiar cualquier cosa para no romper nada, y el desarrollo se apaga.

  • Base monolítica con consultas de varios segundos. No es un problema abstracto de ingeniería. Las consultas lentas golpean directamente a las Core Web Vitals y, a través de ellas, a la conversión y a las posiciones.

  • Imposibilidad de integrarse con una API moderna. Conectar BaseLinker, una nueva pasarela de pago o un marketplace era prácticamente inviable. La tienda no podía crecer en direcciones que hacía tiempo eran el estándar del mercado.

Ninguno de estos puntos por sí solo tumba el negocio. Juntos crean una situación en la que el desarrollo cuesta cada vez más y el riesgo crece cada mes, hasta que una avería pequeña puede volverse existencial. Ese es normalmente el momento en el que una tienda así llega a nosotros: no porque algo se haya roto, sino porque el propietario ha calculado cuánto le costaría una avería que nadie puede arreglar.

Por qué descartamos la reescritura «big bang»

El camino más tentador suele ser el peor: escribir una tienda nueva desde cero al lado y luego, en un fin de semana, cambiar el tráfico de la vieja a la nueva. Suena limpio. En la práctica, con doce años de lógica sin documentar, es pedir una catástrofe.

Con el enfoque «big bang» todo el valor aparece solo el día del cambio: meses de trabajo sin ningún efecto hasta el flag day. Si algo va mal, el rollback suele ser imposible, porque el sistema antiguo ha vivido su propia vida mientras tanto. Y lo más peligroso es que te enteras de la existencia de parte de las reglas de negocio solo cuando un cliente pulsa una ruta que nadie reconstruyó, es decir ya en producción, a la vista de los compradores.

Elegimos el patrón Strangler Fig. El nombre viene del ficus estrangulador, que envuelve un árbol y poco a poco toma su lugar. Del mismo modo el sistema nuevo envuelve al antiguo: asume funciones módulo a módulo hasta que el código viejo se puede finalmente eliminar. La lógica de negocio la reconstruimos en Symfony fragmento a fragmento, mientras la tienda antigua funcionaba y vendía todo el tiempo. Cada módulo recibía tests antes de asumir tráfico real, así que el riesgo quedaba repartido en el tiempo y la reversión de un cambio era local, a nivel de un único módulo y no de toda la tienda.

Técnicamente esto lo une una capa de routing delante de ambos sistemas. Decide qué petición atiende todavía el código antiguo y cuál ha asumido ya el nuevo: primero iban allí las rutas menos arriesgadas y al final el checkout y los pagos. Para el cliente y para Google la dirección sigue siendo la misma; por debajo solo cambia qué sistema responde. Gracias a ello el traslado del tráfico es gradual y reversible: si un módulo nuevo se comporta de forma distinta a la esperada, volvemos a la ruta antigua sin tocar el resto de la tienda.

Criterio «Big bang» (reescritura de una vez) Strangler Fig (módulo a módulo) Riesgo el día del cambio Extremo: todo o nada Repartido: cada módulo por separado Rollback A menudo imposible Local, a nivel de módulo Lógica sin documentar Sale a la luz tras el lanzamiento, en el cliente Sale a la luz por el camino, módulo a módulo Valor antes del cambio Cero hasta el «flag day» Crece con cada módulo Prueba con tráfico real Ninguna: el primer tráfico es el bautismo de fuego Parallel run con tráfico de producción Cuándo tiene sentido Código pequeño y bien comprendido Monolito grande, antiguo y mal documentado

Con doce años de código sin documentar no preguntamos si el «big bang» saldría bien. Preguntamos qué pasaría si no saliera bien, y esa respuesta nadie quería oírla.

El nuevo stack, y por qué precisamente este

La elección de la tecnología no es una cuestión de moda, sino de consecuencias con las que el cliente se queda durante años. Cada elemento del stack tenía que resolver un problema concreto de la lista de deuda técnica y no añadir otro.

Capa Tecnología Para qué (mecanismo) Backend Symfony 6.4 Framework maduro con soporte largo y un ecosistema enorme: fin del bus factor igual a uno API API Platform REST y GraphQL de inmediato: cimiento listo para integraciones (BaseLinker, marketplaces) y un frontal headless en el futuro Base de datos MariaDB 10.11 Base estable y bien indexada en lugar de un monolito con consultas de varios segundos Caché y sesiones Redis El protagonista de la caída del tiempo de carga: alivia la base en las rutas más frecuentes Frontend Twig + Stimulus Renderizado en el servidor, ligero y amigable con el SEO, sin el peso de una SPA completa Buscador Elasticsearch Búsqueda rápida y precisa sobre un gran catálogo de muebles que una base relacional no soporta Colas RabbitMQ Tareas asíncronas (correos, integraciones, recálculos) fuera de la petición del usuario: la página no espera Despliegues Docker + GitLab CI Despliegues repetibles y probados: fin del «en mi máquina funciona» y de la subida manual de archivos

Merece la pena señalar una cosa con honestidad: Symfony es el mismo cimiento sobre el que se apoya Sylius. El desarrollo a medida sobre Symfony tiene sentido allí donde la lógica de negocio es densa y atípica, como en Bogart. Cuando una tienda encaja en su mayor parte en los estándares de la plataforma, recomendamos con más frecuencia Sylius ya hecho que construir desde cero, porque es más rápido y más barato. La elección «a medida o plataforma ya hecha» es una decisión que tomamos en la fase de auditoría y no durante el proyecto.

Dieciséis semanas en cuatro fases

Todo el proyecto se cerró en cuatro fases a lo largo de dieciséis semanas, unos cuatro meses. Ese fue el calendario de Bogart; con un catálogo mayor o una lógica más densa un escenario así puede durar bastante más. Más importante que el número de semanas es el orden de los pasos.

Fase 1 (semanas 1–3): auditoría e ingeniería inversa

El producto más importante de esta fase no fue código, sino documentación. El equipo reconstruyó la lógica de negocio oculta en el código antiguo y mapeó la estructura de URL y la estructura de la base de datos. Surgió también un documento que en cada una de nuestras migraciones llamamos «Qué conservamos sin excepción»: una lista de varios cientos de las URL más importantes por tráfico y conversión. Es la que marca los límites que no se pueden cruzar en las fases siguientes.

Reconstruir lógica sin documentar es trabajo detectivesco, no adivinanza. Se leen las rutas de código allí donde se toman decisiones —cómo se calculan los descuentos, los costes de envío, la disponibilidad de variantes— y se confrontan con lo que realmente pasa en producción, con pedidos reales. Allí donde solo quedaba una persona que recordaba «por qué esto funciona precisamente así», su conocimiento pasó al papel en lugar de seguir existiendo únicamente en su cabeza. Esta fase paga la mayor parte de la deuda antes incluso de escribir la primera línea de código nuevo, porque convierte un bus factor igual a uno en conocimiento compartido y escrito del equipo.

Fase 2 (semanas 4–12): reescritura de los módulos

El corazón del proyecto. Catálogo, carrito, checkout, panel del cliente e integraciones: reconstruidos módulo a módulo, cada uno con tests automáticos que cubren las rutas de compra críticas. Los tests no eran decoración: eran lo que permitía entregar módulos sucesivos con seguridad, porque daban una respuesta inequívoca a la pregunta de si la versión nueva hace exactamente lo que hacía la antigua.

Migración de datos: donde mueren las migraciones silenciosas

Trasladar el catálogo, los pedidos históricos y los clientes es el momento en el que resulta más fácil perder algo de forma irreversible. Llevamos la migración de datos con validación de integridad: después de cada pasada los datos se comparaban con el origen para que ningún registro desapareciera ni se deformara en silencio. Resultado: sin pedidos perdidos en todo el proceso. Con los pedidos eso no es una «métrica agradable», sino una condición de contorno: un pedido perdido es un cliente real y una disputa real.

301 para miles de URL: la capa en la que las migraciones pierden tráfico

La causa más frecuente de la caída de posiciones tras una migración no es la tecnología, sino las direcciones abandonadas. Por eso preparamos un mapeo completo de redirecciones 301 para todas las URL, uno a uno, de la estructura antigua a la nueva. El detalle clave: el mapa lo validó un especialista en SEO y no solo un programador, y estaba importado en las pruebas de regresión antes de la implantación, para detectar cada enlace roto antes de que lo viera Google.

Este enfoque tiene respaldo en cifras. El benchmark del sector para la caída de tráfico orgánico tras una migración es normalmente del 15–30%; en nuestros proyectos nos mantenemos por debajo del 3%. En Bogart eso se tradujo en casi todas las posiciones conservadas, de lo que hablamos más en la sección de resultados.

Las migraciones no pierden tráfico por el código complicado. Lo pierden por las direcciones abandonadas, y por eso en nuestro caso el mapa de redirecciones lo valida un especialista en SEO y no solo un programador.

Fases 3–4 (semanas 13–16): parallel run y cambio sin paradas

Durante unas dos semanas el sistema antiguo y el nuevo funcionaron en paralelo. El parallel run permitió probar la nueva tienda con tráfico real de producción —incluidas pruebas de carga— antes de que algo dependiera del código nuevo. El sentido de esta etapa es simple: en lugar de creer que el sistema nuevo se comportará correctamente, lo compruebas con tráfico real, en condiciones que ningún entorno de pruebas reproduce. Las diferencias de resultados entre el sistema antiguo y el nuevo —el precio en el carrito, el coste de envío, la disponibilidad de una variante— salen entonces a la luz con la tienda antigua todavía en funcionamiento, y no después de que las reporte un cliente. Solo cuando el sistema nuevo soportó tráfico real y dejó de diferenciarse del antiguo allí donde no debía, pasamos al cambio.

El cambio en sí lo hicimos fuera de las horas punta, con un plan de retirada listo. En el caso de Bogart el cutover fue lo bastante exigente como para repartirlo en tres etapas en el tiempo, cada una en una ventana de bajo tráfico, y ninguna exigió apagar la tienda. Las primeras horas después de cada etapa significaron monitorización intensificada, y las posiciones SEO y el comportamiento del sistema los observamos mucho después del arranque. Los clientes pudieron comprar con normalidad durante todo ese tiempo.

Resultados, y cómo leerlos

Cuatro efectos describen mejor el resultado de la migración. Los damos como resultado del proyecto, pero tratémoslos como una ilustración del mecanismo y no como una garantía: cada tienda parte de un punto distinto.

  • Tiempo de carga de la página de producto: de varios segundos a menos de uno. Una aceleración de varias veces, sobre todo gracias a Redis y a la reescritura de las consultas más pesadas. Es el efecto más directo y el punto de partida de los demás.

  • Crecimiento claro de la conversión en tres meses. Aquí la honestidad importa más que una cifra vistosa: al crecimiento contribuyeron tanto el nuevo checkout como la propia velocidad. No atribuimos todo a una sola variable, pero la dirección coincide con lo que muestran los datos de todo el sector.

  • Casi todas las posiciones en Google conservadas. El efecto del mapeo 301 y del mantenimiento de la misma estructura semántica. Frente a un benchmark del sector de una caída del 15–30%, esa es la diferencia entre un arranque tranquilo y meses de reconstrucción del tráfico.

  • Sin pedidos perdidos y sin paradas. El resultado del parallel run, del cambio por etapas y de la validación de la integridad de los datos.

¿Por qué la velocidad se traduce con tanta fuerza en ventas? Porque el coste de una página lenta es medible. En nuestras observaciones un TTFB por encima de 500 ms correlaciona con una caída de la conversión del 5–10%, y por eso apuntamos por debajo de 200 ms. Bajar de varios segundos a menos de uno no es cosmética, sino la eliminación de un freno real a las ventas. El equipo del cliente había temido esta migración durante años; la reacción más frecuente tras el cambio se resumía en la incredulidad de que se pudiera hacer sin paradas y sin caída en Google.

Hay todavía otro resultado que no se ve en las cifras del día del arranque y que cambia más cosas a largo plazo: la tienda puede volver a crecer. Lo que antes era inviable —conectar BaseLinker, entrar en nuevos marketplaces, añadir una nueva pasarela de pago— sobre un stack con API Platform se convierte en una tarea normal y no en un proyecto con riesgo de dañar el conjunto. La migración, por tanto, no era un objetivo en sí misma. Era la condición para que todas las mejoras siguientes fueran siquiera posibles.

Cuándo compensa una migración así, y cuándo no

Reescribir PHP legacy en Symfony es el escenario de migración más largo, más caro y más difícil que gestionamos. No toda tienda lo necesita, y seremos los primeros en decirlo.

Tiene sentido cuando:

  • la lógica de negocio es densa y atípica: configuradores, presupuestos no estándar, reglas que ninguna plataforma ya hecha reproduce;

  • el tráfico orgánico es el canal principal, de modo que el coste de perder posiciones se cuenta en cientos de miles;

  • la escala de la tienda hace que una parada o una caída de visibilidad sean pérdidas reales; y recordemos que una mala elección de proveedor puede costar a una tienda mid-market 500.000 PLN y más el primer año.

No tiene sentido cuando:

  • la tienda es en un 90% un catálogo y un carrito estándar: entonces una plataforma ya hecha (Shopify Plus o Sylius) es más barata y más rápida que construir desde cero;

  • no hay presupuesto ni tiempo para un proyecto de varios meses: existen entonces bastantes rutas de migración más ligeras;

  • nadie en la empresa entiende las reglas antiguas y nadie quiere reconstruirlas: sin ese conocimiento, o sin la disposición a extraerlo, el riesgo crece independientemente de la tecnología.

En otras palabras: Strangler Fig y el desarrollo a medida sobre Symfony son una herramienta para un problema concreto y no la respuesta por defecto. Otros de nuestros proyectos —como la migración de Topeshop a Shopify Plus o de KMK Klinkier a Sylius— muestran que para muchas tiendas la respuesta correcta es una plataforma ya hecha y no reescribir código.

Conclusiones

La migración de Bogart Meble demuestra que doce años de deuda técnica no hay que pagarlos con un único salto arriesgado. Strangler Fig permitió reconstruir la lógica módulo a módulo, el parallel run y el cambio por etapas eliminaron la parada, y el mapeo 301 validado por SEO protegió las posiciones. La lección más importante, sin embargo, es estratégica: una buena migración empieza por una decisión sobre el orden: primero qué protegemos sin excepción y solo después cómo lo reescribimos.

Si te apoyas en un código que temes tocar, empieza por reconocer el terreno y no por elegir la tecnología. Mira cómo abordamos la migración de tienda o escríbenos a través de nuestra página de contacto: veremos tu caso y te diremos con sinceridad si reescribir tiene sentido o si una plataforma ya hecha es el mejor camino.

Etiquetas: #migracja #Symfony #legacy #case study
Compartir: 𝕏 in f

Mantente al día

Nuevos artículos sobre e-commerce y skalowaniu

Una vez a la semana, los viernes. Sin spam, sin relleno. Solo conocimiento práctico de personas que han migrado tiendas con más de 10M PLN de GMV al año.

🔒 Conforme al RGPD. Cancela la suscripción con 1 clic en el pie de cada correo.