Event-driven architecture has a great pitch. Decouple your services, eliminate brittle point-to-point dependencies, scale individual components independently, and build a system where producers and consumers never need to know about each other. On a whiteboard it looks elegant. In a conference talk it sounds inevitable. And for certain classes of problems at certain scales, it genuinely is the right answer. But the gap between how event-driven architecture is sold and what it actually costs to operate is one of the most consistently underestimated traps in modern backend engineering — and I think we do junior and mid-level engineers a real disservice by not being more honest about it.
This is not a post arguing that event-driven architecture is wrong. It is an argument that we talk about it like it is a upgrade and not a trade — and that trade is significantly more expensive than the architecture diagrams suggest.
The Promise Is Real — and That Is Part of the Problem
The benefits of event-driven systems are genuine, which is exactly what makes the pattern so seductive. When you replace a synchronous call chain with an event bus, you do get real decoupling. Service A no longer needs to know that Service B exists. You can add Service C as a new consumer without touching Service A at all. You can replay events to rebuild state. You can absorb traffic spikes behind a queue instead of cascading them through a call stack. These are not made-up benefits.
The problem is that every one of those benefits comes packaged with a corresponding complexity that the pitch glosses over. Decoupling services means you lose the ability to trace a request as a single coherent flow. Replay gives you power over state reconstruction and introduces the obligation to make every consumer idempotent. Absorbing spikes behind a queue means you now have to reason about lag, consumer throughput, and what happens when your queue depth grows faster than your consumers can clear it.
The architecture does not eliminate the hard problems. It relocates them — out of your call stack and into your infrastructure, your observability setup, your data contracts, and your operational runbooks. If you were not already good at those things, you have not simplified your system. You have distributed its complexity into harder-to-see places.
The Steel-Man: Event-Driven Done Right Is Genuinely Powerful
Before I go further, let me take the strongest version of the other side seriously.
At sufficient scale, synchronous request-response systems have fundamental ceilings. When you are processing millions of transactions, ingesting sensor data from thousands of devices, or coordinating workflows across dozens of independent services with wildly different latency profiles — event-driven architecture is not overengineering. It is the only model that maps cleanly onto the actual shape of the problem. Teams at that scale who are not using some form of event-driven design are almost certainly fighting their architecture instead of building with it.
There is also a team autonomy argument that holds up. In large engineering organizations where multiple teams own separate services, synchronous dependencies create organizational coupling as much as technical coupling. If Team A's deployment can break Team B's service, you have a coordination problem that event-driven architecture genuinely helps solve. Conway's Law is real, and the decoupling benefits of events extend to how teams can work independently.
And the tooling has matured meaningfully. Kafka, Pulsar, RabbitMQ, and cloud-native equivalents like AWS EventBridge and Google Pub/Sub have years of production hardening behind them. The operational burden is lower than it was five years ago. Managed services have absorbed significant infrastructure complexity that teams previously had to own themselves.
All of that is true and worth acknowledging. The question is not whether event-driven architecture can be worth it. The question is whether your team and your problem are actually ready for the cost it carries.
What We Consistently Underestimate
In my experience, teams adopting event-driven architecture for the first time consistently underestimate the same set of problems. Not because they are not smart, but because these problems are genuinely invisible until you are inside them.
Observability becomes a first-class engineering problem, not an afterthought. In a synchronous system, tracing a failure is painful but tractable — you follow the call stack. In an event-driven system, a single user action might produce a chain of events processed by five different services over thirty seconds, with each step happening asynchronously and independently. Without distributed tracing instrumented from day one, debugging a production issue becomes an archaeology project. Most teams instrument this after the first major incident. That is the wrong order.
Schema evolution is harder than it looks. When Service A publishes an event and Service B consumes it, you have a data contract. The moment you need to change the shape of that event — add a field, rename a field, remove a field — you have a versioning problem that synchronous APIs handle relatively cleanly with versioned endpoints. Events are trickier. Consumers may be processing old events from a replay. Multiple consumer versions may be running simultaneously during a deployment. Schema registries help, but they add another system to operate and another failure surface to manage.
Eventual consistency is a product problem, not just a technical one. When you tell your team that the system is eventually consistent, you are also telling your product manager, your support team, and your users — whether you realize it or not. The question "why does my dashboard not show the order I just placed?" has an answer in an eventually consistent system. That answer is technically correct and often deeply unsatisfying to everyone outside engineering. The product implications of eventual consistency need to be designed for explicitly, not discovered through support tickets.
Failure modes are non-obvious and expensive. What happens when a consumer fails halfway through processing an event? What happens when it succeeds but crashes before acknowledging? What happens when a poison pill message — one that causes a consumer to crash every time it processes it — enters the queue? Dead letter queues, retry policies, idempotency keys, and consumer group management are all answerable problems. But they require deliberate design up front, and teams that do not think about them before going to production meet them at the worst possible time.
The Question Most Teams Skip
Before adopting event-driven architecture, there is one question that cuts through most of the noise:
Is the problem we are solving inherently asynchronous, or are we making it asynchronous because the architecture sounds good?
Some problems are genuinely asynchronous. Order fulfillment, notification delivery, data pipeline ingestion, audit logging, workflow orchestration across long time horizons — these map naturally onto events because the real-world process they model is itself decoupled in time. Event-driven architecture in those cases is not imposed complexity. It is an accurate model of the domain.
Other problems are synchronous in nature and are being forced into an asynchronous model because the team read about Kafka and wants to build something scalable. A user submits a form and needs an immediate response. A payment needs to be confirmed before a receipt is shown. An API call needs to return data that is accurate right now, not eventually. Wrapping those interactions in events adds latency, adds failure surface, and adds operational complexity — in exchange for decoupling benefits the system does not actually need.
That distinction sounds obvious written out. It is surprisingly easy to miss in the excitement of architecture planning.
Where I Land
Event-driven architecture earns its complexity at scale, in genuinely asynchronous domains, and in organizations large enough that team autonomy is a real engineering constraint. In those contexts, the trade is worth making and the operational investment is justified.
For most teams considering it, the honest conversation should go like this: your current synchronous architecture has real problems. Some of those problems are genuinely solved by events. All of them are traded for a different set of problems that are harder to see, harder to debug, and harder to explain to the rest of your organization. Are you prepared to own that operational surface from day one — observability, schema contracts, failure handling, eventual consistency at the product layer — or are you adopting the architecture for its whiteboard elegance and planning to figure the rest out later?
Figure it out before. The teams that go into event-driven architecture with clear eyes about the full cost are the ones that actually get the benefits. The teams that go in on the pitch alone spend the next year fighting fires they did not know they were building toward.
The architecture is not the problem. The gap between how we talk about it and how hard it actually is — that is the problem worth fixing.