Artículo

¿Plugin o functions.php en WordPress? Cuándo usar cada uno

Has encontrado un fragmento de código que hace exactamente lo que necesitabas. Son doce líneas. Quizá veinte. Lo copias, abres functions.php, lo pegas y WordPress empieza a hacer algo que hace cinco minutos no sabía hacer.

Funciona.

Y ahí está una de las cosas que más me gustan de desarrollar sobre WordPress: una necesidad que parecía requerir otro plugin enorme puede terminar resolviéndose con unas pocas líneas y el hook adecuado.

Pero hay una pregunta que conviene hacerse antes de seguir acumulando snippets:

¿Ese código pertenece realmente a functions.php o debería convertirse en un plugin?

Las dos opciones pueden producir exactamente el mismo resultado. La diferencia importante aparece después: cuando cambias de theme, actualizas la web, quieres ampliar aquella función o vuelves al código seis meses más tarde intentando recordar por qué estaba allí.

Hay una regla sencilla que ayuda mucho a decidir:

Si la funcionalidad pertenece al theme, functions.php puede ser su sitio. Si debería continuar funcionando aunque mañana cambies de theme, normalmente debería estar en un plugin.

A partir de ahí empiezan los matices interesantes.

Qué es realmente functions.php en WordPress

functions.php es un archivo del theme que WordPress carga automáticamente cuando ese theme está activo.

Desde él podemos utilizar acciones y filtros, activar características del theme, modificar determinados comportamientos de WordPress o definir nuestras propias funciones.

Por eso muchas veces se dice que functions.php se comporta de manera parecida a un plugin.

No es solo una comparación informal: ambos pueden utilizar los hooks y funciones que WordPress pone a disposición de los desarrolladores.

Por ejemplo, podríamos cambiar la longitud de los extractos:

add_filter( 'excerpt_length', 'dplugins_longitud_extracto' );

function dplugins_longitud_extracto( $length ) {
    return 30;
}

Son unas pocas líneas.

Podríamos colocarlas en functions.php y funcionarían. También podríamos introducirlas dentro de un pequeño plugin y obtener prácticamente el mismo resultado.

Entonces, ¿para qué complicarnos creando un plugin?

Porque aunque ambos puedan ejecutar PHP, su ciclo de vida no es el mismo.

functions.php pertenece al theme.

Un plugin pertenece a la funcionalidad de la web.

Y esa diferencia aparentemente pequeña resuelve buena parte de la discusión.

La pregunta que utilizo para decidir entre plugin o functions.php

Imagina que mañana cambias completamente el diseño de tu web.

Desactivas el theme actual y activas otro.

Ahora pregúntate:

¿Esta funcionalidad debería seguir existiendo?

Si la respuesta es no, tiene bastante sentido que estuviera asociada al theme.

Si la respuesta es sí, empieza a resultar extraño que dependa de él.

Pensemos, por ejemplo, en una modificación relacionada exclusivamente con la presentación de determinados elementos del theme. Puede tener sentido mantenerla junto al propio theme.

Ahora imagina algo diferente.

Has creado una función que consulta una API externa y guarda determinados datos en WordPress.

¿Qué tiene que ver esa integración con el aspecto de la web?

Nada.

Cambiar de theme no debería hacer que dejara de funcionar.

Ahí tenemos un candidato clarísimo para convertirse en plugin.

Me gusta resumirlo así:

El theme se ocupa principalmente de cómo se presenta la web. El plugin añade una capacidad que la web necesita conservar.

No es una frontera absolutamente perfecta, pero como regla práctica resulta enormemente útil.

Qué ocurre con el código de functions.php cuando cambias de theme

Aquí está uno de los motivos por los que no me gusta utilizar functions.php como un cajón donde guardar cualquier snippet que encontramos.

WordPress ejecuta el functions.php del theme activo.

Cuando cambias de theme, el archivo del anterior deja de formar parte de la ejecución normal de la web.

El código continúa físicamente dentro de la carpeta del theme antiguo, pero su funcionalidad deja de estar disponible.

Supongamos que habíamos colocado allí:

  • un Custom Post Type;
  • una automatización;
  • una integración con una API;
  • una función administrativa;
  • un sistema de notificaciones;
  • una modificación relacionada con usuarios;
  • o cualquier otra característica que realmente pertenece al funcionamiento de la web.

Cambiar el diseño podría provocar que esas funciones desaparecieran.

Y eso es especialmente incómodo porque la web quizá no parezca rota.

El nuevo theme carga perfectamente.

La portada se ve bien.

Pero algo que WordPress hacía ayer ha dejado silenciosamente de hacerlo hoy.

Por eso la ubicación del código no es solo una cuestión de orden. También es una decisión sobre de qué depende esa funcionalidad.

¿Y qué ocurre cuando actualizas el theme?

Aquí tenemos otro problema diferente.

Si modificas directamente el functions.php de un theme desarrollado por otra persona, una actualización puede sobrescribir tus cambios.

Imagina la situación.

Necesitas una pequeña función. Encuentras la solución, modificas functions.php y durante seis meses funciona perfectamente.

Un día actualizas el theme.

La función desaparece.

No porque WordPress haya decidido eliminarla, sino porque modificaste directamente unos archivos que posteriormente fueron sustituidos por la nueva versión del theme.

El child theme evita un problema, pero no resuelve todos

Para modificaciones que realmente pertenecen al theme, un child theme es una solución mucho más apropiada.

El child theme puede disponer de su propio functions.php. WordPress carga ese archivo además del correspondiente al theme padre, por lo que podemos conservar nuestras modificaciones aunque actualicemos este último.

Pero esto no cambia nuestra pregunta original.

Aunque el código sobreviva perfectamente a las actualizaciones:

¿Debería dejar de funcionar si algún día abandonamos ese child theme?

Si la respuesta continúa siendo no, probablemente esa funcionalidad debería seguir viviendo en un plugin.

Cuándo utilizaría functions.php

No quiero convertir functions.php en el malo de esta historia.

No lo es.

Es una parte perfectamente legítima de un theme WordPress y existen muchas situaciones en las que es exactamente el lugar que necesitamos.

Lo utilizaría especialmente para funcionalidades estrechamente relacionadas con el propio theme.

Por ejemplo:

  • registrar características soportadas por el theme;
  • registrar determinados menús;
  • modificar aspectos relacionados con imágenes destacadas;
  • cargar los estilos propios del theme;
  • añadir acciones o filtros necesarios para su funcionamiento;
  • crear pequeñas funciones auxiliares utilizadas por sus plantillas;
  • modificar un comportamiento que solamente tiene sentido mientras ese theme esté activo.

En estos casos existe una relación lógica entre el código y el theme.

Si eliminamos el theme, desaparece también la necesidad de esa función.

Perfecto.

El problema comienza cuando functions.php deja de ser el archivo de funciones del theme y se convierte en el lugar donde terminamos pegando todo lo que queremos que WordPress haga.

Cuándo prefiero crear un plugin

Aquí es donde desarrollar plugins empieza a resultar especialmente interesante.

No hace falta estar construyendo un enorme sistema para justificar un plugin.

Un plugin puede tener una única responsabilidad y ocupar muy poco.

Si necesito que WordPress haga algo que conceptualmente pertenece al sitio y no al theme, prefiero separar esa funcionalidad.

Algunos ejemplos claros serían:

  • conectar WordPress con una API externa;
  • automatizar una tarea;
  • procesar determinados datos;
  • añadir una función al administrador;
  • crear una integración con otro servicio;
  • registrar información que debe conservarse independientemente del diseño;
  • modificar el comportamiento de WooCommerce;
  • crear un sistema personalizado de notificaciones;
  • añadir una funcionalidad específica que ningún plugin existente resuelve como necesito.

