Microservices interview questions in 2026 are scenario-based: interviewers test whether you can reason about distributed transactions, message delivery semantics, and failure handling - not whether you can recite “small, independently deployable services.” The definitions are free. The judgment is what gets graded.
I’ve sat on both sides of these interviews, and microservices is where inflated resumes go to die. Everyone has “microservices experience” on paper. Then the interviewer asks what happens when a payment message gets processed twice, and the room goes quiet. This article is 30 .NET microservices interview questions in the format that actually gets used: a real scenario, how I’d answer it, the answer that gets you rejected, and the follow-up the interviewer chains next.
Everything here is current for .NET 10 in 2026 - the resilience answers use Microsoft.Extensions.Http.Resilience rather than raw Polly, the gateway answer is YARP, and there’s a question on Aspire because interviewers have started asking about it. Let’s get into it.
What Makes a Good Microservices Answer?
A strong microservices answer names the trade-off, then picks a side for a stated context. Not “you can use sagas for distributed transactions” but “I’d use an orchestrated saga here because the flow has four steps and compensation logic, and I want one place to see why an order is stuck.” Interviewers are listening for evidence you’ve operated these systems, not just drawn them on a whiteboard.
The 30 questions below are grouped into 6 categories that mirror how an architecture round actually flows. Jump to the one you want to sharpen:
This page is part of my .NET interview prep series. For the broader set, see the .NET interview questions hub, the ASP.NET Core interview questions for the runtime each service is built on, and the .NET Web API interview questions for the API layer every service exposes.
Architecture and Boundaries
Almost every microservices round opens here - not with definitions, but with “justify the architecture.” The interviewer wants to know if you chose microservices or just inherited the hype.
Q1. Monolith vs Microservices - What Does the Trade Actually Cost?
Junior
Microservices trade in-process simplicity for operational complexity. A monolith gives you one deployment, one database, real ACID transactions, and stack traces that cross the whole feature. Microservices give you independent deployment and scaling per service - and in exchange, every call between services becomes a network call that can fail, every transaction becomes eventual consistency, and every debugging session spans multiple logs.
The framing I use in interviews: microservices solve an organizational problem first and a technical one second. They let many teams ship independently without stepping on each other. If you don’t have that problem, you’re paying the distributed-systems tax for nothing.
Red flag answer: “Microservices are more scalable, so they’re better.” - A monolith behind a load balancer scales fine for most workloads. Unqualified “better” signals you’ve read articles, not operated systems.
Follow-up: “At what team size did the monolith actually start hurting?”
When to Use Microservices (and When Not To)
The honest decision framework behind this question - the signals that justify splitting, and the costs everyone discovers six months in.
Q2. Your CTO Wants to Split the Monolith. Argue Against It.
Mid
This question inverts the usual script to test whether you actually understand the costs. My answer: I’d ask what problem we’re solving. If deploys aren’t blocked on other teams, no component has independent scaling pressure, and the domain boundaries are still shifting, splitting buys nothing and costs plenty - network failure modes, eventual consistency, CI/CD per service, and on-call complexity that multiplies with every box on the diagram.
The strongest signal you can send: a modular monolith is the default, microservices are the escalation. Well-enforced module boundaries inside one process give you most of the coupling benefits with none of the network. Extract a service later when a specific module proves it needs independent scale or deployment - the seams are already there.
Red flag answer: “Sure, microservices are the modern standard.” - Agreeing without interrogating the motive is exactly what this question filters for.
Follow-up: “What evidence would change your mind and justify the split?”
Q3. How Do You Decide Where One Service Ends and Another Begins?
Mid
Along business capability boundaries, not technical layers. A service should own a capability end to end - Ordering, Inventory, Payments - including its data. The test I apply: can this service accept a request and answer it using only its own database? If two “services” constantly need each other’s tables to do anything, that’s one service wearing two costumes.
Domain-Driven Design’s bounded contexts are the formal tool: where the language changes, services split. “Product” means price and description to Catalog, weight and location to Shipping - that language break is a boundary. What I explicitly avoid: splitting by entity (“UserService, OrderService”) or by layer (“ApiService, DataService”), both of which guarantee chatty, coupled services.
Red flag answer: “One service per database table.” - Entity-based splitting creates a distributed monolith where every feature touches five services.
Follow-up: “You got a boundary wrong and two services are chatty. How do you fix it?”
Q4. How Would You Migrate a Monolith to Microservices Without Stopping Delivery?
Senior
The strangler fig pattern: put a routing facade in front of the monolith, then peel off one capability at a time. Pick the first candidate by pain, not by ease - the module with the most independent deployment pressure or scaling need. Build it as a service, route its traffic through the facade, keep the monolith handling everything else, and repeat. The monolith shrinks release by release, and at every step you have a working system you can stop at.
The two details interviewers listen for: data comes last - the new service initially reads from the monolith’s database, and only gets its own store once the write paths have moved - and the facade is temporary infrastructure you plan to delete. In .NET, YARP is a natural fit for that facade: routing rules per path, in config.
Red flag answer: “Rewrite it service by service in a long-running branch and cut over at the end.” - Big-bang cutovers are how two-year rewrites die; the interviewer has seen it happen.
Follow-up: “Your first extracted service needs data the monolith still owns. What now?”
Q5. Should Two Services Share a Library? Where’s the Line?
Mid
Shared contracts are fine; shared domain logic is a trap. A NuGet package holding message definitions and DTOs is healthy - both sides need to agree on the shape of an event anyway, and a package makes the agreement explicit and versioned. The trap is the Company.Common package that accumulates business rules, base entities, and helpers: every change to it forces coordinated redeploys across services, which quietly rebuilds the coupling you split the monolith to escape.
My rule: a shared library must be stable, generic, and owned like a public API - versioned, backward compatible, consumed at whatever version each service pins. The moment something in it changes for one service’s business reason, it belongs to that service, duplicated if necessary. Some duplication between services is cheaper than coupling - a sentence that surprises juniors and lands well with interviewers.
Red flag answer: “Share as much as possible - DRY.” - DRY inside a service, yes. Across service boundaries, DRY recreates the distributed monolith.
Follow-up: “Your shared contracts package needs a breaking change. Walk me through shipping it.”
Communication Between Services
The category where theory meets latency. Expect at least one “which protocol and why” and one “what happens when the other service is down.”
Q6. How Do Microservices Communicate in .NET?
Junior
Two families: synchronous calls where the caller waits - HTTP APIs via HttpClient, or gRPC for service-to-service - and asynchronous messaging where the caller publishes to a broker like RabbitMQ or Azure Service Bus and moves on. The decision driver: does the caller need the answer right now to proceed? A checkout needs the payment result synchronously. Sending the confirmation email should never block checkout - that’s a message.
The point I always add: every synchronous call is temporal coupling - both services must be up at the same moment. Chains multiply that fragility, because availability compounds: five services at 99.9% each give the chain roughly 99.5%. Async messaging cuts the chain; the broker holds the message until the consumer is ready.
Red flag answer: “They call each other’s APIs.” - True and incomplete; if messaging never comes up, neither has most of your real-world experience.
Follow-up: “Your sync call chain is four services deep. What do you do about it?”
Q7. REST, gRPC, or Messaging - How Do You Choose?
Mid
I answer this with the matrix I actually use:
| Criteria | REST/HTTP | gRPC | Messaging |
|---|---|---|---|
| Caller needs immediate result | ✅ | ✅ | ❌ |
| Public / browser-facing | ✅ Best | ⚠️ Needs gRPC-Web | ❌ |
| Internal service-to-service, high volume | ⚠️ Works | ✅ Best | ✅ For events |
| Fire-and-forget, fan-out to many consumers | ❌ | ❌ | ✅ Best |
| Survives consumer downtime | ❌ | ❌ | ✅ |
| Streaming | ⚠️ SSE only | ✅ Bidirectional | ✅ |
| Contract | OpenAPI | .proto (compile-time) | Message schema |
My defaults in 2026: REST at the edge (public clients, third parties), gRPC inside the mesh when latency or streaming matters - binary Protobuf over HTTP/2 is smaller and faster than JSON - and messaging for anything that represents a fact (“OrderPlaced”) rather than a request. The senior signal is naming each option’s failure mode: REST and gRPC fail when the callee is down; messaging fails later and quieter, via backlog growth and poison messages.
Red flag answer: “gRPC is faster, so use it everywhere.” - Speed isn’t the only axis; try debugging a binary protocol with curl, or streaming Protobuf to a browser without a proxy.
Follow-up: “When would you deliberately pick REST over gRPC for internal calls?”
Q8. What Does an API Gateway Do, and What Would You Use in .NET?
Mid
An API gateway is the single entry point in front of your services: it routes external requests to the right service, and centralizes the cross-cutting edge concerns - TLS termination, authentication, rate limiting, request logging - so thirty services don’t implement them thirty times. Clients see one stable surface; you’re free to reshape services behind it.
In .NET in 2026, my default is YARP (Yet Another Reverse Proxy) - Microsoft’s reverse-proxy library that runs as ASP.NET Core middleware, configured from appsettings.json or code, so your gateway is a .NET app you extend with the pipeline you already know. The caution I attach: a gateway is infrastructure, not a home for business logic. The moment it aggregates and transforms per client, that’s a different pattern - see the BFF question below.
Red flag answer: “It’s where you put shared logic between services.” - That’s how gateways become the new monolith; edge concerns only.
Follow-up: “How does the gateway know a service instance is healthy before routing to it?”
Rate Limiting in ASP.NET Core
One of the edge concerns a gateway centralizes - the built-in rate limiting middleware, its four algorithms, and where each fits.
Q9. How Does a Service Find Another Service’s Address?
Mid
Service discovery - the runtime answer to “where is Inventory right now?” when instances scale up, die, and move. In 2026 the common .NET answers are layered: on Kubernetes, DNS does it - you call http://inventory and the cluster’s DNS plus its service abstraction resolve to a healthy pod. Outside K8s, a registry like Consul plays the same role.
The .NET-specific piece worth naming: Microsoft.Extensions.ServiceDiscovery resolves http://inventory per environment - from configuration locally, from DNS in the cluster - so application code never hardcodes an address. Aspire wires the same package up by default, which is why services in an Aspire solution call each other by resource name and it just works.
Red flag answer: “Store the URLs in appsettings per environment.” - Static config can’t track instances that autoscale or get rescheduled; that’s the exact problem discovery exists to solve.
Follow-up: “Client-side vs server-side discovery - who does the resolving in each?”
Q10. You Changed an Event Schema and Broke a Consumer in Production. What Should Have Happened Instead?
Senior
Contracts between services are append-only until proven abandoned. The safe evolution rules: add optional fields freely, never rename or remove a field consumers might read, never change a field’s meaning. Consumers follow Postel’s law - read what you need, ignore what you don’t recognize (JsonSerializer does this by default; it’s one reason JSON events age well). A breaking change isn’t a change, it’s a new contract: publish OrderPlacedV2 alongside V1, migrate consumers at their own pace, retire V1 when its consumer count hits zero.
The senior addition: contract changes need visibility - schema validation in CI, or consumer-driven contract tests that fail the producer’s build when a consumer’s expectation breaks. “We told them in Slack” is not a versioning strategy.
Red flag answer: “Coordinate the deploy so producer and consumers ship together.” - Lock-step deploys mean you’ve built a distributed monolith; independent deployability was the goal.
Follow-up: “How do you even know which services consume your event?”
Q11. Your Mobile Team Complains Every Screen Needs Five API Calls. What Pattern Fixes This?
Senior
Backend for Frontend (BFF) - a thin aggregation service owned by the client team that composes calls to downstream services and returns exactly what the screen needs in one round trip. Mobile gets a BFF shaped for bandwidth and battery; web gets a different one shaped for its pages. The key ownership detail: the BFF belongs to the frontend team, because it changes at UI cadence, not service cadence.
Where I draw the line: a BFF composes and reshapes, it doesn’t own business rules. If a rule matters when the mobile app isn’t the caller, it belongs in a domain service. And you don’t need a BFF per client from day one - it earns its existence when clients genuinely diverge. GraphQL is an alternative answer here, trading REST aggregation for query flexibility at the cost of caching and complexity.
Red flag answer: “Make one big endpoint in the gateway that returns everything.” - Now the gateway holds per-client logic and every screen change is a gateway deploy coordinated across teams.
Follow-up: “BFF vs GraphQL for this - how would you decide?”
Data and Consistency
The category that decides senior offers. Distributed data is where “microservices experience” claims get stress-tested, because these problems don’t exist in a monolith.
Q12. Why Does Each Microservice Need Its Own Database?
Mid
Because shared databases recouple what you just decoupled. If two services touch the same tables, a schema migration in one can break the other, they contend for locks, and “independent deployment” dies - the database becomes the coordination point. Database-per-service means each service owns its schema, migrates on its own schedule, and even picks its own storage engine - orders in SQL Server via EF Core, a product catalog in a document store.
The honest second half interviewers wait for: what it costs. Cross-service joins are gone, so reporting needs a dedicated read store fed by events. Transactions across services are gone too, which is why the next three questions exist. Naming the costs unprompted is the difference between reciting the pattern and having lived with it.
Red flag answer: “They can share a database if you’re careful.” - The schema coupling is the problem, and “careful” doesn’t survive team turnover.
Follow-up: “The finance team needs a report joining orders and payments. Where does it run?”
Q13. Why Not Just Use Distributed Transactions Across Services?
Senior
Because two-phase commit (2PC) requires every participant to hold locks while waiting for the slowest or deadest member. A coordinator asks all services to prepare, then commit; between those phases, every database involved holds locks pending an answer. One slow participant stalls all of them; a crashed coordinator can leave participants blocked in-doubt. Across network boundaries at scale, that’s availability poison - which is why cloud-native message brokers and many modern data stores don’t offer it at all.
The accepted trade in microservices: give up atomicity across services, keep atomicity within each service, and make the cross-service flow eventually consistent via a saga - a sequence of local transactions with compensating actions for failure. Interviewers ask this precisely to hear you articulate that trade rather than reach for TransactionScope across HTTP.
Red flag answer: “Wrap both calls in a TransactionScope.” - Distributed ACID over HTTP doesn’t exist; the code compiles and the guarantee is imaginary.
Follow-up: “So an order spans Payments and Inventory. Walk me through the failure path without 2PC.”
Q14. Saga Pattern: Orchestration or Choreography - Which Would You Pick?
Senior
A saga replaces one distributed transaction with a chain of local ones, each with a compensating action to undo it if a later step fails. The two coordination styles differ in where the flow lives:
| Orchestration | Choreography | |
|---|---|---|
| Flow control | Central orchestrator sends commands | Each service reacts to events |
| Coupling | Services coupled to orchestrator | Services coupled to event contracts |
| Visibility | One place shows saga state | Flow is implicit across services |
| Adding a step | Change the orchestrator | Add a subscriber, touch nothing else |
| Failure handling | Orchestrator triggers compensations | Each service compensates on failure events |
| Fits best | Long flows with branching and compensation | Simple linear flows, fan-out reactions |
My position: orchestration once a flow passes about three steps or has real compensation logic. An order saga - reserve stock, charge payment, arrange shipping - needs one place that knows why an order is stuck, and an orchestrator gives you that state machine explicitly. Choreography shines at the edges: “when OrderShipped, send the email” doesn’t need a coordinator. Pure choreography’s failure mode is the flow nobody can see end to end - you find out when you’re reverse-engineering your own business process from five services’ logs at 2am.
Red flag answer: “Choreography, because loose coupling is best.” - Optimizing one axis while ignoring that the business flow just became invisible.
Follow-up: “A compensation itself fails halfway through. Now what?”
Q15. Your Service Saved the Order but the OrderPlaced Event Never Published. What Pattern Prevents This?
Senior
This is the dual-write problem: the database commit and the broker publish are two systems with no shared transaction, so a crash between them leaves an order saved that downstream services never hear about. The fix is the transactional outbox: write the event into an OutboxMessages table inside the same database transaction as the order. The commit is now atomic - either both the order and its pending event exist, or neither does. A relay (a background worker polling the table, or CDC tailing the log) then publishes each row to the broker and marks it sent.
The guarantee this buys is at-least-once: if the relay crashes after publishing but before marking sent, it publishes again on restart. That’s not a flaw - it’s the contract, and it’s exactly why consumers must be idempotent, which is the next question. In .NET, outbox support ships in messaging libraries like Wolverine and MassTransit (note: MassTransit v9 moved to a commercial license; v8 stays open source) rather than something you hand-roll in production.
Red flag answer: “Publish first, then save.” - Same race, opposite failure: now downstream reacts to an order that doesn’t exist.
Follow-up: “What does the outbox table’s growth look like, and when do you prune it?”
Q16. A Payment Message Got Processed Twice and a Customer Was Charged Twice. Walk Me Through Why, and the Fix.
Senior
Why it happens: brokers deliver at-least-once. The consumer processed the charge, then crashed before acknowledging; the broker, seeing no ack, redelivered - correctly. Retries, outbox relays, and network timeouts all produce the same effect. Duplicates aren’t an anomaly; they’re the delivery contract, and any consumer with side effects has to be idempotent: processing the same message twice must produce the same result as once.
The standard .NET shape - track processed message IDs in the same transaction as the work:
public async Task Handle(PaymentRequested message){ await using var tx = await db.Database.BeginTransactionAsync();
// Same table, same transaction as the side effect - this is the point var seen = await db.ProcessedMessages.AnyAsync(m => m.Id == message.MessageId); if (seen) return; // duplicate - ack and move on
await paymentGateway.ChargeAsync(message.OrderId, message.Amount); db.ProcessedMessages.Add(new ProcessedMessage(message.MessageId));
await db.SaveChangesAsync(); await tx.CommitAsync();}That’s the inbox pattern - the consumer-side mirror of the outbox. Where possible I also push idempotency into the operation itself: an idempotency key on the gateway call, or a natural-key upsert. This question, more than any other on this page, separates people who’ve run message consumers in production from people who’ve read about them.
Red flag answer: “Configure the broker for exactly-once delivery.” - Between two independent systems there’s no such guarantee; only exactly-once processing, which you build via idempotency.
Follow-up: “Where does that dedupe check break if you have multiple consumer instances?”
IHostedService vs BackgroundService in .NET 10
Message consumers in .NET run as hosted services - the lifetimes, DI scope handling, and shutdown behavior your consumer code lives inside.
Q17. A Product Manager Asks Why the Dashboard Shows an Order That Support Says Doesn’t Exist Yet. Explain Eventual Consistency Like You’d Explain It to Them.
Mid
The version I’d actually say: “The order system and the dashboard keep separate copies of the data, connected by messages. When an order is placed, the dashboard’s copy catches up moments later - usually milliseconds, occasionally longer if there’s a backlog. Both will always agree eventually; they’re just not guaranteed to agree at any single instant.”
The engineering half the interviewer is probing: you design UX around the lag instead of pretending it’s zero. Patterns I name: read-your-own-writes (route a user’s immediate read to the source, so they always see their own action), optimistic UI, and honest freshness labels on lagging views. The counter-judgment that earns senior credit: some reads must be strongly consistent - account balance before a withdrawal - and those you serve from the owning service, accepting the coupling deliberately.
Red flag answer: “Make everything strongly consistent so it can’t happen.” - That’s re-coupling every read to the write path; you’ve quietly argued for the monolith without noticing.
Follow-up: “Which reads in an e-commerce system genuinely need strong consistency?”
Q18. Where Does CQRS Actually Earn Its Keep in a Microservices System?
Mid
CQRS (Command Query Responsibility Segregation) separates the write model from the read model, and in microservices it earns its keep in one specific place: cross-service reads. Database-per-service killed your joins (Q12) - so the order-history page that needs orders, payments, and shipping data gets a dedicated read store, fed by events from all three services, shaped exactly like the page. Queries hit one denormalized store instead of fanning out three synchronous calls.
The read store is eventually consistent - it’s a projection built from events, so everything from Q17 applies. The caution I attach: within a single service, CQRS is often just ceremony. I reach for the full pattern when read and write shapes genuinely diverge or a read spans services - not by default.
Red flag answer: “We use CQRS everywhere with MediatR, that’s just how you structure services.” - Pattern-as-ritual; the follow-up “what problem was it solving?” has already ended this interview a few times.
Follow-up: “How does the read store get rebuilt if the projection logic had a bug for a week?”
CQRS and MediatR in ASP.NET Core
The full CQRS deep-dive - when the pattern pays for itself, when it's ceremony, and what the MediatR licensing change means for it.
CQRS Without MediatR in .NET
The same separation with zero libraries - handler interfaces, DI registration, and why the pattern never needed a package.
Resilience and Failure Handling
Every question in this category is some variant of one scenario: a dependency got slow or died, and your service either contained the damage or amplified it.
Q19. What Is a Circuit Breaker, and How Do You Add One in .NET Today?
Mid
A circuit breaker stops your service from hammering a dependency that’s already failing. After a threshold of failures it opens: calls fail immediately without touching the network, giving the dependency room to recover. After a break period it goes half-open, lets a probe call through, and closes again on success. The point isn’t retrying - it’s the opposite: knowing when to stop.
In .NET in 2026 you don’t wire this by hand. The standard resilience handler from Microsoft.Extensions.Http.Resilience (built on Polly v8) stacks the whole strategy - rate limiter, total timeout, retry, circuit breaker, per-attempt timeout - onto an HttpClient in one line:
builder.Services.AddHttpClient<InventoryClient>(c => c.BaseAddress = new Uri("http://inventory")) .AddStandardResilienceHandler();The distinction interviewers probe: retry handles transient blips; the breaker handles sustained failure. Retrying into a dead dependency is how one slow service takes down its callers.
Polly licensing update (July 2026). Polly now participates in the Open Source Maintenance Fee: from November 16, 2026, organizations earning at least US $20,000 from a product that uses Polly are asked to pay US $20 per month per organization. The source stays open under BSD-3, and individuals, hobbyists and smaller organizations owe nothing.
Microsoft.Extensions.Http.ResilienceshipsPolly.Coretransitively, so this applies even without a direct Polly reference.
Red flag answer: “We just retry three times.” - Retry without a breaker or timeouts is an amplifier: your retries are extra load on a service that’s already drowning.
Follow-up: “The circuit is open - what does your caller return to its caller?”
Q20. Your Retries Made an Outage Worse. What Was Missing?
Mid
Three usual suspects. Jitter: if a thousand callers retry on the same fixed schedule, they arrive as synchronized waves - a retry storm. Exponential backoff with randomized jitter spreads the load (the standard handler’s retries do this by default). Timeouts: without a per-attempt timeout, each “retry” waits on a hanging call for minutes, pinning threads and queueing work behind it. A budget: retries need a total-time cap and should respect signals like Retry-After - beyond that, fail fast and let the breaker take over.
And the prerequisite that makes retries safe at all: the operation must be idempotent. Retrying a timed-out POST that actually succeeded server-side is the double-charge scenario from Q16 - the timeout doesn’t tell you the work didn’t happen. Connecting retry-safety to idempotency unprompted is a strong senior signal.
Red flag answer: “Increase the retry count so it eventually gets through.” - More retries into a struggling dependency is more load; you’re DDoSing yourself politely.
Follow-up: “Which HTTP methods are safe to retry by default, and why?”
Q21. Messages Keep Failing and Redelivering Forever. What’s the Mechanism to Stop the Loop?
Senior
A dead-letter queue (DLQ). A malformed or unprocessable message - a poison message - fails, gets redelivered, fails again, forever. Meanwhile it blocks its queue and burns consumer cycles. The fix is bounded processing: after N failed attempts, the broker (or your messaging library) moves the message to a dead-letter queue, and the main queue flows on.
What separates candidates here is knowing a DLQ is a workflow, not a landfill. A growing DLQ needs an alert - it’s a symptom feed of real failures. Each message needs triage: transient cause → replay after the fix; bad data → correct and re-submit; obsolete → discard deliberately. The trap to pre-empt: replayed messages re-enter the consumer out of order, so idempotency (Q16 again) is what makes replay safe at all.
Red flag answer: “Catch the exception and log it, then ack the message.” - Silent message loss; the order that failed just vanished from the system with a log line as its tombstone.
Follow-up: “Who owns DLQ triage on your team, and how would they replay 500 messages safely?”
Q22. One Slow Downstream Service Took Your Whole Checkout Down. Reconstruct the Failure.
Senior
The classic cascade, and it’s a thread and queue problem, not just a latency problem. The recommendations service got slow. Checkout calls it synchronously with no timeout, so every checkout request parks waiting. Connection pools and server queues fill, and checkout - a healthy service - stops responding to everything, including requests that never needed recommendations. The failure travels up the call chain, one hop per repeat, until the whole system is down because one optional widget got slow.
Containment, in the order I’d apply it: timeouts (fail fast instead of parking), circuit breaker (stop calling what’s down), bulkheads (cap concurrent calls per dependency so one can’t exhaust shared pools), graceful degradation (checkout without recommendations is a fine checkout), and load shedding at the edge so overload gets rejected early. The deeper design lesson interviewers want named: an optional dependency was on the critical path - the real fix is making the call async or moving it out of the request entirely.
Red flag answer: “Scale out checkout.” - More instances waiting on the same slow dependency is more waiting, plus a bigger bill.
Follow-up: “How does a bulkhead actually cap the damage in .NET terms?”
Q23. Which Failures Should Users Never Notice? How?
Mid
Any failure of a non-critical dependency should degrade invisibly, not error visibly. Graceful degradation means designing the fallback at build time: recommendations down → serve bestsellers or nothing, checkout proceeds; pricing service slow → last-known price from cache with a bounded staleness window; search down → category browse still works. The page renders; a widget quietly didn’t.
The mechanism behind most fallbacks is a cache: a resilience pipeline catches the failure or open circuit and serves the cached value instead. The judgment layer is a tiering exercise done before the incident - for each dependency, decide whether it’s critical (payment: fail honestly), degradable (recommendations: fallback), or deferrable (email: queue it; the broker is itself a degradation mechanism). Skip that exercise and every dependency is implicitly critical - Q22 is your future.
Red flag answer: “Show a maintenance page if a service is down.” - You made every dependency critical, turning partial failure into total failure by policy.
Follow-up: “Serving stale prices from a fallback cache - what bounds would you put on that?”
Distributed Caching in ASP.NET Core with Redis
The cache layer most fallback strategies lean on - Redis setup, serialization, invalidation, and the failure modes of the cache itself.
Observability and Operations
Almost nobody preps this category, which is exactly why it differentiates. Running microservices is a debugging problem before it’s a design problem.
Q24. A Request Failed Somewhere Across Six Services. How Do You Find Where?
Mid
Distributed tracing. Each request gets a trace ID at the edge, propagated to every downstream call, and each unit of work records a span; the tracing backend reassembles them into one timeline showing where the request went, how long each hop took, and which hop threw. Without it, cross-service debugging is grepping six log streams and guessing at causality.
The .NET specifics worth naming: trace context propagates via the W3C traceparent header, forwarded automatically by HttpClient and ASP.NET Core - the runtime’s Activity API is the span model, and OpenTelemetry (the vendor-neutral standard for traces, metrics, and logs) exports it to any OTLP backend. Two habits complete the answer: put the trace ID on every log event so logs and traces cross-link, and remember the broker hop - context must ride in message headers, or every trace ends right where the interesting bug begins.
Red flag answer: “Check the logs of each service.” - With no shared correlation ID across six services, that’s archaeology, not debugging.
Follow-up: “How does the trace context survive a hop through RabbitMQ?”
Q25. Kubernetes Keeps Restarting a Healthy Pod. Which Probe Is Misconfigured, and What’s the Difference?
Junior
Someone wired a dependency check into the liveness probe. Liveness answers “is this process alive?” - it should test the process itself and almost nothing else, because a failing liveness probe gets the pod killed and restarted. Readiness answers “can this instance take traffic right now?” - it may check critical dependencies, and failing it just removes the pod from load balancing until it recovers. Put a database check in liveness, and a database blip makes Kubernetes restart-loop perfectly healthy pods - restarts that fix nothing, on every pod at once.
In ASP.NET Core this maps directly to tagged health checks: MapHealthChecks("/health/live") filtered to no checks or process-only, and /health/ready running the dependency checks - the readiness endpoint is where your EF Core, Redis, and broker checks belong.
Red flag answer: “Put all the checks on one endpoint and use it for both.” - That’s precisely the misconfiguration this scenario describes.
Follow-up: “Your service is ‘ready’ but a downstream dependency it needs is not. Should readiness fail?”
Health Checks in ASP.NET Core
The full implementation behind this answer - liveness vs readiness endpoints, dependency checks, tags, and Kubernetes probe wiring.
Q26. Twelve Services, Twelve Log Files. What Does Useful Logging Look Like Here?
Mid
Structured and centralized. Structured means log events are data, not sentences - "Order {OrderId} failed for {CustomerId}" with Serilog captures OrderId as a queryable property, so “all events for order 4711 across all services” is a filter, not a regex. Centralized means every service ships logs to one aggregator (Seq, Elasticsearch, Grafana Loki, or OpenTelemetry’s log pipeline) - because logs you have to SSH into a pod to read are logs you won’t read during an incident, and the pod that crashed took its files with it.
The properties that make it work: trace ID on every event (the cross-link to Q24’s traces), consistent service/environment enrichment, and consistent levels so a warning means the same thing everywhere. That last one is a team convention problem more than a tooling problem, which is exactly why interviewers ask.
Red flag answer: “Log to files and copy them off when something breaks.” - During a real incident you don’t know which of twelve services’ files to read, and the crashed pod’s files are gone.
Follow-up: “What would you log at warning vs error, and who gets paged for each?”
Structured Logging with Serilog in ASP.NET Core
Serilog setup, enrichment, request logging, and sinks - the logging foundation every service in the system should share.
Q27. You Can Only Afford a Handful of Alerts. What Do You Monitor Per Service?
Senior
The RED metrics per service: Rate (requests/sec), Errors (failure rate), Duration (latency, as percentiles - p95 and p99, never averages, because an average hides the one-in-twenty request taking four seconds). For queue consumers, the equivalent trio is throughput, failure rate, and queue depth with consumer lag - a growing backlog is the earliest signal a consumer is falling behind, long before anything errors.
The principle that shapes the alert list: alert on symptoms, not causes. Page on “checkout error rate above 2%” or “p99 above SLO” - things users feel. CPU spikes, pod restarts, and slow queries are diagnostic context, not pages; alert on every cause and on-call learns to ignore the pager inside a month. Symptom alerts also survive architecture changes - “checkout is slow” stays meaningful no matter how many services implement checkout.
Red flag answer: “Alert on CPU and memory thresholds for every pod.” - Cause-based noise; users don’t experience CPU, they experience errors and latency.
Follow-up: “Checkout’s error budget for the month is gone by the 10th. What changes?”
Security and Deployment
Short category, high filter value - service-to-service auth in particular is a question most candidates have never actually implemented.
Q28. How Does Service B Know a Request From Service A Is Legitimate - and Who the Original User Was?
Senior
Two separate identities, and naming that distinction is most of the answer. Service identity: does B trust that the caller is A? The standard mechanism is the OAuth client credentials flow - A authenticates to the identity provider (Keycloak, Entra ID, Duende IdentityServer), gets a token scoped for B, and B validates issuer, audience, and scopes like any JWT. In a service mesh, mTLS adds transport-level workload identity, encrypting and mutually authenticating every hop without touching app code.
User identity: when the operation is on behalf of a user, B needs to enforce user-level authorization, not just trust A. Propagating the user’s JWT on internal calls works, but the token’s audience says “gateway”, not “B”, and it’s over-scoped for the hop. The cleaner mechanism is token exchange (RFC 8693): A trades the user token for a new one, correctly audienced and down-scoped for B. The trap this exists to prevent: services blindly trusting a X-User-Id header, which any compromised container can forge.
Red flag answer: “Internal traffic is behind the firewall, so it’s trusted.” - The perimeter model; one compromised service then has free rein over every internal API. Zero-trust exists precisely because of this answer.
Follow-up: “What’s actually inside the token B receives after an exchange - audience, scopes, subject?”
JWT Authentication in ASP.NET Core
The token validation mechanics underneath this answer - issuers, audiences, signing keys, and the middleware that enforces them.
Q29. Why Are Containers the Default Deployment Unit for Microservices?
Mid
Because microservices multiply deployments, and containers make each one self-contained and identical everywhere. The image carries the runtime, dependencies, and app; the same artifact that passed CI runs in production, ending per-machine drift. On top of that uniformity, an orchestrator like Kubernetes does the operational heavy lifting per service - scheduling, autoscaling, rolling deploys, restart-on-crash, and the DNS-based discovery and probes from Q9 and Q25.
The .NET-specific detail that shows currency: the SDK builds container images natively - dotnet publish /t:PublishContainer produces an image with no Dockerfile, on secure defaults (runs as a non-root user since .NET 8; opt into chiseled base images to harden further). Worth knowing both ways: Dockerfiles when you need build-stage control, SDK publish for the common case.
Red flag answer: “Containers make apps faster.” - They’re about consistency and density, not speed; a container adds no performance.
Follow-up: “What belongs in the image vs what arrives at runtime - config, secrets, certs?”
Docker Guide for .NET Developers
Images, multi-stage builds, compose, and the container workflow underneath every microservices deployment answer.
Containerize .NET Apps Without a Dockerfile
The SDK's native container publish - hardened defaults, base image selection, and when you still want a Dockerfile.
Q30. What Problem Does Aspire Solve, and What Is It Not?
Mid
Aspire solves the local dev-loop problem microservices created: running one service is easy, running your system - four services, Postgres, Redis, RabbitMQ, correctly wired - used to mean a hand-maintained compose file and a wiki page. With Aspire, an AppHost project describes the system in C#: each service, each backing resource, and the references between them. aspire run (or dotnet run on the AppHost) starts everything, injects connection strings and service URLs automatically, and gives you a dashboard with logs, traces, and metrics for the whole system - OpenTelemetry wired in by default through its service defaults.
Just as important is what it’s not: Aspire is not a production runtime or an orchestrator - it doesn’t replace Kubernetes. It’s the development and composition layer; for deployment it generates artifacts (Compose files, Kubernetes manifests) for real targets. In interviews it doubles as a currency check on whether your microservices experience is from this decade’s tooling.
Red flag answer: “It’s Microsoft’s Kubernetes.” - It orchestrates your dev loop, not your production cluster; confusing the two suggests you’ve read the name and not run it.
Follow-up: “How does a service resolve the Redis connection Aspire wired up - what actually arrives in its configuration?“
5 Microservices Interview Mistakes That Get Developers Rejected
After enough of these interviews, the rejection patterns are boringly consistent:
- Defaulting to microservices. If your answer to “how would you build X?” starts with drawing seven services, you’ve failed the judgment test before the technical one starts. The strongest openers argue for the simplest architecture that fits.
- No answer for duplicate messages. At-least-once delivery and idempotency (Q16) is the filter question for event-driven experience. If you’ve never thought about it, every claimed year of “messaging experience” gets discounted to zero.
- Hand-waving consistency. Saying “eventual consistency” without being able to walk the failure path - what happens between the order saving and the projection updating - reads as vocabulary without experience.
- Resilience is “we retry”. Retries without timeouts, jitter, breakers, or an idempotency story (Q19-Q20) is the answer that made the outage worse. Interviewers who’ve lived a retry storm will chase this hard.
- Nothing to say about operations. If you can’t describe how you’d find a failing request across services (Q24) or what you’d alert on (Q27), the interviewer concludes someone else operated your microservices for you.
Key Takeaways
- Judgment beats vocabulary. The highest-signal answers argue against complexity: monolith by default, orchestration when flows get real, strong consistency where it’s genuinely needed.
- The data questions decide senior offers. No 2PC, sagas with compensation, the outbox for dual-writes, idempotent consumers - that chain (Q13-Q16) is the spine of every serious microservices interview.
- Every sync call is a liability you chose. Temporal coupling, cascading failures, and availability math all trace back to synchronous chains - know when a message breaks the chain.
- Observability is the sleeper category. Tracing, probes, structured logs, and symptom-based alerting are asked more every year and prepped by almost nobody.
- Show 2026 currency.
AddStandardResilienceHandler, YARP, SDK container publish, Aspire - current tooling names, dropped naturally, quietly separate you from candidates reciting 2019 blog posts.
.NET Interview Questions
300+ real .NET interview questions with answers, red flags, and follow-ups - C#, EF Core, ASP.NET Core, system design
What microservices topics are asked in .NET interviews?
Modern .NET microservices interviews focus on six areas: architecture judgment such as when not to use microservices and how to find service boundaries, communication choices between REST, gRPC, and messaging, distributed data patterns including sagas, the outbox pattern, and idempotent consumers, resilience patterns like circuit breakers, retries with jitter, and dead-letter queues, observability with distributed tracing and health probes, and service-to-service security. Questions are scenario-based rather than definitions, and the data consistency questions carry the most weight for senior roles.
How do you handle distributed transactions in microservices?
You avoid them. Two-phase commit requires participants to hold locks while waiting for the slowest member, which destroys availability across network boundaries, so microservices give up atomicity across services and keep it within each service. The cross-service flow becomes a saga: a sequence of local transactions where each step has a compensating action that undoes it if a later step fails. Combined with the outbox pattern for reliable event publishing and idempotent consumers for safe redelivery, this gives eventual consistency with clear failure handling instead of distributed locks.
What is the difference between saga orchestration and choreography?
In an orchestrated saga, a central orchestrator holds the flow as an explicit state machine and sends commands to each service, giving you one place to see saga state and trigger compensations. In choreography, there is no coordinator: each service reacts to events from other services, which keeps services decoupled but makes the overall flow implicit and hard to trace. Orchestration fits long flows with branching and compensation logic, like order processing. Choreography fits simple linear flows and fan-out reactions, like sending an email when an order ships.
What is the outbox pattern and why does it matter?
The outbox pattern solves the dual-write problem: saving to your database and publishing to a message broker are two systems with no shared transaction, so a crash between the two operations leaves them inconsistent. With an outbox, the service writes the event into an outbox table inside the same database transaction as the business data, making the write atomic. A background relay then reads the table and publishes each event to the broker. This guarantees at-least-once delivery, which is why consumers must be idempotent. In .NET, libraries like MassTransit and Wolverine ship production implementations.
How do microservices communicate in .NET?
Synchronously through HTTP APIs or gRPC when the caller needs an immediate answer, and asynchronously through a message broker like RabbitMQ or Azure Service Bus when it does not. gRPC is preferred for internal service-to-service calls where latency matters, REST for public-facing APIs, and messaging for events and fire-and-forget work. Every synchronous call creates temporal coupling, meaning both services must be up at the same moment, so experienced teams keep synchronous chains short and push everything that can be asynchronous onto the broker.
When should you not use microservices?
When you do not have the problem they solve. Microservices exist to let many teams deploy and scale independently. A small team, a domain whose boundaries are still shifting, no independent scaling pressure, or limited DevOps maturity are all signals to stay with a monolith, ideally a modular one with enforced boundaries. Splitting anyway buys network failure modes, eventual consistency, and multiplied operational overhead with nothing in return. A modular monolith also keeps the seams visible so you can extract a real service later if the pressure appears.
How do you secure communication between microservices?
At two levels. Service identity uses the OAuth client credentials flow, where the calling service obtains a token from the identity provider scoped for the target service, and optionally mutual TLS for transport-level workload identity, typically via a service mesh. User identity, needed when a call happens on behalf of a user, uses token exchange per RFC 8693 to trade the user token for one correctly audienced for the downstream service. The pattern to avoid is trusting internal traffic implicitly or passing user identity as a plain header, which any compromised container can forge.
Is .NET good for microservices in 2026?
Yes. .NET 10 ships most of the microservices toolchain in the platform itself: built-in resilience through Microsoft.Extensions.Http.Resilience, OpenTelemetry integration for distributed tracing, health check middleware for Kubernetes probes, native container image publishing without a Dockerfile, YARP for API gateways, and first-class gRPC. Aspire adds system-level orchestration for local development with service discovery and telemetry wired in by default. Combined with mature messaging libraries and Kubernetes support, .NET is one of the strongest platforms for microservices today.
If a question here exposed a gap, the FREE .NET Web API Zero to Hero course builds the foundations these questions sit on - API design, EF Core, authentication, Docker, and the production concerns that turn a working API into an operable service. It’s the fastest way to turn a shaky architecture answer into one backed by something you’ve actually built.
This is one spoke of my broader interview prep series. Start at the .NET interview questions hub for the cross-topic greatest hits, drill the runtime with the ASP.NET Core interview questions, and cover the API layer with the .NET Web API interview questions.
If this helped, bookmark it for your next interview, or share it with someone prepping for theirs.
Happy Coding :)
What's your take?
Push back, share a war story, or ask the obvious question someone else is wondering. I read every comment.