In This Guide
- 1. An Honest Comparison — Not the Marketing Version
- 2. Why Monoliths Win (More Often Than You Think)
- 3. When Microservices Actually Make Sense
- 4. The Modular Monolith — The Best of Both Worlds
- 5. Migration Patterns — Strangler Fig and Beyond
- 6. The Hidden Costs of Microservices
- 7. The Decision Framework
- 8. Frequently Asked Questions
The microservices conversation has matured. In 2018, the question was "should we use microservices?" In 2026, the question is "should we use microservices yet?" The industry has learned — sometimes painfully — that microservices solve organizational scaling problems, not technical ones. If you don't have the organizational problems, you don't need the organizational solution.
1. An Honest Comparison — Not the Marketing Version
| Dimension | Monolith | Microservices | Honest Take |
|---|---|---|---|
| Development speed | Fast early, slows as codebase grows | Slow early, consistent at scale | Monolith wins until 50+ developers |
| Deployment | Deploy everything together | Deploy services independently | Independent deploy is real — but coordination overhead is hidden |
| Scaling | Scale the whole app | Scale individual services | Most apps don't need differential scaling |
| Debugging | Stack trace tells you everything | Distributed tracing across services | Distributed debugging is 10x harder, always |
| Data consistency | Single database, ACID transactions | Eventual consistency, sagas, 2PC | Distributed transactions are the hardest problem in microservices |
| Team autonomy | Shared codebase, merge conflicts | Teams own their services end-to-end | This is the real reason to adopt microservices |
| Operational complexity | 1 server, 1 deploy, 1 log stream | Kubernetes, service mesh, distributed logging | Microservices require a platform team. Do you have one? |
2. Why Monoliths Win (More Often Than You Think)
The "monolith to microservices" narrative implies monoliths are a phase you grow out of. That's not true. Many successful companies at significant scale run monoliths:
| Company | Architecture | Scale | Why It Works |
|---|---|---|---|
| Shopify | Modular monolith (Ruby on Rails) | $2B+ GMV/day, 3000+ engineers | Invested in tooling to manage monolith at scale |
| Stack Overflow | Monolith (C# / .NET) | 100M+ monthly visitors | 9 servers total, performance-optimized monolith |
| Basecamp / Hey | Monolith (Ruby on Rails) | Millions of users, ~70 employees | Small team, simple architecture, fast iteration |
| Amazon (early) | Started monolith, split at 150+ teams | Migrated when team coordination broke | Split driven by organizational pain, not technical limits |
Monolith advantages people underestimate:
- Refactoring is easy — rename a function, update all callers, done. In microservices, API changes require versioning, deployment coordination, and backward compatibility.
- Testing is straightforward — integration tests hit real code paths. In microservices, you need contract testing, consumer-driven contracts, or end-to-end tests across services.
- Transactions just work — a database transaction spans your entire business operation. In microservices, you need sagas, compensation events, and eventual consistency.
- One developer can understand the whole system — this matters enormously for debugging and architecture decisions.
3. When Microservices Actually Make Sense
Microservices solve organizational problems. If you have these symptoms, microservices might be the right move:
| Symptom | Why Monolith Hurts | How Microservices Help |
|---|---|---|
| 10+ teams stepping on each other | Merge conflicts, deploy queues, shared ownership | Teams own their service, deploy independently |
| Different scaling requirements | Search needs 10x more compute than auth | Scale search independently from auth |
| Different technology needs | ML team needs Python, backend is Java | Each service uses the best tool for the job |
| Deploys take hours, break unrelated features | Everything deploys together, blast radius is the whole app | Deploy one service without affecting others |
| Compliance/security isolation | PCI-scoped code mixed with everything else | Payment service isolated with strict controls |
Notice: all these are organizational problems, not technical ones. If you have 5 developers and no scaling issues, microservices add complexity without solving real problems. The famous quote from Martin Fowler still holds: "Don't even consider microservices unless you have a system that's too complex to manage as a monolith."
4. The Modular Monolith — The Best of Both Worlds
A modular monolith is a single deployable application with strong internal boundaries between modules. Each module has its own data, its own public API, and its own business logic — but they all deploy together and share a process.
src/
├── modules/
│ ├── auth/
│ │ ├── auth.module.ts // Module registration
│ │ ├── auth.service.ts // Business logic
│ │ ├── auth.controller.ts // HTTP handlers
│ │ ├── auth.repository.ts // Data access (own tables only)
│ │ └── auth.public-api.ts // ✅ The ONLY way other modules call auth
│ ├── orders/
│ │ ├── orders.module.ts
│ │ ├── orders.service.ts
│ │ ├── orders.controller.ts
│ │ ├── orders.repository.ts // Can NOT access auth tables
│ │ └── orders.public-api.ts
│ └── notifications/
│ ├── notifications.module.ts
│ └── ...
├── shared/ // Only truly shared utilities
│ ├── database.ts // Connection pool
│ └── events.ts // In-process event bus
└── app.ts // Composes all modules
// Rule: modules communicate via public APIs or events, NEVER by
// importing internal files or querying each other's database tables.
// This makes future extraction to microservices trivial.
We recommend the modular monolith as the default architecture for new projects. It gives you the development speed and simplicity of a monolith, with the organizational clarity of microservices. When (if) you need to extract a service, the module boundary is already defined — it's a weekend project, not a 6-month migration. Most of our clients never need to extract. The ones that do are glad the boundaries were already clean.
5. Migration Patterns — Strangler Fig and Beyond
If you've decided to migrate, never do a big-bang rewrite. The Strangler Fig pattern (named after a vine that gradually replaces a tree) is the safest approach:
| Pattern | How It Works | Risk | Best For |
|---|---|---|---|
| Strangler Fig | Route traffic to new service for new features, old monolith for old ones | Low — gradual, reversible | Most migrations |
| Branch by Abstraction | Create interface, implement in monolith AND new service, switch | Low — feature flagged | Extracting a specific module |
| Parallel Run | Run both old and new, compare results, switch when confident | Medium — double compute cost | Critical paths where correctness matters |
| Big Bang Rewrite | Rewrite everything, switch over at once | Very high — "second system effect" | Almost never. Seriously, don't. |
What to Extract First
Don't start with the hardest, most critical service. Start with something that:
- Has clear boundaries (few dependencies on other modules)
- Is not on the critical transaction path (not payments or auth)
- Would benefit from independent scaling (notifications, image processing, search)
- Has a team willing to own it end-to-end
6. The Hidden Costs of Microservices
Microservices advocates talk about the benefits. Here are the costs they don't mention in conference talks:
| Hidden Cost | What It Means | Infrastructure Required |
|---|---|---|
| Service discovery | How does Service A find Service B? | Consul, Kubernetes DNS, AWS Cloud Map |
| Distributed tracing | Following a request across 8 services | Jaeger, Zipkin, Datadog APM, OpenTelemetry |
| Configuration management | Managing config for 30 services | Vault, AWS Parameter Store, Consul KV |
| API versioning | Service B changes its API, Service A breaks | API contracts, schema registries, consumer-driven contracts |
| Data consistency | Order created in Order Service but Payment Service rejected | Sagas, outbox pattern, event-driven architecture |
| Platform team | Someone has to manage Kubernetes, CI/CD for 30 repos, monitoring | 2-4 dedicated engineers minimum |
7. The Decision Framework
| If You Have... | Recommendation | Reasoning |
|---|---|---|
| New project, <10 developers | Modular monolith | Ship fast, clean boundaries, extract later if needed |
| 10-50 developers, some team friction | Modular monolith → selective extraction | Extract the modules causing the most friction |
| 50+ developers, deploy queues, team bottlenecks | Microservices (with platform team) | Organizational scaling requires service boundaries |
| Extreme scaling variance between features | Hybrid — extract hot-path services | Search, ML inference, media processing as separate services |
| Compliance isolation requirements | Hybrid — extract regulated components | Payment processing, PII handling in separate, hardened services |
8. Frequently Asked Questions
Should a startup begin with microservices?
Almost never. Startups need to iterate fast, pivot easily, and ship features with a small team. Microservices add overhead that slows all of this down. Start with a well-structured monolith (ideally modular). You can always extract services later when you've found product-market fit and are scaling the team. The startups that succeed with microservices from day one typically have founders with deep microservices experience from previous companies.
How small should a microservice be?
Ignore advice about lines of code or "fits in your head." A service should be owned by one team (2-pizza team rule), represent a bounded context from your domain, and be independently deployable. If two services always deploy together, they should be one service. If one service handles two unrelated business capabilities, it might need splitting. Think in terms of business boundaries, not code size.
Do microservices have to use different databases?
Ideally yes — "database per service" is the standard pattern because it ensures loose coupling. But in practice, some teams start with separate schemas in the same database instance (to reduce operational overhead) and move to separate instances later. The critical rule: services must not directly query each other's tables. Always go through the service's API. The database is an implementation detail.
How do we handle transactions across microservices?
Use the saga pattern — a sequence of local transactions where each service completes its part and publishes an event. If a step fails, compensating transactions undo the previous steps. For example: Order Service creates order → Payment Service charges card → if payment fails → Order Service cancels order. The outbox pattern ensures events are reliably published. This is genuinely hard — it's the #1 technical challenge in microservices. See our event-driven architecture guide for implementation details.
Can we use Kubernetes without microservices?
Absolutely. Kubernetes is useful for any containerized application — monolithic or not. It gives you auto-scaling, health checks, rolling deploys, and infrastructure abstraction. A monolith on Kubernetes gets the same operational benefits. You don't need microservices to justify Kubernetes (or vice versa). See our Kubernetes vs Docker Swarm comparison for more.
Pillai Infotech LLP
We help teams choose the right architecture for their stage — and migrate when they outgrow it. From modular monoliths to microservice extraction, our architects have been through it. Let's design your architecture.