The WordPress Event System: Understanding Hooks in the Broader Programming Context

WordPress’s hook system is an example of an “event driven” architecture or programming system.

If you work in WordPress, you probably use WordPress hooks quite often in your work—probably most commonly hooking in new functions to change functionality through add_action() and add_filter().

Today, let’s talk about what the hooks system is: an example of an “event-driven” architecture or programming system. Events have a vast history in the world of computer sciencei, and there’s a lot of wisdom about their strengths and liabilities. Here, we’ll cover the essential tradeoffs of an event-driven system. By understanding the higher-level patterns in event-driven systems generally, we as WordPress developers can better understand and work with the WordPress hooks system.

What Event-Driven Architecture Is

I’ve heard “event-driven programming” as a common term, but the Wikipedia page for “event-driven architecture” is the best match for the idea:

Event-driven architecture (EDA), also known as message-driven architecture, is a software architecture pattern promoting the production, detection, consumption of, and reaction to events.

In event-driven systems, one system creates “events,” which as many other systems as necessary then respond to. For example, if an email program triggers a “user just sent an email” event, perhaps nothing else in the system is set to do anything about it—or perhaps dozens of other systems are listening, and are all set to do things in response, like “Print a copy of the email’s text” or “Check the email recipient’s address off the user’s ‘To-contact’ list in her CRM software.”

If you’ve ever seen the website “If This Then That,” it’s all about creating event-driven relationships in your life:

If This Then That

These real-life examples resemble the types of relationships that an event-driven system makes available in your code, and you can start to see the power and intuition behind them.

Advantages of Event-Driven Systems

Event-driven architectures have several key advantages.

Event-Driven Systems are Highly Extensible

It’s easy to supplement the behvior that even-driven systems were originally built with.

Perhaps the most powerful aspect of event-driven systems (especially WordPress’s hooks system) is that it’s easily extensible: it’s easy for other people to supplement the behavior that the system was originally built with. That WooCommerce and other systems can turn WordPress into an e-commerce platform is a major example of the core system being extended in way it wasn’t originally intended for.

WordPress core can update itself independently of the plugins and themes that depend on it. Without this decoupling, the WordPress plugin ecosystem couldn’t exist.

What’s great about the event-driven way of allowing for extension is that you can work with the assumption that an event has no listeners when you write your code. In other words, it’s easy to tell an event to fire (for example, a WordPress action) without any need for the firing code to worry about what other code in the system is doing with that event.

Because the code that fires events doesn’t have to worry about how those events are consumed, WordPress core (WordPress’s main “event firer”) can update itself independent of the theme and plugin code that depends on it. This separation allows both to move along at their own pace—a fundamental fact that allows the entire WordPress plugin ecosystem to exist.

“Weak Coupling”

There’s a computer science term for the property that the pieces of event-driven systems don’t need to move in lock-step: “weak coupling.” In event-driven systems, there’s weak coupling between the event sender (such as WordPress core itself) and the event-reactor (such as a plugin).

The opposite of weak coupling is “strong coupling,” where the two systems must change at the same time or everything breaks. (I’m sure for those burnt by a missing plugin update or similar, that sounds like WordPress at times…)

Greater Apparent Simplicity

Code that is written to emit events can look very simple.

Code that is written to emit events can look very simple. Once you, as a developer, learn to process apply_filters and do_actions in WordPress code as signposts that you can generally safely ignore, you get a lot of great benefit. Code that can be extended, “hooked onto,” or changed by lots of other lines of code in the system doesn’t really need to be complicated to match all those extenders. In fact, it can be intentionally simple because it’s not in any way coupled to those callers.

But—and here we start to shade into the drawbacks of event-driven system—this apparent simplicity isn’t totally free: you’re keeping your code simple-looking by pushing complexity out onto the systems that interact with your code.

The Drawbacks of Event-Based Systems

Event-driven systems have several drawbacks.

Apparent Simplicity Can Hide Deep Complexity

This is the flip side of the previous point: event-based systems can seem simple because the complexity just moves elsewhere. Fortunately, code that reacts to (or “hooks onto”) events doesn’t usually have to be terribly complex—but in my experience, two common occurrences can complicate this code:

  1. If your event doesn’t pass you some data you need, then you must find a way to get it, almost as if from scratch. This can lead to duplicated code and double work depending on who owns what.
  2. The interactions and time-based flow can itself cause subtle issues. In WordPress, for example, it’s possible to try to “hook onto” the wrong event (action) in WordPress and do something that you’re not yet allowed to do—or to end up doing something too late. In either case, your code can either break things or silently fail, and the reason why will not be obvious from your code itself, which will look quite reasonable. So in WordPress, the difference between, say, the init and plugins_loaded action hooks is both very important and not necessarily obvious.

Debugging and Tracing Causes is Complicated

In an event-driven system, anything can change the state of the system by responding to a given event, and so event-driven systems are very difficult to debug.

Procedural code, which is in many ways a direct contrast to event-driven code, makes it very clear and explicit what follows what. If you know the order with which the interpreter will execute the code, you know almost everything you need to figure out what caused what, and to debug the system. in general, a procedural system with named functions has a pretty limited field of possible causes for a behavior you’re seeing.

This is less true in event-driven systems. Because event-driven systems inherently (by the very uncoupling choice we discussed) don’t list everything they’ll assemble until the system is fully assembled, they’ll only reveal all the details of the system on execution of the entire system together. This means that it’s very difficult to trace all the possible reasons that a particular thing may come into a specific state.

In other words, in an event-driven system, anything can change the state of the system by responding to a given event, and so event-driven systems are very difficult to debug.

This complexity is pretty much baked into an event-driven system, and in is often worth the cost. Sure, there’s some increased complexity, but 95% of the time you don’t have problems.

But an experienced WordPress developer who has spent hours tracking down the causes of a bug by activating and deactivating a million themes and plugins will know what I’m referring to. That is the the complexity that comes with event-based systems.

What I Hope We’ve Learned

I hope you’ve come away a better understanding of the trade-off that come with WordPress’s hooks system, and a slightly more grounded understanding of how the event-based nature of WordPress fits in within the broader disciplines of computer science and programming.

Event-based systems are exceptionally flexible and easy to bend to your needs.

Event-based systems are exceptionally flexible and easy to bend to your particular need. The ability of multiple different actors to operate safely and independently from each other is great. And it’s empowered by them all listening to the same events. That is what makes WordPress’s add_action and add_filter event-listeners so valuable.

On the flip side, so much is happening, but there exists no central record of what happens when and why.

This very flexibility is also the reason why these systems can be very complicated to walk into blind: so much is happening, but there exists no central record of what happens when and why. At worst, this means that you have to search through every line of code executing in the system to find what you want to change. It’s not impossible, but it’s a bit of a hassle at times.

But those are the trade-offs of event-driven systems. Happy hacking!

Image credit: Seattle Parks

2 Responses