Practice this topic in a realistic system design interview
When a user clicks Place Order, several things have to happen: validating the cart, checking prices, creating the order, reserving inventory, capturing payment, sending a confirmation email, and updating analytics.
Some of those actions must complete before the user gets a response. Others can happen later.
That is the core difference between synchronous and asynchronous communication.
In synchronous communication, the caller sends a request and waits for the answer.
In asynchronous communication, the sender records work for later and continues without waiting for the final result.
Loading simulation...
Neither style is better in every case. Most real systems use both. The design skill is knowing which parts of a workflow need an immediate answer and which parts can safely happen in the background.
This chapter covers synchronous and asynchronous communication, their trade-offs, and how to choose between them.
Synchronous communication is the familiar request-response model.
The caller asks for something, waits, and then continues after it receives a response or error.
Common examples include HTTP APIs, gRPC calls, GraphQL queries, database queries, and RPC between internal services. Synchronous calls are a good fit when the caller truly needs the answer before it can continue.
A mobile app showing an account balance needs an immediate answer.
If the account service or database is unavailable, the app cannot honestly show the current balance. A synchronous call is appropriate because the user is waiting for that specific result.
Synchronous communication is easy to understand, but it gets risky when one user request has to call many services in a row.
Suppose a request must call three services, and each service is available 99.9% of the time.
If all three must succeed for the request to succeed, the combined availability is roughly:
This simplified math assumes the services fail independently and there is no fallback. The lesson is still useful: every required synchronous call can make the user-facing path less available.
Good synchronous systems use timeouts, careful retries, circuit breakers, caching, graceful fallback behavior, and short call chains.
Asynchronous communication separates the sender from the final processing.
The sender writes a message, event, or job to a middle layer such as a queue, topic, stream, or reliable table. A receiver processes it later.
The sender usually knows that the message was accepted. It does not immediately know whether every follow-up action succeeded.
That difference matters.
After an order is created, several follow-up actions can happen in the background.
The user can receive an order ID once the order is safely saved. Email, analytics, and search indexing can happen later. If email is slow, checkout does not need to fail.
Payment and inventory are more subtle. Some businesses require those to complete before confirming the order. Others create a PENDING order and confirm it after payment and inventory succeed. The communication style must match the product promise.
Asynchronous communication trades immediate certainty for buffering, retries, and independent processing.
With async workflows, different parts of the system can temporarily disagree. This is often called eventual consistency: the system may not agree everywhere right now, but it should settle into the correct state after the background work finishes.
For a short time, the order exists before inventory has been reserved. That is not automatically wrong, but the state must be clear. Names like PENDING, PROCESSING, CONFIRMED, and FAILED matter because users and support teams need to understand what is happening.
Use synchronous communication when:
Typical examples are checking login and authorization, fetching an account balance, reading a product page, validating a coupon during checkout, checking whether a username is available, or returning search results.
For synchronous calls, set timeouts. Without a timeout, one slow service can leave the whole request stuck.
Use asynchronous communication when:
Typical examples are sending emails or push notifications, updating search indexes, processing videos or images, generating reports, shipping analytics events, replicating data to a warehouse, and running webhook delivery retries.
Async is not a way to avoid thinking about correctness. It means correctness is handled with clear states, retries, safe-to-repeat processing, monitoring, and repair tools.
Most real systems mix both styles. Some calls need an answer right now. Other work can run later, and the caller can pick up the result when it is ready.
The caller waits for a response.
Use for reads, validation, and operations where the caller needs the result now.
The sender submits a task. One worker processes it.
Use for background jobs such as image processing, report generation, or webhook delivery.
The publisher sends an event. Multiple subscriptions receive it.
Use when several independent services need to react to the same event.
The client submits a long-running job and receives the result later.
Use for exports, media processing, ML jobs, and other workflows where the result matters but does not need to be immediate.
Most systems combine both styles.
The synchronous path should do only the work required to give the user a correct response. The asynchronous path handles follow-up work that can wait.
The outbox is shown because it avoids a common bug: saving the order but failing to publish the event.
Before choosing sync or async, ask:
The communication style affects far more than the code. It changes the user experience, failure behavior, and the work needed to run the system.
Synchronous communication gives immediate answers and simple control flow, but the caller has to wait for the services it calls. Asynchronous communication adds buffering and retries, but the result arrives later and the system must handle temporary disagreement, retries, duplicates, ordering, and monitoring.
Use synchronous calls when the caller genuinely needs the answer now, and asynchronous messaging when work can happen later, receivers need to scale independently, or multiple services must react to the same event.
Most good architectures use both: a small synchronous user-facing path, followed by asynchronous background work for everything that does not need to block the response.
10 quizzes