Watch the 40-second version on YouTube
Event-driven architecture looks brilliant on the whiteboard. Then Friday arrives and your "decoupled" services form a single failure-shaped organism.
Everyone loves the diagram: services publishing events, consumers reacting, everything loosely coupled, arrows everywhere. Then one producer double-publishes an OrderCancelled event, and three services react by cancelling, un-cancelling, and re-cancelling the same order until your support queue catches fire.
Here is the part the conference talks skip: event-driven does not remove coupling. It moves it from compile time to runtime, where it is harder to see and impossible to grep. A REST call fails loudly in your face. An event fails quietly, hours later, in a consumer you forgot existed, written by a team that left the company in 2023.
The non-obvious angle: event-driven systems are not decoupled. They are invisibly ordered. Every consumer secretly depends on the order, timing, and exactly-once delivery of events the bus never actually promised. You get the worst of both worlds: distributed-systems problems with monolith-era debugging tools. Your tracing span ends at the publish call. After that, good luck.
The event-bus checklist
Before you reach for the bus, answer yes to all four:
- Can every consumer handle this event twice without corrupting state? (Idempotency, not hope.)
- Can every consumer handle it out of order without corrupting state? (Sequencing, not hope.)
- If the producer is dead, does the system degrade gracefully or just stop? (Design the dead-bus scenario on purpose.)
- Could you name every consumer of this event right now, from memory? (If not, you have a governance problem, not an architecture.)
If you answered no to #4, you already have a distributed monolith. The bus just made it harder to admit.
One naming rule that saves careers
Events are a fine tool for things that are genuinely facts about the past: payment.settled, user.signed_up. They are a terrible tool for commands in disguise: cancel.the.order. If your event name starts with a verb, it is a command. Admit it, make it a call, and move on.
Use the bus where it shines: real domain facts, consumers that tolerate delay and duplicates, and a producer that keeps working even when nobody is listening. Everywhere else, a boring synchronous call is still undefeated. Fewer arrows on the diagram. Fewer pages at 3 AM.
Top comments (0)