HT
How Things Work
System Online

Inter-Service Communication

How services talk — synchronous REST and gRPC vs asynchronous messaging, the temporal-coupling trade-off, and choosing the right style per interaction.

How It Works

Microservices have to talk to each other, and the style you choose shapes resilience, latency, and coupling. Synchronous calls (REST, gRPC) give an immediate answer but couple services in time. Asynchronous messaging decouples them for resilience and scale at the cost of eventual consistency. The skill is matching the style to each interaction rather than picking one globally.

1
Synchronous: Request/Response

The caller sends a request and blocks until the response arrives. REST over HTTP/JSON is the universal default — simple, debuggable, broadly compatible. gRPC uses HTTP/2 and binary Protobuf for compact, low-latency, strongly-typed calls (with streaming). Both require the callee to be available right now.

2
Asynchronous: Messaging

The sender publishes a message or event to a broker and returns immediately, without waiting for the receiver. The receiver processes it whenever it can. Sender and receiver are decoupled in time — either can be down, restart, or run at a different speed without breaking the other.

3
The Coupling Trade-off

Synchronous calls create temporal coupling: a chain of services must all be up and fast, so one slow or failed service can cascade into the whole request. Async messaging removes that coupling and adds resilience and load leveling — but introduces eventual consistency, since the work happens later and the sender doesn't get an immediate result.

4
Choosing Per Interaction

It's not one global choice. Use synchronous calls when the caller genuinely needs an answer to proceed (a query, a validation). Use asynchronous events for notifications, workflows, and anything where 'eventually' is fine. Most mature systems mix both, leaning on async to decouple and sync where an immediate answer is required.

Key Concepts

⏱ïļSynchronous

Request/response where the caller blocks until it gets a reply (REST, gRPC). Simple but temporally coupled.

ðŸ“ĻAsynchronous

Fire-and-forget messaging via a broker; sender doesn't wait. Decoupled and resilient, but eventually consistent.

🌐REST

HTTP + JSON, resource-oriented. The universal, human-readable default for service and public APIs.

⚡gRPC

Contract-first RPC over HTTP/2 with binary Protobuf — compact, fast, typed, supports streaming.

🔗Temporal Coupling

When services must be up at the same time for an interaction to succeed — a property of sync calls.

🔄Eventual Consistency

The result of async work — state converges over time rather than being immediately visible.

REST vs gRPC vs messaging
tsx
1// SYNC — REST: simple, blocking, both services must be up
2const res = await fetch(`${ORDERS}/orders/42`); // caller blocks here
3const order = await res.json();
4
5// SYNC — gRPC: contract-first, binary, fast, supports streaming
6service OrderService { // orders.proto
7 rpc GetOrder(GetOrderRequest) returns (Order);
8 rpc WatchOrders(Empty) returns (stream Order); // server streaming
9}
10const order = await client.getOrder({ id: 42 }); // generated, typed stub
11
12// ASYNC — messaging: fire-and-forget, decoupled, resilient
13await bus.publish("OrderPlaced", { orderId: 42 }); // returns immediately
14// ...elsewhere, independently, possibly minutes later:
15bus.subscribe("OrderPlaced", async (e) => { await ship(e.orderId); });
ðŸ’Ą
Why This Matters

Communication style is one of the highest-impact decisions in a distributed system — it determines how failures propagate and how the system scales. It's a constant interview theme (REST vs gRPC vs queues, sync vs async) and a frequent source of real production fragility when teams default to deep synchronous chains.

Common Pitfalls

⚠Building deep synchronous call chains, where one slow service times out the whole request.
⚠Reaching for async messaging when the caller actually needs an immediate, consistent answer.
⚠Ignoring eventual consistency in async flows — UIs that assume instant results show stale data.
⚠Chatty fine-grained calls between services, multiplying network round trips and latency.
⚠No timeouts, retries, or idempotency on inter-service calls, so transient failures become outages or duplicates.
Real-World Use Cases

1The Synchronous Call Chain That Cascades

Scenario

Placing an order calls payment, which calls fraud-check, which calls a partner API — all synchronously. When the partner API slows down, the latency stacks up the whole chain and order placement times out for everyone.

Problem

A deep synchronous chain means the slowest, least reliable link determines the success and latency of the entire operation. Temporal coupling turns one degraded dependency into a system-wide failure.

Solution

Keep only the truly required check synchronous (e.g. payment authorization) and move the rest to async events: publish 'OrderPlaced' and let fraud-check, partner notification, and fulfillment react independently via messaging. The user's request returns fast; downstream slowness no longer blocks it.

ðŸ’Ą

Takeaway: Long synchronous chains are fragile — latency and failures compound. Reserve sync calls for what the caller truly needs now, and decouple the rest with async events to contain slowness and failure.

2Choosing gRPC for Hot Internal Paths

Scenario

An internal recommendation service is called thousands of times per second by other services. Using REST/JSON, serialization overhead and HTTP/1.1 connection churn are measurable in latency and CPU.

Problem

Verbose JSON and HTTP/1.1 add real cost at high call volumes between services, and the loose contract invites mismatches between client and server payload shapes.

Solution

Switch the internal call to gRPC: a shared .proto contract generates strongly-typed clients/servers, Protobuf shrinks payloads, and HTTP/2 multiplexes many calls over one connection. Latency and CPU drop, and the contract is enforced at compile time.

ðŸ’Ą

Takeaway: For high-volume internal service-to-service calls, gRPC's binary protocol, HTTP/2 multiplexing, and contract-first typing beat REST. Keep REST for public, low-volume, or broadly-consumed APIs where simplicity and compatibility win.

Join the discussion

Comments

?

Loading comments...

Cookie preferences

We use essential cookies for sign-in and preferences. With your permission, we also use basic analytics to improve lessons.