From Monolith to Microservices: What the Books Don't Tell You
The hard-won lessons from migrating enterprise monoliths to microservices architectures. Spoiler: it's not about the technology.
Antonio J. del Águila
Knaisoma
We have been part of more monolith-to-microservices migrations than we care to count. Some succeeded. Many did not. And in every single case, the outcome had less to do with the technology chosen and more to do with how the organization approached the problem.
The conference talks and the architecture books give you the theory. What they rarely give you is the ground truth of what it actually feels like to decompose a 15-year-old monolith while keeping the business running. This post is about that ground truth.
The organizational challenge
Before you draw a single architecture diagram, you need to answer a fundamental question: is your organization structured in a way that can own and operate microservices?
Conway’s Law is not a suggestion. It is a force of nature. If your organization has a frontend team, a backend team, and a database team, you will not build microservices. You will build a distributed monolith with network calls where function calls used to be. We have seen this happen at a major telecommunications company. They spent 18 months “migrating to microservices” and ended up with 40 services that all had to be deployed together in a specific order. They had not decomposed the system. They had distributed it.
Team boundaries must precede service boundaries. Before splitting a single service, restructure your teams around business capabilities:
- Payments team owns the payments service, its database, and its API
- Inventory team owns the inventory service, its database, and its API
- Each team can deploy independently, on their own schedule
This is not a technical decision. This is an organizational design decision that requires executive sponsorship. If you cannot get that sponsorship, reconsider whether microservices are the right move.
The strangler fig in practice
The Strangler Fig pattern is the textbook approach, and it works. But the textbooks leave out the messy details.
Start with the edges, not the core. The most successful migrations we have led began with capabilities that had clear boundaries and limited coupling to the core domain. A notifications service. A reporting pipeline. An authentication gateway. These give your team migration experience without risking the crown jewels.
# Phase 1: Edge services
[Monolith] --> [New: Notification Service]
[Monolith] --> [New: Reporting Service]
# Phase 2: Supporting domains
[Monolith] --> [New: User Profile Service]
|
v
[New: Notification Service]
# Phase 3: Core domains (after learning)
[Monolith (reduced)] --> [New: Order Service]
[New: Payment Service]
[New: Inventory Service]
Maintain backward compatibility ruthlessly. During migration, you will have a period where both the monolith and the new services need to coexist. This period always lasts longer than you plan for. At one insurance company, the “6-month migration” took 3 years. Plan your APIs and data flows accordingly.
// API versioning is non-negotiable during migration
// Old endpoint (monolith) stays alive
// GET /api/v1/orders/{id} -> monolith
// New endpoint (microservice) is introduced alongside
// GET /api/v2/orders/{id} -> order-service
// Router decides based on migration state
{
"routing_rules": {
"/api/v1/orders/*": { "target": "monolith", "status": "deprecated" },
"/api/v2/orders/*": { "target": "order-service", "status": "active" },
"/api/v1/orders/legacy/*": { "target": "monolith", "status": "frozen" }
}
}
Feature flags are your safety net. Every migrated capability should be behind a feature flag that lets you route traffic back to the monolith within minutes. Not hours. Minutes. The moment your rollback requires a deployment, you have lost your safety net.
Data is the hard part
We cannot emphasize this enough: the technology challenges of microservices are manageable. The data challenges will keep you up at night.
Shared databases are the enemy. The monolith almost certainly has a single database with complex joins spanning multiple business domains. Decomposing that database is the hardest part of the entire migration. There is no shortcut.
Start by identifying data ownership:
- Which service is the source of truth for each entity?
- Where do you need eventual consistency vs. strong consistency?
- What cross-domain queries exist, and how will they work when the data is separated?
Event sourcing is your friend, but not on day one. Many teams reach for event-driven architecture too early. Before you build an event bus, start with simple patterns:
- API calls between services for synchronous needs
- Change data capture from the monolith’s database for async replication
- Materialized views in consuming services for read-heavy patterns
# Start simple: CDC from monolith to new services
apiVersion: kafka.strimzi.io/v1beta2
kind: KafkaConnect
metadata:
name: monolith-cdc
spec:
config:
connector.class: io.debezium.connector.postgresql.PostgresConnector
database.hostname: monolith-db.internal
database.dbname: monolith
table.include.list: public.orders,public.order_items
topic.prefix: monolith.cdc
Once your team has experience operating event-driven patterns, you can evolve toward a full event sourcing architecture. But trying to decompose a monolith and adopt event sourcing simultaneously is a recipe for disaster.
When not to migrate
This is the section most architects skip, but it might be the most important one.
Do not migrate if your monolith is working. If your deployment frequency is acceptable, your mean time to recovery is low, and your teams can ship features without stepping on each other, you do not have a problem that microservices solve. A well-structured modular monolith can take you very far.
Do not migrate if you have fewer than 50 engineers. Microservices trade development complexity for operational complexity. If you do not have the headcount to staff dedicated platform engineering, on-call rotations for multiple services, and observability infrastructure, the operational overhead will crush you.
Do not migrate all at once. If someone presents a plan to “rewrite the monolith as microservices” in one big-bang project, run. Incremental migration with the Strangler Fig pattern is the only approach we have seen succeed at scale.
The best architecture is the one that lets your organization deliver value to customers reliably and sustainably. Sometimes that is microservices. Sometimes it is a well-structured monolith. The skill is knowing which one you actually need, not which one is trending on conference stages.
Stay updated
Get insights on engineering transformation delivered to your inbox.
Newsletter coming soon.