The verdict
Use synchronous request/response when the caller needs an immediate answer to continue and the operation is fast and reliable. Use asynchronous messaging when you want to decouple services, absorb load spikes, tolerate a slow or failing downstream, or fan out to many consumers. Async buys resilience and scale at the cost of complexity — eventual consistency, harder debugging, and the need for idempotency and dead-letter handling. Most systems use both, deliberately.
How to decide
- 1Does the caller need the result now to continue, or can the work happen later?
- 2How coupled should these services be — can the callee be down without breaking the caller?
- 3Do you need to absorb bursts or shield a slow/fragile downstream?
- 4Do multiple consumers need the same event (fan-out)?
- 5Pick per interaction; a hybrid (sync for the read path, async for side effects) is common and correct.
The options
Synchronous (request/response)
Caller sends a request and waits for the reply.
Best for
- Reads the caller needs right now
- Simple, fast, reliable operations
- Flows where an immediate result drives the next step
Strengths
- Simple to reason about and debug — one call, one response
- Immediate result and error to the caller
- Strong, in-the-moment consistency
Weaknesses
- Tight temporal coupling — caller is blocked on the callee
- Failures and latency cascade through call chains
- Downstream slowness becomes upstream slowness
Asynchronous (messaging / events)
Producer emits a message; consumers handle it later.
Best for
- Work that can happen in the background
- Absorbing bursts and protecting slow downstreams
- Fan-out to multiple independent consumers
Strengths
- Loose coupling and independent scaling
- Resilience — a queue buffers spikes and downstream outages
- Natural fan-out to many consumers
Weaknesses
- Eventual consistency and harder end-to-end reasoning
- Requires idempotency, retries, ordering care, and dead-letter queues
- Debugging spans producers, brokers, and consumers
Trade-offs at a glance
| Dimension | Synchronous (request/response) | Asynchronous (messaging / events) |
|---|---|---|
| Coupling | Tight (temporal) | Loose |
| Perceived latency | Blocks caller | Returns fast; work later |
| Downstream failure | Cascades | Absorbed by the queue |
| Load absorption | Poor | Good (buffering) |
| Consistency | Immediate | Eventual |
| Debuggability | Easier | Harder (distributed) |
| Best for | Immediate answers | Decoupling / fan-out / spikes |
In the interview
When you connect two services in a design — 'how do these talk to each other?' The interviewer is checking whether you weigh coupling, resilience, and consistency rather than defaulting to REST calls everywhere.
What a Staff answer includes
A Staff answer ties the choice to whether the caller needs an immediate answer, then names the resilience win of async and its real costs — idempotency, ordering, dead-letter queues, eventual consistency. It often proposes a hybrid and explains where each boundary sits and why.
Follow-ups you should expect
- →How do you prevent a synchronous call chain from cascading failures?
- →How do you make the async consumer idempotent?
- →How do you handle a message that keeps failing?
- →How does the user experience change when you move this to async?
Common mistakes
- ✕Long synchronous call chains that cascade failures and latency.
- ✕Async for a simple call that genuinely needs an immediate answer.
- ✕Forgetting idempotency and dead-letter handling on consumers.
- ✕Hidden temporal coupling — 'async' that still assumes the consumer is up.
Further reading
Related decisions
Kafka vs RabbitMQ vs SQS
Kafka is a replayable log for event streaming; RabbitMQ is a flexible broker for task queues; SQS is a zero-ops managed queue on AWS.
Monolith vs Microservices
Start with a modular monolith. Move to microservices only for a concrete driver — independent scaling, team autonomy, or fault isolation — and only with the ops maturity to run a distributed system.