Microservices Architecture Explained: Is It Worth It?
The Architecture That Became a Default โ Without Earning It
Somewhere around 2015, microservices stopped being an architectural pattern and became an aspiration. Startups that had not shipped a single feature to paying users were drawing service diagrams with twelve boxes. Engineering blogs published Netflix and Uber's architectures as if the problems they solved applied universally. "We are building with microservices" became a hiring pitch.
The result was a generation of teams running distributed systems they did not need, debugging network failures that would not exist in a monolith, and spending more engineering time on infrastructure than on product. Microservices are not wrong. They solve real problems. But they solve problems that most teams do not have yet โ and adopting them before you have those problems is one of the more expensive architectural mistakes you can make.
This post explains what microservices actually are, what problems they genuinely solve, and the honest answer to whether your team should be using them.
๐ฏ Quick Answer (30-Second Read)
- What it is: An architectural style where an application is split into small, independently deployable services that communicate over a network
- Main benefit: Independent scaling, independent deployments, team autonomy at large org size
- Main cost: Distributed systems complexity โ network failures, latency, data consistency, observability overhead
- When it's worth it: Large engineering orgs (50+ engineers), services with genuinely different scaling needs, teams blocked by deployment coupling
- When it's not: Early-stage products, small teams, anything where the coordination cost exceeds the autonomy benefit
What Microservices Actually Are
A microservices architecture splits an application into a set of small, independently deployable services. Each service owns a specific business capability โ user auth, payments, notifications, search โ runs in its own process, and communicates with other services over a network (typically HTTP/REST or gRPC, or via an async message queue).
The contrast is a monolith: a single deployable unit where all application logic runs in one process, shares one database, and is deployed together.
Monolith
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ-โโโ
โ Auth โ Payments โ Notifications โ
โ Search โ Orders โ User Profiles โ
โ (shared database) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ-โโโ
Single deployment unit
Microservices
โโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโโโโโโโ
โ Auth โ โ Payments โ โ Notifications โ
โโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโโโโโโโ
โโโโโโโโโโ โโโโโโโโโโ โโโโโโโโโโโโโโโโโ
โ Search โ โ Orders โ โ User Profiles โ
โโโโโโโโโโ โโโโโโโโโโ โโโโโโโโโโโโโโโโโ
Each service has its own database and deployment
Each service in a microservices architecture is supposed to be:
- Independently deployable โ you can release the Payments service without touching Auth
- Independently scalable โ you can run 20 instances of Search and 2 of Notifications
- Owned by a single team โ Conway's Law in practice
- Loosely coupled โ changes to one service do not require changes to others
In theory, this gives large engineering organisations the ability to move fast at scale. In practice, achieving all four properties simultaneously is genuinely hard.
What Problems Microservices Actually Solve
Independent Scaling
A monolith scales as a unit. If your search feature is CPU-intensive and your auth service is lightweight, you cannot scale search independently โ you scale the whole application. Microservices let you allocate resources where the load actually is.
This matters when different parts of your system have meaningfully different load profiles. It does not matter when your entire application fits comfortably on three servers.
Independent Deployments
In a monolith, every deployment is a full application deployment. A change to the notification template requires deploying the entire system. At a large engineering organisation with dozens of teams committing to the same codebase, this creates a deployment bottleneck โ everyone waits for everyone else, release trains form, and deployment frequency drops.
Microservices let each team deploy their service on their own schedule. The Payments team ships three times a day without waiting for the Search team's refactor to land.
Team Autonomy and Organisational Scaling
Conway's Law states that systems mirror the communication structure of the organisations that build them. Microservices are the architectural expression of that law โ each service maps to a team, each team owns a domain, and inter-team communication happens through service interfaces rather than shared codebases.
This is the most underrated reason large companies adopt microservices. It is not primarily a technical decision โ it is an organisational one. Amazon's two-pizza team model and microservices architecture are the same idea expressed differently.
Technology Heterogeneity
Microservices let different services use different languages, databases, and frameworks where it genuinely makes sense. Your ML inference service can run Python. Your high-throughput event processor can run Go. Your CRUD API can run Node. In a monolith, the whole application is one language and one stack.
In practice, technology heterogeneity is more liability than asset for most teams โ it increases the operational surface area and makes it harder to move engineers between services. Use it selectively, not as a default.
The Real Costs Nobody Mentions in the Pitch
Network Failures Are Now Application Failures
In a monolith, a function call cannot fail due to a network timeout. In microservices, every inter-service call can fail, time out, return a partial response, or succeed on one retry and fail on another. Your application code must handle all of these cases. Circuit breakers, retry logic with exponential backoff, timeout budgets, and fallback behaviour become mandatory engineering concerns at every service boundary.
Distributed Data Consistency
A monolith with a single database gets ACID transactions for free. Operations that touch multiple tables either fully succeed or fully roll back. In microservices with separate databases per service, a transaction that spans two services โ create an order and charge the payment โ has no native atomicity. You implement eventual consistency with sagas or two-phase commit, both of which add significant complexity.
Observability Overhead
Debugging a bug in a monolith means looking at one log stream, one stack trace. Debugging a bug that crosses four services means correlating distributed traces, matching request IDs across log streams, and understanding which service introduced the latency or the wrong state. Without proper distributed tracing (Jaeger, Zipkin, Datadog APM), production debugging in a microservices environment is genuinely painful.
Operational Complexity
A monolith has one deployment pipeline, one runtime environment, one set of infrastructure concerns. Microservices multiply all of these by the number of services. Each service needs its own CI/CD pipeline, its own container image, its own Kubernetes deployment manifest, its own health checks, its own alerting rules. For a team of five engineers running twelve services, the operational overhead is unsustainable.
The Right Approach vs The Wrong Approach
The right approach is starting with a monolith and extracting services when you hit a specific, measurable problem that microservices solve. Martin Fowler calls this the "MonolithFirst" pattern. Build the domain model correctly in a monolith. When you have a service that needs independent scaling, extract it. When a team is blocked by deployment coupling, extract the relevant domain. Let the problems drive the architecture.
The modular monolith is the underrated middle path. A well-structured monolith with clear domain boundaries โ separate modules with explicit interfaces, no cross-module database queries โ gives you most of the code organisation benefits of microservices with none of the distributed systems complexity. When you eventually need to extract a service, the boundary is already drawn.
The wrong approach is designing a microservices architecture upfront for a product that does not exist yet. Starting with microservices means starting with distributed systems complexity before you understand your domain well enough to draw service boundaries correctly. Early service boundaries are almost always wrong โ and refactoring a service boundary in a distributed system is far more painful than refactoring a module boundary in a monolith.
The other wrong approach is treating microservices as a scaling solution for a single-team product. If five engineers are deploying twelve services, the coordination cost exceeds the autonomy benefit by a wide margin. Microservices scale teams, not just systems โ the organisational benefit only materialises when the org is large enough to have the problems they solve.
My Take
The reason microservices became over-adopted is that the companies publishing their architectures โ Netflix, Uber, Amazon โ were solving genuine problems at genuine scale. What got lost in translation is that those architectures are outputs of specific organisational and scaling constraints, not inputs to a new project. The best outcome of adopting microservices is a large engineering organisation where teams ship independently, services scale to their actual load, and a failure in one domain does not cascade to others. The worst outcome โ and the more common one โ is a six-person startup running a distributed monolith: all the operational complexity of microservices, none of the autonomy benefit, and a distributed tracing bill to show for it. Right now, the industry is correcting. "Monolith" is no longer a dirty word. Teams are openly talking about modular monoliths, majestic monoliths, and service extraction as a response to growth rather than a starting point. Where this is heading: the boundary between monolith and microservices will blur further as platforms like Cloudflare Workers and serverless architectures make function-level deployment easy โ the deployment unit gets smaller without the full operational overhead of a service mesh.
Comparison Table
| Concern | Monolith | Modular Monolith | Microservices |
|---|---|---|---|
| Deployment complexity | Low | Low | High |
| Operational overhead | Low | Low | Very high |
| Independent scaling | No | No | Yes |
| Team autonomy | Low at scale | Medium | High |
| Data consistency | Easy (ACID) | Easy (ACID) | Hard (eventual) |
| Debugging | Simple | Simple | Complex (distributed tracing) |
| Right team size | 1โ20 engineers | 10โ50 engineers | 50+ engineers |
| Time to first feature | Fast | Fast | Slow |
Real Developer Use Case
A SaaS company with 8 engineers launched with a microservices architecture modelled on a blog post about Netflix. They had 11 services: auth, users, billing, notifications, search, analytics, file upload, API gateway, and three domain services. Each had its own repo, its own CI/CD pipeline, and its own Kubernetes deployment.
18 months in, a feature that touched billing and notifications took three weeks to ship โ two days of actual code, the rest coordinating deployments, managing API versioning, and debugging a distributed race condition. New engineers took six weeks to become productive because the local development environment required running all 11 services.
They consolidated into a modular monolith with three extracted services (search for Elasticsearch integration, file upload for S3 handling, and analytics for a separate data warehouse). Deployment complexity dropped by 70%. Feature cycle time halved. New engineer onboarding dropped from six weeks to two.
Frequently Asked Questions
Should a startup use microservices?
Almost never at the beginning. Startups need to move fast, change direction, and validate assumptions โ all of which are harder with microservices. Start with a well-structured monolith. Extract services when you have specific scaling or team autonomy problems that the monolith cannot solve. The companies that publish their microservices architectures built them after achieving scale, not before.
What is the difference between microservices and a monolith?
A monolith is a single deployable unit where all application logic runs in one process and typically shares one database. Microservices split the application into independent services that communicate over a network, each with its own deployment and typically its own database. The key differences are deployment independence, scaling granularity, and operational complexity.
What is a modular monolith?
A modular monolith is a single deployable application structured into well-defined modules with explicit boundaries and interfaces โ no cross-module database queries, clear domain ownership, and minimal coupling between modules. It gives you the code organisation benefits of microservices without the distributed systems overhead. It is also the easiest starting point for eventually extracting services if needed.
How do microservices communicate?
Synchronously via HTTP/REST or gRPC โ one service calls another and waits for the response. Or asynchronously via a message queue (Kafka, RabbitMQ, SQS) โ one service publishes an event and another consumes it independently. Async communication is more resilient to failures and reduces coupling but makes the system harder to reason about.
When should I extract a service from a monolith?
When you have a specific, measurable problem: a part of the system needs to scale independently, a team is blocked by deployment coupling, or a component has such different operational requirements (language, runtime, scaling profile) that the monolith cannot accommodate them cleanly. Extract one service at a time, validate the decision, then continue. Never extract all at once.
Conclusion
Microservices are a solution to specific problems โ independent scaling, deployment autonomy, organisational scaling at 50+ engineers โ not a default architecture for every project. The costs are real: distributed systems complexity, data consistency challenges, and operational overhead that can consume a small team entirely.
Start with a monolith. Structure it well. Extract services when the problems they solve become real and measurable. The teams that do this ship faster early, understand their domain better when they do split, and draw service boundaries that actually hold.
The goal is software that ships reliably and scales when needed โ not an architecture diagram that looks impressive before a single user has signed up.
Related reads: How Twitter Handles 500 Million Tweets a Day ยท Kubernetes Explained: Why Companies Actually Use It ยท Why Apps Crash Under High Traffic