From Monolith to Microservices: A Practical Guide for CTOs at Growing Startups
Your monolith got you here. It let a small team ship fast, deploy through a single pipeline, and debug with a stack trace instead of tracing logs across five different services. It worked. And if you followed the principles of a well-designed modular monolith, it gave you a solid foundation.
But now something has changed. You have three teams working on the same repository and merge conflicts are constant. The payment processing module needs to scale differently from the notifications module. A deploy that should take ten minutes takes an hour because it requires cross-team coordination. A bug in the reporting module blocks the release of a billing feature that's been ready for a week.
These are real signals. And the answer isn't "let's rewrite everything as microservices this quarter." The answer is to evolve gradually.
When it's actually time to migrate
You don't migrate because microservices are trendy. You migrate because the monolith has become a bottleneck for your business. Here are the concrete signals:
Multiple teams stepping on each other. Two teams touch the same code, PRs block each other, and every merge requires coordination that takes longer than the development itself. Conway's Law kicks in: your architecture needs to reflect your organization.
Diverging scaling needs. Your search API needs to scale horizontally with user traffic, but your file processing service needs machines with more RAM and CPU. You scale the entire monolith to satisfy the most demanding component, paying for infrastructure the rest doesn't need.
Deploys that are too long and too risky. A three-line change requires deploying the entire application. The CI pipeline takes 45 minutes. And every deploy is an anxiety-inducing event because any part of the system could break.
One failure brings everything down. A memory leak in the reporting module takes down the customer API, the admin dashboard, and the billing system. There's no fault isolation.
If you recognize two or more of these signals, it's time to plan the migration. If you only see one, you can probably solve the problem with better internal modularization.
The Strangler Fig Pattern: don't rewrite, extract
The metaphor comes from the strangler fig, a plant that grows around an existing tree until it eventually replaces it entirely. Your monolith is the tree. The new services are the fig. And the key point is that the tree stays alive while the fig grows.
You don't rewrite the monolith. You extract services one by one, gradually, while the monolith keeps running in production. Each extraction is a small, controlled step. If something goes wrong, the monolith is still there as a fallback.
Step 1: Identify the boundaries
Before extracting anything, you need a clear map of your monolith's modules and their dependencies. If you designed a modular monolith with well-defined bounded contexts, this part is easier. If not, now is the time to do it.
Look for natural seams: modules that communicate with the rest through clear interfaces, that have their own data model, that represent a distinct business domain. In Domain-Driven Design terms, you're looking for bounded contexts.
Draw the dependency graph. Identify which modules depend on which. The ones with the fewest inbound and outbound dependencies are the easiest candidates for extraction.
Step 2: Extract the highest-pain module first
Don't start with the easiest module or the hardest one. Start with the one causing the most pain. Typically, it's the one that meets one or more of these conditions:
- It scales differently from the rest of the system.
- It changes frequently and creates conflicts with other teams.
- It has distinct technology requirements (needs GPUs, a different database, a specific runtime).
- Its failures disproportionately impact the rest of the system.
Extract that module as an independent service. Deploy it separately. Give it its own CI/CD pipeline. Assign a team to own it.
Step 3: Put an API gateway or reverse proxy in front
This is where the Strangler Fig magic happens. You place an API gateway (Kong, NGINX, AWS API Gateway, Traefik) in front of your entire system. Requests that used to go directly to the monolith now pass through the gateway.
The gateway decides: this request goes to the new service, this one still goes to the monolith. You can make the switch gradually — start by routing 5% of traffic to the new service, monitor, and progressively ramp up to 100%.
If the new service fails, the gateway can redirect back to the monolith. Migration without existential risk.
Step 4: Move the data (the hardest part)
This is where most migrations get complicated. As long as your new service is still reading from and writing to the same database as the monolith, you don't have a real microservice — you have a distributed monolith, which is the worst of both worlds.
The goal is for each service to own its own database. Here's the path to get there:
- Dual writes. The new service writes to its own database AND to the monolith's database during a transition period. You verify that the data is consistent.
- Event-based synchronization. You introduce an event system (Kafka, RabbitMQ, SNS/SQS) to keep data in sync between the service and the monolith.
- Cutover. When you trust that the new service is the source of truth, you drop the writes to the monolith's database. Other modules that need that data fetch it through the new service's API or consume its events.
Accept that there will be a period of eventual consistency. Don't try to maintain ACID transactions across services — that path leads to distributed transactions and two-phase commits, which are fragile and slow.
Step 5: Repeat
Extract the next service. And the next. Each extraction should take weeks, not months. If an extraction is taking three months, you're probably extracting a module that's too large or the dependencies weren't as clean as you thought.
With each extraction, the monolith gets smaller and more manageable. Eventually, what remains is just another service — or it naturally decomposes into the last two or three services.
The infrastructure you need
Microservices aren't just code in separate repositories. They need supporting infrastructure that didn't exist in your monolithic world:
Service discovery. Services need to find each other. Consul, Kubernetes internal DNS, or your cloud provider's service discovery.
Centralized logging. With ten services, you can't SSH into each machine to read logs. You need ELK Stack, Datadog, Grafana Loki — a single place where all logs converge.
Distributed tracing. A single user request can touch five services. Without tracing (Jaeger, Zipkin, Datadog APM), debugging an error is guesswork about which service caused it. Implement OpenTelemetry from the very first service.
Per-service CI/CD. Each service has its own pipeline. You change the payments service, only the payments service deploys. GitHub Actions, GitLab CI, or whatever tool you use — but independent per service.
Monitoring and alerts. Health checks, latency metrics, error rates, resource saturation — per service. If you can't see the state of each service on a dashboard, you're navigating blind.
What not to do
Don't extract everything at once. The temptation to "do the full migration in one quarter" is strong. Resist it. Each extraction is a controlled risk. Ten simultaneous extractions are chaos.
Don't create nano-services. A service with a single endpoint and 200 lines of code shouldn't be a service. It has all the operational overhead of a microservice with none of the benefits. A service should represent a business domain that stands on its own.
Don't start with the most coupled module. If the users module is tangled with the entire system, it's the worst candidate for the first extraction. Start with something that has fewer dependencies.
Don't forget monitoring. Every service you extract without proper monitoring is a black box in production. Observability first, extraction second.
Team structure: every service needs an owner
A microservice without a responsible team is an orphan service. And orphan services accumulate tech debt at an alarming rate because nobody feels ownership over maintaining them.
Every service needs a team that is its clear owner. That team decides its roadmap, manages its deploys, and responds when it goes down at three in the morning. It doesn't have to be a team dedicated exclusively to that service — a team can own two or three related services. But ownership must be explicit.
This is exactly what the inverse Conway maneuver describes: you design your architecture to reflect the team structure you want. If you want autonomous teams that ship independently, you need services that can be developed and deployed independently.
The migration is a process, not a project
The migration from monolith to microservices doesn't have an end date on a Gantt chart. It's an ongoing process that evolves with your organization. You extract services when the pain justifies it, not because a plan says it's time.
At Conectia, we work with European startups that are at exactly this point: the monolith served them well to reach product-market fit, and now they need to evolve the architecture to scale. The senior engineers and architects from LATAM that we provide have guided this transition multiple times — they know what to extract first, how to avoid the classic mistakes of distributed migrations, and how to do it all without stopping feature delivery.
Because that's the key: your business can't stop while you migrate. Users keep using the product. The product team keeps requesting features. The migration has to happen in parallel with all of that. And that requires engineers who have done this before.
Is your monolith becoming a bottleneck? Talk to a CTO — we connect you with senior architects who have guided this transition without stopping delivery.


