Parte del caso Libelium + iris360: De un cambio estético a un cambio de arquitectura

Retirar el generador de landings: cuando el código y el editor son dos fuentes de verdad

PUBLICADO: 2026-10-07ACTUALIZADO: 2026-10-14
AUTOR: Manuel PrietoREVISADO POR: Manuel Prieto
Arquitectura

Si el diseño de una página vive en dos sitios, uno de los dos miente. Lo sabía en teoría y aun así construí un sistema entero para mantener dos copias de cada landing. Durante la reescritura de las webs de Libelium e iris360 cada landing tenía su post en Elementor y una clase PHP que reproducía el mismo diseño. El editor mandaba y el código era una foto de seguridad. Lo borré a primeros de octubre.

Dos libros de caja

Una tienda que apunta las ventas en un cuaderno y, por si acaso, en un segundo cuaderno que copia el primero. Al principio cuadran. Al cabo de un mes alguien corrige uno y olvida el otro, y llega el cierre sin saber cuál es el bueno. Un cuaderno de seguridad solo sirve mientras nadie lo trate como un cuaderno más.

Cómo acabé con dos copias de cada página

La idea sonaba razonable. El editor es donde trabaja quien redacta, y git es donde vive todo lo demás, así que cada landing existía en los dos. Un comando volcaba la clase al post y otro congelaba el post en una clase. Con el tiempo el sistema cubrió 29 landings, entre home, páginas corporativas, productos y soluciones.

Para no depender de mi disciplina lo automaticé. Cada vez que alguien guardaba una landing en Elementor, una tarea regeneraba la clase y la dejaba en un commit. Entre el 14 y el 30 de septiembre el repositorio acumuló 37 de esos commits, casi todos sin que nadie los escribiera.

Cargando diagrama...

El dibujo ya delata el problema. El post genera la clase y la clase puede regenerar el post. Un ciclo que se alimenta a sí mismo funciona hasta que algo lo interrumpe a medias.

Lo que se rompió sin avisar

Algo lo interrumpió varias veces, y casi siempre en silencio.

Primero fue el retardo. Tras guardar, la clase tardaba hasta cinco minutos en actualizarse porque dependía de un contenedor que corría ciclos de 300 segundos. Lo moví a un proceso en segundo plano y bajó a menos de un segundo y medio.

Después los permisos. Un despliegue hecho como administrador dejó 52 ficheros con otro propietario, y la sincronización dejó de regenerar una landing sin que nadie lo notara.

Lo peor llegó el 16 de septiembre. Cuando el comando de sincronización no podía hablar con Docker desde dentro del contenedor, recurría a un fallbackGlosarioFallbackUn Fallback (conmutación por error) es una estrategia de seguridad mediante la cual un sistema delega una tarea a un recurso secundario cuando el recurso principal falla, se encuentra saturado o no está disponible.Ver término completo → que sobrescribía la clase de la home con una versión muy reducida. El registro decía que todo había ido bien. Pasó dos veces el mismo día. Lo único que salvó la versión buena fue un commit anterior y una copia que guardé a mano.

Ahí entendí que no estaba manteniendo una copia de seguridad. Estaba manteniendo una segunda fuente de verdad que además tenía permiso para pisar a la primera.

Medir cuánto habían divergido

Después de lo de la home quise saber cuántas landings más estaban desalineadas. Comparé cada post con la clase que lo habría generado. Solo 11 de las 29 coincidían. Las otras 18 habían divergido.

Al desglosar esas 18, la mayoría eran ruido: seis arrastraban un icono fantasma de un fallo conocido de un widget, tres llevaban URLs de localhost escritas en la clase y tres diferían en un relleno. Seis tenían contenido real distinto, con pasos de un proceso, listas de certificaciones o textos de insignia, y ahí no había arreglo mecánico posible. Alguien tenía que decidir qué versión era la correcta, página por página. Corregí 12 de las 18 ese día y las seis de contenido quedaron esperando una decisión humana.

Esa cifra me convenció más que cualquier fallo suelto. Dos copias no se mantienen sincronizadas por buena voluntad, y cada desajuste obliga a un juicio caso a caso.

La decisión

El 2 de octubre, el mismo día que cerré el monorepoGlosarioMonorepoRepositorio de control de versiones que aloja varios proyectos, paquetes o sitios en un único árbol de código. Aplicado a WordPress con varias marcas, un monorepo guarda una sola copia del tema, del motor y de los widgets compartidos, y una carpeta por sitio con lo que es propio de cada uno. Evita las copias divergentes de los repositorios clonados y los commits de actualización de puntero de los submódulos de Git.Ver término completo →, retiré el generador y la sincronización completos. Ya no hay clases que representen landings. El contenido se edita en Elementor y es la única fuente.

Generador de landingsHasta el 2 de octubre
  • ✕Dos copias del diseño. Una en la base de datos y otra en git. 11 de 29 coincidían al auditarlas.
  • ✕Fallos silenciosos. Un registro que decía que todo iba bien tras reducir la clase de la home.
  • ✕Decisión caso a caso. Por cada desajuste había que elegir quién mandaba.
Copias de contenidoDesde el 2 de octubre
  • ✓Una sola fuente. El contenido se edita en Elementor y el código no lo regenera.
  • ✓Copia cada 2 horas. Si nada cambió, no se guarda una nueva.
  • ✓Restauración sobre la misma entrada. Con una copia previa automática del estado actual.

Lo que sustituye a las 29 clases es una copia de contenido, y esa palabra importa. Cada dos horas, el cron del servidor exporta a JSON páginas, plantillas, tipos de contenido propios, menús y una lista cerrada de opciones. Calcula una huella del conjunto y, si no cambió, no guarda nada. Las copias de más de 14 días se borran, salvo la última, y el estado más reciente sube a un repositorio de contenido propio de cada sitio.

Restaurar vuelve a escribir cada entrada sobre su mismo identificador, y solo en el sitio que generó la copia, porque los identificadores son de cada sitio. Antes de restaurar crea una copia del estado actual y, si esa falla, no restaura. La copia no sabe nada de diseño. Solo guarda lo que hay y lo devuelve.

Lo que no sé

Probé la restauración en local, no en producción. Modifiqué un título, los datos de Elementor y una opción, mandé una entrada a la papelera y al restaurar la huella coincidió con la original.

Hay una incidencia abierta. Desde un reinicio de la base de datos de producción el 7 de octubre, el comando de copia responde que ya hay una en curso, probablemente por un bloqueo huérfano. Esa jornada me apoyé en la última copia anterior al reinicio.

Tampoco medí cuántas ediciones se perdieron con el sistema viejo ni cuánto tiempo me costaba mantenerlo. El flujo nuevo tiene menos piezas que pueden fallar sin avisar, y esa conclusión es mía y no sale de una medición. El mismo patrón de dos copias reapareció en los datos de producto y lo cuento en la separación entre dispositivo y especificación. Las reglas escritas que lee el agente de IA en cada sesión las desarrollo en otro artículo.

Qué me llevo

Si necesitas una copia de seguridad de algo, hazla de solo lectura. En cuanto la copia puede escribir sobre el original, se convierte en una segunda fuente con derechos de sobrescritura, y empezarás a decidir quién gana en cada conflicto. Una copia de seguridad que vuelca el estado y lo restaura bajo demanda no compite con nada. El principio general lo tienes en la lección fuente única de verdad, editor frente a código, y el contexto completo del proyecto en el caso de Libelium e iris360 y en WordPress en monorepo en vez de multisite.

Preguntas frecuentes sobre la retirada del generador