Modern WordPress: Not an Oxymoron

Presentation Cover

WordPress, the jack-of-all-trades blog platform/CMS, has undoubtedly made its mark on the web. But let’s be real - having to develop anything custom on top of it can be hard-going once you’ve become used to more modern PHP frameworks such as Laravel or Symfony.

Late last year I gave a lightning talk at PHPSW exploring the highs and lows (ok, mostly the lows) of working with WordPress, and then uncovering how you can actually keep your sanity while wrangling with the complexity spirit demon. You can check out the slide deck here.

First, some context - I’m going to sound like I’m hating on WordPress, but I actually have a huge amount of affection for the platform - it’s battle-tested to say the least, it’s how I originally earned my PHP chops, and I still build on it regularly both at my agency day job and outside of it.

Bear in mind also that I’m coming at this very much from an agency angle - i.e. you have clients and you need to build useful things for them within specific constraints, like time and budget, and their existing solutions and infrastructure.

WordPress Woo

Ok, with that out of the way, let’s start with the positives! Here’s what makes WordPress shine:

  1. Popularity: WordPress reigns supreme, powering a significant chunk (43%) of the internet. It’s not just huge, it’s HUGE. With its massive user base, you’re never short on resources, tutorials, or a helping hand from seasoned WordPress developers.

  2. Documentation: WordPress boasts extensive and well-maintained documentation - see its venerable codex and the more recent code reference. Whether you’re a beginner or an expert, you’ll find answers to many a burning question and uncover almost every piece of functionality you might ever need.

    The codebase is also extensively commented, which partly drives the above resources. Depending on your philosophy around comments, that may or may not be a good thing, but it is extremely helpful when you’re grokking functions/methods and wondering what they do.

  3. Plugins galore: Need a plugin for a specific use case? WordPress has you covered. With literally thousands of plugins available in the directory, you’ll find everything from e-commerce solutions to membership systems and beyond.

WordPress Woe

Now, let’s mix animal metaphors and address the elephant in the room — WordPress is an absolute pig to work with from a development perspective.

Over the years it’s earned a reputation of being quick and easy to set-up, customise and hack on. Its “famous five minute install” feeds into this narrative, and this idea has been further compounded by the absolutely enormous amount of copy-pastable WordPress code that exists on Stack Overflow and WordPress’ own support forum to enable you to add or over-ride specific bits of functionality.

The problem however is that a huge proportion of that copy-pasta is garbage, and the “quick and easy” myth starts to fall apart as soon as you need to do anything reasonably complex or well-factored or maintainable or version-controlled or devops related. My personal experience is that after the undoubted initial velocity scaffolding a basic content-based website, WordPress soon becomes much slower and harder to work with than other frameworks.

Why is that?

  1. Clunky DX: Traditional WordPress development is a cumbersome endeavour. Say goodbye to not so modern luxuries like Composer and PSR-4 standards, and be prepared for a kludgy laborious process that will leave you longing for early retirement. PHP has pretty incredible tooling these days - even compared with supposedly sexier languages - but WordPress doesn’t even try to play nice with any of it out of the box.

  2. Quality of Plugins: While having so many plugins at your disposal can be useful, you’ll need to tread carefully. Many are abysmally coded or poorly maintained, creating a breeding ground for security vulnerabilities and compatibility nightmares. Even the top-tier plugins can turn brittle when you need to enable any kind of deeper customisation, but at least they are (usually) fairly well documented.

  3. Outdated Coding Patterns: WordPress and its plugin ecosystem still carry remnants of older PHP versions and old fashioned coding practices.

    Think huge files full of inconsistently named global functions with no or seemingly random typing. Where there are signs of OOP and classes, the files and methods are still huge, and there is only a cursory attempt at encapsulation or separation of concerns.

    WordPress was a PHP trailblazer and created its own Exception/Error implementation before the language even had Exceptions. Kudos to the devs, but confusingly, WordPress still uses this implementation even while the rest of the PHP world has moved on.

    Everything (yes everything, all the code) lives in the public web folder. You’d better hope those htaccess rules are working properly or you’ve already been pwned.

    The sacred commandment of backward compatibility has left WordPress’ codebase aching for a much-needed refactor, but for actually quite sensible reasons, this will probably never happen unless new versions of PHP force specific changes. Having such a HUGE user base means change becomes very difficult indeed to manage and implement.

    That’s not to say there aren’t any modern libraries and patterns in use. For example over on the front end, Gutenberg, the WordPress block editor, is built with React - however I personally find its implementation a little odd. That may be because I’m less well versed in React - I’m more of a Vue guy - or maybe it’s because Gutenberg still needs to co-exist with all the legacy jQuery mess and other tech debt.

    They do things differently round here, same as they’ve always done it. I mean, the core project still uses SVN for version control 😬

  4. Global Scope: Prepare for a tussle with global namespaces and variables - WordPress is chock full of them. These lurking foes can lead to naming conflicts and make your own code a nightmare to test. Don’t get me wrong, I love a convenient global helper as much as the next pragmatic dev, but almost everything is global in WP.

    The loop showcases this pervasive global state antipattern - are you within the loop? How do you know? Has it already been run? Do you need to reset it? What exactly does the_post() do, and why is it named so inscrutably/unhelpfully? There is actually an open ticket to address all of this palaver in WordPress core by replacing it with a generator function, but it’s been 4 years and there’s been close to zero movement on it.

    Debugging and grasping the runtime state can feel like reaching into the void due also to an over-reliance on content-based config, as well as WordPress’ giant action/filter hooks salad. Is WordPress’ hook system convenient and flexible? Absolutely. Is it also confusing as hell and prone to weird race conditions? Definitely.

  5. Frontend Frustrations: Integrating modern frontend build tools and frameworks with WordPress can be a true test of patience. Juggling JavaScript libraries, CSS frameworks, and build processes, and deployment steps will leave you feeling like you’re herding proverbial cats - the default architecture just isn’t suited to it.

  6. Inefficiency FTW: The WordPress REST api was released to great fanfare back in 2016. And yeah it’s full featured and relatively well designed, even though it still has its quirks. The problem? It’s slow as molasses in real world applications.

    Also rather than retiring or deprecating it, WordPress kept its legacy Ajax implementation around. Again to be fair, this does make sense - it is heavily used in the admin, and a full admin rewrite would be extremely painful. It’s also faster than the REST api. But as a developer it jars to have to deal with two entirely separate mechanisms for almost the exact same use case.

    Also know that the Entity Attribute Value pattern (or “meta” in WordPress jargon) is the reigning king here - all your SQL are belong to big inefficient query slowdown. This won’t particularly matter on small data sets without a lot of relations etc, but it really will start to when you get beyond a certain level of complexity - at which point you’re probably going to wish you hadn’t used custom posts at all and just created your own tables.

