Skip to content

Why Great Systems Don't Make You Wait for Everything

Order confirmation shouldn't wait on payment, inventory, email, and analytics to all finish. How separating the critical path from everything else keeps event-driven systems responsive.

By 2 min read
  • Architecture Breakdown
  • Event-Driven Architecture
  • Distributed Systems
  • Software Architecture
  • System Design

You click "Place Order."

The system could wait for:

  • Payment confirmation.
  • Inventory update.
  • Email notification.
  • Analytics.
  • Recommendation updates.
  • Loyalty points.
  • Shipping workflow.
  • Fraud checks.

...and only then return: "Order successful."

But that's a terrible user experience. And at scale, it's a terrible architecture.

Why Great Systems Don't Make You Wait for Everything

The better approach separates what must happen now from what can happen afterward.

A simplified flow looks like this:

  1. πŸ›’ User places an order.
  2. ⚑ Order Service validates and creates the order.
  3. βœ… User gets a response.
  4. πŸ“’ An OrderCreated event is published.
  5. πŸ”€ Multiple services react independently β€” Inventory, Notifications, Analytics, Recommendations, Shipping.

The order service doesn't need to know how every downstream system works. It simply publishes that something happened, and other services decide what to do about it. That's the fundamental idea behind event-driven architecture.

One event can trigger many independent workflows without making the original request wait for all of them.

But there's a trade-off. Now you're dealing with:

  • Eventual consistency.
  • Duplicate events.
  • Failed consumers.
  • Event ordering.
  • Retries and replay.

So event-driven architecture isn't automatically "better." It moves complexity from synchronous coordination into asynchronous coordination β€” the part many architecture diagrams don't show.

One lesson I've learned from designing systems at scale: Don't make the user wait for work that doesn't need to block them. Keep the critical path small. Move everything else behind it.

The goal isn't to make every operation asynchronous. It's to make a deliberate decision about what deserves to be on the critical path.

Where have you seen unnecessary synchronous work make an application slower than it needed to be?

Keep reading