Microservices Interview Questions and Answers (2026)
Microservices interview questions probe whether you understand the real tradeoffs — not just the buzzwords — behind splitting a system apart: distributed transactions, observability, and when a modular monolith is actually the better call. Here are the ones that come up most.
-
What are the real tradeoffs between a monolith and microservices?
A monolith is a single deployable unit — simpler to develop, test, and deploy initially, with easy in-process calls and straightforward transactions, but it couples teams and release cycles, and scaling means scaling the whole app even if only one module is hot. Microservices split the system into independently deployable services around business capabilities, enabling independent scaling, technology choice per service, and team autonomy (each team owns and deploys its service). The cost is real: network calls replace function calls (latency, partial failure), distributed data means no more simple ACID transactions across services, and you now need infrastructure for service discovery, observability, and deployment orchestration that a monolith never required. Microservices are a solution to organizational/scaling problems, not a default best practice — many successful systems start as a "modular monolith" and split only when a genuine scaling or team-boundary pain point appears.
-
What is service discovery and why is it needed?
In a dynamic environment where service instances scale up/down and get rescheduled with new IPs (common in Kubernetes or with autoscaling), hardcoding downstream service addresses breaks quickly. Service discovery solves this via a registry (e.g., Eureka, Consul, or Kubernetes' built-in DNS-based service discovery) that services register with on startup and that clients query to find healthy instances. There are two patterns: client-side discovery, where the calling service queries the registry directly and load-balances itself (e.g., Netflix Eureka + Ribbon), and server-side discovery, where a load balancer or the platform (Kubernetes Service/kube-proxy) handles routing transparently and the caller just hits a stable virtual address. Kubernetes has made server-side discovery via DNS the de facto default for most new systems, reducing the need for a separate discovery library in application code.
-
What problem does an API gateway solve?
An API gateway is a single entry point that sits in front of your microservices, handling cross-cutting concerns so individual services don't each reimplement them: routing requests to the right backend service, authentication/authorization, rate limiting, request/response transformation, TLS termination, and sometimes response aggregation for clients that need data from multiple services in one call. It also decouples external API contracts from internal service topology — you can refactor, split, or merge backend services without breaking external clients. The tradeoff is that the gateway becomes a critical single point of failure and potential bottleneck if not made highly available and horizontally scaled, and it can become a dumping ground for business logic if teams aren't disciplined about keeping it thin (routing/cross-cutting concerns only, not domain logic). Common implementations: Kong, Spring Cloud Gateway, AWS API Gateway, NGINX.
-
Synchronous REST vs asynchronous messaging between services — when do you use each?
Synchronous REST/gRPC calls are simple to reason about and give an immediate response, which fits request-reply use cases like "fetch this user's profile to render a page now." The downside is temporal coupling — if the downstream service is slow or down, the caller blocks or fails too, and chains of synchronous calls compound latency and failure probability. Asynchronous messaging (via Kafka, RabbitMQ, SQS) decouples services in time — the producer publishes an event and moves on, and consumers process it independently, which is a better fit for workflows that don't need an immediate response (order placed → send confirmation email → update inventory → notify shipping) and improves resilience since a consumer being temporarily down doesn't block the producer. Most real systems use both: sync calls for user-facing reads, async events for cross-service side effects and eventual state propagation.
-
What is the database-per-service pattern, and why use it?
Each microservice owns its own database (or schema) that no other service accesses directly — other services must go through the owning service's API to read or write that data. This preserves service autonomy: a team can change their schema, switch database technology (SQL vs NoSQL), or scale their database independently without coordinating with every other team, and it prevents the classic "shared database" anti-pattern where a schema change breaks five unrelated services because they all queried the same tables directly. The cost is that queries spanning multiple domains (e.g., "all orders with customer details") can no longer be a simple SQL JOIN — you need API composition, event-driven data replication (e.g., a read-optimized view built by consuming events), or CQRS. This is also precisely why distributed transactions/sagas become necessary — you can't just wrap a multi-service operation in one local DB transaction anymore.
-
Explain the saga pattern for distributed transactions.
Since a business transaction spanning multiple services can't use a single ACID database transaction, the saga pattern breaks it into a sequence of local transactions, each in one service, where each step publishes an event/message that triggers the next step. If a step fails, previously completed steps are undone via compensating transactions (not a true rollback, but a semantically inverse action — e.g., "cancel reservation" to compensate "reserve inventory"). There are two coordination styles: choreography, where services react to each other's events with no central controller (simple for few steps but hard to trace as complexity grows), and orchestration, where a central saga orchestrator explicitly tells each service what to do and handles failure/compensation logic centrally (more visibility, but the orchestrator becomes a critical component). Example: an order saga might reserve inventory → charge payment → confirm shipping, compensating by releasing inventory and refunding if a later step fails.
-
What is the circuit breaker pattern and why is it important?
A circuit breaker wraps calls to a remote service and tracks failures; when failures exceed a threshold, it "opens" the circuit and fails fast for subsequent calls (often returning a fallback) instead of letting every caller keep hitting a struggling or dead downstream service with full-timeout requests. This prevents cascading failure — a common microservices failure mode where one slow/dead service exhausts caller thread pools and connection pools, which then makes the caller slow, which cascades to its own callers. After a cooldown, the breaker moves to "half-open" and allows a trial request through to test if the downstream has recovered, closing the circuit again if it succeeds.
@CircuitBreaker(name = "inventoryService", fallbackMethod = "fallback") public Stock checkStock(String sku) { return inventoryClient.get(sku); } Stock fallback(String sku, Throwable t) { return Stock.unknown(); }Resilience4j and Hystrix (now in maintenance) are the common Java implementations.
-
What does eventual consistency mean and why do microservices accept it?
Eventual consistency means that after data is updated, dependent copies/views across the system will converge to a consistent state, but not instantaneously — there's a window where different services or read replicas may show stale or differing data. Microservices largely accept this because the CAP theorem forces a tradeoff: since services communicate over an unreliable network and each owns its own database, maintaining strict cross-service consistency (like a two-phase-commit distributed transaction) would sacrifice availability and add heavy coupling/latency, which defeats much of the purpose of splitting into services in the first place. In practice this means designing UX and business logic to tolerate a brief staleness window (e.g., "order placed" shows immediately, but the analytics dashboard updates a few seconds later via an event), and using idempotent consumers plus retries so that eventual convergence is reliable even with message redelivery or out-of-order delivery.
-
How do you version APIs between microservices without breaking consumers?
The main strategies: URI versioning (
/v1/orders,/v2/orders) is the simplest and most visible but can lead to duplicated route logic; header/media-type versioning (Accept: application/vnd.company.v2+json) keeps URLs stable but is less discoverable; and semantic/additive versioning where you avoid bumping a version at all by only ever adding optional fields and never removing or renaming existing ones ("tolerant reader" pattern), which works well for internal service-to-service contracts. Whichever scheme you pick, the more important discipline is backward-compatible evolution: never remove or repurpose a field consumers depend on without a deprecation window, use consumer-driven contract testing (e.g., Pact) to catch breaking changes before deploy, and run old and new versions side-by-side during a migration so consumers can move at their own pace rather than a forced simultaneous cutover. -
What makes observability harder in microservices than in a monolith?
In a monolith, a stack trace tells the whole story of a request. In microservices, a single user request can fan out across a dozen services, so you need three complementary pillars: distributed tracing (a trace ID propagated through every hop via context headers, e.g., OpenTelemetry/Jaeger/Zipkin, letting you see the full request path and where latency accumulated), centralized structured logging (aggregating logs from all service instances into one searchable place like ELK or Loki, correlated by that same trace/request ID), and metrics/dashboards (per-service latency, error rate, and saturation — the "RED" or "USE" methods — typically via Prometheus/Grafana). Without deliberately building this in from the start, debugging a production issue becomes guesswork — you literally cannot answer "why was this request slow" by SSHing into one box, because the answer is spread across services, each with its own logs and clock.
-
What is the strangler fig pattern and when would you use it?
Named after the strangler fig vine that gradually grows around and eventually replaces a host tree, this pattern is used to incrementally migrate a legacy monolith to microservices (or to a new architecture generally) without a risky "big bang" rewrite. You place a facade/proxy (often the API gateway) in front of the existing system, then extract one capability at a time into a new service, redirecting matching traffic to the new service while everything else still routes to the legacy monolith. Over time, more and more functionality is "strangled" out of the monolith until it can eventually be decommissioned entirely. This lets you ship incremental value, keep the system running throughout the migration, validate each extracted service in production before moving to the next, and reduces risk versus a rewrite where nothing ships until the entire new system is done and correct.
-
What are common microservices anti-patterns?
A few recurring ones: the "distributed monolith" — services are deployed separately but so tightly coupled (shared database, synchronous call chains, lockstep deployments) that you get all the operational complexity of microservices with none of the independence benefits. Overly fine-grained "nanoservices" that split too aggressively, so simple operations require chatty calls across many services, hurting both performance and cognitive overhead. Sharing a database across services (breaks autonomy, discussed earlier). Missing an API gateway, forcing every client to know every service's location. No circuit breakers or timeouts, letting one failing service cascade. Skipping distributed tracing/observability until it's an emergency. And premature adoption — teams reaching for microservices architecture before they have an organizational or scaling reason to pay its complexity cost, when a well-structured modular monolith would have served them better and longer.
Practicing for a real interview? Get live AI help with your answers, in the browser, free to start.
Start a free session