Y aquí aparece una idea que me parece especialmente atractiva del desarrollo de plugins:

un plugin no tiene por qué intentar resolver cien problemas.

Puede resolver uno.

Y resolverlo muy bien.

A veces necesitamos una función tan concreta que instalar una herramienta enorme para conseguirla resulta desproporcionado.

Ahí es donde un pequeño plugin personalizado empieza a tener mucho sentido.

Cinco ejemplos para saber qué elegir

La teoría está bien, pero esta decisión se entiende mucho mejor cuando ponemos necesidades reales encima de la mesa.

1. Quiero cambiar la longitud de los extractos

Tenemos una modificación sencilla que puede estar relacionada con la forma en que nuestro theme presenta los contenidos.

Elegiría: functions.php, si el comportamiento forma parte específicamente de ese theme.

No necesitaría crear automáticamente un plugin para una modificación tan pequeña y vinculada a la presentación.

2. Quiero registrar un Custom Post Type que debe conservar sus contenidos

Imagina que creamos un tipo de contenido llamado “Proyectos”.

Dentro tendremos decenas o cientos de proyectos y queremos conservar esa estructura aunque dentro de dos años rediseñemos completamente la web.

Elegiría: plugin.

El diseño puede cambiar.

Los proyectos siguen siendo proyectos.

No me interesa que la existencia de esa estructura dependa del theme que esté activo.

3. Quiero conectar WordPress con una API externa

Supongamos que necesitamos consultar periódicamente un servicio externo, recibir información y almacenarla o procesarla en WordPress.

Elegiría: plugin.

La integración pertenece al funcionamiento de la web, no a su apariencia.

Y probablemente será una funcionalidad que queramos ampliar, depurar o configurar en el futuro.

Recuerdo una experiencia personal que tuve hace un par de años mientras programaba.

Todo empezó con una petición bastante sencilla: enviar automáticamente a un servicio externo algunos datos cada vez que se publicaba una entrada en WordPress.

La primera versión era casi ridícula de lo pequeña que era. Utilicé el hook de publicación, preparé los datos y los envié mediante la HTTP API de WordPress.

Publicaba una entrada, el otro sistema recibía los datos. Perfecto.

Hasta que un día el servidor externo tardó demasiado en responder.

La publicación se hizo más lenta y pensé: esto no debería depender de que el otro servidor esté disponible justo en ese momento.

Así que cambié el planteamiento. En lugar de enviar inmediatamente la información, WordPress dejaba anotado el trabajo pendiente y una tarea programada intentaba enviarlo después.

Entonces apareció otro problema: ¿qué ocurría si la API devolvía un error?

Añadí reintentos.

Después necesité guardar el último código de respuesta para saber por qué había fallado. Luego un pequeño registro de actividad. Más tarde, una pantalla donde configurar la URL de la API y las credenciales sin tenerlas escritas directamente en el código.

Lo que inicialmente podía explicar como “cuando publiques, envía estos datos” ya tenía varias responsabilidades: detectar el evento, preparar información, almacenarla temporalmente, comunicarse con otro servidor, gestionar errores, reintentar y ofrecer configuración.

Era como empezar instalando un timbre y descubrir poco a poco que también necesitabas el telefonillo, el cableado, la fuente de alimentación y un cuadro donde comprobar si algo había fallado.

En ese momento dejé de añadir funciones una debajo de otra y separé el plugin por responsabilidades.

Una parte escuchaba a WordPress. Otra preparaba los datos. Otra hablaba con la API. Otra gestionaba la cola y los reintentos.

Y ahí ocurrió algo que me encanta del desarrollo de plugins: la funcionalidad final seguía pareciendo muy sencilla para quien utilizaba WordPress.

Publicaba una entrada y los datos aparecían donde tenían que aparecer.

Toda la complejidad estaba detrás, exactamente donde debía estar.

