Microservices Architecture
Independently deployable services, each aligned to a bounded business capability, communicating over a network with their own data stores.
What it is
Microservices is an architectural style that structures an application as a collection of small, independently deployable services. Each service is owned by a small team, implements a single business capability, communicates with other services via well-defined APIs (HTTP/REST, gRPC, or messaging), and maintains its own data store. Services are independently versioned, deployed, and scaled. The "micro" in microservices refers to the scope of responsibility — each service does one thing well — not necessarily to the size of the codebase. A microservice can be tens of thousands of lines of code if it owns a rich business domain; the key characteristic is independent deployability and data ownership.
Why it exists
Microservices emerged from the pain of large-scale monolith deployment bottlenecks at companies like Netflix, Amazon, and eBay. Conway's Law states that organizations design systems mirroring their communication structure — a large team working on a single codebase naturally creates coordination overhead and slow deployments. Microservices flips this around: small, autonomous teams own small services, enabling parallel development, independent deployment cadences, and failure isolation. The architecture also enables technology heterogeneity — a machine learning service can use Python while the order service uses Java. The cost is significant operational complexity, which is why microservices are an organizational solution as much as a technical one.
When to use
- Multiple autonomous teams need to deploy independently without blocking each other on a shared release train.
- Different components have dramatically different scaling requirements that justify independent scaling.
- The organization has DevOps maturity: container orchestration, CI/CD pipelines, distributed tracing, and on-call culture are all in place.
- The domain is well understood — bounded contexts are stable and unlikely to require frequent boundary changes.
- Technology heterogeneity is genuinely required — different services benefit from different languages, databases, or ML runtimes.
When not to use
- Team size is small (under ~15 engineers) — the operational overhead of microservices will dominate productive feature work.
- The domain is not yet well understood — wrong service boundaries become expensive network contracts that are hard to change.
- The organization lacks DevOps maturity — without CI/CD automation, container platforms, and distributed observability, microservices become chaos.
- Strong consistency across the system is a hard requirement — distributed transactions are extremely complex.
Typical architecture
Clients interact through an API Gateway. Services communicate synchronously via REST/gRPC or asynchronously via a message broker. Each service owns its own database, and a service mesh handles cross-cutting concerns like mTLS and retries.
Client / Mobile / Web
│
┌──────▼──────┐
│ API Gateway │ (auth, rate-limit, routing)
└──┬──┬──┬───┘
│ │ │
┌─────▼┐ │ ┌▼──────┐
│Order │ │ │User │ Each service:
│Svc │ │ │Svc │ - owns its DB
└──┬───┘ │ └───┬───┘ - deploys independently
│ ┌─▼──┐ │ - scales independently
│ │Pay │ │
│ │Svc │ │
│ └──┬─┘ │
│ │ │
┌──▼──────▼────▼──────┐
│ Message Broker │ (Kafka / RabbitMQ)
└──────────────────────┘
Service Mesh (Istio/Linkerd): mTLS, retries, circuit breakers
Pros and cons
Pros
- Independent deployability — teams ship on their own schedules.
- Fault isolation — a crashed service does not bring down the entire system.
- Independent scalability — scale only the services under load.
- Technology freedom — choose the right tool for each service.
- Smaller codebases are easier for a team to fully understand and own.
Cons
- Massive operational overhead — container orchestration, service discovery, distributed tracing, multi-service CI/CD.
- Distributed system fallacies apply — the network is not reliable, latency is not zero.
- No distributed transactions — eventual consistency is the default and requires careful design.
- Integration testing is hard — spinning up multiple services locally is complex.
- Service boundaries are hard to change once they become network contracts.
Implementation notes
Align service boundaries to bounded contexts from Domain-Driven Design, not to technical layers. Avoid sharing databases between services — this is the single most common mistake that creates hidden coupling. Each service must be independently deployable, so invest early in a robust CI/CD pipeline per service. Implement distributed tracing (OpenTelemetry) from day one — debugging cascading failures across services without traces is nearly impossible. Use a service mesh for cross-cutting concerns (mTLS, circuit breaking, retries) rather than implementing them in every service individually. Start with an API Gateway to centralize authentication, rate limiting, and request routing.
Common failure modes
- Shared database: Two services writing to the same database schema — the most common anti-pattern that recreates monolith coupling across a network.
- Chatty services: A single user action triggers 10 synchronous service-to-service calls, amplifying latency and failure probability with each hop.
- Distributed monolith: Services are separately deployed but tightly coupled through shared libraries, synchronous call chains, or shared configuration.
- Missing observability: No distributed tracing means production bugs become impossible to debug across service boundaries.
- Wrong service boundaries: Boundaries drawn around technical concerns (Auth, DB, Cache) rather than business capabilities lead to enormous amounts of inter-service communication for simple operations.
Decision checklist
- Does each team own exactly one or a few services end-to-end (you build it, you run it)?
- Is there a mature container orchestration platform (Kubernetes) and CI/CD pipeline per service?
- Is distributed tracing (OpenTelemetry) and centralized logging in place before go-live?
- Does each service own its own database with no direct shared access?
- Are service boundaries aligned to stable bounded contexts, not technical layers?
- Is an API contract testing strategy (consumer-driven contracts with Pact) defined?
Example use cases
- Netflix: Decomposed its DVD-era monolith into hundreds of microservices to enable independent team deployments and massive horizontal scaling across AWS regions.
- Amazon: Jeff Bezos's "two-pizza team" mandate and internal service APIs became the blueprint for AWS itself — services as independently deployable products.
- Uber: Rider, driver, pricing, and dispatch services evolve independently and can tolerate partial failures without taking down the entire platform.
Related patterns
- Modular Monolith — The recommended intermediate step before microservices; cheaper boundary changes, same design discipline.
- Event-Driven Architecture — Frequently combined with microservices for async inter-service communication.
- Service-Oriented Architecture — Microservices' predecessor; coarser-grained, ESB-centric, enterprise-focused.
Further reading
- Microservices — Martin Fowler & James Lewis' original article defining microservices.
- Building Microservices — Sam Newman, O'Reilly. The definitive practitioner's guide.
- GOTO 2014 — Microservices — Martin Fowler's original conference talk.