Microservices Interview Questions
Practical microservices interview questions on discovery, tracing, communication trade-offs, and deployment independence.
A set of practical microservices interview questions — the kind that probe whether you've actually built and operated one of these systems, not just diagrammed one.
Q: What mechanisms exist for service discovery, and when would you pick one over another?
On Kubernetes, DNS-based discovery is usually enough — a Service object gives you one stable name and the platform load-balances behind it, with no client-side discovery code at all. Outside Kubernetes, or when a client needs to see and choose among individual healthy instances itself, a registry like Consul or Eureka is used instead: instances self-register with a heartbeat/health check, and callers query the registry for currently-healthy instances before calling one directly.
Q: What is a correlation ID and why is it essential in a microservices architecture? It's a unique identifier generated once per incoming request and propagated through every header (for synchronous calls) or event field (for asynchronous ones) to every service that touches that request. Without it, a failing request's logs are scattered across many services' log files with no way to tie them together; with it, filtering every log store for one ID reconstructs the full cross-service timeline of what happened.
Q: What's the fundamental trade-off between synchronous and asynchronous communication between services? Synchronous calls (REST/gRPC) give an immediate answer but create temporal coupling — the caller blocks on, and fails alongside, a slow or unavailable callee. Asynchronous calls (events) remove that coupling — the producer doesn't wait, and new consumers can be added later with zero changes to it — at the cost of no immediate answer, eventual consistency, and a harder debugging story across the resulting event chain.
Q: What does "independent deployability" actually require in practice, beyond separate codebases? Each service needs its own build and deploy pipeline, a versioned API contract, and backward compatibility during a deprecation window, because callers can never be assumed to upgrade in lockstep. Skip any of these and deploying one service still forces coordination with others — which is exactly the coupling splitting the system up was meant to remove.
Q: What's the difference between a liveness probe and a readiness probe, and why does mixing them up cause incidents? Liveness asks "is this process still working, or should it be restarted?" Readiness asks "is this instance ready to accept traffic right now?" Using one check for both means a temporarily slow dependency (a sluggish database, say) gets a perfectly healthy process killed and restarted, instead of simply being pulled from the load balancer until the dependency recovers on its own.
Q: Why is a database shared between two "independent" microservices considered an anti-pattern? It reintroduces tight coupling through the schema itself — either service's migration can silently break the other, and nothing stops a write from bypassing the owning service's business logic entirely. It also removes each service's ability to scale, choose its own database technology, or evolve its data model independently, which was the actual point of splitting the system up in the first place.
Q: Why doesn't two-phase commit work well as a distributed transaction mechanism across microservices? It requires every participant to hold locks for the entire duration of a coordinator's round trip, so a slow or crashed coordinator can leave services blocked indefinitely, and it requires every participant to speak the same distributed-transaction protocol — something most third-party APIs (a payment gateway, for instance) simply don't support. The Saga pattern avoids both problems by using a sequence of independent local transactions with explicit compensating actions instead of a single cross-service atomic commit.
Q: What's the difference between choreography and orchestration as ways of implementing a saga? In choreography, each service reacts to events published by others and decides its own next step — no central coordinator, but the overall flow only exists implicitly, spread across every service's event handlers. In orchestration, one central component issues a command for each step and explicitly runs the correct compensations in reverse order if a step fails — the whole flow is visible in one place, at the cost of every step now depending on that orchestrator.
Q: What has to be true of a compensating action for a saga to be safe to retry? It must be idempotent — a compensation can run more than once (after a timeout, or a duplicate failure event), so releasing already-released stock or refunding an already-refunded payment has to be a safe no-op rather than an error or a double-refund. Saga steps should also generally be ordered so a genuinely irreversible action (physically shipping a package, a non-retractable email) happens last, after every reversible step has already succeeded.
Q: What problem does an API gateway's request aggregation solve, and what's the trade-off of adding a BFF (Backend for Frontend) on top of it? Aggregation lets the gateway make several backend calls in parallel and hand a client back one combined response, instead of the client making multiple round trips and needing to know which services own what data. A BFF takes this further by giving each client type (mobile, web) its own dedicated gateway tailored to that client's exact needs — worth the extra services to build and operate once different clients' needs have genuinely diverged, but unnecessary complexity to add before that point.
Q: Why is contract testing particularly valuable in a microservices architecture, compared to relying on end-to-end tests alone? A consumer-driven contract test lets a provider service verify, in its own CI pipeline, that it still satisfies every consumer's recorded expectations — catching a breaking API change at the speed and isolation of a fast test, without needing every consumer service actually running. End-to-end tests catch the same class of bug too, but only by standing up every participating service at once, which is slow, comparatively flaky, and far more expensive to run on every change.
Q: What's the difference between the bulkhead pattern and a circuit breaker, and why do you often need both? A bulkhead isolates the resources (threads, connections) used to call one dependency, so a slow dependency can only exhaust its own allocation instead of starving the whole service — it limits how much damage a struggling dependency can do. A circuit breaker tracks failure rate and stops calling a dependency altogether once it's clearly failing, protecting both the caller (no more wasted latency on doomed calls) and the failing dependency itself (no more retry traffic slowing its recovery). They solve different failure modes and are normally used together: the bulkhead caps concurrent exposure, the circuit breaker cuts off calls entirely once failures cross a threshold.
Q: Token bucket, sliding window, and fixed window rate limiting — what's the practical difference, and which would you default to?
Fixed window resets a simple counter at clock-aligned intervals, which is cheap but allows a client to burst up to roughly double the intended rate right across a window boundary. Sliding window avoids that boundary problem by weighting or tracking requests continuously rather than resetting sharply, at the cost of more state per client. Token bucket refills tokens at a steady rate and lets requests consume them, naturally allowing a controlled burst up to the bucket's capacity while still enforcing a hard steady-state rate — it's the default in most modern rate-limiting libraries (Bucket4j, ASP.NET Core's TokenBucketRateLimiter, Go's golang.org/x/time/rate) specifically because that burst tolerance matches real client traffic better than a hard reset boundary does.
Q: Where should rate limiting be enforced — the API gateway, the individual service, or both? The gateway is the natural default: enforcing a client's overall budget once, at the edge, is simpler to operate than repeating the same logic in every service, and it's where other cross-cutting concerns (auth, TLS termination) already live. But a gateway-only limit doesn't protect a service from an internal caller that reaches it directly, bypassing the gateway, and it can't express an endpoint-specific limit tighter than a client's overall budget. Most real systems do both: a coarse per-client limit at the gateway, and a finer per-endpoint limit in the owning service itself as defense in depth.
Q: How does mutual TLS (mTLS) differ from an API key or a JWT for service-to-service authentication, and when would you reach for it? An API key or JWT is an application-level credential sent as a header — simple to implement, but the credential itself is directly usable by anyone who obtains it, and revocation means a database lookup, a blocklist, or waiting for expiry. mTLS authenticates both sides during the TLS handshake itself, before any application data is even sent, and the private key backing a certificate never travels over the wire — intercepting traffic doesn't hand over a usable credential the way intercepting a header does. mTLS costs more to set up (a CA, certificate issuance, rotation), which is why it's usually reserved for zero-trust internal networks and regulated environments, and why it's typically rolled out fleet-wide via a service mesh (Istio, Linkerd) rather than hand-implemented per service.
Q: Why are short-lived, dynamically generated secrets considered safer than a static long-lived password, and what does rotating a static secret actually require? A static secret is a liability that only grows over time — every engineer who ever had access, every log line that might have captured it, every backup that included it, remains a way it could have leaked, with no way to know if it already has. Rotating it manually means coordinating an update across every service using it, which is exactly why rotation is so often deferred indefinitely. A system like Vault's dynamic database secrets engine instead issues a unique credential per lease that expires automatically after a set period, so a leaked value is only useful until it expires and rotation happens continuously as a side effect of normal operation — at the cost of clients needing to renew or re-fetch a lease rather than treating a credential as permanent.