Stop Patching Your Code: A Real WordPress Plugin Architecture

I had a client come to me with a custom-built membership plugin that was the heart of their business. It was also a total mess. The thing had started small, but over five years and three different developers, it had become a fragile tower of code. Adding a simple feature would take weeks, and every update broke something new. They were stuck. The core of their problem wasn’t a specific bug; it was a fundamental flaw in their WordPress plugin architecture.

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 identify the slowest parts and refactor them. A quick win, right? Maybe clean up the jumble of functions in the main plugin file. But I’ve learned that’s just a band-aid. The real problem wasn’t any single piece of code. It was the lack of a central nervous system. The plugin had no brain; it was just a collection of twitching limbs.

The Right Way: A Central “Manager” Class

Instead of patching holes, you need to give the plugin a proper bootstrap process. The solution is a dedicated class that has one job and one job only: to assemble all the other pieces of the plugin. Think of it as a factory foreman. It doesn’t do the work, but it makes sure all the workers (the other classes) are in place and ready to go. This approach is a more robust take on some of the object-oriented design principles I first saw detailed over on carlalexander.ca.

This main class is responsible for instantiating your other classes—your event manager, your CPT registrar, your API endpoints—and making sure they’re talking to each other. Here’s the kicker: you keep the constructor lean and do the actual “wiring up” in a dedicated `load()` method. This method then gets attached 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 are loaded. No more guessing games.
  • Dependencies are explicit. The Manager class passes objects to the classes that need them. This is way better than using globals or singletons. Trust me on this.
  • The next developer who works on this won’t want to tear their hair out. They have a clear entry point and can see how all the pieces connect.

Look, this stuff gets complicated fast. If you’re tired of debugging someone else’s mess and just want your site to work, drop my team a line. We’ve probably seen it before.

Building plugins that don’t fall apart after six months isn’t about writing clever code. It’s about writing organized, predictable code. And a central manager class is the first step to achieving that sanity.

Leave a Reply

Your email address will not be published. Required fields are marked *