Event driven programming
Programming reactions to events is mostly a UI idea, and it has been slowly moving into the backend for years. I think it should move faster.
The shape of it
Imperative code looks like this:
first: do the first thing
loop: repeat until end of the world
check whether something happened
if something happened, do something about it
if we should stop, stop looping
last: do a last thing
Event driven code looks like this:
when starting: do the first thing
when something happens: do something about that something
when stopping: do a last thing
The second one is shorter, but that isn’t the point. The point is which of the two you could read out loud to the person who asked for the feature.
Why it’s worth the trouble
Business requirements are already expressed as events. Nobody has ever written a specification that says “loop continuously and check whether the user has clicked Register.” They write: when a user clicks Register, show them the sign-up form. When a user clicks Forgot Password, take their email and send them a magic link.
Event driven code is the closest thing to writing that down directly. Everything else is translation, and translation is where meaning gets lost and where the argument about whether the code does what was asked comes from.
It also makes decoupling almost free. Parts of the system don’t call each other, they emit and listen, so changing how they connect means rewiring the flow rather than editing the participants. That reduces the impedance of the whole thing: easier to modify, easier to understand code somebody else wrote, easier to scale.
So why doesn’t everyone do this
Three honest reasons.
Languages represent it badly. The control flow is real but it isn’t written anywhere you can see it. Following a program means reconstructing it in your head from a scatter of handlers:
var net = require('net');
var client = new net.Socket();
client.connect(1337, '127.0.0.1', function () {
console.log('Connected');
client.write('Hello, server! Love, Client.');
});
client.on('data', function (data) {
console.log('Received: ' + data);
client.destroy();
});
client.on('close', function () {
console.log('Connection closed');
});
That’s about as small as a program gets, and the order things happen in is still nowhere on the page. You know it because you know how sockets work, not because you read it.
You have to change how you think, and that’s harder than it sounds. We’re all trained to translate from the real world into code. Event driven asks you to stop translating, which is a strange instruction to follow after years of practice at it.
The observability tooling is scarce and mostly awkward to install. When you can’t see the event flow, the thing that made the code flexible is the same thing that makes it opaque.
Where that leaves it
The model matches how the work is described and the tooling hasn’t caught up with the model. So you gain in how well the code expresses intent and you pay in how easily anyone can follow it, and which of those matters more depends entirely on the system.
For anything where requirements change often and parts need to come apart, I’d take the trade.
Adapted from a talk given on 6 March 2020. The original write-up is gone; this is rebuilt from the slides.