Top 40 Microservices Interview Questions and Answers (2026)
40 microservices interview questions with short answers and the trap behind each, plus seven patterns (retry, circuit breaker, idempotency, saga, outbox, rate limiting, correlation id) that run for real in a downloadable Java 25 file with its actual output.
A microservices architecture splits one application into small services that each own one business capability and their own data, run as separate processes, and talk over the network (HTTP, gRPC or messaging). Each service can be built, deployed and scaled independently — and every call between them can now fail, time out or arrive twice. Almost every interview question below is a variation on that last sentence.
The 40 answers are short on purpose: what to say in the first 30 seconds, then the trap the interviewer is usually probing for. The patterns that decide interviews — retry, circuit breaker, idempotency, saga, outbox, rate limiting, correlation ids — also run for real in one file you can download, and the console output further down is that file’s actual output.
Problem in a distributed system
Pattern
Question #
Remote call fails for a moment
Retry with backoff and jitter
25
Remote service is down, callers pile up
Timeout, circuit breaker, bulkhead
24, 26, 27
Client retries a payment
Idempotency key
19
One business action spans several services
Saga with compensation
16–17
DB write and event publish must both happen
Transactional outbox
18
One client floods the system
Rate limiting (token bucket)
29
Which request is this log line from?
Correlation / trace id
32
Versions used here. JDK 25.0.4.1 for the lab file (it uses ScopedValue, final in Java 25), Spring Boot 4.1.1 on Spring Framework 7.0.9 for the framework answers. Everything is checked against those releases on 22 October 2026; the lab itself has no dependencies and runs with java MicroservicesPatternsLab.java.
The architecture these answers assume
The shape every question below assumes: gateway in front, one database per service, a broker between them.
Read the diagram left to right. The gateway handles the edge; each business service owns its database; services talk synchronously when the caller needs an answer and through the broker when it does not. Every arrow is a place where a call can be slow, fail, or arrive twice — which is exactly what the resilience and consistency questions are about.
Seven patterns that actually run
Interviewers like candidates who can show a pattern, not just name it. MicroservicesPatternsLab.java is one file with no dependencies and a fake clock, so the output is deterministic: download MicroservicesPatternsLab.java and run java MicroservicesPatternsLab.java on JDK 25. The snippets below are abbreviated excerpts of that file; the output blocks are its real output.
Retry with exponential backoff and jitter
long ceiling = Math.min(cap, base << (attempt - 1)); // 100, 200, 400 ... capped
long wait = (long) (rnd.nextDouble() * ceiling); // full jitter: anywhere in [0, ceiling)
[1] retry with exponential backoff + full jitter (seeded Random)
attempt 1 failed (503 from inventory), ceiling=100ms, sleeping 73ms
attempt 2 failed (503 from inventory), ceiling=200ms, sleeping 149ms
attempt 3 failed (503 from inventory), ceiling=400ms, sleeping 139ms
attempt 4 -> 200 OK
The ceiling doubles (100, 200, 400 ms) but the actual wait is a random point below it: 73, 149, 139 ms. The jitter is what stops a thousand clients from retrying in the same millisecond. The lab prints the wait instead of sleeping, so it runs instantly.
Circuit breaker
String call(Supplier<String> remote) {
if (state == State.OPEN) {
if (now - openedAt < openMillis) return "REJECTED (circuit open, no call made)";
state = State.HALF_OPEN; // one probe call is allowed through
}
try {
String r = remote.get();
failures = 0; state = State.CLOSED;
return r;
} catch (RuntimeException e) {
failures++;
if (state == State.HALF_OPEN || failures >= threshold) { state = State.OPEN; openedAt = now; }
return "FAILED (" + e.getMessage() + ")";
}
}
Three timeouts open the circuit; calls 4 and 5 are rejected without touching the payments service, which is the point — no thread waits, and the dependency gets quiet time to recover. After the open period the breaker lets one probe through, it succeeds, and the state returns to CLOSED.
Idempotency key
String result = processed.computeIfAbsent(key, k -> { charges[0]++; return "charged#" + charges[0]; });
Four requests, two charges: the retried pay-9f2 returns the stored result. In production the key and result live in a database or Redis with an expiry, and the check-and-act must be atomic (a unique constraint is the simplest way).
Saga with compensation
Deque<Step> done = new ArrayDeque<>();
try {
for (Step s : steps) { s.run(); done.push(s); }
} catch (RuntimeException e) {
while (!done.isEmpty()) done.pop().undo(); // reverse order
}
[4] orchestrated saga: payment fails, earlier steps are compensated in reverse
do createOrder
do reserveStock
!! chargePayment declined
undo reserveStock
undo createOrder
Payment declines, so the saga undoes the completed steps in reverse order. Note that compensation is a new business action (cancel the order), not a database rollback — the order existed, and other services may have seen it.
Transactional outbox and an idempotent consumer
orders.add("order-42"); // one local transaction:
outbox.add(new OutboxRow(1, "OrderCreated(order-42)", false)); // state change + event row
...
if (seen.add(eventId)) applied++; // consumer-side dedupe
[5] outbox: state change and event are ONE local transaction; relay is at-least-once
committed: orders=[order-42] outbox=1 row(s)
relay published, then crashed before marking the row sent
broker now holds 2 copies of the same event
consumer dedupes by event id: applied 1 time(s)
This is the failure the outbox is designed around: the relay crashes after publishing but before marking the row sent, so the broker gets the event twice. At-least-once delivery is the price of never losing an event, and the consumer’s dedupe turns it into effectively-once.
A bucket of five allows a burst of five, rejects the next two with 429, then admits one request per 500 ms (two tokens per second). After a quiet period it refills, capped at capacity — the 3000 ms request sees a full-to-two bucket, never more than five.
[7] correlation id via ScopedValue across three hops
[trace-7f3a] gateway received request
[trace-7f3a] orders calling inventory
[trace-7f3a] inventory reserved 2 units
The id is bound once at the edge and read three method calls deeper without being passed as a parameter. ScopedValue is final in Java 25 and is the virtual-thread-friendly replacement for ThreadLocal here. Across processes you still propagate it in a header.
Each question is an accordion — open the ones you want to drill. They are grouped: fundamentals (1–8), communication and APIs (9–14), data and consistency (15–22), resilience (23–29), delivery, observability and security (30–36), and design judgement (37–40).
1. What are microservices, and how are they different from a monolith?
Independently deployable services, each owning one business capability and its own data, communicating over the network. A monolith ships as one unit with one database. The honest trade: microservices buy independent deployment and scaling and pay for it with network failure modes, distributed data and operational overhead. Mention that a modular monolith is often the right starting point.
2. When should you NOT use microservices?
When the team is small, the domain is not yet understood, or nobody can run the platform (CI/CD, observability, Kubernetes). Wrong service boundaries are far more expensive to fix across processes than across packages. Start with a modular monolith and extract a service when a module has a different scaling, release or ownership need.
3. How do you decide service boundaries?
Along business capabilities and bounded contexts (Domain-Driven Design), not technical layers. A good boundary lets one team change a service without coordinating, and keeps data that changes together inside one service. Smell: two services that must always be deployed together are one service.
4. What is a bounded context?
A DDD term: the area in which a domain model and its vocabulary are consistent. “Customer” means different things to billing and to shipping, so each gets its own model rather than a shared one. Bounded contexts are the best starting candidates for service boundaries.
5. What is the database-per-service rule and why does it matter?
Each service owns its schema and nobody else reads its tables. Sharing a database couples release cycles and schemas and turns a “service” into a distributed monolith. The cost is that joins across services disappear, which is why you need APIs, events and read models (see CQRS).
6. Synchronous vs asynchronous communication — how do you choose?
Synchronous (REST, gRPC) when the caller needs the answer now and can tolerate the callee being up. Asynchronous (events, queues) when the work can happen later, when you want temporal decoupling, or when many consumers care. Chains of synchronous calls multiply latency and failure probability: five services at 99.9% each are about 99.5% together.
7. What is an API gateway and what belongs in it?
A single entry point that handles cross-cutting edge concerns: routing, authentication, TLS, rate limiting, request logging. It should not contain business logic. In Spring land this is Spring Cloud Gateway; the Zuul migration guide covers the move.
8. What is service discovery?
The mechanism that lets services find each other when instances come and go. Client-side (a registry like Eureka or Consul plus a load-balancing client) or server-side (a load balancer, or Kubernetes Services and DNS). On Kubernetes you usually get this for free and skip the registry.
9. REST vs gRPC vs messaging between services?
REST for public and simple internal APIs, easy to debug and cache. gRPC for internal high-throughput calls with a strict contract (Protobuf, HTTP/2, streaming). Messaging (Kafka, RabbitMQ) for events and work that need not be answered immediately. Many systems use all three.
10. How do you version a service API without breaking consumers?
Prefer backwards-compatible changes (add fields, never remove or repurpose). When you must break, run two versions side by side (path or header) and retire the old one on a published date. Consumer-driven contract tests tell you who actually depends on what.
11. What are consumer-driven contract tests?
The consumer publishes the requests and responses it relies on; the provider build verifies them (Pact or Spring Cloud Contract). It catches breaking changes without standing up the whole system, which is the part of the test pyramid that integration tests do badly.
12. How should services report errors over HTTP?
With a consistent machine-readable body. Spring Framework 7 and Boot 4 support RFC 9457 ProblemDetail directly — see ProblemDetail and global exception handling. Use status codes honestly: 4xx the caller can fix, 5xx it cannot, 429 with Retry-After for throttling.
13. What is the BFF (backend-for-frontend) pattern?
A thin gateway per client type (web, mobile) that aggregates downstream calls and shapes responses for that client. It stops one general API from bending to every client and keeps chatty mobile round trips down.
14. How do services authenticate each other?
Short-lived tokens (OAuth 2 client credentials, or the user’s JWT relayed) and mutual TLS between services. The user token should carry the end-user identity downstream so authorization decisions are made close to the data. See token relay and mTLS.
15. Why can’t you use a distributed ACID transaction (2PC) across services?
Two-phase commit needs every participant reachable and holds locks across the network, so availability drops to the product of all participants’ availability and latency to the slowest one. Message brokers and most NoSQL stores do not take part in XA anyway. Microservices replace one atomic transaction with a sequence of local transactions plus compensation.
16. What is the saga pattern?
A business transaction as a series of local transactions; if step N fails, the earlier steps are undone by compensating transactions. Orchestration has a central coordinator telling each service what to do; choreography has services reacting to each other’s events. Full comparison with Kafka: saga: orchestration vs choreography.
17. Orchestration vs choreography — which would you pick?
Choreography is simple for two or three steps and loosely coupled, but the flow is implicit and hard to follow as it grows. Orchestration makes the flow explicit and testable in one place at the cost of a coordinator that must not become a god service. Rule of thumb: choreography until you cannot draw the flow on one slide.
18. What is the transactional outbox pattern?
Write the state change and the event to an outbox table in the same local transaction; a relay publishes rows to the broker afterwards. This removes the dual-write problem (DB commit succeeds, publish fails, or the reverse). The relay is at-least-once, so consumers must be idempotent. Spring Modulith ships this: event publication registry.
19. How do you make an operation idempotent?
Have the client send an idempotency key; the server stores the key with the result and returns the stored result for a repeat instead of acting again. The lab above shows four requests producing two charges. Natural idempotency (PUT, “set status to PAID”) is better when you can model it that way.
20. What is CQRS and when is it worth it?
Separate the model you write to from the models you read from, so reads can be denormalised views built from events. It pays off when reads and writes have very different shapes or load, or when a view needs data from several services. It costs eventual consistency and extra moving parts; see CQRS without a framework.
21. What is eventual consistency and how do you explain it to a product owner?
After a write, different parts of the system may briefly disagree, and they converge without anyone doing anything. Concretely: an order is placed, the confirmation page shows it, the warehouse dashboard shows it two seconds later. Design the UI for it (show “processing”, not a lie).
22. How do you query data owned by several services?
API composition (a gateway or aggregator calls each service and merges, fine for small result sets) or a read model fed by events (a view table or search index, better for large or filtered queries). Never reach into another service’s database.
23. What failure modes does a remote call add compared to a method call?
It can be slow (not just fail), fail after the work was done, fail halfway, or be delivered twice. Slow is the worst: threads block waiting, pools fill up, and the caller fails because of a callee that is merely degraded. This is why timeouts come first.
24. Why must every remote call have a timeout?
Without one, a hung dependency holds a thread (or a connection) forever and the failure spreads upwards. Set the client timeout shorter than the caller’s own deadline, so total latency stays bounded across a chain.
25. How should retries be done?
Only for idempotent operations or when an idempotency key is sent; with exponential backoff, a cap, a limited attempt count and jitter so thousands of clients do not retry in lockstep. Retrying inside several layers multiplies load (3 × 3 × 3 = 27 calls), so retry at one level. Spring Framework 7 has @Retryable built in: @Retryable and @ConcurrencyLimit.
26. How does a circuit breaker work?
It counts failures. After a threshold it opens and rejects calls immediately without touching the dependency, giving it room to recover. After a wait it goes half-open and lets a probe call through: success closes it, failure re-opens it. Resilience4j is the usual library: circuit breaker with Spring Boot.
27. What is a bulkhead?
Isolating resources per dependency (a separate thread pool, semaphore or connection pool) so a slow dependency can exhaust only its own share. Named after ship compartments. Spring 7’s @ConcurrencyLimit is a lightweight version.
28. What is a fallback and what are its dangers?
A degraded answer when the real one is unavailable — cached data, a default, an empty list. Dangerous when it hides the outage (stale prices shown as current) or when the fallback itself calls another fragile service. Make fallbacks visible in metrics.
29. How do you protect a service from being overloaded?
Rate limiting at the edge (token bucket or sliding window), load shedding when saturated (reject early with 429/503 rather than queue forever), bounded queues and per-dependency concurrency limits. The lab shows a token bucket allowing a burst of five, rejecting two, then refilling.
30. Why does every service need health checks?
So the platform can tell “process is running” (liveness) from “can serve traffic now” (readiness) and stop routing to or restart the right pods. Boot 4 exposes both groups; for the pitfalls with CPU limits and graceful shutdown see Kubernetes probes and graceful shutdown.
31. What are the three pillars of observability?
Logs (what happened), metrics (how much, how fast) and traces (the path of one request across services). Micrometer plus OpenTelemetry give you all three in Spring Boot 4; setup in Micrometer and OpenTelemetry guide.
32. How do you trace a request across services?
Generate a trace id at the edge, propagate it on every call (the W3C traceparent header, or a message header for events) and put it into every log line. In-process, context must travel with the work; the lab uses a ScopedValue so no method takes the id as a parameter.
33. Where should configuration and secrets live?
Configuration outside the artifact (environment variables, ConfigMaps, a config server) so one image runs in every environment; secrets in a secret manager or Kubernetes Secrets, never in the repo or image. Rotate them without a rebuild.
34. What is a deployment strategy for zero downtime?
Rolling update (replace instances gradually, needs readiness probes and backwards-compatible schemas), blue-green (switch traffic between two full environments) or canary (send a small percentage to the new version first). Database changes use expand-and-contract so old and new code both work during the roll.
35. How do you do database migrations when several versions run at once?
Expand and contract: first add the new column (old code ignores it), then deploy code that writes both, backfill, deploy code that reads the new one, and only then drop the old column. Never ship a migration that breaks the version still running.
36. How do you secure internal service traffic?
Zero-trust inside the network: mTLS between services, authorization on every service not just the gateway, least-privilege tokens with a narrow audience, and network policies. “It is inside the cluster” is not a security control.
37. Why is a shared library between services a risk?
It couples release cycles: change the library and every service must upgrade together, which is the distributed-monolith failure. Share contracts (schemas, API definitions) and small stable utilities, not domain logic.
38. How would you migrate a monolith to microservices?
Strangler fig: put a routing layer in front, move one capability at a time behind it, and delete the old code as each one proves itself. Start with a module that has a clear boundary and a real reason to be separate; avoid the big-bang rewrite. Enforce module boundaries first with Spring Modulith.
39. What is the Spring stack for microservices in 2026?
Spring Boot 4 for each service, Spring Cloud Gateway at the edge, Spring Kafka for events, Resilience4j (or Spring 7’s built-in retry and concurrency limit) for resilience, Micrometer plus OpenTelemetry for observability, Spring Security for tokens. Kubernetes usually replaces Eureka and the config server.
40. How do you test a microservice?
Mostly unit and slice tests; a few integration tests against real dependencies in containers; contract tests for every service boundary; and a handful of end-to-end checks on critical paths. Avoid a large end-to-end suite — it is slow, flaky and tells you where it broke last.
Common interview questions
Follow-ups that interviewers use after the questions above:
“Walk me through what happens when the payment service is down during checkout.” (timeout, circuit breaker, saga compensation, what the customer sees)
“How do you guarantee an event is published exactly once?” (you do not — at-least-once plus idempotent consumers; see Kafka exactly-once)
“How would you split this monolith?” (strangler fig, one bounded context at a time)
“How do you debug a slow request that touches six services?” (trace id, spans, the slowest span)
“Why not just share the database?” (coupling, schema lock-in, the distributed monolith)
Distributed monolith. Services that share a database or must deploy together get the costs of microservices and none of the benefits.
No timeouts. The default of “wait forever” turns one slow dependency into a full outage.
Retrying non-idempotent calls without an idempotency key — double charges, duplicate orders.
Retries at every layer. Three layers of three retries is 27 calls against an already struggling service.
Dual writes. Saving to the database and publishing to the broker as two separate steps, then losing events when one fails. Use the outbox.
Splitting too early. Drawing service boundaries before the domain is understood; extracting nanoservices.
Treating the happy path as the design. Interviewers want to hear what happens on failure, not just on success.
Should you even build microservices? If one team owns the product and releases together, probably not yet. The patterns above (timeouts, idempotency, outbox) are worth learning regardless, because a modular monolith that calls an external payment provider needs most of them too. See the modular monolith vs microservices decision framework.
No Comments yet!