Free lesson · Microservices Interview
Synchronous vs Asynchronous Communication - When to Use Which
Synchronous vs Asynchronous Communication - When to Use Which
The previous three modules addressed the structural questions of microservices - what they are, how to decompose them, and how to migrate to them. This module addresses the question that follows: once your services exist as separate, independently deployable units, how do they talk to each other?
My name is Nikhil Joshi. I work on cross-region data replication for Google Cloud Spanner, which means my daily work involves designing communication patterns between services that span continents. The decisions covered in this module - synchronous versus asynchronous, which protocol, which messaging pattern - are decisions I make weekly. They are also decisions that system design interviewers probe in every microservices interview.
This first lesson covers the most fundamental choice: should two services communicate synchronously (request-response, the caller waits) or asynchronously (the caller does not wait)?
The answer is not "always async" or "always sync." It depends on the specific interaction - what the caller needs, how much latency is acceptable, what consistency model the operation requires, and what happens when the communication fails. This lesson gives you the framework for making that decision.
The Three Communication Models
Before comparing sync and async, let me define three specific communication models that cover the spectrum.
Request-Response (Synchronous)
Service A sends a request to Service B and blocks until it receives a response. The caller waits. The operation is complete when the response arrives.
This is the model you are most familiar with from HTTP. A client sends a GET request, the server processes it, and the client receives the response. In microservices, one service makes an HTTP or gRPC call to another service and waits for the result before proceeding.
Characteristics:
- The caller is blocked until the response arrives (or the timeout fires)
- The caller gets immediate confirmation of success or failure
- The caller and the callee must both be available at the time of the call
- Latency is additive - if Service A calls Service B which calls Service C synchronously, the total latency is the sum of all three
Fire-and-Forget (Asynchronous)
Service A sends a message to a message broker (Kafka, RabbitMQ, SQS) and immediately continues processing. Service A does not wait for Service B to process the message. Service A does not know when - or whether - Service B processes the message.
Characteristics:
- The caller is not blocked - it resumes immediately after publishing the message
- The caller does not get confirmation that the message was processed (only that it was accepted by the broker)
- The caller and the callee do not need to be available simultaneously
- Latency is decoupled - Service A's response time is independent of Service B's processing time
Request-Async-Response
Service A sends a request and receives an immediate acknowledgment ("I got your request, I will process it"). The actual result arrives later through a callback, a webhook, or a polling mechanism. This model combines elements of both sync and async - the initial exchange is synchronous (the caller gets an acknowledgment), but the processing is asynchronous.
Characteristics:
- The caller gets immediate acknowledgment, not the final result
- The final result arrives later through a separate channel
- The caller needs a mechanism to receive the result (callback URL, polling endpoint, event subscription)
- Useful when processing takes seconds or minutes - too long for the caller to block, but the caller still needs the result
graph LR
subgraph Sync["Synchronous"]
A1["Service A"] -->|"Request"| B1["Service B"]
B1 -->|"Response"| A1
end
subgraph Async["Fire-and-Forget"]
A2["Service A"] -->|"Message"| Q["Message\nBroker"]
Q -->|"Delivers later"| B2["Service B"]
end
subgraph ReqAsync["Request-Async-Response"]
A3["Service A"] -->|"Request"| B3["Service B"]
B3 -->|"ACK (202)"| A3
B3 -->|"Result (later)"| CB["Callback /\nEvent"]
CB --> A3
end
This diagram shows three communication models. Synchronous: Service A sends a request to Service B and waits for the response. Fire-and-Forget: Service A sends a message to a broker, which delivers it to Service B later - Service A does not wait. Request-Async-Response: Service A sends a request, gets an immediate acknowledgment, and receives the actual result later through a callback or event.
The Decision Framework
Choosing between these models depends on four factors. Evaluate each one for the specific interaction you are designing.
Factor 1 - Does the Caller Need the Result to Continue?
This is the most important factor. If Service A cannot proceed without Service B's response - because it needs the response to return a result to the user or to make its next decision - the communication must be synchronous (or request-async-response if the user can tolerate a delay).
Example: Price calculation during checkout. The Order Service needs the Pricing Service to calculate the total before displaying it to the user. The Order Service cannot continue without the price. This interaction must be synchronous.
Example: Sending a notification after order placement. The Order Service does not need to wait for the notification to be sent before confirming the order to the user. The notification can be sent asynchronously. Fire-and-forget is appropriate.
The rule: if the caller needs the result to fulfil its immediate responsibility, use synchronous. If the result is a side effect that can happen later, use asynchronous.
The grey area is where the caller does not strictly need the result but the user experience is better with it. When a user places an order, the Order Service does not need the estimated delivery time to confirm the order - but showing the estimated delivery time on the confirmation screen is a better experience. The choice depends on the cost of making the call synchronous (added latency, availability coupling) versus the UX value of including the data. In this case, a reasonable design is to attempt the synchronous call with a short timeout (200ms) and fall back to "delivery estimate will be sent by email" if the call fails. The order confirmation is not blocked by the delivery estimate - the estimate is best-effort.
This "best-effort sync with graceful degradation" pattern is common in production systems. It preserves the user experience when the downstream service is healthy and degrades gracefully when it is not. In interviews, proposing this pattern demonstrates that you think about communication decisions in terms of user experience tradeoffs, not just technical models.
Factor 2 - What Latency Is Acceptable?
Synchronous communication adds latency to the caller's response. Each synchronous call in the request chain adds network round-trip time plus the callee's processing time. If the chain is long (A calls B calls C calls D), the latencies compound.
At Google, we design internal services with a "latency budget" - a maximum total latency for the end-to-end request. If the budget is 200ms and the first service consumes 50ms, the remaining services in the chain share 150ms. Synchronous chains that exceed the budget must be restructured - typically by converting some interactions from synchronous to asynchronous.
The latency math is worth doing explicitly. Consider a checkout flow where the Order Service synchronously calls three downstream services:
- Pricing Service: 30ms average, 120ms P99
- Inventory Service: 20ms average, 80ms P99
- Payment Service: 200ms average, 800ms P99
The average total latency for the synchronous chain is 250ms, which is acceptable. But the P99 is 1,000ms - one second - because tail latencies compound. At P99.9, it could be several seconds. This is the tail latency amplification problem: even if each service is individually fast, a chain of synchronous calls produces a long tail.
The mitigation options are: make some calls asynchronous (convert inventory to an event), make calls parallel instead of sequential (call pricing and inventory simultaneously), or accept the tail latency and design the frontend to handle it gracefully (loading indicators, optimistic UI).
Tip: In interviews, stating a latency budget demonstrates production awareness. "For this user-facing request, I have a latency budget of 300ms. The API gateway adds 10ms, the Order Service needs 50ms for its own processing, so I have 240ms for downstream calls. I can afford one synchronous call to the Pricing Service, but the notification, inventory update, and analytics event should all be asynchronous to stay within budget."
Factor 3 - What Consistency Model Does the Interaction Require?
Synchronous communication naturally provides strong consistency - the caller knows the operation succeeded or failed before proceeding. Asynchronous communication provides eventual consistency - the operation will happen eventually, but the caller does not know exactly when.
Some interactions require strong consistency. When the Payment Service charges a credit card, the Order Service needs to know immediately whether the charge succeeded before confirming the order. This cannot be eventually consistent - you cannot tell the user "your order is confirmed" and then discover an hour later that the charge failed.
Other interactions tolerate eventual consistency. When the Inventory Service decrements stock after an order, the stock level can be slightly stale for a few seconds without causing problems. The decrement will happen, just not instantaneously.
| Consistency Requirement | Communication Model | Example |
|---|---|---|
| Must know result immediately | Synchronous | Price calculation, payment charge |
| Must know result soon (seconds) | Request-async-response | Credit check, fraud analysis |
| Eventually consistent is acceptable | Fire-and-forget | Notification, analytics, inventory update |
Factor 4 - What Happens When the Communication Fails?
This is the factor that most candidates underestimate. Every network call can fail. The question is: what happens to the caller when it does?
Synchronous failure impact. If Service B is unavailable, Service A cannot complete its operation. Service A either retries (adding latency), falls back to a default response (degraded but functional), or fails entirely (the user sees an error). Synchronous coupling creates an availability dependency - Service A's availability is bounded by Service B's availability.
Asynchronous failure impact. If the message broker is available (and it should be - brokers are designed for high availability), Service A's message is persisted regardless of Service B's availability. Service B processes the message when it comes back online. Service A is not affected by Service B's downtime. The cost is that Service A does not know whether Service B ever processes the message - it trusts the broker to deliver it.
The practical implication: the more synchronous dependencies a service has, the lower its effective availability. If Service A has synchronous dependencies on Services B, C, and D, and each has 99.9 percent availability, Service A's effective availability is at most 99.9% × 99.9% × 99.9% = 99.7%. This is the multiplicative availability problem, and it is why mature microservices architectures push as many interactions as possible to asynchronous.
Interview: The availability multiplication argument is one of the strongest signals you can send in an interview. "I am making the inventory update asynchronous because if I make it synchronous, I couple the order service's availability to the inventory service's availability. If inventory has 99.9 percent uptime, that is 8.7 hours of downtime per year that would also take down order placement. By making the update asynchronous through an event, order placement succeeds even when inventory is temporarily unavailable." This reasoning shows production reliability thinking.
Common Interview Patterns
Here is how the sync/async decision maps to the most common microservices interactions in interview scenarios.
Typically Synchronous
- Query operations - "get the product details," "calculate the price," "check if the user exists." The caller needs the result to continue.
- Validation operations - "validate the credit card," "check the fraud score." The caller needs the result before proceeding.
- Authentication and authorisation - "is this token valid," "does this user have permission." Must be resolved before the request proceeds.
Typically Asynchronous
- Notifications - email, SMS, push. The user does not need to wait for the email to be sent.
- Analytics and logging - event tracking, audit logs, metrics. Background processing.
- Downstream updates - inventory decrement, search index update, cache invalidation. Side effects that do not need to block the primary operation.
- Cross-service data replication - event-carried state transfer, CDC. Background synchronisation.
Context-Dependent
- Payment processing - synchronous if the user needs immediate confirmation (credit card charge returns success/failure in real time), request-async-response if the payment takes time to process (bank transfers, 3D Secure verification, PayPal redirects). The communication model is driven by the payment method's latency characteristics, not by architectural preference.
- Order creation - the order itself can be created synchronously, but downstream effects (inventory, notification, analytics) should be asynchronous events triggered by the order creation. This is the most common hybrid pattern in microservices: synchronous critical path, asynchronous fan-out for side effects.
- Search index updates - asynchronous in most systems (eventual consistency is acceptable), but synchronous in systems where stale search results cause business problems (e.g., removing a dangerous product listing, taking down a legally infringing item).
- Fraud detection - depends on the risk model. Low-risk transactions can be screened asynchronously (process the payment, flag suspicious transactions for review). High-risk transactions should be screened synchronously (block the transaction until the fraud engine returns a verdict). Some systems use a two-tier approach: a fast synchronous check that catches obvious fraud, followed by an asynchronous deep analysis that can flag the transaction for manual review after the fact.
Interview: The strongest interview technique for communication decisions: explicitly classify each interaction. After proposing your service boundaries, walk through the interactions between services and classify each one: "Order to Pricing is synchronous because checkout needs the price immediately. Order to Inventory is asynchronous because stock updates can be eventually consistent. Order to Notification is fire-and-forget because the user does not wait for the email." This systematic classification demonstrates that you are making deliberate communication decisions rather than defaulting to one model.
The Hybrid Reality
In practice, most microservices architectures use both synchronous and asynchronous communication. The same service might make synchronous API calls for queries and publish asynchronous events for updates. The checkout flow might be synchronous up to order creation and then fan out into asynchronous events for everything that happens after.
The art is in choosing the right model for each specific interaction based on the four factors above. The anti-pattern is choosing one model for everything - all sync (which creates tight coupling and availability problems) or all async (which makes simple queries unnecessarily complex and introduces latency where the user expects immediate responses).
A useful mental model is the synchronous spine, asynchronous fan-out pattern. The primary request path - the sequence of operations that the user is waiting for - uses synchronous communication for the minimum necessary steps. Everything else - side effects, downstream updates, notifications, analytics - fans out asynchronously via events. The spine is fast and focused. The fan-out is decoupled and resilient.
For a food delivery checkout: the spine is Order Service → Pricing Service (sync) → confirm to user. The fan-out is Order Service publishes OrderPlaced event → Inventory Service, Notification Service, Analytics Service, and Restaurant Service all consume the event asynchronously. The user's checkout experience is 100ms (the sync spine), while the downstream processing happens over the next few seconds without the user waiting.
What Comes Next
This lesson gave you the framework for choosing between synchronous and asynchronous communication. The next three lessons examine the specific protocols and technologies for each: REST for synchronous HTTP communication, gRPC for high-performance synchronous communication, and GraphQL for client-driven query composition. Each has specific strengths and tradeoffs in a microservices context.