So why do you even WordPress?

Given these DX challenges, you might wonder why developers still bother. Here are a few reasons:

  1. Client Preference: Clients often gravitate toward WordPress for its user-friendly admin interface and familiarity - it feels less risky to them because they know it. And to deliver projects optimally, you’ll want to cater to their needs and leverage their existing preferences and knowledge rather than teaching them new tricks all the time.

  2. Budget Considerations: WordPress provides an array of ready-made solutions through plugins, themes, and its built-in admin system. While these off-the-shelf options may not be as well suited as bespoke alternatives, they can significantly cut down development time and cost. Think WooCommerce for online shops, or ACF for custom admin UI, or Yoast for SEO.

  3. Legacy Projects: Inherited or existing WordPress projects often demand ongoing maintenance, improvements, or unforeseen feature additions. Scrapping them altogether isn’t always feasible, making it crucial to find ways to breathe new life into these projects and bring them up to modern standards in terms of maintainability. Face it, you’re never going to do that big rebuild in the sky, and 43% of the internet isn’t going to magically transition to Nuxt/Laravel/Rails/Django or whatever your framework poison is.

Embracing Modern WordPress Development

Frameworks

Despite all of the above, custom WordPress development can be made bearable when approached with the right patterns. You could of course just roll your own, but there are a whole host of tools and frameworks aiming to make that process easier. Enter the heroes of modern WordPress development:

  • WPackagist: This packagist repository mirrors publicly available plugins and themes from the WordPress directory.

    This means you can manage third party plugins using Composer, alongside standard PHP libraries, and commit the versions into your git repo instead of leaving things up to chance (or committing the whole dependency!). Say farewell to plugin dependency hell.

    At the agency, we also run a Satis instance for our private composer repos.

  • Frameworks: The ones I’ve tried and tested include Lumberjack, Themosis, TypeRocket and last but not least - Forme, which I wound up developing myself and actively maintain.

    These Open Source frameworks bring MVC architecture, database migrations and ORMs, DI containers, fast custom routing, and view templating to the WordPress realm. They bridge the gap between traditional WordPress development and modern PHP practices from Laravel/Symfony/Cake et al, injecting much-needed sanity into your workflow.

    Some frameworks take it up a notch, adding features such as further wrappers around WordPress core components, code generation tools, job queues, deployment systems, testing setups, and even packaging solutions for your plugins or themes.

    There are also other related libraries and projects within the space including Bedrock, Timber and upstatement/routes.

A Balancing Act

Of course, these frameworks aren’t a magical panacea - there are always trade-offs.

For a start, they tend to be quite heavily engineered and opinionated, adding a bunch of asbtraction and indirection on top of core WordPress which is likely over-kill for very simple projects. Sometimes it’s a lot easier to just stick to the core patterns.

Similarly, at the other end of the complexity scale, you do probably need to question whether WordPress is really the right solution at all. Could you nudge your client to try something else? Do you have the time and budget to build out a more bespoke admin or e-commerce system? Sometimes these requirements can be slightly over-stated anyway - WordPress doesn’t have to be the default.

But as soon as you are in WordPress land and dealing with more than a couple of custom data models, or you need to do some complex plugin or theme customisation, I would say a framework of some sort becomes pretty much indispensible.

An example of something real that I’ve dealt with a couple of times is having to build a custom API for an existing WooCommerce site, with a customer or admin-facing SPA which needs to then consume that API. I’d never want to do that in vanilla WordPress.

Another thing to bear in mind is that integration testing, in particular, can be quite a challenge to set up mainly due to the variable behaviour and quality of third party plugins and their idiosyncrasies. This isn’t really the frameworks’ fault though.

However, armed with your nice new shiny decoupled architecture, some proper testing strategies and tools like PestPHP and Cypress, you can ensure the stability and reliability of your WordPress projects, even when your clients or PM thrown you a curveball feature request.

And finally…

If you’re considering a new greenfield WordPress project, or you have a legacy one that needs a refresh/refactor, I would recommend taking at least one of the frameworks mentioned in this post for a spin. You might be pleasantly surprised.

Since I’ve made this shift myself, I now really enjoy working on custom WordPress development projects. I’ve discovered that it doesn’t have to be synonymous with wading through a morass of spaghetti code. Whisper it, but it can even be fun.