El sitio de marketing de Testlify contiene más de 11.000 páginas. La configuración heredada de WordPress incurría en una sobrecarga de renderizado en el servidor y cuellos de botella de plugins en cada solicitud, afectando la velocidad de carga de las páginas y el posicionamiento en buscadores. Lideré la decisión arquitectónica de migrar el sitio a Next.js 16, con el despliegue actualmente en curso.
La decisión arquitectónica
- Basado en caché, en lugar de WordPress: Las páginas se renderizan una vez en la primera solicitud y quedan en caché indefinidamente, en lugar de consultar una base de datos en cada solicitud o requerir una reconstrucción por lotes de varias horas para más de 11.000 páginas. Un webhook de Payload CMS invalida únicamente las páginas concretas a las que afecta un cambio de contenido.
- Integración con un CMS headless: El contenido se gestiona a través de Payload CMS, un CMS headless autoalojado respaldado por Postgres, que permite a los equipos de marketing editar y publicar páginas mientras los hooks de invalidación de caché recogen automáticamente cada cambio.
- Migración a escala: Planifiqué y ejecuté la migración por etapas de más de 11.000 páginas, verificada página por página contra el mapa del sitio en vivo de producción, preservando las estructuras de URL, las etiquetas canónicas y la autoridad SEO existente en todas las categorías.
Por qué importó
Mover un sitio de más de 11.000 páginas fuera de WordPress hacia una arquitectura basada en caché recorta los tiempos de carga de página, mejora el rendimiento técnico de SEO, y permite que los equipos de marketing publiquen actualizaciones sin depender de despliegues de ingeniería.
Responsabilidad, todavía en curso
Lideré la decisión arquitectónica de abandonar WordPress. No es una construcción en solitario: más de 2.500 commits de aproximadamente 15-20 colaboradores desde julio de 2026, activo hasta este mismo mes, con el cambio de DNS a producción como el único paso que queda según la última verificación de estado.
Por qué esto no es Jamstack clásico
El enfoque obvio para más de 11.000 páginas es la generación estática en tiempo de compilación. Aquí no encaja: una reconstrucción completa de todos los idiomas y tipos de contenido tomaría horas, y una sola edición de contenido no debería requerir reconstruir todo el sitio para publicarse. En su lugar, cada ruta dinámica (categoría, autor, hr-tools, hr-guide, blog, test-library, y más) sigue la misma convención: la compilación prerrenderiza exactamente una página de muestra por tipo de ruta, ya que Cache Components requiere al menos un parámetro contra el cual compilar y la propia compilación no debe ejecutar ninguna consulta a la base de datos. Cualquier otra página bajo esa ruta se renderiza en su primera solicitud real bajo los Cache Components de Next.js 16, y luego permanece en caché indefinidamente (cacheLife fijado a la propia constante INFINITE_CACHE de Next) hasta que un hook afterChange de Payload CMS llama a un endpoint de revalidación firmado para esa página específica. Los cambios de contenido se propagan en segundos, no en un ciclo completo de reconstrucción, y la caché sigue comportándose como una caché de borde de CDN desde el lado del visitante. El propio endpoint de revalidación está firmado con HMAC y se verifica con una comparación de tiempo constante, y está deliberadamente restringido a invalidar una sola etiqueta a la vez: solo una ruta de administración separada, activada manualmente, puede purgar una etiqueta a nivel de colección, de modo que un secreto de webhook filtrado puede invalidar, en el peor de los casos, una sola página, no desbordar de golpe la caché de todo el sitio.
Dos errores del framework, encontrados en producción y corregidos en el origen
Dos problemas distintos de Next.js 16 salieron a la luz durante la compilación, ambos ahora documentados directamente en next.config.ts para que la próxima persona no los redescubra por las malas. Primero, cacheComponents activa en silencio enablePrerenderSourceMaps por defecto, lo que estaba matando la compilación de producción por falta de memoria (OOM) durante la generación estática de páginas; se rastreó hasta el propio código fuente de Next y se corrigió con una anulación explícita. Segundo, usar Infinity como tiempo de vida de caché se serializa a JSON como null en su camino hacia los workers de compilación de Next, lo que degrada esa ruta en silencio a no cacheada en lugar de lanzar un error, rompiendo el prerenderizado en todos los idiomas hasta que se detecta; se corrigió usando la propia constante INFINITE_CACHE de Next en lugar de un Infinity a secas.
Un catálogo que se niega a servir un resultado parcial
El catálogo de test-library obtiene datos de una API ascendente en páginas de 60 filas. Si más del 2% del total anunciado vuelve faltante, esa obtención se rechaza por completo en lugar de publicarse como un catálogo parcial, y sigue sirviendo en su lugar el último snapshot en caché que se sabe correcto (un seed.json local cubre los arranques en frío o que el servicio ascendente esté caído por completo). Que los datos estén desactualizados es un modo de fallo aceptable aquí; un catálogo de producto silenciosamente incompleto no lo es.
Una corrección de SEO para un caso límite de streaming
Bajo el streaming de Cache Components, el notFound() automático de Next no puede confirmar un estado HTTP 404 antes de que el shell de la página ya haya empezado a transmitirse al cliente, de modo que un rastreador que solo lea ese shell nunca lo vería. La corrección inyecta manualmente una directiva noindex en el primer flush, para que un rastreador que se detenga en el shell reciba de todos modos la señal correcta incluso antes de que se resuelva el resto de la página.
Modelo de datos y escala
- Las más de 11.000 páginas provienen de múltiples tipos de contenido (categoría, autor, hr-tools, hr-guide, leadership, test-library) cruzados con cada idioma admitido a través de
next-intl, no de una única fuente de contenido plana. - Cada URL de WordPress en producción se comprueba contra la salida de este proyecto en una auditoría dedicada (
docs/audits/url-parity-with-production.md): una URL que WordPress sirve hoy y que este proyecto responde con 404 es tráfico perdido, no una brecha aceptable. - Las pruebas unitarias (Vitest) cubren la generación del mapa del sitio, el respaldo de la semilla del catálogo y la rotación de contenido relacionado; Playwright cubre los flujos de principio a fin.
Construir vs. comprar
Payload CMS autoalojado sobre Postgres, en lugar de un CMS headless SaaS: mantiene la propiedad del contenido y la base de datos dentro de la empresa, en lugar de pagar una factura de SaaS por asiento para un sitio de marketing. Los propios Cache Components de Next.js y la invalidación basada en etiquetas, en lugar de una capa de CDN/ISR hecha a mano: el comportamiento de caché proviene del framework en lugar de una infraestructura que este equipo tendría que construir y operar por su cuenta.
Stack
Next.js 16, React, TypeScript, Payload CMS sobre Postgres, next-intl para la localización, Vitest para las pruebas unitarias, Playwright para las pruebas de principio a fin, desplegado en Vercel.