Microservices
Microservices split a system into independently deployable services organized around business capabilities.
Core Idea
A microservices architecture decomposes an application into a set of small, autonomous services. Each service runs in its own process, owns its data, and communicates with other services through well-defined APIs or asynchronous messaging. Services are organized around business capabilities rather than technical layers.
Each service owns its data store and communicates via APIs or events
Benefits
- Independent deployment: Each service can be built, tested, and deployed independently without coordinating with other teams
- Technology diversity: Different services can use different languages, frameworks, and data stores
- Independent scaling: Services under heavy load can be scaled independently
- Team autonomy: Small teams can own and evolve their services without blocking other teams
- Fault isolation: A failure in one service does not necessarily bring down the entire system
- Easier replacement: Individual services can be rewritten without affecting the rest of the system
Costs
- Distributed system complexity: Network failures, latency, partial failures, and eventual consistency become everyday concerns
- Operational overhead: Service discovery, load balancing, monitoring, logging, and tracing across services
- Data consistency: Cross-service transactions are difficult; sagas and compensation logic add complexity
- Testing difficulty: Integration testing across services is harder than testing within a single process
- Deployment infrastructure: Requires container orchestration, CI/CD pipelines per service, and configuration management
- Cognitive load: Understanding the system requires tracing requests across multiple services
Operational Complexity
Running microservices in production requires significant investment in infrastructure and tooling:
- Container orchestration (Kubernetes or equivalent)
- Service mesh for inter-service communication
- Distributed tracing to follow requests across services
- Centralized logging and monitoring
- Health checks and circuit breakers
- API gateways for external traffic
- Secret management across services
Data Ownership
Each service owns its data store. No service should directly access another service's database. This ensures loose coupling and allows services to change their internal storage without affecting others.
When data needs to be shared, services communicate through APIs or events. This often leads to data duplication, which is an accepted trade-off for autonomy.
Service Boundaries
Good service boundaries are aligned with business capabilities, not technical functions. Signs of well-drawn boundaries:
- A service can be understood and developed by a small team
- Changes to one service rarely require changes to other services
- The service has a clear, cohesive purpose
- The service owns a distinct piece of the domain
Signs of poor boundaries:
- Services that always need to be deployed together
- Circular dependencies between services
- Services that share database tables
- Changes that require coordinated releases across multiple services
Distributed Systems Problems
Microservices inherit all the challenges of distributed computing:
- Network is not reliable: Calls between services can fail, time out, or return partial results
- Latency is not zero: Inter-service communication adds latency that compounds across call chains
- Consistency is eventual: Without distributed transactions, data across services may be temporarily inconsistent
- Partial failure: Some parts of the system may be unavailable while others continue operating
- Clock skew: Different machines may have slightly different clocks, making time-based ordering unreliable
When Not to Use Microservices
- Small teams (fewer than 8-10 developers)
- New products where the domain boundaries are not yet clear
- Organizations without mature DevOps practices
- When the operational cost exceeds the organizational benefit
- When a modular monolith would provide sufficient structure
- When the team does not have experience running distributed systems
If you cannot build a well-structured monolith, you will not be able to build a well-structured microservices system. Start with a modular monolith and extract services only when there is a clear need.