The short answer
If the feature should disappear when you change the design, it can live in functions.php. If the website still needs it after switching themes, it belongs in a plugin.
That rule solves most cases. A custom post type for “Projects” is website functionality. A custom image size used by one theme layout is theme functionality. One should survive a redesign; the other does not have to.
Use functions.php for theme behaviour
WordPress loads the active theme's functions.php file automatically. It behaves a lot like a plugin, but its lifecycle is tied to that theme. Switch the theme and the code stops running.
Good candidates include:
- registering navigation menu locations used by the theme;
- adding theme support for features such as post thumbnails;
- registering image sizes required by a specific layout;
- enqueueing that theme's CSS and JavaScript;
- small filters that change how the active theme displays content.
Build a plugin for site functionality
A plugin is the better home when the feature represents business logic, stores important data, integrates an external service or should be reusable. It does not need to be a huge public plugin with a settings screen. A private plugin containing one carefully named feature is completely valid.
Use a plugin for things such as:
- custom post types and taxonomies;
- booking logic, calculators and custom forms;
- API and Google Sheets integrations;
- WooCommerce business rules;
- scheduled tasks, data imports and admin tools;
- shortcodes or blocks that must work after a redesign.
The practical benefit is separation. A designer can replace the theme without accidentally deleting the website's core functionality.
A minimal custom plugin
Create a folder inside wp-content/plugins, for example ligmajer-site-tools, and add a PHP file with a valid plugin header:
<?php
/**
* Plugin Name: Ligmajer Site Tools
* Description: Site-specific functionality.
* Version: 1.0.0
* Author: Jozef Ligmajer
*/
defined( 'ABSPATH' ) || exit;
function ligmajer_register_project_type() {
// Register site functionality here.
}
add_action( 'init', 'ligmajer_register_project_type' );Activate it in the WordPress admin and the functionality becomes independent of the theme. Prefix functions, classes and options—or use namespaces—to avoid collisions with WordPress, themes and other plugins.
Moving existing snippets safely
- Make a backup and test on staging.
- Create and activate the site-specific plugin with an empty bootstrap file.
- Move one logical feature at a time.
- Remove the original snippet from
functions.phpso it does not run twice. - Test the frontend, admin, forms, scheduled events and error log.
Do not move code blindly. A snippet may depend on theme templates, theme constants or hooks that only exist in the old design. Separate those pieces before migration.
The decision checklist
- Would the feature still be needed after changing the theme? Plugin.
- Does it store business data or define content structure? Plugin.
- Is it an integration, background task or business rule? Plugin.
- Does it only control the current design or presentation? Theme.
- Is it a tiny temporary experiment? Test it safely, then put the final version where it actually belongs.
The point is not to create a plugin for every three-line snippet. The point is to stop tying permanent website functionality to a replaceable design.