Fue entonces cuando entendí que una buena automatización no consiste solo en conseguir que algo suceda automáticamente.

Consiste en conseguir que siga funcionando cuando las cosas no salen exactamente como esperabas.

4. Quiero cambiar algo exclusivamente visual del theme

Tenemos una modificación que solamente existe porque nuestro theme actual presenta determinado elemento de una manera concreta.

Elegiría: functions.php del child theme, si la modificación requiere PHP y está realmente ligada al theme.

Cuando cambiemos de diseño, probablemente ya no necesitemos ese comportamiento.

5. Quiero automatizar una tarea que hago manualmente

Aquí empieza una de mis partes favoritas.

Supongamos que cada semana tienes que realizar la misma operación dentro de WordPress: revisar determinados datos, modificar algo y ejecutar una acción.

Y un día aparece la pregunta:

¿Por qué sigo haciendo esto yo?

Si WordPress dispone de los hooks y APIs adecuados, quizá podamos conseguir que lo haga automáticamente.

Elegiría: plugin.

La automatización tiene una finalidad propia y debería funcionar independientemente del diseño de la web.

Además, este tipo de desarrollo tiene algo especialmente satisfactorio: la primera vez que compruebas que una tarea que antes requería tu intervención acaba de ejecutarse sola.

Es una funcionalidad pequeña.

Pero WordPress acaba de aprender a hacer algo nuevo.

Tabla rápida: functions.php o plugin

Necesidadfunctions.phpPluginMotivo
Funcionalidad exclusiva del themeNo necesariamenteDepende directamente del theme
Configuración propia del themeNoForma parte de su funcionamiento
Pequeña modificación visual mediante PHPNo necesariamentePierde sentido al cambiar de theme
Custom Post Type permanenteMejor noLos contenidos deben sobrevivir al cambio de theme
AutomatizaciónMejor noEs funcionalidad independiente
Integración con una APIMejor noNo depende del diseño
Función administrativa personalizadaNormalmente noPertenece al funcionamiento de WordPress
Funcionalidad personalizada de WooCommerceNormalmente noDebe sobrevivir al cambio de theme
Código que quieres reutilizar en otras websNoUn plugin es portable y reutilizable

No utilizaría esta tabla como una ley absoluta.

La utilizaría como una señal.

Cuando casi toda la columna derecha empieza a apuntar hacia “plugin”, probablemente ya sabemos dónde debería vivir nuestro código.

Un plugin pequeño puede ser más limpio que veinte snippets

Hay una fase bastante reconocible en muchas instalaciones WordPress.

Primero añades un snippet.

Después otro.

Luego aparece uno que modifica el administrador.

Otro conecta con un servicio.

Otro ejecuta una tarea programada.

Individualmente todos parecen demasiado pequeños como para preocuparnos.

Hasta que un día abres functions.php y encuentras cientos de líneas pertenecientes a cosas completamente diferentes.

Funciona.

Pero ya no resulta agradable tocarlo.

Ese momento es una buena oportunidad para preguntarse si algunas de esas funcionalidades deberían independizarse.

Crear un plugin no significa necesariamente convertir veinte líneas en una arquitectura empresarial con quince clases y ocho directorios.

Puede seguir siendo sencillo.

Un plugin WordPress mínimo necesita un archivo PHP con una cabecera que permita a WordPress identificarlo. Después podemos ir añadiendo estructura únicamente cuando la complejidad lo justifique.

Por ejemplo:

<?php
/**
 * Plugin Name: Mi funcionalidad personalizada
 * Description: Añade una funcionalidad específica a WordPress.
 * Version: 1.0.0
 */

defined( 'ABSPATH' ) || exit;

/**
 * A partir de aquí vive nuestra funcionalidad.
 */

Eso ya establece una frontera muy interesante.

Este código ya no pertenece al diseño.

Tiene nombre.

Tiene versión.

Puede activarse.

Puede desactivarse.

