Microservices architecture is the standard approach at large Indian product companies. Understanding when and how to decompose services, handle distributed transactions, and design for failure is essential for senior backend and architecture interviews at Swiggy, Flipkart, Razorpay, and PhonePe. This guide covers microservices interview questions for India in 2026.
Microservices vs monolith: trade-offs and when to choose each
Microservices fundamentals:
1. Monolithic architecture: All application components (user interface, business logic, database access) are in a single deployable unit. Advantages: simple to develop and debug (one codebase, one deployment), no network latency between components, easy ACID transactions (one database), simple to scale horizontally (deploy more instances of the whole application). Disadvantages: as the team and application grow, the codebase becomes complex and difficult to understand; deployment risk increases (a bug in one component can bring down the whole application); cannot independently scale different components (if only the search is under load, you scale the entire monolith).
2. Microservices architecture: The application is decomposed into small, independently deployable services, each owning its own data store. Advantages: independent deployability (deploy the payment service without touching the user service), independent scaling (scale only the order processing service during a sale), technology diversity (use the best tool for each service: Python for ML, Go for the payment service, Node.js for the notification service), team autonomy (each service is owned by one team that can work and deploy independently). Disadvantages: distributed systems complexity (network failures, latency, partial failures), data consistency challenges (no single transaction across services), operational complexity (each service needs its own CI/CD pipeline, monitoring, scaling configuration), service discovery and communication overhead.
3. When to stay with a monolith: Fewer than 20-30 engineers: microservices add operational complexity that small teams cannot handle effectively. The product is still finding product-market fit: the domain boundaries are not yet clear; premature decomposition leads to poorly aligned services that need to be recombined later. The team does not have strong DevOps capabilities: microservices require Kubernetes, distributed tracing, and per-service CI/CD. Rule: start with a 'majestic monolith' (well-structured, modular monolith); move to microservices when the monolith becomes the bottleneck (deployment contention, scale requirements diverge).
4. When to adopt microservices: Different scaling requirements for different components, multiple teams stepping on each other in the same codebase, different reliability requirements (the payment service needs 99.99% uptime; the recommendation engine can tolerate more), the need for independent deployment cadences (the search team needs to deploy 10x per day; the billing team deploys weekly).
Service decomposition and domain-driven design
Service boundaries and design:
1. How to identify service boundaries: Domain-Driven Design (DDD): break the system into bounded contexts. A bounded context is a logical boundary within which a domain model is consistent. Each bounded context becomes a candidate microservice. Example: e-commerce bounded contexts: Product Catalogue, Inventory, Order Management, Payment, Shipping, User Profile, Search, Reviews, Recommendations. Each has its own model (the meaning of 'Product' is slightly different in Catalogue vs Inventory vs Order).
2. The Strangler Fig pattern: For migrating from a monolith to microservices: gradually extract services from the monolith. The monolith continues to serve traffic; new functionality or extracted functionality is served by the new microservice; a router (API gateway or reverse proxy) decides which traffic goes to the monolith and which goes to the new service. Over time, the monolith shrinks ('strangles') as more services are extracted. Safer than a 'big bang' rewrite.
3. Database per service: Each microservice owns its own database (or schema); no service accesses another service's database directly. This ensures loose coupling: the payment service can change its database schema without affecting the order service. The challenge: data that spans services must be queried via APIs, not SQL joins. Denormalization and data duplication are often necessary (the order service stores a snapshot of the product price at the time of order, not a foreign key to the product service).
4. API design between services: Each service exposes a well-defined API. Synchronous APIs (REST, gRPC): the caller waits for a response; use for operations that need an immediate answer. Asynchronous APIs (Kafka, RabbitMQ): the caller publishes an event; the consumer processes it in its own time; use for operations where the caller does not need an immediate response (send a confirmation email after an order is placed).
Distributed transactions and the Saga pattern
Distributed transactions:
1. Why distributed transactions are hard: In a monolithic application with one database, you can wrap multiple operations in a single ACID transaction: either all operations commit or all roll back. In microservices, each service has its own database. A transaction that involves multiple services (create an order, reduce inventory, charge the customer) cannot use a single database transaction across service boundaries.
2. The Saga pattern: A Saga is a sequence of local transactions across multiple services, each publishing events or messages to trigger the next local transaction. If any local transaction fails, the saga executes compensating transactions to undo the preceding transactions.
Choreography-based Saga: No central coordinator. Each service listens for events and publishes events when it completes its local transaction or when it fails (publishing a compensating event). Decoupled; harder to understand the overall flow.
Orchestration-based Saga: A central orchestrator (a separate service or a workflow engine like Temporal) tells each service what to do and listens for responses. The orchestrator handles failures (calls the compensating transaction on the appropriate service). Easier to understand the overall flow; the orchestrator is a potential single point of failure.
3. Compensating transactions: A compensating transaction undoes the effect of a completed local transaction. For example: if payment fails after an order is created, the compensating transaction for the Order service is to cancel the order. Compensating transactions are not always possible (you cannot unsend an email). This is why Sagas are eventually consistent (not immediately consistent like ACID transactions).
4. Idempotency in distributed systems: Because messages can be delivered more than once (at-least-once delivery), services must be idempotent: processing the same message twice must produce the same result as processing it once. Implementation: include a unique idempotency key in each message; the service stores processed keys and rejects duplicates.
Practise microservices architecture and system design interview questions with HireStepX's AI voice interviewer. Get scored feedback on your explanations of service decomposition, distributed transactions, and resilience patterns. First 2 sessions free.
Practice freeService communication, resilience, and observability
Microservices operations:
1. Service discovery: In microservices, services need to find each other's addresses dynamically (not hardcoded IPs). Client-side discovery: each client queries a service registry (Consul, Eureka) to get the address of a service instance, then connects directly. Server-side discovery: the client sends a request to a load balancer or API gateway, which queries the registry and forwards the request. Kubernetes: service discovery is built in; a Service resource provides a stable DNS name and load balancing to a set of pods.
2. Circuit breaker pattern: If a downstream service is slow or unavailable, calls to it will pile up, consuming threads and connections in the calling service, eventually crashing it. Circuit breaker: after N consecutive failures, 'open the circuit' (return an error immediately without calling the downstream service). After a timeout, try one request ('half-open' state): if it succeeds, close the circuit; if it fails, keep it open. Libraries: Resilience4j (Java), Polly (C#), Hystrix (Netflix; deprecated but widely known). This prevents cascade failures.
3. Rate limiting and bulkhead: Rate limiting: limit how many requests a service accepts per second (protects from overload). Bulkhead: isolate resources for different consumers (allocate a separate thread pool for calls to each downstream service; a slow downstream service only exhausts its own thread pool, not the shared pool). Named after the bulkheads in ships that prevent one flooded compartment from sinking the whole ship.
4. Distributed tracing: In a microservices architecture, a single user request may pass through 5-10 services. Distributed tracing assigns each request a unique trace ID; each service adds a span (a timed segment of work). The spans are collected in a tracing backend (Jaeger, Zipkin, Tempo) that visualises the end-to-end request path, showing which service took how long and where errors occurred. Essential for debugging latency issues in microservices.
Frequently asked questions
Explore more