How services talk to each other is the most consequential design choice in a microservices system, so it is one of the most heavily interviewed. The question is never really "which protocol?" — it is "do you understand what each communication style costs you when the network misbehaves?" This page covers the communication questions asked across levels, and the failure-handling follow-ups that decide the answer.
What are the two fundamental styles of inter-service communication?
Synchronous request/response, where the caller blocks until it gets an answer (REST over HTTP, gRPC), and asynchronous messaging, where the sender publishes a message and does not wait (Kafka, RabbitMQ, SQS). Everything else is a variation on these two.
The trade-off is the whole answer. Synchronous is intuitive and gives an immediate result, but it creates temporal coupling: if the callee is down or slow, the caller is down or slow too. Asynchronous removes that coupling and absorbs load spikes, but you pay with eventual consistency, more moving parts, and the need to handle duplicate messages.
Interview note: Trap: "which is better?" Neither — the senior answer names the use case. Immediate answer needed → synchronous. Fire-and-forget, fan-out, or decoupling → asynchronous.
REST vs gRPC — when would you choose gRPC?
Choose gRPC for internal, high-throughput, low-latency service-to-service calls: it uses HTTP/2, binary Protobuf serialization, and a strongly typed contract, and it supports streaming. Choose REST/JSON at the edge and where universality, human-readability, and browser support matter.
gRPC's typed contract catches breakage at compile time and its binary format is smaller and faster than JSON. The cost is that it is less approachable — you cannot curl it as easily, and browser support needs a proxy. A common architecture is gRPC between internal services and REST exposed through the gateway.
Interview note: Follow-up: "what does Protobuf give you over JSON?" A schema, smaller payloads, faster (de)serialization, and generated client/server stubs — with schema evolution rules that keep old and new clients compatible.
How do you handle a synchronous call to a service that is slow or down?
Every remote call gets a timeout; a circuit breaker stops sending requests to a failing dependency; and where sensible, a fallback returns a degraded response instead of an error. An unbounded blocking call is the root cause of most cascading failures.
@CircuitBreaker(name = "catalog", fallbackMethod = "fallback")
public Product getProduct(String id) {
return webClient.get()
.uri("/products/{id}", id)
.retrieve()
.bodyToMono(Product.class)
.timeout(Duration.ofMillis(800)) // never wait forever
.block();
}
private Product fallback(String id, Throwable t) {
return Product.placeholder(id); // degrade gracefully
}
The point to make explicit: a circuit breaker without a timeout barely helps, because a call that never returns never trips the breaker.
What is a cascading failure, and how does async help avoid it?
One slow service causes callers to block and exhaust their thread pools, so they fail too, and the failure climbs the call graph. Timeouts, circuit breakers, and bulkheads contain it — and asynchronous messaging avoids it structurally, because the sender never blocks on the consumer.
If Orders publishes an event instead of calling Payments directly, a Payments outage means the event waits in the broker, not that Orders falls over. That resilience is the main reason teams push write-side workflows onto a message backbone.
Why is idempotency essential, and how do you implement it?
Because messaging is at-least-once and clients retry, the same operation can arrive twice — an idempotent operation makes duplicates harmless. Implement it with a unique key (request ID or event ID) that you record once processed, so a repeat is a no-op.
if (processed.putIfAbsent(event.getId(), Boolean.TRUE) != null) {
return; // duplicate delivery — already handled
}
applyPayment(event);
Interviewers use this to separate people who have run a broker from people who have only read about one. Exactly-once processing is achievable with idempotency plus dedup; exactly-once delivery across a network is essentially a myth.
How do retries make things worse, and how do you retry safely?
Naive immediate retries against an overloaded service multiply the load and cause a retry storm that deepens the outage. Retry only idempotent operations, use exponential backoff with jitter, cap the attempts, and let the circuit breaker cut retries off entirely when the dependency is clearly down.
Interview note: Trap: "add retries to make it reliable" — half right. Retries plus backoff plus idempotency plus a breaker is reliable; retries alone are a foot-gun.
How do you get data from another service without a shared database?
Call its API synchronously when you need it live, or subscribe to its events and maintain a local read model (data duplication) when you need it fast and can tolerate eventual consistency. You never reach into another service's database.
The event-carried-state-transfer approach — services publish their changes and interested services keep their own copy — removes runtime coupling entirely, at the price of managing stale data windows. Which you pick depends on freshness requirements.
How do you trace a request across several services?
Propagate a correlation/trace ID through headers so every service logs it, and use distributed tracing (Zipkin, Jaeger, OpenTelemetry) to see per-hop latency and causality. Without it, debugging a multi-hop call is grepping logs by timestamp and hoping.
What happens to in-flight requests during a deployment?
Use rolling deployments with health checks and graceful shutdown so a terminating instance drains in-flight requests before exiting, and clients retry idempotent calls against healthy instances. For async consumers, the broker simply redelivers unacknowledged messages to another consumer.
What interviewers really test
Communication questions are a proxy for one thing: do you design for the network being unreliable? Anyone can name REST and Kafka. The candidates who pass describe timeouts, backoff, idempotency, and eventual consistency without being prompted, because they have watched a missing timeout take down a system. Prepare to defend one synchronous choice and one asynchronous choice from real work.
Pair this with the API gateway questions, where edge communication and protocol translation come up, and the event-driven architecture set for the asynchronous half in depth. Ground the concepts with the microservices learning path, and use a focused mock interview to practice defending a communication design under follow-up pressure.
Frequently Asked Questions
What is the difference between synchronous and asynchronous communication?
When should microservices use messaging instead of REST?
Is REST or gRPC better for microservices?
Why is idempotency important in microservices communication?
What is a cascading failure and how do you prevent it?
Want to Build Your Career in Java Full Stack with AI?
Join CodeBegun and train with working industry engineers — Explore the Java Full Stack program

