Enterprise WordPress is not the version with the most features, it’s the version where every moving part was chosen on purpose, lives in version control, and can be rebuilt from scratch by someone who has never seen it.
You already know the site I’m about to describe, because you’ve probably inherited one. Sixty-plus plugins. A commercial theme with a bundled page builder. No staging environment. Edits made directly on the live site. The agency that built it gone three years ago.
The reason that site fails isn’t aesthetic. Every one of those plugins is third-party code executing with full database access. Updates become roulette. Nobody can reproduce the site, so nobody can safely change it, so nobody does, and the whole thing calcifies until a redesign becomes a re-platform.
“Enterprise” in this context isn’t a measure of traffic or feature count, it’s a measure of consequence. A credit union, a hospital, a public company. When the cost of an outage and the cost of a breach are what define the tier, the architecture has to be defined by what it leaves out. What follows is the exact stack I use on those builds, drawn from a production build for a financial institution, and every section answers the same question: what happens to this decision three years from now, when someone else owns it?
Infrastructure: Let the Host Be the Host
Pick a host that hands you primitives, not one that hands you a plugin.
My recommendation at this tier is Kinsta, and the reasons that matter have nothing to do with the marketing page: container isolation per site, real SSH access, WP-CLI available server side, one-click staging that genuinely matches production, PHP version control, automatic backups, and edge caching handled at the platform level.
That list establishes the first rule of the whole architecture: caching and backups are infrastructure concerns, not plugin concerns. Every caching plugin in a WordPress install is a symptom of a host that couldn’t do its job.
The alternatives fail in predictable ways:
- A bare VPS: you are not being paid to patch nginx, and the day it needs patching is the day you’re on vacation.
- Budget shared hosting: no isolation, no CLI, noisy neighbors, and no path to a real staging environment.
The test for any host at this tier is simple: can you SSH in, run WP-CLI, and script a deploy? If not, it’s not an enterprise host, whatever the pricing page says.
The Code Boundary: What Belongs in Git
Version control what you author. Nothing else.
WordPress core, third-party plugins, and the uploads directory are not your code and don’t belong in your repository. The pattern I use inverts the .gitignore: ignore everything, then explicitly allowlist the things you actually wrote.
# ignore everything
/*
# then allowlist what you actually wrote
!wp-content/
wp-content/*
!wp-content/themes/
wp-content/themes/*
!wp-content/themes/your-theme/
!wp-content/plugins/
wp-content/plugins/*
!wp-content/plugins/your-functionality-plugin/
!wp-content/mu-plugins/
The payoff is legibility. Your diff contains only your decisions, and git log reads like a changelog a client could follow. There’s a Composer-managed alternative for teams that want full dependency pinning, and it’s a fine choice, but honestly the allowlist approach is simpler and sufficient for most sites.
The Theme Is a Presentation Layer, Not an Application
A minimal theme you fully own beats a feature-rich theme you inherited.
Commercial themes fail at this tier for a structural reason: they’re built to demo well and convert a sale. That’s why they ship a page builder, a slider, a shortcode library and an options framework. Buy one and you inherit thousands of lines you didn’t write, can’t remove, and must keep updating forever.
Every theme I build starts from my own minimal starter, which I’ve documented in how I build custom WordPress themes. What it deliberately provides: global context variables for common WordPress data, Twig templating for PHP, npm for compiling SCSS, and native ES modules loaded through WordPress rather than a JavaScript build step.
The organizing pattern that actually scales is one file per concern in a lib/ directory, each loaded from functions.php:
lib/
acf.php # field groups, options pages, local JSON
cleanup.php # strip WordPress output you do not use
security.php # hardening rules
shortcodes.php # editor-facing components
helpers.php # template utilities
forms.php # form handling and notifications
Not a three-thousand-line functions.php nobody wants to open. The benefit stated plainly: you can read the entire theme in an afternoon, and there is nothing in it to update.
Content Modeling with ACF Pro
Model content, not layout, because the difference between a CMS and a page builder is whether your structure survives a redesign.
ACF’s flexible content field is a page builder you control. You define the available modules, each backed by a template file you wrote. Editors compose pages from a system instead of facing a blank canvas: freedom inside constraints you set. I’ve made the full argument for this over Gutenberg blocks in Advanced Custom Fields vs. Gutenberg Blocks, and on enterprise builds it isn’t close.
Local JSON is the part most teams miss. Point ACF at a directory in your theme and every field group is written to disk as JSON and committed to git. Field configuration becomes code. You deploy a field change exactly the way you deploy a template change, and that kills an entire category of bug: the field that exists on staging because someone created it there, and doesn’t exist in production.
Options pages handle the genuinely global values: the legal disclaimer, the rate-effective date, the phone number in the footer.
The long-term payoff arrives at redesign time, when you rewrite templates, not content. The model outlives the design.
One caution: resist making every module infinitely configurable. A module with twenty options is a page builder with extra steps. The constraints are the product.
The Brand Functionality Plugin
Anything that must survive a theme change belongs in a plugin.
The test is a single question: if you switched themes tomorrow, what would break that has nothing to do with how the site looks? That’s your plugin. In it goes: custom post types and taxonomies, third-party API integrations, redirects, business logic, custom database tables, admin customizations, WP-CLI commands. The theme keeps templates, markup and presentation helpers, nothing else.
Make it one site-specific plugin, not twelve micro-plugins. One place to look, one thing to version, one thing to reason about, organized internally the same way as the theme with one file per concern.
A worked example from the build this post draws on: a rate system that pulls from an external spreadsheet into custom database tables, then exposes them to templates through a small set of helper functions. The data layer and the presentation layer never touch, and when the site was redesigned the rate system didn’t change by a line.
mu-plugins for Code That Must Never Be Off
Some things cannot be allowed to be deactivated, and mu-plugins are the only honest place to put them.
What belongs here: cache purging, environment detection, and anything that must run even when normal plugins are not loaded. That last clause is the whole point, and here’s the concrete lesson that justifies it: migration tools unload regular plugins during migration requests. A cache-purge hook registered in your functionality plugin will never fire when a content sync finishes. An mu-plugin still loads, so the purge still runs, and your editors never see stale pages after a content push.
The related discipline: credentials and environment config belong in wp-config.php, never in wp_options. The options table is one of the tables a content sync overwrites, so anything stored there gets silently replaced by the source environment’s value on every push. It works perfectly until the first sync, which is the worst kind of working.
The Plugin List, Deliberately Short
Every plugin is three things at once: a dependency, an attack surface, and a future update that breaks something on a Thursday afternoon.
Here is the complete list from a production enterprise build:
- ACF Pro: the content modeling layer the entire editing experience is built on. The one plugin that earns unlimited trust, because removing it would mean rebuilding the site.
- Rank Math: metadata, schema, sitemaps and redirects. Lighter than the obvious alternative, more capability in the free tier, and far better schema control.
- Google Authenticator: two-factor on every administrator account, non-negotiable for regulated clients. The WordPress login form is among the most attacked endpoints on the internet.
- WP Migrate DB Pro: moving content between environments safely, with search-replace handled correctly. Substitutable with scripted WP-CLI if the team prefers to own it.
- The functionality plugin: everything specific to this business. The one on the list you actually wrote.
Five. The rule for adding a sixth: could you write it in fifty lines inside the functionality plugin? Then write it. A plugin is justified when it solves a problem that is genuinely hard, genuinely generic, and genuinely maintained.
It’s worth naming what the fifty-five plugins you skipped would have cost, because the cost is real even when nothing breaks: update surface, conflict risk, database bloat, page weight, and the review time every single one adds to a security audit. I’ve written before about why well-built WordPress sites are so rare, and the plugin count is usually where the diagnosis starts.
Environments and the Promotion Path
Code and content flow in opposite directions, and each needs a rule that is never broken.
Code moves one way: local, then git, then staging, then production. Never backwards, never edited in place on the server.
Content moves the other way, and needs its own deliberate path: production down to local for debugging, staging up to production for staged content releases. WP Migrate DB Pro handles this well, or scripted wp db export and wp search-replace for teams that prefer to own the process.
WP-CLI aliases are the highest-leverage config file in the entire stack. Define your environments once and every maintenance command becomes a one-liner from your laptop:
# wp-cli.yml
@production:
ssh: user@production-host
@staging:
ssh: user@staging-host
# then, from your laptop
wp @production cache flush
wp @staging db export
Custom WP-CLI commands extend the same idea to anything repeatable: cache purges, imports, data backfills. If you’ve done it twice by hand, it should be a command.
The discipline, stated bluntly: never edit code in production, and never put data somewhere that gets overwritten.
Caching Is an Architecture Decision, Not a Final Step
The hard part was never caching, it’s invalidation, and it has to be designed at the same time.
Name the layers honestly: object cache, host page cache, and the CDN or edge. Content can be stale at any one of them, independently. So the purge path gets designed alongside the cache path: every layer needs a trigger, and the triggers need to run in order, origin first so it serves fresh HTML, edge last so the first miss after the purge pulls that fresh copy.
The failure mode you’ll recognize immediately: an editor publishes, nothing changes on the site, so they publish again, then they call you. That is not a WordPress problem. It’s a missing invalidation design.
This is also where the first section pays off. Caching is tractable at this tier precisely because it lives in infrastructure, where you can purge it programmatically from a deploy script or an mu-plugin, rather than in a settings page you have to click through.
Anti-Patterns Worth Naming Out Loud
Each of these is a real scar from a real site, and most inherited sites have at least two:
- Page builders that store layout as serialized markup in post content. The content becomes unportable. You can never migrate it, only rebuild it.
- Configuration in wp_options on a site with content syncs. It works until the first push, then silently reverts.
- Caching failed API responses. Cache the success, never the error. One rotated API key written into a ninety-day cache takes down every page that depends on it, and keeps it down long after the key is fixed.
- Executing CMS-stored PHP. It always begins as a convenience for legal disclaimers and ends as an unauditable remote code execution path with a friendly editing interface.
- Forty plugins installed to avoid writing two hundred lines of code.
- Editing in production, which is every item above compressed into one habit.
What the Restraint Buys
Go back to the inherited site from the opening and picture its opposite.
A codebase a competent developer can read and understand in an afternoon. Updates that are boring, because there are five plugins instead of sixty. A security surface you can actually enumerate, which is the only kind you can defend. A redesign that means rewriting templates, not re-platforming the business.
None of this is exotic. It’s ordinary WordPress with the discipline to say no, and the discipline is the whole architecture. It’s also exactly what I build for banks, credit unions and other organizations where the consequences are real, as an enterprise WordPress consultant, usually paired with the security and performance work that this kind of architecture makes possible in the first place.