Modernizando códigos base WordPress legacy: Estrategia 2026 para sitios corporativos

Modernizando códigos base WordPress legacy: Estrategia 2026 para sitios corporativos

Última verificación: 20 de septiembre de 2026
19 min de lectura
Guía
Desarrollador full-stack
Soluciones enterprise

Un código base WordPress legacy es aquel en el que nadie puede predecir qué romperá un cambio. La edad no es el problema: lo es la falta de medición. Esta guía trata de producir primero esas mediciones y después sustituir el código en un orden donde cada paso se pueda deshacer.

Los sitios de los que hablamos se reconocen enseguida: construidos entre 2015 y 2020, un tema clásico con un functions.php que pasó de las dos mil líneas, varios plugins a medida sin repositorio upstream y una tabla wp_options que nadie ha mirado desde el lanzamiento. Normalmente siguen funcionando. Lo que ha dejado de funcionar es la capacidad de cambiarlos sin sustos.

WordPress 7.1.1 salió el 17 de septiembre de 2026. La distancia entre esa versión y un stack de 2018 rara vez está en el núcleo. Está en la versión de PHP de debajo, en los scripts que encola el tema y en una base de datos que lleva años acumulando filas de plugins que ya no existen.

Conozca más sobre el desarrollo WordPress profesional en WPPoland.


#1. Definiendo la deuda técnica

Empiece por los números que se pueden leer en un sitio en producción, no por una opinión sobre el código.

#La brecha PHP

El suelo y la recomendación son cosas distintas. El trunk de WordPress sigue fijando $required_php_version = '7.4' en src/wp-includes/version.php, mientras que la página de requisitos recomienda PHP 8.3 o superior y MariaDB 10.11 o MySQL 8.0. Es decir: el núcleo arranca en 7.4, y arrancar en 7.4 significa hacerlo sin parches.

Lo que decide la migración son las fechas de php.net, no un porcentaje de mejora prometido:

Rama PHPSituación a 20 de septiembre de 2026
7.4Fin de vida el 28 de noviembre de 2022
8.0Fin de vida el 26 de noviembre de 2023
8.1Fin de soporte de seguridad el 31 de diciembre de 2025
8.2Soporte de seguridad hasta el 31 de diciembre de 2026
8.3 y posterioresCon soporte activo

Un plan que apunte a 8.2 tiene unos tres meses de margen. Apunte a 8.3 o 8.4.

#Recoger deprecaciones desde producción, sin WP_DEBUG

Esta es la parte que la mayoría de auditorías hace mal. El núcleo dispara do_action( 'deprecated_function_run', ... ) dentro de _deprecated_function() antes de comprobar WP_DEBUG; lo único condicionado es la llamada a trigger_error(). Lo mismo vale para deprecated_hook_run, deprecated_argument_run, deprecated_file_included, deprecated_class_run y deprecated_constructor_run. Un mu-plugin pequeño le da un inventario real del tráfico en vivo sin mostrar nada en pantalla:

<?php
// mu-plugins/deprecation-log.php
$hooks = array(
	'deprecated_function_run',
	'deprecated_hook_run',
	'deprecated_argument_run',
	'deprecated_file_included',
	'deprecated_class_run',
	'deprecated_constructor_run',
);

foreach ( $hooks as $hook ) {
	add_action(
		$hook,
		static function () use ( $hook ) {
			error_log( $hook . ' ' . wp_json_encode( func_get_args() ) );
		},
		10,
		4
	);
}

Déjelo corriendo un ciclo de negocio completo. Una importación semanal o una página de informe trimestral tocan código deprecado que ningún rastreo del front-end alcanza.

#Comprobar que los archivos son los archivos

wp core verify-checksums y wp plugin verify-checksums comparan lo instalado con los hashes de WordPress.org. En un sitio con una década de historia esto encuentra casi siempre un plugin parcheado a mano por alguien que ya no está en el equipo. Esos archivos son los que vuelven atrás en la siguiente actualización, y son los primeros que deben entrar en control de versiones.

#La trampa jQuery

Antes de culpar a jQuery, cuente cuántas copias hay. El núcleo registra exactamente una: script-loader.php declara jquery-core en la versión 3.7.1 y jquery-migrate en la 3.4.1, con jquery como alias que depende de ambas. Si en una página aparecen varias versiones, vienen de plugins y temas que encolan su propia copia con otro handle, o que escriben una etiqueta <script src> a pelo en el footer. wp_scripts()->queue le dice cuál es cuál en un minuto. Desregistrar la jQuery del núcleo para forzar una copia de CDN es justo el cambio que rompe el escritorio: deje el handle del núcleo en paz y elimine los duplicados.

jquery-migrate merece una decisión aparte. Quitarlo de la cola es una línea, y si entonces la consola se llena de errores, esos errores son la lista real de patrones de jQuery 1.x que quedan en el tema.

#Escaneo de compatibilidad con un objetivo concreto

PHPCompatibilityWP existe para que los sniffs no marquen los polyfills que el núcleo ya trae:

vendor/bin/phpcs -p wp-content/themes wp-content/plugins \
  --standard=PHPCompatibilityWP \
  --extensions=php \
  --runtime-set testVersion 8.3-

Sin testVersion el resultado dice muy poco. Con él obtiene una lista de archivos ordenada por lo que le separa de la versión a la que quiere llegar.

El inventario que debería tener al cerrar la primera semana son cuatro columnas: versión de PHP actual, deprecaciones por endpoint, archivos que fallan el checksum y errores de PHPCS por plugin. Todo lo que viene después se prioriza desde esa tabla.


#2. Estrategia de refactorización: El patrón Strangler Fig

No intente cambiarlo todo de una vez. El strangler fig de Martin Fowler, escrito en 2004 y revisado en agosto de 2024, es la única estrategia de reescritura que mantiene en pie un sitio que factura: ponga el sistema nuevo al lado del viejo, mande una porción del tráfico al nuevo y deje que el viejo encoja hasta que no quede nada que cortar.

WordPress ofrece dos costuras naturales para esto, y las dos están en el núcleo.

#Fase 1: La actualización del núcleo

Antes de mover plantillas, deje la infraestructura en condiciones: PHP en una rama con soporte, WordPress en la última estable, MySQL 8.0 o MariaDB 10.11 según la página de requisitos, HTTPS en todo el sitio y copias de seguridad que alguien haya restaurado alguna vez de verdad.

#Fase 2: El cambio progresivo de UI

La primera costura es template_include. Es un filtro, así que una sola ruta puede pasar al código nuevo mientras todas las demás siguen entrando por las plantillas viejas. Con eso basta para llevar la página de contacto, la sección de empleo o un tipo de contenido a una plantilla reescrita con sus propios activos, y para devolverla a su sitio con una línea si cae la conversión.

La segunda es el soporte híbrido de plantillas de bloques. locate_block_template(), en src/wp-includes/block-template.php, sale antes de tiempo salvo que current_theme_supports( 'block-templates' ) sea cierto, y cuando ese soporte está declarado solo considera plantillas de bloques con especificidad igual o mayor que la plantilla PHP que la jerarquía ya encontró. En la práctica:

add_action(
	'after_setup_theme',
	static function () {
		add_theme_support( 'block-templates' );
	}
);

A partir de ahí, un archivo templates/page-contact.html dentro del tema clásico se queda con esa ruta exacta. single.php, archive.php y el resto siguen igual. Este es el mecanismo que convierte “migrar a bloques” en una serie de entregas pequeñas en lugar de un fin de semana con un plan de vuelta atrás que nadie ha probado.

Ordene las porciones por riesgo, no por entusiasmo. Una buena primera porción es una plantilla con tráfico real, un resultado medible y ningún checkout dentro. Una mala primera porción es la portada, porque le da el mayor radio de impacto posible el día en que menos conoce el código nuevo.

#Fase 3: Toma de control total

Estrangule las plantillas PHP antiguas hasta que el sitio entero funcione sobre el stack moderno. Dos reglas hacen que esto se sostenga. Cada porción se lleva sus propios activos, para que la plantilla nueva no herede sin querer la hoja de estilos global antigua. Y el código viejo se borra en la misma entrega que lo jubila, porque una plantilla muerta que se queda en el repositorio la va a editar alguien antes de seis meses.


#3. Modernización de base de datos: El borrón y cuenta nueva

Casi todos los informes de “la base de datos va lenta” en un WordPress antiguo se reducen a tres cosas, y solo una tiene que ver con el tamaño.

#Gestión de autoload

El autoload es el que se paga en cada petición. Desde WordPress 6.6 la columna autoload ya no guarda solo yes y no. La nota de desarrollo de la Options API introdujo on, off, auto-on, auto-off y auto, y wp_filter_default_autoload_value_via_option_size(), en src/wp-includes/option.php, se niega a autocargar cualquier valor serializado que supere el filtro wp_max_autoloaded_option_size, con 150000 bytes por defecto. Esa protección actúa cuando la opción se escribe. No hace nada con el blob que un plugin desinstalado dejó ahí en 2019.

Lea la lista real con SQL, no con el panel de un plugin:

SELECT option_name, LENGTH(option_value) AS bytes, autoload
FROM wp_options
WHERE autoload IN ('yes', 'on', 'auto-on', 'auto')
ORDER BY bytes DESC
LIMIT 25;

Esos cuatro valores son los que wp_load_alloptions() trata como autocargados. Importa, porque wp option list --autoload=on --format=total_bytes en WP-CLI solo coincide con autoload='on' OR autoload='yes'. En un sitio con 6.6 o posterior, donde las opciones nuevas entran como auto, ese comando informa de una huella de autoload menor que la que usted paga en cada carga de página. Use la consulta, o lea el número de WP-CLI sabiendo qué deja fuera.

#Limpieza de meta

La postmeta huérfana es un recuento, no un misterio. Cuéntela antes de decidir que importa:

SELECT COUNT(*)
FROM wp_postmeta pm
LEFT JOIN wp_posts p ON p.ID = pm.post_id
WHERE p.ID IS NULL;

Las filas sin post superviviente se pueden borrar, y de todas formas antes se hace un volcado de la base de datos. La meta que pertenece a un plugin que ya retiró es una pasada aparte y más cuidadosa: agrupe por meta_key, contraste los prefijos con los plugins que sigue usando y borre por clave.

Las revisiones antiguas son el tercer bloque, y el post_type se escribe en inglés aunque el sitio esté en español:

DELETE FROM wp_posts
WHERE post_type = 'revision'
AND post_date < DATE_SUB(NOW(), INTERVAL 6 MONTH);

#Motores de almacenamiento

Los índices son la parte que nadie lee. src/wp-admin/includes/schema.php da a wp_postmeta una clave primaria sobre meta_id, una clave post_id y una clave meta_key recortada a 191 caracteres, porque utf8mb4 usa cuatro bytes por carácter y el límite histórico de índice era de 767 bytes. No hay índice compuesto sobre (post_id, meta_key). Una consulta que filtra por ambas columnas, que es lo que hace una plantilla de archivo cargada de meta en cada fila, no puede usar un solo índice para las dos. En un sitio grande la solución es un índice compuesto añadido por migración, no otra capa de caché encima.

Dos comprobaciones más van en la misma pasada. Confirme que todas las tablas son InnoDB, porque una tabla MyISAM heredada de una importación de la época de MySQL 5.1 bloquea al escribir e ignora las transacciones. Y confirme que el juego de caracteres es utf8mb4: el núcleo elige utf8mb4_unicode_520_ci en determine_charset() cuando el servidor lo admite, pero una base creada antes de WordPress 4.2 y migrada con mysqldump seguirá en utf8 y seguirá perdiendo en silencio los caracteres fuera del plano básico.


#4. Migración a CSS y JS modernos

El front-end de un WordPress legacy no suele ir lento por el framework que usa. Va lento porque nada en el tema sabe qué encola lo demás.

#De CSS monolítico a modular

Un tema legacy con un único style.css editado a mano y sin bundler no tiene forma de entregar un cambio acotado. Que la salida sea Tailwind, CSS modules o propiedades personalizadas importa mucho menos que poder responder a “qué selectores necesita esta plantilla”. Una estructura que permite responder a eso se parece a esto:

styles/
├── critical.css (en línea en <head>)
├── components/
│   ├── header.css (en todas las páginas)
│   ├── blog.css (solo en páginas de blog)
│   └── product.css (solo en páginas de producto)
└── utilities.css (clases utilitarias compartidas)

Una vez existe un build, la carga de activos por plantilla es posible, y esa carga por plantilla es lo que mueve la ruta de renderizado, no la elección de framework.

#Module bundling

Use la estrategia de carga que el núcleo ya trae. Desde WordPress 6.3, wp_enqueue_script() acepta un array en el último parámetro:

wp_enqueue_script(
	'site-main',
	get_theme_file_uri( 'build/main.js' ),
	array(),
	filemtime( get_theme_file_path( 'build/main.js' ) ),
	array(
		'strategy'  => 'defer',
		'in_footer' => true,
	)
);

La trampa está documentada en la nota de desarrollo de 6.3 e implementada en WP_Scripts::filter_eligible_strategies(): el núcleo recorre el árbol de dependencias y, si algún script encolado que depende del suyo no está también diferido, el suyo se emite como bloqueante. Puede poner defer en un handle, no ver ningún atributo defer en el HTML y no tener nada en el log. El núcleo escribe data-wp-strategy en la etiqueta siempre que se registró una estrategia diferida, se haya degradado o no, así que buscar solo ese atributo devuelve todos los scripts diferidos. La señal de degradación es una etiqueta con data-wp-strategy y sin el defer o async correspondiente. Un solo plugin antiguo que encole un script bloqueante dependiente de su bundle basta para tirar abajo el cambio entero.

Ese mismo archivo rechaza además una estrategia sobre un handle alias, que es por lo que wp_script_add_data( 'jquery', 'strategy', 'defer' ) no hace nada útil: jquery no tiene src.

#Eliminación de jQuery

El proceso es gradual y se apoya en el inventario de la sección 1:

  1. Listar los handles encolados con wp_scripts()->queue y separar la jQuery del núcleo de las copias duplicadas.
  2. Identificar qué plugins dependen del handle jquery.
  3. Quitar jquery-migrate de la cola y tratar los errores de consola como la lista de trabajo.
  4. Sustituir las funcionalidades simples por JavaScript nativo.
  5. Retirar la dependencia cuando ya no quede nada que la pida.

#5. Fortalecimiento de seguridad para sitios antiguos

El código legacy no es inseguro por antiguo. Es inseguro porque las superficies que expone se configuraron cuando los valores por defecto eran otros.

#Escaneo automatizado

Vale más una verificación de checksums programada, un registro de deprecaciones y alertas de integridad de archivos que un escáner que devuelve una puntuación de severidad. Las herramientas habituales para el resto de la pasada son PHPStan para análisis estático de PHP, npm audit para dependencias JavaScript y WPScan para vulnerabilidades ya publicadas.

#Bloqueo de API

La API REST no se puede apagar, y los snippets que dicen hacerlo son peores que no tocar nada: el núcleo la usa para el editor de bloques, Site Health y las contraseñas de aplicación. Lo que sí puede hacer es exigir autenticación para todo lo que no sea explícitamente público:

add_filter(
	'rest_authentication_errors',
	static function ( $result ) {
		if ( ! empty( $result ) || is_user_logged_in() ) {
			return $result;
		}

		return new WP_Error(
			'rest_not_logged_in',
			__( 'El acceso a la API REST requiere autenticación.' ),
			array( 'status' => 401 )
		);
	}
);

Después pruebe sin sesión iniciada las partes del sitio que dependen de ella: formularios de comentarios, bloques de búsqueda, formularios de contacto montados sobre endpoints REST y cualquier consumidor headless. En un sitio corporativo de marketing este filtro suele ser inofensivo. En uno con una función interactiva para visitantes anónimos la romperá, y es mejor descubrirlo en staging.

