I had a client come to me with a custom-built membership plugin that ran their whole business. It was also a total mess. It had started small, but over five years and three different developers it had turned into a fragile tower of code. Adding a simple feature took weeks, and every update broke something new. They were stuck. Their problem was not a specific bug. It was a flaw in the plugin’s architecture itself.
When you let a plugin grow organically without a clear plan, you end up with code spaghetti. Files requiring other files, hooks and filters scattered everywhere, and no single source of truth for how the plugin actually starts up. It becomes impossible to reason about.
My first thought was to just find the slowest parts and refactor them. A quick win. Maybe tidy up the jumble of functions in the main plugin file. But I have learned that is just a band-aid. The problem was not any single piece of code. It was that the plugin had no center: no clear place where everything started and connected.
A central manager class
Instead of patching holes, give the plugin a proper bootstrap process. That means a dedicated class with one job: to assemble all the other pieces of the plugin. Think of it as a factory foreman. It does not do the work, but it makes sure all the workers, the other classes, are in place and ready to go. This is a more robust take on some of the object-oriented design principles I first read about on carlalexander.ca.
This main class instantiates your other classes, the event manager, the CPT registrar, the API endpoints, and makes sure they are talking to each other. Keep the constructor lean and do the actual wiring up in a dedicated load() method. That method then attaches to a late-firing WordPress hook like wp_loaded.
<?php
final class MyPlugin_Manager
{
private $loaded = false;
public function __construct( private string $plugin_file )
{
// Constructor is for basic setup, not for hooking things up.
// We can store the main plugin file path here for later.
}
public function load(): void
{
if ( $this->loaded ) {
return;
}
// 1. Instantiate services
$event_manager = new MyPlugin\EventManagement\EventManager();
$post_types = new MyPlugin\PostTypes\Portfolio();
// 2. Register subscribers/listeners
$event_manager->add_subscriber( new MyPlugin\Subscribers\PortfolioSubscriber( $post_types ) );
$this->loaded = true;
}
}
// In your main plugin file (my-plugin.php)
$plugin_manager = new MyPlugin_Manager(__FILE__);
add_action('wp_loaded', [$plugin_manager, 'load']);So what’s the point?
Why go to all this trouble? Because it makes your code predictable and maintainable. When you structure your plugin this way:
- You know exactly when and how your plugin’s components load. No more guessing.
- Dependencies are explicit. The manager class passes objects to the classes that need them, which beats reaching for globals or singletons.
- The next developer who touches this will not want to tear their hair out. They get a clear entry point and can see how the pieces connect.
This stuff gets complicated fast. If you are tired of debugging someone else’s mess and just want your site to work, drop my team a line. We have probably seen it before.
Plugins that do not fall apart after six months are not the result of clever code. They come from organized, predictable code, and a central manager class is where that starts.