System design

Should We Always Build Decoupled Systems?

Kafka, Pub/Sub, and queues are powerful, but asynchronous messaging is not the default answer for every interaction. Start with the business requirement.

  • system design
  • event-driven architecture
  • Kafka
  • Pub/Sub
Illustration comparing a direct synchronous service call with an asynchronous event-driven path through a message broker.
Original visual by Rishi Bytes

Messaging platforms such as Kafka, Pub/Sub, and RabbitMQ make it possible to connect services without requiring every producer to know every consumer.

That is powerful. It does not mean every interaction should become asynchronous.

The useful question is not “Should this architecture be decoupled?” It is:

Which parts of this workflow need an immediate answer, and which parts can progress independently?

Synchronous does not automatically mean badly coupled

Consider a payment or reservation request. The user needs to know whether the action succeeded. A synchronous request is often the clearest way to return that immediate result.

That does not require one giant tightly connected system. A service can expose a stable contract, hide its implementation, and still respond synchronously. The caller depends on the contract and availability of that service, but not necessarily on its internal design.

The synchronous path is appropriate when:

  • the caller cannot continue without the response;
  • users need immediate confirmation;
  • the operation is short and predictable;
  • consistency must be established before returning; or
  • handling a delayed result would make the experience more complicated.

Adding a broker to that critical path may introduce retries, correlation identifiers, timeout states, and eventual consistency without creating meaningful business value.

Where asynchronous messaging helps

Now consider what happens after a user publishes a post. The API may need to confirm that the post was stored, but notifications, search indexing, recommendations, and analytics do not all need to finish before the user receives a response.

Publishing an event allows those consumers to work independently.

This is useful when:

  • consumers need to scale at different rates;
  • temporary downstream failures should not block the producer;
  • several teams need to react to the same business event;
  • workloads arrive in bursts and can be buffered;
  • eventual consistency is acceptable; or
  • new consumers should be added without changing the producer.

The broker becomes a boundary between the event that occurred and the work triggered by it.

Decoupling moves complexity

Asynchronous systems improve isolation, but they do not remove complexity. They relocate it.

A production event-driven workflow needs decisions about:

  1. delivery guarantees and duplicate messages;
  2. idempotent consumers;
  3. ordering requirements;
  4. retries and dead-letter handling;
  5. schema compatibility;
  6. observability across asynchronous steps;
  7. replay and retention; and
  8. ownership when an event is technically delivered but not successfully processed.

If the team is not prepared to operate those behaviours, a message broker can make a simple workflow harder to understand and debug.

Most useful systems combine both

A practical architecture often uses a synchronous core with asynchronous side effects.

For example:

  1. A booking API validates the request and commits the reservation synchronously.
  2. It records an event reliably, often using an outbox or equivalent pattern.
  3. Email, analytics, loyalty, and notification consumers process that event independently.

The user gets an immediate answer for the part that matters now. Secondary work receives the scaling and fault-isolation benefits of messaging.

Decide from requirements

Before introducing asynchronous messaging, ask:

  • What response does the user or caller require?
  • What is the tolerated delay?
  • What consistency model is acceptable?
  • What happens during partial failure?
  • Does independent scaling solve a measured problem?
  • Can the team observe and replay the workflow?

Architecture is not improved by maximising the number of intermediaries. It improves when each dependency and failure mode is intentional.

Messaging systems provide powerful capabilities. Use them where independent progress, buffering, fan-out, or fault isolation creates real value. Keep direct interactions where an immediate result is the simplest honest contract.

A similar trade-off appears in storage design: sometimes a deliberate copy reduces latency without becoming accidental debt. See Why I’m Breaking the DRY Rule.