Why we don't advocate WordPress for everything

Use the right tool for the job. (August 2014)

We're reasonable people, and we advocate using the right tool for the job rather than just throwing out invective-fulled bait. 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 reasons we think WordPress is great:

It is here and it is good enough.

Which means that it is an infinitely better proposition anything which is not here.

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 is 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.

See?

We 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 web 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 nuclear weapons shelters are 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 sales, 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 reads. (Ask us about that time someone thought it was a good idea to put a hit counter on each page. Hint: that writes to the database every time a page is accessed...)

And yet, we've seen WordPress sites with very few plugins doing really basic tasks 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; a simple blogging engine should not hit 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

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 extra trip to the wp_postmeta table. If you want to display an image with each post (what WordPress calls "featured image"), that is a trip to two database tables; 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 programmers who complain about having to do some programming. The problem with WordPress plugins is that 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:

  • Inheritance. include() and get_header and get_footer do not count here. Real templating languages allow you to define a base template and then selectively override parts of it. So you want a custom CSS file on one of your pages and only that page: Simple:
    {% extends "pages/default.html"%}
    {% block extra_css %}<link rel="stylesheet" type="text/css" href="special-stuff.css" />{% endblock %}
    
  • Auto-escaping. In a real templating language you should have to explicitly declare which variables contain trusted data (like HTML) and everything else is treated as untrusted, and should not be printed without escaping (in the context of HTML, that means being printed with angle brackets and quotes converted into HTML entities).
  • Limited logic. That means that you should not be able to do operations that have side-effects like writing data. Which is to say, real template languages fit into architectures which enforce a clear distinction between back-end heavy-lifting and presentation logic. WordPress as it stands encourages far too much logic to be written into templates. We know all about this; deadlines are what they are and so even we are guilty of this on some of the WordPress websites we've worked on (if you want to read this page as "Why These Guys Suck at WordPress Development", feel free).

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

Wat

PHP stopped mangling input data for a reason, fully expecting that code written by incompetent people would break. They stopped because it is 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".)

Summary

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:

  • Is your data model substantially more complicated than that of a blog (articles displayed in reverse chronological order)? Consider not using WordPress. A hatchback does fine for going to the shops, but you don't make an off-roader out of it by welding it to a Range Rover, and you don't make a fast off-roader by towing that with a Ferrari. Use the right tool for the job!
  • Will a bunch of static files do? You techies have no excuse for having websites with one or two pages running on WordPress; write some damn HTML like a real gender-neutral noun and save yourself some headaches.
  • And the obvious plug: We write custom web sites, both big and small, for sensible budgets. Many of them even use WordPress! Do get in touch with us to discuss your needs.