Y mañana puede crecer sin convertir functions.php en un almacén de funcionalidades inconexas.

Separar funcionalidades también facilita entender qué está pasando

Hay otra ventaja que a veces me parece incluso más importante que la organización: la capacidad de razonar sobre el sistema.

Supongamos que algo deja de funcionar.

Si tenemos 700 líneas de snippets mezclados dentro del theme, encontrar el origen puede convertirse en una pequeña expedición arqueológica.

En cambio, si tenemos:

mi-integracion-api

automatizacion-pedidos

notificaciones-personalizadas

resulta mucho más evidente dónde mirar.

Podemos desactivar una funcionalidad.

Probar.

Volver a activarla.

Registrar errores.

Actualizarla independientemente.

Incluso sustituirla sin tocar el theme.

No se trata simplemente de tener carpetas más bonitas.

Se trata de reducir las cosas que debemos tener en la cabeza cuando aparece un problema.

¿Crear muchos plugins pequeños perjudica el rendimiento?

Esta duda aparece bastante porque existe la idea de que “muchos plugins hacen lento WordPress”.

El número por sí solo dice muy poco.

Dos plugins pueden realizar consultas pesadísimas, cargar recursos externos en todas las páginas y ejecutar procesos innecesarios.

Veinte pequeños plugins bien construidos pueden hacer muy poco trabajo.

Lo importante es qué hace el código y cuándo lo hace.

Por eso no mantendría una funcionalidad dentro de functions.php simplemente para poder decir que la web tiene un plugin menos.

Si la separación tiene sentido arquitectónico, prefiero organizarla correctamente y después preocuparme por que su ejecución sea eficiente.

Esta es precisamente una de las partes que más me atraen del desarrollo de plugins: no basta con conseguir que algo funcione.

Después aparece otra pregunta:

¿Podemos conseguir que haga exactamente lo mismo con menos trabajo, menos dependencias y menos posibilidades de interferir con otras partes de WordPress?

Ahí una solución funcional empieza a convertirse en una solución buena.

Un detalle importante: evita nombres de funciones genéricos

Tanto si escribimos código en un theme como si desarrollamos un plugin, compartimos espacio con WordPress, otros plugins y otros desarrolladores.

Por eso una función llamada:

function guardar_datos() {
    // ...
}

no me entusiasma.

Otro plugin podría utilizar exactamente el mismo nombre.

Es preferible utilizar un identificador suficientemente distintivo:

function dplugins_guardar_datos() {
    // ...
}

En proyectos más grandes también podemos utilizar clases o namespaces.

No es un detalle puramente estético. Una colisión de nombres puede producir errores difíciles de localizar.

Y este tipo de pequeñas decisiones son las que hacen que desarrollar un plugin resulte mucho más interesante que limitarse a conseguir que “el código funcione”.

Queremos que funcione hoy.

Pero también queremos entenderlo mañana.

No conviertas automáticamente cada snippet en un plugin

Después de todo lo anterior sería fácil caer en el extremo contrario.

“Entonces todo debería ser un plugin.”

Tampoco.

Si estamos desarrollando un theme propio y necesitamos una función exclusivamente relacionada con él, functions.php puede ser exactamente el sitio correcto.

Si necesitamos configurar características soportadas por el theme, registrar sus menús o ejecutar código necesario para sus plantillas, no ganamos nada creando plugins artificiales únicamente para mantener vacío functions.php.

La pregunta no debería ser:

¿Qué opción es técnicamente más profesional?

Las dos lo son cuando se utilizan correctamente.

La pregunta debería ser:

¿A quién pertenece esta funcionalidad?

¿Al theme?

¿O a WordPress como aplicación?

Esa pregunta suele producir mejores decisiones que cualquier regla basada en el número de líneas.

Cómo sé que un código de functions.php está pidiendo convertirse en plugin

Hay algunas señales que me hacen detenerme.

