Marcio Cunha

How to migrate a monolith to microservices smoothly

Learn how to break down legacy systems into microservices using the Strangler Fig pattern to isolate databases and APIs in production.

Marcio Cunha•3 min
Also available in:PortuguêsEspañol
Summary
  • The Strangler Fig pattern replaces parts of an old system with a new one gradually and invisibly to the end user.
  • Breaking apart a monolithic database requires isolating tables by business domains before deleting legacy schemas.
  • Traffic routing via a reverse proxy directs specific requests to the new microservice without crashing the system.
  • Dual data synchronization ensures that simultaneous operations do not corrupt records during the transition phase.
  • Constant monitoring of latency and errors prevents minor bugs in new code from affecting global stability.

Why the monolith stalls innovation and how Strangler Fig helps

When a software system grows too large, any simple change turns into an engineering nightmare. This is the classic monolith, a giant program where all code runs together and relies on a single shared database. Changing one line of code can take down the entire shopping cart. To solve this without pulling the plug, we use the Strangler Fig pattern, a strategy inspired by a vine that wraps around a host tree until it completely replaces it.

In practice, this means placing an intelligent intermediary in front of the old system. This intermediary, often a reverse proxy or network router, intercepts user calls. As we build new microservices—small, independent programs handling a single task—we redirect specific routes to them while the rest continues running on the legacy system. Thus, the transition happens piece by piece, without disrupting users.

Mapping the business domain to plan database splitting

The biggest mistake in a microservices migration is trying to break the database all at once. Instead of looking at technical tables, we must look at business boundaries. If your company sells products, the payments module should be isolated from the shipping module. To understand the complexity of keeping data consistent across services, it is helpful to review how to use the Saga pattern for consistency in microservices to prevent severe financial discrepancies.

Before writing any new code, map out which tables talk to each other and where the foreign keys tie everything together. When two tables rely heavily on complex joins, separating them requires defining clear API contracts. In practice, you build a service layer that still reads from the legacy database but exposes clean data to the new ecosystem.

Slicing APIs and routing traffic safely

Once business boundaries are defined, the next step is building the first microservice and placing it behind the router. If you need the new service to handle heavy queries without slowing down, it is essential to structure data properly, much like how to speed up queries with partial indexes to optimize initial response times.

The central router decides who handles each request based on the accessed URL. For instance, the route /api/v1/users still goes to the monolith, while /api/v2/products goes straight to the new microservice written in Go or Node.js. If anything goes wrong, you simply tweak the router rule to send traffic back to the legacy system instantly.

Synchronizing data between the monolith and the new service

During the transition, the real world demands that the old database and the new database live in harmony for weeks or months. To prevent data drift, we use dual-write strategies or Change Data Capture (CDC) events. When a customer updates their profile in the new system, that change must reflect in the monolith if any legacy routine still depends on it.

This phase requires operational patience and rigorous load testing to ensure the network handles background message volume. Observability tools and detailed logs help track down exactly where data might have gone missing, allowing immediate fixes before users notice any glitch. To explore these architectural concepts further in production, see our related technical analysis on How to use RAG with local embeddings without data leaks.

Shutting down legacy code without leaving traces

The final act of the migration happens when the microservice handles one hundred percent of the traffic for that specific feature. At that point, the old code inside the monolith becomes dead weight that can be safely removed. The database also undergoes cleanup, dropping orphan tables and freeing up precious connections and storage.

Migrating to microservices is not a speed race, but a marathon of architectural precision. By slicing the system in stages using Strangler Fig, your team gains autonomy to ship features faster, reduce downtime, and scale infrastructure surgically where the business truly needs it.