We now know what microservices are, what advantages and costs they bring, and how they compare with the monolith. What remains is the most important question, and the one least often answered with rigor: when should you adopt them? Far too many organizations make this decision out of fashion, under pressure from a vendor, or because "Netflix does it", and end up paying the complexity of a distributed system without getting any of its advantages.
This lesson offers concrete, verifiable criteria for deciding. We will look at the factors that genuinely tip the balance (organization, DevOps maturity, domain knowledge, scaling, availability, delivery speed), the "monolith first" approach, the signs that a monolith is asking to be split and the signs that point the other way, the minimum prerequisites without which you should not start, a decision checklist, the hidden costs nobody puts in the slide deck, and the most common anti-patterns. The exercises will ask you to decide on several different fictional scenarios, including TechCorp itself. We will not go into how a migration is executed (lesson 08-01) or into the decomposition technique (lesson 02-02): here the point is deciding whether, not how.
Contents
- The criteria that matter
- The "monolith first" approach
- Signs that a monolith is asking to be split
- Signs that it is NOT a good idea
- Minimum prerequisites
- Decision checklist
- Hidden costs
- Anti-patterns
- Common mistakes and tips
- Exercises
- Conclusion
- The criteria that matter
1.1 Size and structure of the organization: Conway's Law
In 1967, Melvin Conway observed that organizations design systems that mirror their communication structure. If you have three teams, you will have three subsystems; if the teams communicate poorly, the interfaces between their subsystems will be poor.
Applied to our decision:
- A single small team (say, fewer than 8-10 people) gets almost nothing from the inter-team autonomy microservices offer, because there is nobody to become independent from. It does get, however, all of the operational cost.
- Several teams getting in each other's way while working on the same code and the same deployment are the clearest sign that service boundaries aligned with those teams would help.
- Structure matters as much as size: if teams are organized by technical layer (front-end, back-end, database, systems), microservices fit poorly, because every service would need every team. If they are organized by business capability (orders team, catalog team), they fit naturally.
There is even a strategy called the inverse Conway maneuver: reorganize the teams first, around the desired business capabilities, so that the resulting architecture follows that shape. At TechCorp, today there is a single back-end team; part of the journey will consist of Luis's team genuinely becoming "the Orders team".
1.2 DevOps maturity and automation
Microservices multiply the number of things that have to be built, tested, deployed and watched. If today the monolith is deployed by hand, if there are no reliable automated tests, or if nobody knows what is happening in production until a customer complains, splitting the system will turn one problem into six. Ask yourself: can we deploy to production with one click and with confidence? Do we have centralized metrics and logs? If the answer is no, that is the first job, not the split.
1.3 Domain knowledge: stable versus exploratory
Splitting a system into services means fixing boundaries between business capabilities. Those boundaries are expensive to move later: relocating a responsibility between two services means renegotiating contracts, migrating data and coordinating deployments.
- If the domain is stable and well understood (an online store that has been selling for years, with clear areas such as catalog, orders and payments), the boundaries can be drawn with confidence.
- If the domain is exploratory (a startup that still changes its business model every quarter), any split will be premature and will have to be redone. In a monolith, moving a responsibility from one module to another means changing a few files; in microservices, it is a project.
1.4 Differentiated scaling needs
If every part of the system grows in load in a similar way, replicating the monolith works fine. Selective scaling only adds value when there are parts with very different load profiles (at TechCorp, the catalog receives a hundred times more reads than payments receives writes) or with very different resource needs (a service that generates heavy reports next to another that serves lightweight requests).
1.5 Availability requirements
If a failure in a secondary part of the system (emails, statistics) cannot be allowed to bring down the critical part (charging), the isolation offered by separate services is valuable. If the whole application has the same requirements and a one-hour outage is acceptable, that argument carries little weight. Careful: microservices offer the possibility of isolating failures, not the guarantee; it has to be designed.
1.6 Delivery speed
Is the pace of delivery limited by the architecture? If teams wait for the weekly deployment, if a small change requires testing everything, if review queues clog up with conflicts between teams, the architecture is holding delivery back. If the pace is limited by other things (lack of people, unclear requirements, slow decisions), microservices will not speed it up.
Summary of criteria
| Criterion | Tips toward microservices when... | Tips toward the monolith when... |
|---|---|---|
| Organization | Several business-capability teams getting in each other's way. | One small team, or teams per technical layer. |
| DevOps maturity | CI/CD, containers and observability already work. | Manual deployments, no automated tests or monitoring. |
| Domain | Stable and well understood. | Exploratory, changing. |
| Scaling | Parts with very different load profiles. | Homogeneous or low load. |
| Availability | Need to isolate the critical from the secondary. | Uniform, tolerable requirements. |
| Delivery speed | Held back by deployment coupling. | Held back by other causes. |
- The "monolith first" approach
Martin Fowler and other authors have argued for years for a very pragmatic stance: start with a monolith, even if you believe you will end up with microservices. The arguments:
- You do not know the domain well enough at the start. The boundaries you draw on day one will be wrong, and in a monolith correcting them is cheap.
- The initial cost of microservices delays value. A new product needs to reach the market, not a Kubernetes cluster.
- A well-organized modular monolith can be split later with relative ease, by extracting modules that already have clear boundaries.
- Almost every microservices success story started as a monolith that grew until it hurt. Attempts to start directly with microservices have a much worse track record.
The practical way to apply it: build a modular monolith from the start (modules with an internal API, each owning its tables), automate deployment and observability, and extract a service only when a specific module has a specific reason (scaling, its own team, a different technology, isolation). This is exactly the path TechCorp will follow in the course: starting from a monolith that already hurts, not from a blank page.
flowchart LR
A[Initial<br/>monolith] --> B[Modular<br/>monolith]
B --> C{Does a module have<br/>a concrete reason<br/>to be separated?}
C -- No --> B
C -- Yes --> D[Extract that<br/>service]
D --> C
- Signs that a monolith is asking to be split
These signs, especially when several appear at once, indicate that the monolith has reached a point where its cost exceeds that of microservices:
- Slow, infrequent, dreaded deployments. Deployment has become an event ("deployment Friday"), requires maintenance windows and is frequently rolled back.
- Teams stepping on each other. Constant conflicts in the code, waiting to merge changes, one team blocked by another.
- Inefficient, expensive scaling. The whole application is replicated because of one part; the infrastructure bill grows faster than the business.
- Failures that propagate. A bug in a secondary module takes down critical functions.
- Test suites that take tens of minutes and that everyone tries to skip.
- Very long onboarding time for new developers because nobody understands the whole system.
- A real technological need in one part (for example, a data model that is torture in the current database).
TechCorp shows almost all of these signs, as we will see in lesson 01-05.
- Signs that it is NOT a good idea
With the same clarity, situations in which splitting would be a mistake:
- A single small team that works well with the monolith.
- A product still searching for its business model; the domain changes too much.
- No automation: manual deployments, no reliable tests, no monitoring. Fix that first.
- Low or uniform load: a modest server handles everything comfortably.
- The motivation is fashion, a résumé or a vendor presentation, not a concrete problem.
- The monolith hurts because of poor code quality, not because of deployment coupling. The solution is to refactor toward a modular monolith; distributing the mess only makes it worse.
- The organization is not willing to change: teams per layer, an operations department that centralizes everything, very hierarchical decisions. The architecture will collide with the structure and lose.
- Minimum prerequisites
Before extracting the first service, these four pillars should be reasonably in place. Without them, every new service adds risk instead of removing it.
| Prerequisite | What it means in practice | Course module where it is covered |
|---|---|---|
| CI/CD | Every change is built and tested automatically; deployment to production is a fast, repeatable process, not a ritual. | 5 |
| Containers (or equivalent) | Each service is packaged with its dependencies reproducibly and runs the same locally, in testing and in production. | 5 |
| Observability | Centralized logs, metrics and, as soon as there is more than one service, distributed traces. Without this, debugging is guesswork. | 6 |
| Ownership culture | Teams responsible for their services end to end, operation included ("you build it, you run it"), and allowed to deploy without asking a committee for authorization. | cross-cutting |
Good advice: build these pillars on the monolith before splitting it. A monolith with CI/CD, containers and good observability is already a much better system, and if it is eventually split, the split will be infinitely safer.
- Decision checklist
Use this list as a matrix. It is not a mathematical formula, but a way of forcing yourself to look at every factor. Count the affirmative answers in each block.
Block A. Reasons to split (the more, the more sense it makes)
- [ ] There are two or more teams getting in each other's way while working on the same code and deployment.
- [ ] Teams are (or can be) organized by business capability.
- [ ] There are parts of the system with clearly different scaling needs.
- [ ] A failure in secondary parts has taken down (or can take down) critical functions.
- [ ] Deployments are slow, infrequent or dreaded because of coupling.
- [ ] The domain is stable and its areas can be named without debate.
- [ ] There is a real technological need in some specific part.
Block B. Ability to do it (all should be yes, or on the way)
- [ ] Reliable CI/CD exists.
- [ ] The team knows how to build and operate containers.
- [ ] There are centralized logs and metrics, and there is willingness to add traces.
- [ ] Teams can deploy without external authorization and take responsibility for operation.
- [ ] There are budget and people to operate more infrastructure (broker, gateway, orchestrator, several databases).
Block C. Warning signs (a single one should make you stop and think)
- [ ] The main motivation is fashion, a vendor or a résumé.
- [ ] The total team is very small (fewer than about 8-10 people) with no growth expected.
- [ ] The business model or the domain changes frequently.
- [ ] The current pain is due to poor code quality, not deployment coupling.
Indicative interpretation:
| Result | Recommendation |
|---|---|
| A ≥ 4, B complete, C empty | Adopt microservices incrementally, extracting first the module with the clearest reason. |
| A ≥ 4, B incomplete | Build the prerequisites on the monolith first; in the meantime, modularize it. |
| A ≤ 3, C empty | Modular monolith; revisit in 6-12 months. |
| Any C checked | Do not split yet; address the cause of the warning sign. |
- Hidden costs
There are costs that rarely appear in the initial proposal and always appear on the bill:
- Supporting infrastructure the monolith did not need: message broker, gateway, orchestrator, container registry, observability tooling, secrets management. Each one has to be installed, upgraded, secured and operated by someone who knows how.
- Duplication: utility code, configuration, pipelines, monitoring dashboards, security policies... multiplied by the number of services.
- Contract coordination: time spent agreeing, documenting and versioning APIs and events, and maintaining backward compatibility.
- More complex development and test environments: bringing up "the whole store" locally goes from
npm startto orchestrating a dozen containers. - Training: the team has to learn to reason about distributed systems (idempotency, retries, eventual consistency), and that curve is real.
- On-call and support: more services means more alerts and more things that can wake someone up at three in the morning.
- Cost of undoing: if the split turns out to be a mistake, consolidating services again is as expensive as splitting them.
- Anti-patterns
"Microservices by fashion" (resume-driven architecture)
The architecture is adopted because it is what the competition does, because it looks good in a job posting, or because a cloud vendor recommends it. There is no concrete problem to solve. Typical result: a system that is more complex, more expensive and slower to develop than the monolith it replaced, with no tangible advantage.
Nanoservices
Services so small that their scope is not a business capability but a technical function ("postal code validation service", "date formatting service"). The cost of communication and operation far exceeds the value of the separation. Rule of thumb: if a service cannot do anything useful without calling three others, it is probably too small.
Distributed monolith
We have already named it in earlier lessons because it is the most damaging anti-pattern: several processes that share a database, that must be deployed together because their contracts change in lockstep, or that depend synchronously on one another in such a way that none works if another goes down. It has all the cost of the network and none of the advantages of autonomy. How to detect it:
| Question | If the answer is yes... |
|---|---|
| Does a schema change in one service force a change in another? | They share data: distributed monolith. |
| Do several services have to be deployed in a specific order and at the same time? | Coupled contracts: distributed monolith. |
| If service X goes down, do all the others stop working? | Runtime coupling with no resilience. |
| Is there a shared "models" library that everyone imports and that must be updated everywhere at once? | Coupling through shared code. |
Other frequent anti-patterns
- Entity service: one service per table (
customer-service,address-service,phone-service) instead of per business capability. - Shared services layer that everyone depends on ("common utilities service"), which reintroduces central coupling.
- Big bang: rewriting the entire monolith as microservices in one go, in a two-year project, delivering nothing along the way. The incremental alternative is studied in lesson 08-01.
Common Mistakes and Tips
- Deciding without data. "The monolith is slow" or "the teams step on each other" should be quantifiable: deployment frequency, test suite duration, number of rollbacks, infrastructure cost per order. Without numbers, the decision is an opinion.
- Skipping the prerequisites "because we'll add them later". Later never comes, and in the meantime a distributed system is being operated blind.
- Splitting by layers or by tables. Boundaries must follow business capabilities; anything else produces nanoservices or entity services.
- Ignoring the organization. If teams are not reorganized around the capabilities, Conway's Law will pull the system back into the shape of the organization, with the added complexity.
- Treating the decision as final and global. It is not "all or nothing": you can extract one service, check whether it pays off, and decide the next step with what you learned.
- Tip: fill in the checklist from section 6 with at least two other people and compare answers. The discrepancies are the most valuable conversation you can have before deciding.
Exercises
In each scenario, apply the checklist from section 6 and make a reasoned decision: microservices (incremental), modular monolith, or "not yet". Justify it with the criteria from the lesson.
Exercise 1: TechCorp Labs, a 4-person startup
A former TechCorp team founds a startup to sell educational electronics kits. They are four people (three developers and one product person). They have been going for six months; they have changed the business model twice (first direct sales, then a monthly subscription, and now they are debating adding a marketplace). They deploy by hand with git pull on a server. They receive about 300 orders a month. The technical founder has read about microservices and wants to "do it right from the start".
Exercise 2: an insurance company with 12 teams
An insurance company has a monolithic Java platform with twelve development teams organized by product (home, auto, life, health...) working on the same repository. Deployments are monthly, require a night-time window and a change committee, and are frequently rolled back. The test suite takes 90 minutes. They have CI, containers in pre-production, and a platform team that already operates Kubernetes for other systems. The products are stable (they have been selling them for fifteen years). The pricing area consumes ten times more CPU than the rest and forces everything to be over-provisioned. Teams complain about waiting weeks for their changes to reach production.
Exercise 3: TechCorp itself
With what you know about TechCorp (a Node.js monolith with one PostgreSQL, six functional areas, dreaded weekly deployments, outages during campaigns because of catalog saturation, a back-end team that is starting to organize by area with Luis leading Orders, basic CI and containers in testing but no orchestration in production and no observability beyond the server logs), decide what you would recommend to Marta and what she should do before extracting the first service.
Solutions
Exercise 1: TechCorp Labs
- Block A: practically no boxes checked. One team, minimal load, no deployment problems due to coupling.
- Block B: no CI/CD or observability; manual deployments.
- Block C: three warning signs: very small team, exploratory domain (two pivots in six months), and motivation to "do it right" with no concrete problem.
- Decision: monolith, and it does not even need to be very modular yet. What does make sense: automating deployment (simple CI/CD) and adding basic monitoring. If in a year the business stabilizes and the team grows, evolve toward a modular monolith. Starting with microservices here would delay the product by months and lock in boundaries for a domain that does not exist yet.
Exercise 2: the insurance company
- Block A: twelve teams getting in each other's way (yes), organized by product (yes, by business capability), differentiated scaling in pricing (yes), slow and dreaded deployments (yes), stable domain (yes). Five or six boxes.
- Block B: CI yes, containers yes, a Kubernetes platform operated by a specialized team yes. Observability probably partial (traces would need checking). Ownership culture: not entirely; there is a change committee that approves deployments; that is the point to work on.
- Block C: no clear warning sign; the pain comes from coupling, not from code quality (although that is worth checking).
- Decision: adopt microservices incrementally. Obvious candidate for the first extraction: pricing, which has the clearest scaling reason and probably a sharp domain boundary. In parallel, change governance so that teams can deploy without a committee (with automated controls in the pipeline) and secure distributed observability before there are more than two or three services.
Exercise 3: TechCorp
- Block A: teams stepping on each other (yes, there are starting to be several), organization by business capability (under way), differentiated scaling (yes, catalog versus the rest), failures that propagate (yes, campaigns take down the whole store), dreaded deployments (yes), stable domain (yes, it is a store with clear areas). At least five boxes.
- Block B: basic CI (partial), containers only in testing (partial), no orchestration in production (no), observability limited to logs (no), nascent ownership culture (partial).
- Block C: no warning sign; the pain is real and comes from coupling.
- Decision: yes to microservices, but incrementally and with homework first. Before extracting the first service, Marta should: consolidate CI/CD for the monolith, take containers to production with an orchestrator, set up observability (centralized logs, metrics, and prepare for traces), and consolidate teams by area with operational responsibility. While that is being done, modularize the monolith so that the boundaries of catalog, orders, etc. become explicit. The first reasonable candidate to extract is the catalog, because of its crystal-clear scaling reason and because it is mostly read-only (low risk). This is, in fact, the roadmap the course will follow, presented in the next lesson.
Conclusion
Deciding whether to adopt microservices is not a matter of taste or trends, but of weighing concrete problems against concrete capabilities. The criteria that tip the balance are the structure of the organization (Conway's Law), DevOps maturity, the stability of the domain, differentiated scaling and availability needs, and a real brake on delivery speed. The "monolith first" approach recommends starting with a modular monolith and extracting services only when there is a specific reason; the pain signals (dreaded deployments, teams stepping on each other, inefficient scaling, failures that propagate) indicate when that moment has come, and the opposite signals (small team, changing domain, no automation, motivation by fashion) indicate when it has not. The four prerequisites (CI/CD, containers, observability and ownership culture) should be built on the monolith before splitting it, and the decision checklist forces you to look at every factor at once, including the hidden costs and the anti-patterns to avoid (fashion, nanoservices, distributed monolith).
We applied these criteria to TechCorp and the conclusion is that its case justifies the migration, with homework first. In the next lesson we will present that case in full detail: who TechCorp is, what its monolith looks like today, what problems it suffers, which core business flow will accompany us throughout the course, the map of services we will build, and the module-by-module roadmap.
Microservices Course
Module 1: Introduction to Microservices
- Basic Concepts of Microservices
- Advantages and Disadvantages of Microservices
- Comparison with the Monolithic Architecture
- When to Adopt Microservices: Decision Criteria
- The Course Case Study: TechCorp's Online Store
Module 2: Microservice Design
- Microservice Design Principles
- Decomposing Monolithic Applications
- Defining Bounded Contexts
- Data Management: One Database per Service
- Distributed Consistency: Sagas, CQRS and Event Sourcing
Module 3: Communication between Microservices
- RESTful APIs
- Asynchronous Messaging
- Communication Protocols: gRPC, GraphQL
- API Gateway and Backend for Frontend
- Service Discovery and Load Balancing
- API Contracts and Versioning
Module 4: Implementing Microservices
- Choosing Technologies and Tools
- Building a Simple Microservice
- Configuration Management
- Hands-On Integration: Consuming APIs and Publishing Events
- Testing Microservices: Unit, Integration and Contract Tests
Module 5: Deployment and Orchestration
- Containers and Docker
- Orchestration with Kubernetes
- CI/CD for Microservices
- Deployment Strategies: Rolling, Blue-Green and Canary
- Service Mesh: Istio and Linkerd
Module 6: Monitoring and Maintenance
- Monitoring and Logging
- Distributed Tracing with OpenTelemetry
- Error Handling and Recovery
- Scalability and Performance
- SLOs, Alerts and Incident Management
Module 7: Security in Microservices
- Authentication and Authorization
- Communication Security
- Security Practices
- Container and Kubernetes Security
