Monoliths vs Microservices: A Love-Hate Relationship


“Every act of creation is first an act of division.”
— Picasso, probably describing software architecture.
I’m not giving TLDR because your attention time is too short, go read it all ;).
Software architecture isn’t just about patterns and principles, it’s about trade-offs. And few debates highlight those trade-offs between monoliths and microservices.
They’re not like south pole or north pole. They’re just… different. And as with all different things in tech, people argue about them on Twitter.
This post aims to unpack their strengths, weaknesses, and how to make the right call, without the hype ;).
The Monolith: The Default Starting Point
Here’s the internet definition of Monolith.
A monolith is a geological feature consisting of a single massive stone or rock, such as some mountains.
— wikipedia
Monoliths are simple, not in the “lacking capability” way, but in the “everything’s in one place and just works” kind of way.
You’ve got:
One codebase
One database
One deployment artifact
Fewer moving parts
# Build and deploy the whole app
npm run build && docker build -t myapp . && docker run -d myapp
This makes them great for:
Early-stage products because your biggest risk is building the wrong thing, not scaling the right thing.
Small teams because you don’t need an org chart just to understand the codebase.
Fast iteration since you can trace a bug across the whole app without switching contexts, terminals, or AWS accounts.
But there are caveats:
You can’t scale individual parts, it’s all or nothing (like your new girlfriend).
Tight coupling can happen quickly. Your "user" module might start importing "billing," which depends on "email," and before you know it, half the app is involved.
Deployments become riskier as the app grows. One tiny change can require a full redeploy of everything.
You can think of monoliths like smartphones. Everything’s packed together. It’s efficient, portable, and great until you want to upgrade just the camera and not the entire device.
Microservices: The Divide-and-Conquer Strategy
Microservices break your application into multiple independent services, each responsible for a specific domain.
This lets you:
Deploy independently
Scale components separately
Use the right tool/language for each service (if you like chaos)
Your typical architecture ends up looking something like:
[frontend] --> [auth-service] --> [user-service] --> [payment-service] --> [email-service]
Each service:
Has its own codebase
Talks over HTTP or gRPC
Owns its own data (ideally)
This shines when:
You have multiple teams working on different parts of the system
Scalability becomes an issue (e.g., payment service handles much more load than others)
You want fault isolation e.g., if the recommendation engine goes down, checkout still works
But the trade-offs are real:
Operational complexity explodes: now you need service discovery, API gateways, centralized logging, tracing, monitoring, retries, rate-limiting, and more.
Data consistency becomes a challenge. You’re now dealing with eventual consistency and distributed transactions (and possibly, regret).
Debugging spans network calls. A simple bug might mean digging through five services, three dashboards, and two dashboards that monitor the dashboards.
When to Use What: A Practical View
Here’s the short version:
Project Stage | Architecture You Want |
MVP / Startup | Monolith (keep it simple) |
Growing fast | Modular Monolith |
Scaling issues | Begin splitting into services |
Large team/org | Microservices, if needed |
Avoid this common trap: premature microservice-ification.
A lot of teams jump into microservices too early and end up spending months building infra instead of features.
Here’s my twitter handle if you wanna argue more 😄.
Modular Monoliths: The Middle Ground
There’s a sweet spot between monoliths and microservices: the modular monolith.
You still have one deployable unit, but the code is structured into strict modules, each owning a clear responsibility.
src/
├── users/
├── orders/
├── notifications/
├── shared/
Each module:
Has clear boundaries
Avoids reaching into others’ internals
Uses domain events or interfaces for communication
This makes it easier to extract a module into a service if and when needed. It's not glamorous, but it's maintainable, and that's a feature.
Real-World Considerations
Deployment
Monoliths are easy: one pipeline, one deploy.
Microservices need orchestration, usually Kubernetes, service discovery (Consul, etcd), and some YAML therapy.
Testing
Monoliths: easier integration testing, but tests may be slow.
Microservices: unit tests per service are fine, but integration tests across services require Docker Compose or full staging environments.
Teams
Monoliths make sense for small teams: fast iteration, less coordination.
Microservices work better when teams are split by domain and operate semi-independently.
The Law of Software Gravity
A good guiding principle:
Start monolithic. Modularize. Extract services when the pain justifies the cost.
Your architecture should evolve based on real needs, not hype. Think evolution, not revolution.
There’s no architectural purity award. There is, however, a metric for deploy speed, feature delivery, and bug response times. Optimize for those.
Final Thoughts
You don’t need to pick sides.
Both monoliths and microservices have their place. The real question is: what problems are you solving, and what level of complexity can your team reasonably handle?
If you’re a solo dev or small startup: go monolith. Structure it well. Extract services only when necessary.
If you’re at scale, with distributed teams and well-defined domains: go microservices but invest in tooling, observability, and communication.
Just don’t fall into the trap of copying Netflix’s architecture without Netflix’s problems.
Post-Script Actionable Wisdom:
Use microservices to manage complexity not to create it.
Don’t confuse “more services” with “more mature architecture.”
Review your architecture every 6–12 months. Codebases age faster than bananas.
That’s all for now. Remember, scaling your codebase is less about how many services you have, and more about how clean your boundaries are.
Happy deploying.
Subscribe to my newsletter
Read articles from Nayan Radadiya directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
