Parte I — De Umbraco 11 a 18, con el sitio entero escrito a mano dentro del master
Hay una versión de este trabajo que suena bien en una propuesta: «migramos de Umbraco 11 a 18 y modelamos el contenido en componentes reutilizables». Es cierta y no sirve para nada, porque esconde el orden. Y el orden es casi todo lo que importa, porque cada paso solo se podía dar si el anterior estaba hecho.
Los dos sitios eran un caso corriente. Un CMS instalado hacía años que en realidad no administraba nada: guardaba los campos de SEO y punto. Todo lo demás —los titulares, los párrafos, las tarjetas, los servicios— estaba escrito a mano dentro de las vistas Razor. Para cambiar una palabra hacía falta un desarrollador, un pull request y un despliegue.
Y el despliegue estaba roto desde 2023. Por eso el pie de página llevaba dos años diciendo «© 2024»: no es que a nadie le importara, es que nadie podía arreglarlo.
Primero: el repositorio mentía
Lo primero no fue tocar Umbraco. Fue descubrir que no podíamos.
El último commit en main era de abril de 2023. La aplicación en Azure, en cambio, había seguido editándose directamente todo ese tiempo: las vistas en septiembre de 2023, los archivos de uSync en junio, los modelos regenerados en septiembre de 2024. Tres años de trabajo que solo existían en el servidor.
Lo grave no era el desorden. Era esto: si desplegábamos main tal como estaba, el sitio se quedaba sin estilos. El CSS y el JavaScript que sirve la web en producción nunca habían estado en el repositorio. Un despliegue «normal», el que cualquiera habría hecho el primer día, habría dejado los dos sitios en texto plano.
Así que el paso cero fue bajarnos la realidad. Copiamos desde la aplicación de Azure —solo lectura— las vistas, los archivos de uSync y todo wwwroot, y lo metimos en un commit que dice lo que es: una foto de lo que hay, no una aprobación de lo que hay. A partir de ahí ya existía un punto de partida honesto y comparable.
Si se lleva una sola cosa de este artículo, que sea esta: antes de migrar nada, compruebe que puede reproducir lo que ya está en línea. En un proyecto que lleva años sin desplegarse, esa comprobación falla más veces de las que uno esperaría.
Después: subir de versión, una por una
De 11 a 18 hay seis saltos. Los hicimos como seis, no como uno: 13.16 sobre .NET 8, 14.3.4, 15.4.4 sobre .NET 9, 16.5.1, 17.6.0 sobre .NET 10, y 18.1.0. Cada uno compilando y levantando antes de empezar el siguiente.
Suena tedioso y fue lo barato del proyecto, por una razón que conviene decir en voz alta: el costo de esta migración no depende de cuántas versiones cruce, sino de cuánto backoffice haya escrito usted. El salto de 13 a 14 es la reescritura completa del backoffice —de AngularJS a componentes web— y no hay capa de compatibilidad: cada property editor, cada dashboard y cada content app propio se rehace desde cero. Estos dos sitios no tenían ninguno. Por eso pasaron de largo por la pared que hace cara esta migración para casi todo el mundo.
Si su sitio sí tiene backoffice propio, esa es la conversación de presupuesto. No los seis saltos: cuente sus property editors.
Lo segundo que cambia de verdad está más adentro. Desde la versión 15, recorrer el árbol de contenido ya no se hace igual: Children() y sus parientes necesitan ahora unos servicios de navegación y, sobre todo, necesitan que usted les diga en qué idioma pregunta. El código viejo sigue compilando. Simplemente contesta otra cosa.
Con la versión arriba, arreglamos el despliegue. Estaba roto porque las acciones de GitHub que usaba fueron retiradas, y hasta que no volvió a funcionar todo lo demás era teórico: un CMS que no se puede desplegar no es un CMS, es un backup.
Antes de tocar el contenido: una foto
Aquí es donde el proyecto se pone interesante, porque sacar el HTML de las vistas y meterlo en el CMS cambia la forma del marcado. Deja de servir la comprobación fácil —«el HTML es idéntico»— y hay que reemplazarla por otra cosa.
Lo que hicimos fue fotografiar los dos sitios, en escritorio y en móvil, antes de mover una sola línea. La idea es obvia. Lo que no es obvio es que la primera foto salió mal dos veces, y las dos veces habría mentido a favor nuestro.
La primera: las páginas cargan un par de scripts de terceros que aquí nunca terminaban de responder, así que el navegador jamás daba la página por cargada y tres de ocho capturas se vencían por tiempo. Los bloqueamos. Además de arreglar la captura, es lo correcto por otro motivo: un script de un tercero no debería poder cambiar lo que ve una comparación.
La segunda es la que da miedo. El tema arranca sus elementos invisibles y los va revelando al hacer scroll. Una captura de página completa no hace scroll. Resultado: la primera línea base capturó el hero sin una sola palabra encima — y habría capturado el «después» exactamente igual. Las dos fotos habrían coincidido perfectamente, y habrían coincidido en no mostrar el contenido que el script existía para vigilar.
Una comparación que sale bien porque los dos lados están igual de vacíos es peor que no tener comparación, porque además da tranquilidad.
El paso grande: sacar el HTML del master
Recién ahí movimos el contenido.
El primer instinto es buscar la plantilla común entre los dos sitios y extraerla. No existe: los dos sitios no solo se ven distinto, están armados distinto. De ocho secciones, una sola aparece en los dos, y aun así una ocupa 134 líneas y la otra 55. No había una página que extraer; había piezas que se repetían con otra forma.
Así que la unidad no fue la página sino el bloque. Salieron ocho tipos —el hero, la tarjeta, la grilla de tarjetas, el contacto, el texto enriquecido, el servicio, la lista de servicios, el muro de logos— y cada uno guarda lo que el negocio quiere decir, nunca cómo se ve. El cómo se ve vive en un archivo por tema, y el tema se elige leyendo una propiedad en la raíz del sitio.
La frontera entre las dos cosas se ve mejor con un ejemplo pequeño. El editor elige que un servicio es de tipo settings; el tema traduce eso a la clase del icono que le toque:
["settings"] = "sl-config",
["quality"] = "sl-diamond",
["training"] = "sl-globe",
["support"] = "sl-config", // se quedó sin iconos y reutilizó el primero
Ese mapa es feo y lo dejamos así a propósito, porque es la prueba: el tema se quedó corto y al contenido le dio igual. Si mañana entra un tema con iconos SVG, cambia esta tabla y no cambia ni un campo de contenido.
Y una advertencia para quien vaya a hacer lo mismo: hoy, si el editor necesita un quinto tipo de servicio, vuelve a necesitar un desarrollador. La frontera está bien puesta, pero está puesta donde el proyecto la necesitaba, no donde quedaría perfecta.
Lo que se perdió por el camino
Con los dos sitios ya montados sobre bloques, comparamos contra producción. Producción servía 22 imágenes; nuestra versión, 17.
La mayor parte tenía explicación: siete logos de clientes habían cambiado de carpeta al entrar a la biblioteca de medios, y dos insignias las habíamos quitado a propósito. Tres eran pérdidas de verdad.
La grilla de cuatro tarjetas del sitio colombiano alternaba texto, imagen, imagen, texto. Al pasarla a bloques nos llevamos las dos tarjetas de texto y dejamos las dos de imagen atrás, porque el modelo que habíamos diseñado tenía campo para el texto y no para eso. Y una imagen que iba sola después del carrusel de logos no tenía dónde ir, así que el bloque del muro de logos tuvo que ganar un campo nuevo.
Ninguna de las tres lanzó un error. Las páginas cargaban con código 200 y se veían razonables. Se encontraron contando imágenes contra el sitio en línea.
Y al limpiar aparecieron cinco secciones del tema que ya no usaba nadie. Una de ellas pintaba dieciséis logos de clientes que venían de la plantilla comprada: logos de mentira presentados como clientes. No estaba enlazada desde ninguna parte, así que nunca llegó a verse. Estaba a un include distraído de verse.
Lo que nos llevamos
Tres cosas, y ninguna es sobre Umbraco.
La primera: el paso que parece administrativo —comprobar que el repositorio reproduce lo que está en línea— era el único que, si se saltaba, borraba tres años de trabajo ajeno.
La segunda: cada comprobación automática hay que probarla contra un fallo conocido. Nuestra línea base visual pasaba con los dos lados vacíos. Si no la hubiéramos revisado con los ojos, el proyecto habría tenido una red de seguridad que solo sabía decir que sí.
La tercera: nadie se dio cuenta de que los dos sitios no compartían composición hasta que alguien se sentó a contar cuántas secciones coincidían de verdad. La decisión de arquitectura no salió de la experiencia ni del gusto. Salió de una resta.
Y el pie de página ya no puede quedarse en 2024, porque el año lo calcula la plantilla y el texto lo cambia un editor sin pedirle nada a nadie.