Back to System Design Concepts
Q14Question 14 of 16

Synchronous vs Asynchronous Communication

Key Concepts
  • Synchronous (REST/gRPC) blocks the caller until it gets a response : simple reasoning, tight coupling, cascading failure risk.
  • Asynchronous (Kafka/SQS) sends a message and continues : decoupled, buffered, resilient, but results are not immediate.
  • Use sync when the caller genuinely needs the result to proceed (balance read, authentication).
  • Use async when work can happen in the background, you need to buffer spikes, or one event triggers many reactions.
  • Real systems mix both : the sync path does the minimum for the user, everything deferrable goes async.
  • Protect sync edges with timeouts, retries, and circuit breakers so a slow dependency cannot cascade.
  • Async consumers must be idempotent because delivery is typically at-least-once.

Definition

Sync (REST/gRPC) waits for an immediate answer but couples caller to callee; async (Kafka/SQS) hands off a message and decouples, trading immediacy for resilience.
  • Synchronous communication : the caller sends a request and blocks waiting for the response (REST, gRPC), giving immediate results but tight coupling.
  • Asynchronous communication : the caller sends a message and continues without waiting (Kafka, SQS), giving decoupling and resilience but eventual rather than immediate results.
  • Cascading failure : when a slow sync dependency ties up the caller's threads and brings down the whole chain.
  • Circuit breaker : a pattern that stops calling a failing service after a threshold, returning a fallback instead of blocking.

When Rajesh asks a teller for his balance, he waits at the counter until the teller answers : that is synchronous. When Rajesh drops a loan application in a box and leaves, the loan department processes it later : that is asynchronous. National Bank of India uses conversations when the customer needs an answer right now, and drop-boxes when the work can happen in the background.

Understanding the Concepts

What is synchronous communication?

Synchronous calls using REST or gRPC are request-response: the caller blocks until it gets a reply or times out. This is simple to reason about and ideal when the caller genuinely needs the result to continue.

JAVA
package com.bank.communication;

import java.math.BigDecimal;

public class SyncBalanceClient {
    public BigDecimal getBalance(String accountId) {
        var resp = restClient.get()
            .uri("/accounts/{id}/balance", accountId)
            .retrieve().toEntity(BigDecimal.class);  // caller WAITS here
        return resp.getBody();
    }
}

What is asynchronous communication?

Async messaging using Kafka or SQS is fire-and-forget or publish-subscribe. The caller sends a message to a broker and continues immediately, while consumers process it later.

JAVA
package com.bank.communication;

public class AsyncTransferPublisher {
    public void onTransferCompleted(Transfer t) {
        kafkaTemplate.send("transfer.completed", t.accountId(), toJson(t));
        // Caller continues; receipts, fraud, analytics happen later
    }
}

What are the trade-offs?

| Aspect | Synchronous | Asynchronous |
|---|---|---|
| Coupling | Tight (both must be up) | Loose (decoupled by broker) |
| Result | Immediate | Eventual |
| Failure mode | Cascading | Buffered |
| Complexity | Simple | Needs idempotent consumers |
| Use case | Balance read, auth | Notifications, analytics |

When should you use sync?

Use synchronous when the caller needs an immediate answer to proceed and the operation is a query or short command, such as authentication, balance read, or payment authorization.

When should you use async?

Use asynchronous when work can happen in the background, when you want to decouple producers from consumers, when you need to buffer spikes, or when one event must trigger many independent reactions.

How do real systems mix both?

The synchronous path does the minimum needed to answer the user correctly, and everything deferrable is pushed onto an asynchronous path. For a transfer: commit the money synchronously, publish a transfer-completed event, return success, and let receipt, fraud check, and analytics run asynchronously.

JAVA
package com.bank.service;

import java.math.BigDecimal;

public class HybridTransferService {
    public TransferResult transfer(String from, String to, BigDecimal amount) {
        // SYNC: commit the money (must be immediate and correct)
        ledger.debitCredit(from, to, amount);

        // ASYNC: everything else can happen in the background
        eventBus.publish("transfer.completed",
            Map.of("from", from, "to", to, "amount", amount));

        return TransferResult.success(); // return fast
    }
}

How do you protect sync edges?

Protect synchronous calls with timeouts, retries with exponential backoff, and circuit breakers so a slow dependency cannot cascade and bring down the whole system.

JAVA
package com.bank.resilience;

public class CircuitBreakerExample {
    @CircuitBreaker(name = "account-service", fallbackMethod = "fallback")
    public BigDecimal getBalance(String accountId) {
        return accountClient.getBalance(accountId); // may timeout
    }

    public BigDecimal fallback(String accountId, Exception e) {
        return cachedBalance.get(accountId); // serve cached on failure
    }
}

Why must async consumers be idempotent?

Async delivery is typically at-least-once, meaning a message can be redelivered if the consumer crashes before acknowledging. Consumers must handle duplicates gracefully by checking a message id.

What should a senior engineer know?

Designing which work is synchronous and which is asynchronous is one of the highest-leverage decisions in a distributed system. It directly determines both how fast the user-facing response feels and how resilient the system is to partial failure.

Visual Overview

DIAGRAM
           Hybrid Sync + Async Architecture

  ┌──────────┐   sync (REST)   ┌──────────────┐
  │  Client  │────────────────►│  Transfer    │
  │  (Rajesh)│◄────────────────│  Service     │
  └──────────┘   fast response └──────┬───────┘
                                      │ publish event (async)
                                      ▼
                              ┌───────────────┐
                              │    Kafka      │
                              │  (broker)     │
                              └──┬────┬────┬──┘
                                 │    │    │
                          ┌──────▼┐ ┌─▼───┐ ┌▼────────┐
                          │Receipt│ │Fraud│ │Analytics│
                          │Service│ │Check│ │Service  │
                          └───────┘ └─────┘ └─────────┘
                          (async)   (async)  (async)

  Sync path: caller blocks, gets immediate result
  Async path: caller returns fast, work happens later
  Protection: timeout -> retry -> circuit breaker

Key Takeaway

Synchronous and asynchronous communication are two ways services talk, and I choose between them by the nature of the interaction. Synchronous REST or gRPC is request-response where the caller blocks until it gets a reply. It is simple and right when the caller genuinely needs the result, like reading a balance. Its weakness is tight coupling and cascading failures.

Asynchronous messaging through Kafka or SQS decouples services in time. The producer sends a message and continues immediately while consumers process later. This buffers bursts and means a slow downstream never blocks the fast upstream.

In practice at National Bank of India, I make the sync path do the minimum needed : commit the money, return success. Everything deferrable : receipt, fraud check, analytics : goes async. This both speeds up the user response and keeps the transfer resilient.

The senior insight is that this is deliberately a mix. Sync calls form thin, latency-sensitive edges protected with timeouts, retries, and circuit breakers. Async messaging carries everything deferrable. Designing which work is sync and which is async is one of the highest-leverage decisions in a distributed system.

Remember one sentence: sync for immediate answers the user needs, async for everything deferrable, and protect sync edges with circuit breakers so nothing cascades.

---

Share
Use ← → arrow keys to navigate