Sepa qué está ya restringido antes de añadir reglas. El endpoint de usuarios es un susto recurrente, pero WP_REST_Users_Controller aplica has_published_posts en las peticiones de colección siempre que quien llama no supera current_user_can( 'list_users' ), lo que cubre tanto a visitantes sin sesión como a cuentas con pocos permisos. Los tipos de contenido que acepta son get_post_types( array( 'show_in_rest' => true ) ), no los públicos, y las dos listas no coinciden. Así que el endpoint devuelve autores que han publicado en un tipo expuesto en REST, no todas las cuentas. La enumeración de autores en un sitio corporativo suele escaparse por las redirecciones de /?author=1 y por los archivos de autor, no por REST.

Las contraseñas de aplicación, además, son solo para HTTPS: wp_is_application_passwords_supported() devuelve is_ssl() || 'local' === wp_get_environment_type(). Un sitio antiguo que todavía sirve el escritorio con contenido mixto sobre HTTP plano no las ofrece, y por eso las integraciones en esos sitios acaban compartiendo la contraseña real de un administrador. Arreglar el certificado es la corrección de seguridad; la contraseña de aplicación es la consecuencia.

#Actualización de dependencias

Cada biblioteca de terceros se actualiza o se sustituye:

  • Librerías PHP con vulnerabilidades publicadas
  • Componentes JavaScript con CVE activos
  • Fuentes y activos cargados desde CDN de terceros
  • Integraciones contra APIs ya retiradas

Haga a la vez el inventario de plugins, porque cada plugin que retira es una superficie que deja de mantener.


#6. Migración gradual a Gutenberg

#Compatibilidad tema legacy + Gutenberg

Para temas construidos entre 2015 y 2018, el soporte se habilita por partes. add_theme_support( 'block-templates' ) es el interruptor que abre la migración plantilla a plantilla de la sección 2; el resto son ajustes del editor:

add_action('after_setup_theme', function() {
    // Soporte básico de bloques
    add_theme_support('wp-block-styles');
    add_theme_support('align-wide');
    add_theme_support('responsive-embeds');

    // Paleta de colores del tema legacy
    add_theme_support('editor-color-palette', [
        ['name' => 'Primario', 'slug' => 'primary', 'color' => '#1a1a2e'],
        ['name' => 'Secundario', 'slug' => 'secondary', 'color' => '#16213e'],
    ]);
});

El esquema de theme.json va por la versión 3. No existe una versión 4, así que cualquier ejemplo que la declare hará que el núcleo ignore el archivo.

#Reemplazo de page builders

La migración desde Elementor, Divi o Visual Composer a Gutenberg sigue un proceso estructurado:

  1. Inventario: documentar cada página y su constructor actual
  2. Priorización: empezar por las páginas con más tráfico
  3. Conversión: recrear los layouts con patrones de bloques
  4. Verificación: comparar visual y funcionalmente
  5. Activación: cambiar página a página con redirecciones de prueba
  6. Limpieza: desactivar el page builder antiguo cuando todas las páginas estén migradas

Compruebe la versión del constructor contra la API de plugins de wordpress.org (api.wordpress.org/plugins/info/1.0/elementor.json para Elementor, que hoy publica la 4.2.4 como estable) y no contra el readme.txt del repositorio, que se queda desfasado.


#7. Métricas de éxito de modernización

No prometa porcentajes antes de medir. Lo que sí se puede fijar antes de empezar es el instrumento con el que se va a comprobar cada cosa, y el valor de partida del sitio tal como está hoy.

Qué se mideInstrumentoCuándo se toma
LCP e INP de campoInforme CrUX o RUM propioAntes de la primera porción y 28 días después
TTFBWebPageTest o los logs del servidorPor plantilla, no como media del sitio
Consultas SQL por páginaQuery MonitorEn las plantillas con tráfico, con caché de objetos activa
Bytes autocargadosLa consulta SQL de la sección 3Antes y después de la limpieza de wp_options
Errores PHP por díaRegistro de errores del servidorDurante todo un ciclo de negocio
Deprecaciones por endpointEl mu-plugin de la sección 1Durante todo un ciclo de negocio

Del lado del negocio, la única cifra honesta antes de empezar es la que ya tiene: tráfico orgánico, conversión y coste de mantenimiento actuales. La comparación se hace después, contra esa misma línea base y con la ventana de medición decidida de antemano.


#8. Por qué WPPoland es su experto en modernización

