Microservices are one of the most cargo-culted architectural patterns in software. "Netflix uses microservices" became the justification for splitting a three-person startup's Rails app into fifteen services — and then everyone wondered why deployments broke every day. In 2026 the industry has more scar tissue and better frameworks for making the decision honestly.
What changed in 2026
- Platform engineering matured. Internal developer platforms (IDPs) based on Backstage and Crossplane abstract Kubernetes complexity, making the operational overhead of microservices lower than it was in 2019.
- The modular monolith got a renaissance. Frameworks like Elixir's Umbrella, .NET Aspire, and Nx monorepos show that clean module boundaries inside a single process give most of the design benefits without the network hop.
- eBPF-based service meshes. Cilium and Istio ambient mesh run observability and mTLS at the kernel level — no sidecar proxy needed, reducing per-service overhead from ~50 MB to near zero.
- AI workloads drive new shapes. LLM inference services are single-purpose, GPU-bound, and bursty — a natural fit for isolation. But the orchestration layer calling them can stay monolithic.
What microservices actually mean
A microservice is a service that:
- Is independently deployable — changing it does not require redeploying anything else.
- Owns its own data store — no other service reads its database directly.
- Has a single bounded context — aligns to a domain concept (Order, Inventory, Notification), not a technical layer.
The "micro" in microservices refers to scope of responsibility, not lines of code. A service can be 10k LOC and still be a microservice if it owns one domain.
Monolith vs microservices comparison
| Dimension |
Modular Monolith |
Microservices |
| Deployment |
One artifact |
Many pipelines |
| Latency |
In-process calls |
Network hops (1–10 ms each) |
| Transactions |
ACID within DB |
Sagas or 2PC (complex) |
| Team autonomy |
Shared codebase |
Independent ownership |
| Operational cost |
Low |
High (infra, observability, mesh) |
| Good fit |
1–5 teams |
5+ teams, distinct scaling needs |
Service boundaries: the hard part
The most important design decision is where to draw service boundaries. Wrong boundaries cause services to be tightly coupled over the network — the worst of both worlds.
Use Domain-Driven Design (DDD) bounded contexts as your guide:
# Good boundaries (aligned to domain)
OrderService → owns orders + order lines
InventoryService → owns product stock levels
NotificationService → owns email/SMS/push dispatch
PaymentService → owns charge records
# Bad boundaries (technical split, not domain split)
DatabaseService → anti-pattern, shared data store
UtilsService → no single domain concept
UserProfileService + UserAuthService
→ two services for one aggregate (creates coupling)
Inter-service communication
| Pattern |
Protocol |
Best for |
| Synchronous RPC |
gRPC, HTTP/2 |
Low-latency queries |
| Async events |
Kafka, NATS, Pub/Sub |
State changes, fan-out |
| Choreography |
Events, no orchestrator |
Loose coupling |
| Orchestration |
Temporal, AWS Step Functions |
Complex multi-step workflows |
Prefer async events for writes and synchronous calls only for reads where you need the data immediately.
How to start (the right way)
- Build a modular monolith first. Clear module interfaces in a single codebase.
- Define bounded contexts. Map your domain with DDD; resist splitting before you understand the seams.
- Identify the first extraction candidate. Look for: distinct scaling requirements, team ownership boundary, or a part that changes at a very different rate.
- Extract one service. Keep the monolith running; run the new service alongside it.
- Validate the boundary. If you constantly call back into the monolith for data, the boundary is wrong.
Common mistakes
Shared database. Two services sharing a Postgres schema is a distributed monolith. Schema changes require coordinating both services; you get none of the isolation benefits.
Synchronous chains. A calls B calls C calls D — each hop adds latency and failure surface. A 10 ms call chain of five services becomes 50+ ms plus retry complexity. Prefer async for multi-step flows.
No service contract. If Service A calls Service B without a versioned API contract, every B deployment can silently break A. Define OpenAPI specs or Protobuf schemas; run contract tests in CI.
Premature extraction. Extracting a service from a monolith you haven't modularised is like building a house with wet cement. Modularise first.
What to skip
- Nanoservices — one function per service creates thousands of network boundaries and is operationally unmaintainable.
- Microservices as the first architecture for a new product — you do not know your domain boundaries yet. Start with a monolith.
- Rolling your own service mesh — Linkerd, Istio, or Cilium handle retries, mTLS, and distributed tracing with battle-tested code. Don't reinvent them.
FAQ
How many services is too many?
There is no fixed answer. A useful heuristic: if a single team cannot hold all service contracts in their heads, you may have too many. Conway's Law applies — your services should mirror your team structure.
Do microservices require Kubernetes?
No. You can run microservices on VMs, AWS ECS, Fly.io, or Railway. Kubernetes helps at scale but adds significant complexity below ~10 services.
How do I handle transactions across services?
Use the Saga pattern: a sequence of local transactions, each publishing an event, with compensating transactions for rollback. Temporal makes this significantly easier to implement correctly.
What is the difference between microservices and SOA?
Service-Oriented Architecture (SOA) typically used a heavy central ESB (Enterprise Service Bus) and coarse-grained services. Microservices are finer-grained, communicate over lightweight protocols, and avoid central orchestration.
Where to go next