Si el código:

  • empieza a crecer;
  • necesita configuración propia;
  • guarda datos;
  • interactúa con servicios externos;
  • ejecuta automatizaciones;
  • modifica el administrador;
  • tiene varias funciones relacionadas entre sí;
  • necesita mantenimiento independiente;
  • quiero reutilizarlo;
  • o debe continuar funcionando cuando cambie el theme;

entonces probablemente ha llegado el momento de sacarlo de allí.

Y hay algo curioso en ese proceso.

Una funcionalidad puede empezar como diez líneas copiadas provisionalmente en functions.php.

Después añadimos una condición.

Luego necesitamos guardar una opción.

Más tarde aparece un segundo hook.

Finalmente queremos una pequeña pantalla de configuración.

En algún momento deja de ser “un snippet”.

Se ha convertido en una funcionalidad.

Y convertirla en plugin no es complicarla.

Muchas veces es simplemente reconocer lo que ya es.

Recuerdo una función que empezó con apenas unas líneas en functions.php.

Solo quería que WordPress guardara automáticamente un pequeño dato cada vez que publicaba una entrada. Funcionó a la primera y durante un tiempo no pensé más en ello.

Hasta que necesité consultar ese dato desde el administrador.

Añadí unas líneas más.

Después quise poder activarlo o desactivarlo sin tocar código. Añadí una opción.

Más tarde pensé: “Si cambio de theme, ¿por qué debería desaparecer todo esto?”.

Ahí me di cuenta de que estaba haciendo algo parecido a guardar las herramientas de un taller dentro del salón de casa. Podían estar allí y funcionar perfectamente, pero sencillamente ya no era su sitio.

Saqué el código de functions.php y creé un pequeño plugin.

Lo curioso es que, una vez separado, empecé a verlo de otra manera. Ya no era “aquel código que añadí al theme”. Era una funcionalidad con una tarea concreta.

Podía activarla, desactivarla, mejorarla y llevármela a otra instalación de WordPress.

Y apareció otra idea. Luego otra.

Lo que había empezado con unas pocas líneas terminó teniendo su propia pantalla de configuración y varias funciones relacionadas.

Ese día entendí algo que todavía me parece una de las partes más interesantes de desarrollar para WordPress:

un plugin no siempre nace porque alguien decide crear un plugin. A veces nace porque una pequeña solución empieza a pedir su propio sitio.

Entonces, ¿plugin o functions.php?

Si necesitas una respuesta rápida, quédate con esta:

Usa functions.php para código relacionado con el theme. Utiliza un plugin para funcionalidades que deberían continuar existiendo independientemente del theme.

Y si todavía tienes dudas, haz la prueba mental:

Mañana cambio completamente el theme. ¿Quiero que esto siga funcionando?

No → functions.php puede tener sentido.

Sí → probablemente quieres un plugin.

Después habrá casos especiales, decisiones de arquitectura y proyectos donde la frontera no sea tan evidente.

Pero esa sencilla pregunta evita una cantidad sorprendente de problemas.

Y, sobre todo, nos obliga a pensar en algo que me parece fundamental cuando desarrollamos para WordPress:

No solo queremos solucionar el problema que tenemos delante.

Queremos decidir dónde debe vivir esa solución.

Porque a veces todo empieza con una frase aparentemente insignificante:

“Solo necesito que WordPress haga esto.”

Y precisamente ahí puede comenzar un plugin muy interesante.

¿Tienes una funcionalidad que ya no encaja en functions.php?

Si tienes un snippet que ha empezado a crecer, una funcionalidad que debería sobrevivir a los cambios de theme o simplemente necesitas que WordPress haga algo que los plugins existentes no resuelven bien, puedes contarme qué necesitas.

No hace falta que sepas cómo debería programarse.

Lo interesante empieza precisamente ahí: entender el problema y encontrar la forma más sencilla de convertirlo en una funcionalidad mantenible.

Cuéntame qué necesitas que haga WordPress →