En WPPoland trabajamos el rescate de sitios WordPress, y siempre por este orden.

  1. Due diligence y auditorías: entregamos el inventario técnico de la sección 1, con prioridades y el coste estimado de cada intervención.

  2. Despliegues incrementales: modernizamos por etapas, cada una con su criterio de aceptación y su forma de volver atrás.

  3. Objetivos medidos, no prometidos: acordamos qué métrica se mueve, con qué instrumento se mide y en qué ventana, antes de tocar el código. Lo que no se pueda medir no entra en el alcance.

  4. Transferencia de conocimiento: documentamos el proceso y formamos a su equipo para que mantenga el código modernizado por su cuenta.


#9. Conclusion: No deje que el pasado lo detenga

Un sitio WordPress legacy no es automáticamente un lastre. Lo es la deuda técnica que nadie gestiona, y la diferencia entre ambos casos está en si alguien puede decir cuánto cuesta un cambio antes de hacerlo.

La secuencia de esta guía es deliberada. Mida la brecha de PHP contra las fechas de php.net, recoja deprecaciones del tráfico real con los hooks que el núcleo ya dispara, verifique los archivos contra los checksums y después estrangule las plantillas ruta a ruta con template_include o block-templates. Arregle el autoload y los índices de postmeta, porque están en cada petición. Arregle la carga de scripts, porque el núcleo le da el mecanismo y los plugins antiguos se lo quitan en silencio. Todo lo demás es una preferencia mientras eso no esté hecho.

Si el sitio sigue con un stack de 2018, el primer entregable no es código. Es un documento: versión de PHP, registro de deprecaciones, fallos de checksum, lista de plugins con responsable y las plantillas que cargan con el tráfico.

¿Su sitio corporativo está atrapado en 2018? Escriba a WPPoland para una auditoría y empiece por el inventario, no por la reescritura.


#Recursos relacionados

Siguiente paso

Transforma el artículo en una implementación real

Este bloque refuerza el enlazado interno y lleva al lector al siguiente paso más útil dentro de la arquitectura del sitio.

¿Quieres implementar esto en tu sitio?

Si quieres transformar el artículo en mejoras concretas, rediseño o un plan de implementación, puedo cerrar el alcance y ejecutar.

Cluster relacionado

Explora otros servicios WordPress y base de conocimiento

Refuerza tu negocio con soporte técnico profesional en áreas clave del ecosistema WordPress.

FAQ del artículo

Preguntas frecuentes

Respuestas prácticas para aplicar el tema en la ejecución real.

SEO-readyGEO-readyAEO-ready3 Q&A
¿Es mejor reconstruir desde cero o refactorizar?#
Refactorice cuando el modelo de contenido merezca conservarse y las URL estén trayendo tráfico, porque una reconstrucción paga la migración dos veces: una por el código y otra por cada redirección y cada campo personalizado. Reconstruya cuando el tema no tenga proceso de build, ni historial en control de versiones, ni forma de probarlo, porque entonces cada cambio es un riesgo nuevo y no queda nada que estrangular de forma incremental.
¿Cómo manejo plugins personalizados antiguos?#
Ejecute primero wp plugin verify-checksums para encontrar los archivos editados a mano, y después PHPCompatibilityWP sobre la carpeta del plugin con testVersion apuntando al PHP al que quiere llegar. Lo que no tenga upstream, falle el checksum y acumule errores duros es candidato a reescritura, y parte de lo que vive en esos plugins ya está en el núcleo: la API REST desde 4.7 y las contraseñas de aplicación desde 5.6.
¿Puedo usar Gutenberg en un tema construido en 2015?#
En parte. Un tema clásico puede declarar add_theme_support( 'block-templates' ), y a partir de ahí locate_block_template() deja que una plantilla HTML del tema se quede con una ruta concreta mientras el resto de plantillas PHP siguen funcionando. Ese es el paso híbrido, y es lo que permite migrar plantilla a plantilla en lugar de cambiarlo todo en una sola entrega.

¿Necesitas un FAQ adaptado a tu sector y mercado? Preparamos una versión alineada con tus objetivos de negocio.

Hablemos

Artículos Relacionados