DEV Community

Cover image for When event-driven architecture is actually overkill
turboline-ai
turboline-ai

Posted on

When event-driven architecture is actually overkill

There's a thing that happens in engineering teams. Someone reads a great blog post about Kafka, or watches a talk on distributed event streams, and suddenly the next sprint has tickets for decoupling everything into producers, consumers, and topics. For a CRUD app that handles 200 requests a day.

This isn't a knock on those engineers. Event-driven architecture is genuinely powerful. But "powerful" and "appropriate" are two different things, and conflating them creates real pain.

What async complexity actually costs you

The trade-off is almost never discussed honestly. Teams adopt event-driven patterns for the scalability story and then discover the operational story: eventual consistency bugs that only surface in production, message ordering edge cases that are genuinely hard to reason about, retry logic that silently causes duplicate side effects, and distributed tracing overhead that makes debugging feel like archaeology.

None of that is unique to any one tool or framework. It's just the nature of async. When you decouple systems in time, you also decouple them from easy observability. That's a fundamental property, not a fixable bug.

The actual question to ask first

Before you reach for a message queue or an event broker, ask what the problem actually is. Not what you're worried it might become in three years, but what it actually is right now.

If your bottleneck is a slow third-party API call that blocks a user-facing response, async helps. If your bottleneck is a report that takes 40 seconds to generate, a background job helps. If your bottleneck is that your monolith is hard to deploy, async events will not help with that -- you'll just have a distributed monolith that's also hard to deploy and now has race conditions.

Where real-time requirements actually matter

There are systems where event-driven architecture isn't optional. Crypto trading infrastructure, for instance. When a DEX liquidity pool shifts, or a large on-chain transaction lands that could move a price, the downstream systems that depend on that data need to react in milliseconds, not the next time a cron job wakes up. Polling-based architectures in that context don't just underperform -- they're structurally incapable of the latency required.

Same with fraud detection on payment rails, live sports betting markets, or anything that needs to act on the state of the world as it actually is right now, not as it was 30 seconds ago.

These are the contexts that justify the operational overhead. The event model earns its complexity because the alternative -- missing a time-sensitive signal -- has a real cost.

What "start simple" actually means in practice

"Start simple, then introduce async where the problem genuinely demands it" sounds obvious, but it requires some honesty about your constraints.

Simple means: synchronous request/response where you can get away with it. A task queue (not a full event mesh) when you need background work. A single database that's the source of truth, not five services each maintaining their own local state.

The migration path to async is much cleaner when you've lived with the synchronous version long enough to know exactly where the pain is. You're solving a real, observed bottleneck rather than an imagined future one. The integration points are obvious because you've already felt them.

The meta-lesson

Architecture decisions compound. The choice to introduce an event broker means every new engineer who joins has to understand it. Every incident will potentially involve tracing messages across topics. Every new feature has to consider the event schema and backward compatibility.

That cost is worth it in the right context. The problem is that "we might need to scale someday" is often doing a lot of work to justify it in the wrong one.

Build the simple version first. You'll know when you need more.

Top comments (0)