Microservices & Distributed Systems Interview Questions¶
Architecture patterns, scalability, resilience, messaging, and distributed computing principles.
1. When should you use microservices vs monolith?¶
View Answer
Monolith: Single Unified Application
All features in one codebase, one deployment unit.
Advantages:
- ✅ Simple to build initially
- ✅ Easier debugging (single process)
- ✅ Single database (ACID transactions)
- ✅ Better performance (in-process calls)
Disadvantages:
- ❌ Hard to scale (scale entire app)
- ❌ Tight coupling (changes affect entire system)
- ❌ Technology lock-in (one language/framework)
- ❌ Deployment risk (one bug breaks everything)
Microservices: Distributed Services
Multiple independent services, each with own responsibility.
Advantages:
- ✅ Independent scaling (scale what you need)
- ✅ Loose coupling (change one without affecting others)
- ✅ Technology freedom (different stacks per service)
- ✅ Faster deployment (smaller units)
- ✅ Fault isolation (one service down ≠ entire system down)
Disadvantages:
- ❌ Complex distributed system (network issues, latency)
- ❌ Harder debugging (logs across multiple services)
- ❌ Data consistency challenges (eventual consistency)
- ❌ Operational overhead (monitoring, deployment, versioning)
- ❌ Network latency (inter-service calls)
Choose Monolith When
- MVP/startup (speed to market)
- Small team (coordination overhead)
- Simple, well-defined domain
- ACID transactions critical
- Low scalability requirements
Choose Microservices When
- Large team (independent development)
- Scalability required (different services scale differently)
- Technology diversity needed
- Independent deployment important
- Fault isolation critical
Recommendation
Start with monolith, migrate to microservices when you outgrow it. Most companies shouldn't start with microservices.
2. What's the difference between synchronous and asynchronous communication?¶
View Answer
Synchronous Communication
Caller waits for response (blocking).
Examples: REST API calls, gRPC, direct method calls
Client calls Service A
Client blocks, waiting for response
Service A processes and returns result
Client receives response
Advantages:
- ✅ Simple to implement
- ✅ Immediate feedback
- ✅ Error handling straightforward
Disadvantages:
- ❌ Tight coupling (client waits for server)
- ❌ Cascading failures (if Service A slow, client slow)
- ❌ Less resilient (network issues block caller)
- ❌ Poor scalability (threads waiting)
Asynchronous Communication
Caller doesn't wait for response (fire-and-forget or callback).
Examples: Message queues (Kafka, RabbitMQ), event-driven, webhooks
Client sends message to Queue
Client continues immediately
Service A consumes message when ready
Service A processes and publishes result event
Client receives event via callback/subscription
Advantages:
- ✅ Loose coupling (caller doesn't wait)
- ✅ Better resilience (queue buffers load)
- ✅ Scalability (no thread blocking)
- ✅ Can handle spikes (queue absorbs load)
Disadvantages:
- ❌ Complex (eventual consistency, ordering issues)
- ❌ Debugging harder (distributed flow)
- ❌ Guaranteeing delivery complex
- ❌ Error handling less obvious
Comparison Table
| Aspect | Synchronous | Asynchronous |
|---|---|---|
| Latency | Immediate but may be slow | Delayed but responsive |
| Coupling | Tight | Loose |
| Complexity | Simple | Complex |
| Resilience | Poor (cascading failures) | Good (buffering, retry) |
| Best For | Request-response (GET, queries) | Long-running operations, events |
3. Explain the saga pattern for distributed transactions¶
View Answer
The Problem: Distributed Transactions
ACID transactions don't work across multiple databases/services. Need alternative pattern.
Example: Money Transfer
- Service A: Debit account from_account
- Service B: Credit account to_account
- If Service B fails, Service A's debit is stuck!
The Saga Pattern: Two Approaches
Orchestration (Centralized Coordinator)
One service orchestrates the saga steps.
Saga Orchestrator
↓ Step 1: Debit Account A
← Success
↓ Step 2: Credit Account B
← Failure → Compensate (refund A)
Code Example:
@Service
public class TransferSaga {
public void transfer(String fromId, String toId, BigDecimal amount) {
try {
Step 1: accountService.debit(fromId, amount);
Step 2: accountService.credit(toId, amount);
} catch (Exception e) {
// Compensate: reverse the debit
accountService.credit(fromId, amount);
}
}
}
Choreography (Event-Driven)
Each service listens for events and publishes new events.
AccountService: "TransferInitiated" event
→ (publishes) "MoneyDebited"
→ LedgerService listens
→ (publishes) "MoneyCredited"
→ If fails: (publishes) "TransferFailed"
→ AccountService listens and refunds
Comparison
| Aspect | Orchestration | Choreography |
|---|---|---|
| Coordination | Centralized (Saga Orchestrator) | Decentralized (event listeners) |
| Complexity | Simpler to understand | Complex (distributed logic) |
| Coupling | Tight (orchestrator knows all steps) | Loose (services listen to events) |
| Testing | Easier (centralized logic) | Harder (distributed) |
Compensating Transactions (Rollback)
For each forward step, define a compensating transaction:
- Debit: Compensate with Credit
- Create Order: Compensate with Cancel Order
- Reserve Inventory: Compensate with Release Inventory
4. How does the circuit breaker pattern work?¶
View Answer
The Problem
If a service is down, repeatedly calling it wastes resources and causes timeouts.
Circuit Breaker Solution
Three states: CLOSED → OPEN → HALF_OPEN → CLOSED
┌─────────────┐
│ CLOSED │ ← All requests pass through
├─────────────┤
│ Failure count exceeds threshold
├─────────────┤
│ OPEN │ ← Requests fail immediately (no call to service)
├─────────────┤
│ After timeout
├─────────────┤
│ HALF_OPEN │ ← Allow single test request
├─────────────┤
│ If success: Back to CLOSED
│ If failure: Back to OPEN
Implementation (Spring Cloud)
@Service
public class PaymentService {
@CircuitBreaker(
name = "paymentService",
fallback = "paymentFallback"
)
public Payment processPayment(Order order) {
return paymentGateway.charge(order);
}
// Fallback when circuit is OPEN
public Payment paymentFallback(Order order, Exception e) {
return new Payment("PENDING", "Service temporarily unavailable");
}
}
States Explained
| State | Behavior | Requests Pass? |
|---|---|---|
| CLOSED | Normal operation, count failures | ✅ Yes |
| OPEN | Reject all requests immediately | ❌ No (return fallback) |
| HALF_OPEN | Allow single test request | ⚠️ One only |
Benefits
- Fail fast: Don't wait for timeouts
- Resource protection: Stop wasting threads/connections
- Time to recover: Gives failing service time to recover
- Cascading failure prevention: Stop propagating errors
5. What's eventual consistency and when is it acceptable?¶
View Answer
Eventual Consistency Definition
Data across services will eventually be consistent, but not immediately.
Short-term: Different services may have different views of data Long-term: Data will synchronize
Time 0: Account A has $100
Time 0: Transfer $50 initiated
Time 1: Service A shows $50 (debited)
Time 1: Service B still shows old $0 (not credited yet)
Time 2: Service B syncs and shows $50 (eventually consistent)
Strong Consistency vs Eventual Consistency
| Property | Strong Consistency | Eventual Consistency |
|---|---|---|
| Data Sync | Immediate | After some delay |
| Implementation | Distributed transactions, locks | Event-driven, message queue |
| Performance | Slower (must wait for sync) | Faster (no waiting) |
| Availability | Lower (may be unavailable during sync) | Higher (always available) |
When Eventual Consistency is Acceptable
- ✅ Social Media: Like count doesn't need to be perfect immediately
- ✅ Analytics: Reports don't need real-time accuracy
- ✅ Caching: Cache stale data is acceptable
- ✅ Read-heavy: Most users just reading, not writing
When Strong Consistency is Required
- ❌ Banking: Money transfer must be atomic
- ❌ Inventory: Cannot oversell
- ❌ Payments: Charge must debit account correctly
Strategies for Eventual Consistency
- Event-driven: Publish events, consumers update independently
- Message queues: Async updates via Kafka, RabbitMQ
- CQRS: Separate read/write models, reconcile later
- Compensating transactions: Fix inconsistencies with sagas
6. Explain the database per service pattern and its challenges¶
View Answer
Pattern: Database Per Service
Each microservice has its own private database. No shared database.
UserService OrderService PaymentService
↓ ↓ ↓
UserDB OrderDB PaymentDB
(Private) (Private) (Private)
Advantages
- ✅ Loose coupling: Database schema changes don't affect other services
- ✅ Technology choice: Each service can use optimal DB (SQL, NoSQL, etc.)
- ✅ Scalability: Scale each database independently
- ✅ Data privacy: Services can't accidentally access other service's data
Challenges
1. Distributed Transactions
Cannot use ACID transactions across databases.
- Solution: Saga pattern (orchestration or choreography)
2. Data Consistency
Services may have inconsistent views of data.
- Solution: Event-driven architecture, eventual consistency
3. Complex Queries Across Services
Cannot JOIN tables across databases.
-- IMPOSSIBLE: User and Order in different DBs
SELECT u.name, COUNT(o.id)
FROM users u
JOIN orders o ON u.id = o.user_id;
- Solution: CQRS (materialized views), API calls to aggregate data
4. Reporting & Analytics
Difficult to aggregate data from multiple services.
- Solution: Data warehouse, event streaming, analytics DB
Database Selection Per Service
UserService → PostgreSQL (relational)
OrderService → MongoDB (documents)
PaymentService → Redis (key-value cache)
AnalyticsService → Elasticsearch (search)
Recommendation
Start monolithic (shared DB), migrate to database-per-service as you scale. The operational overhead is significant.
7. What's a consumer group and how do offsets work in Kafka?¶
View Answer
Kafka Topic Structure
Topic: order-events
├─ Partition 0: [msg0] [msg1] [msg2] ...
├─ Partition 1: [msg0] [msg1] [msg2] ...
└─ Partition 2: [msg0] [msg1] [msg2] ...
Each partition is an immutable log of messages in order.
Offset: Message Position
Offset = unique sequential identifier for each message within a partition.
Partition 0:
Offset 0: {"order": "123", "amount": "$100"}
Offset 1: {"order": "124", "amount": "$200"}
Offset 2: {"order": "125", "amount": "$150"}
↑
Consumer reads from here (offset tracking)
Consumer Group: Multiple Consumers
Multiple consumers reading same topic in parallel.
Topic: orders (3 partitions)
Consumer Group A:
Consumer 1 → Partition 0
Consumer 2 → Partition 1
Consumer 3 → Partition 2
(Each reads independently, parallel processing)
Consumer Group B:
Consumer 1 → All 3 partitions
(Different group, reads entire topic)
How It Works
Kafka stores: Consumer Group → Topic → Partition → Offset
Consumer Group A:
Topic=orders, Partition=0, Offset=42 (last message read)
Topic=orders, Partition=1, Offset=150
Topic=orders, Partition=2, Offset=88
Offset Commit Strategies
| Strategy | When | Semantics |
|---|---|---|
| Auto-commit | After polling | At-most-once (may lose messages) |
| Manual commit after processing | After business logic | At-least-once (may duplicate) |
| Transactional | With database transaction | Exactly-once (best) |
Example: Consumer Group
// Consumer 1: Process Partition 0
while (true) {
ConsumerRecords<String, String> records = consumer.poll(100);
for (ConsumerRecord record : records) {
// Process message
processOrder(record.value());
}
consumer.commitSync(); // Update offset
}
Rebalancing
If a consumer dies, its partitions reassigned to others.
8. How do you handle distributed transactions?¶
View Answer
The Problem: Distributed Transactions
Traditional ACID transactions don't work when data is spread across multiple services/databases.
Solutions
1. Saga Pattern (Primary Approach)
Orchestrate multi-step transactions with compensating transactions.
See Question 3 for detailed explanation.
2. Event Sourcing
Store all state changes as immutable events.
Events Log:
- TransferInitiated(from=A, to=B, amount=50)
- MoneyDebited(account=A, amount=50)
- MoneyCredited(account=B, amount=50)
Replay events to rebuild state
3. CQRS (Command Query Responsibility Segregation)
Separate read and write models.
4. Two-Phase Commit (❌ Avoid)
Coordinator says "prepare to commit", then "commit".
Theoretical but rarely used in practice:
- ❌ Blocking (locks held during coordination)
- ❌ Slow (requires multiple round trips)
- ❌ Coordinator failure = deadlock
Practical Approach
| Scenario | Solution |
|---|---|
| Money transfer | Saga with compensating transactions |
| Inventory + Order | Event-driven with eventual consistency |
| Audit trail needed | Event Sourcing |
| Complex queries needed | CQRS with materialized views |
Guarantees
- At-most-once: Messages may be lost
- At-least-once: Messages may be duplicated
- Exactly-once: Perfect, but expensive (use for critical operations)
Additional Topics¶
See Sample Topics for more areas to explore: - How do you define service boundaries? - What's service discovery and why do you need it? - How do you monitor and trace requests across services? - Explain the bulkhead pattern - When should you use RabbitMQ vs Kafka? - How do you handle network failures in distributed systems?
See also: Microservices Principles, Messaging & Kafka, Service Discovery, Interview Questions Overview