Why we don't advocate WordPress for everything.

We're reasonable people, and we advocate using the right tool for the job rather than just throwing out invective-fulled linkbait. As much as it might seem like we hate WordPress (the software, not the service powered by it), we can boil what we really feel down to this:

If you need a blogging tool and a website with a few pages, WordPress might be a good idea. If your data model is substantially different from posts with title and text, then it's probably a bad idea.

And because we're fair and balanced, we'll start out by talking about all the things for which we think WordPress is great:

It is here and it is good enough.

Which means that it's an infinitely better proposition than some custom CMS which is not.

WordPress is a de-facto standard

The implications of this are probably bigger than they might appear; "everyone else uses it" is a poor excuse to use anything. But consider.

For one, any web host that does not have the facilities to run WordPress is a dead web host; you may as well try and run a petrol station with a four-foot height barrier on the entrance. Oh, for sure, there is some custom there, but that's a heck of a niche. The result is that hosting WordPress is cheap and available everywhere.

What's more, it is really really easy to find WordPress developers. A one-off custom CMS, or a more obscure CMS, will leave you in a really unpleasant place if you fall out with the developers of your site.

(We avoid this risk of lock-in by not being bad; should a client choose to migrate away from us we will always offer a complete copy of the source code and their site's database, with documentation for developers. You can avoid this risk by always getting a commitment in writing. And we are professionals at migrating data from companies that try to hold their clients' data hostage, even if that means writing tools to recreate years of e-commerce order history by scraping HTML from the admin backend of a site. Hey, hit us up, we're awesome.)

WordPress Duplicator is fantastic.

This ties in with the hosting-as-commodity thing above: WordPress Duplicator is a real miracle of a plugin, that makes migrating from one host to another almost painless (and if you have access to a fast upstream pipe you can take out the "almost" from that sentence). What it does is commoditise your web hosting; it makes it almost as easy to migrate your site to another host as it would be if your site was a bunch of static HTML files.

With a fast pipe, migrating your site from one host to another is a process that takes a two-digit number of minutes, rather than several hours and tears. The simplicity of Duplicator combined with the plentiful hosting is that you can treat web hosting as any other commodity – and commodity markets in which no provider has a lock-in always work out best for consumers.

The WordPress admin is rather lovely.

Much effort has gone into making the WordPress admin backend as slick as possible, and it shows. It's not an overstatement to say that it's heading into the realm of beautiful; it requires just about zero technical skill for someone to post stuff on their website.

That is superb and we have only good things to say about it.


Even we, who avoid WordPress as much as possible, have good things to say about WordPress. Hooray for WordPress, insofar as we don't have to develop for it. You can safely ignore any of the rest of this piece if you have a small blog or a website with a few pages running under WordPress.

With that said, we don't like the Wordpress architecture. It started out as a simple blog script and grew into a content management system which got mistaken for a development platform. None of this is to detract from WordPress being a good enough tool for blogging, just like a shed works great for holding tools. But when you start telling everyone that your tool shed is a good nuclear weapons shelter, you're going to get held to a much higher standard.

It is usually slow, and load will kill it.

Speed isn't a minor concern. It's well-documented that search engines will de-rank sites that load slowly, and it is also well-established that a large proportion of users will hit the "Back" button if a site takes more than a couple of seconds to load. If your business is selling stuff online then a little attention to speed will dramatically improve your business, which is another way of saying slow load times will kill your business.

We've also learned from experience that in the real world of pile-em-high-sell-em-cheap shared hosts, your speed bottleneck is almost invariably on database access. Thus, queries should be as few as possible, and those queries should to the maximum possible extent be simple SELECT-type queries.

And yet, we've seen WordPress sites with very few plugins doing really, really basic stuff which have a baseload of over 30 SQL queries (for contrast, we have written two e-commerce sites which have a baseload of 8 and only exceeded 20, in total, on two URLs).

The result is that WordPress sites on shared hosts or cheap VPSes tend to explode as soon as they encounter any substantial load. Oh, there are plugins that will help you out there, which should not be necessary; it should not be so that a simple blogging engine hits databases so hard that a modest traffic spike will get you kicked off of your shared hosting account.

That WordPress plugin and theme developers tend to treat database queries as free greatly exacerbates this. That's a cultural problem, made worse by...

Post as Mother of All Things

(Warning: we stake out a pretty unpopular position which a lot of smart people disagree with, which is keep your data in as few tables as possible and do as few SQL JOINs as possible; we're denormalising speed-freaks, because in the real world of shared hosting database access is costly and speed matters.)

WordPress's core data type is the Post. This is, to simplify massively, a database table that has few columns, the most critical of which are the type of the entry ("page" for a page and "post" for a blog post), the title, the date added, the text of the entry, and one or two other things you don't care about. The rest is metadata, joined from a second database table.

This works great in the sense that you can treat these things interchangeably; in particular, it means whatever search you are using can work across every kind of post (whether that's a page, a post, or a custom post type) for free. As we've said, *WordPress works great for things that fit into the post-with-title-and-text model*, and this applies to WP's custom posts architecture, too.

The downside is that every page load that displays anything other than "post" title, text, and time requires an expensive JOIN on the wp_postmeta table. If you want to display an image with each post (what WordPress calls "featured image"), that is in fact two expensive SQL JOINs; one to join a "meta" field identifying the ID (number) of the featured image, the other to join another database table with the image by that ID.

Multiply that by 5 or 10 or however many things you have on a page, and you're gonna have a bad time, at least when you get an unexpected traffic spike.

If you want a practical lesson to take away from this: WordPress e-commerce plugins are usually a terrible idea. We've seen many of them and absolutely none of them were done well; it was less pain to write e-commerce systems from scratch than it was to try and restore these to some kind of sanity.

Talking of which...

Plugins tend to be terrible

Because in general [database transactions] are not needed and they add extra overhead

Also transactions are not supported on all table types and you might want to use a different table type for performance reasons

You would do better looking in to what corrupted the table and how you could have detected that.

Transactions would have only helped you identify you had a problem sooner not fixed the problem!

— What Plugin Developers Actually Believe

We could complain about the plugin architecture, but...oh, let's do that anyway: it is ugly and requires far too much boilerplate. But meh: that's a developer's problem and not yours, and we're not ones to be programmers who complain about having to do some programming. The problem with WordPress plugins: Most of them are really bad.

We've seen popular plugins that have added an extra twenty SQL queries just to load and do nothing else. We've seen one add fifteen JavaScripts to the start of every single page, enough to obliterate all the efforts we put into making the site load quickly. The worst of them, Advanced Custom Fields (filed under "brilliant idea with poor execution"), hits databases so hard that one site we looked at was taking eight seconds to start spitting out HTML, running solo on a quad-core Xeon server.

Meanwhile, as the WordPress team have worked hard to secure the core of WordPress, it seems that plugin developers are trying their hardest to ensure that WordPress websites are still trivially easy to break into. Thanks for carrying the flame, chaps.

"Well, it's PHP so what do you expect" is not a good answer to this. Yes, it's easy to write bad PHP code, but it's equally easy for a good developer to write solid, stable, fast PHP code as well once she understands the language's quirks.

So when someone suggests "well, we'll just use WordPress with plugin X" to solve your problems, start asking questions about how that will impact the site's load time, and demand metrics for it. And do look through the plugin's release history; if it reads something like "fix obvious security problem" repeated a dozen times then your site is going to be turned into something you did not at all intend.

The templating system is not a templating system

Retrieves the information pertaining to the currently logged in user, and places it in the global variable $current_user.

— This Is Your Brain On WordPress

Let's get our objection clear: PHP is not a templating language. It is a fully-fledged programming language with some weird string literal syntax. Many things have been written about PHP itself, some fair, most pretty unfair. (Hint: you don't get to complain about inconsistent function naming and two minutes later advocate writing in Python, because well, how big is your laundry pile?)

This is what real templating engines do:

If you're wondering what a real template language looks like, there is a PHP port of Django's templating engine. Actually, it's so good that there are two.

The implications of an awful template engine are bigger than you might think; many of the awful things in WordPress (like The Loop) are just outgrowths of a really bad not-a-templating-system-at-all template system.

URL routing is pain

But writing excessive amounts of logic into a template is sometimes necessary, because WordPress doesn't have anything like a proper URL router. What we really want to do is say "pass all requests with a path matching this to this function. Out of the box, WordPress has nothing suitable. Oh, it's doable, but good luck jumping through those hoops and whoops, you only get to do that kind of URL routing by routing your custom URL to render a certain page, and then you can put your code in a template. Great.

There are plugins out there to help you with that. You know our feelings about adding plugins, and if WordPress is going to position itself as a solution to Everything, this stuff should be in the WordPress core.

It randomly screws with input data. It shouldn\'t.

The big shocker when we started WordPress development was this: WordPress inserts backslashes before quote marks in HTTP POST and GET data. Hey, it took us a while to figure that one out because we blamed PHP and drove ourselves nuts for half an hour wondering why a thing that was removed in our version of PHP was somehow, magically working in our version of PHP.

The reasoning here is that old versions of PHP did this, and plugins might still be expecting backslashes in input data, i.e. plugin developers are not very clever and are still, in 2014 shoving random variables into SQL strings without sanitising them. Yes, that does actually mean that you have to use stripslashes() on any user-supplied data as if it's 2001, and that WordPress is single-handedly keeping alive the old tradition of seeing random backslashes before.

No, we're not complaining about calling a function. But the response of any sane developer to this kind of behaviour is who on Earth thought this was a good idea and you can listen to the justifications for it and they all sound reasonable, just as your crazy uncle sort of makes a point from time to time and then you remember he's actually explaining how the world is run by a secret cabal of pan-dimensional super-lizards and you're all


PHP stopped mangling input data for a reason, fully expecting that code written by incompetent people would break. They stopped because it is really, really rude to randomly screw with input data in order to defend against one attack vector (and ignoring all the others). If you're not smart enough to use prepared statements (or even stuff like escaping your strings) to avoid SQL injections, you are not smart enough to write code that other people depend on. Think of it as "you must be at least this high to ride the software development".

(Should you think that contradicts the above about auto-escaping HTML output being a feature of good templating engines: It doesn't. Escaping data for use in a specific context, such as "don't allow variables containing HTML tags to be shown on HTML pages" is a very different thing to "assume any user-supplied data will be shoved into a database by code written by really, really bad developers".)


Non-developers won't need to care about much of this. For many sites, which fall into WordPress' data model, do not see large loads and do not install many plugins, WordPress works pretty well. Just get it behind a Varnish cache on a cheapo VPS and you'll be fine.

And some better ideas: