Stackbook Logo
← Back to Tracks

From Monolith to Microservices: A Guided Journey

A staged, low-risk migration track: decide whether to migrate, modularise first, extract services with the strangler pattern, and handle data safely.

5 min read

Moving from a monolith to microservices is one of the most expensive architectural changes an organisation can make, and many teams undertake it for the wrong reasons. A monolith is not a failure state; a well-structured one is often the best architecture for a long time. This track helps you decide whether to migrate, and if so, how to proceed in small, reversible steps.

Learning goals

  • Decide with evidence whether migration is justified.
  • Modularise the monolith before extracting anything.
  • Extract a first service using the strangler pattern, including its data.
  • Measure outcomes and know when to stop.

Step 1: Decide whether to migrate

Valid reasons usually come from a specific, measurable pain:

  • Teams block each other because of shared release cycles and merge conflicts.
  • One component has very different scaling needs, such as image processing next to CRUD.
  • A component needs a different technology or stricter isolation, for example for compliance.
  • Deployment frequency is limited by the blast radius of the whole application.

Weak reasons include fashion, a desire to use new technology, and a vague belief that smaller is better. If the pain is slow builds or messy code, the cure may be modularisation, not distribution.

Exercise: write a one-page problem statement with two measurable current-state numbers, such as lead time for changes and the number of teams per release, plus the target you expect to reach.

Step 2: Modularise inside the monolith

Extracting a poorly structured module just moves the mess across a network. First make boundaries real in code.

  1. Identify candidate modules using domain analysis, for example by bounded context.
  2. Enforce dependency direction with automated checks, so modules interact through explicit interfaces.
  3. Separate data ownership: each module touches only its own tables, even though they share one database.
  4. Replace cross-module joins with calls through the module interface.
orders  -> (interface) -> inventory
orders  X-- direct table access to inventory tables

Many teams find this step delivers most of the benefit, and the modular monolith becomes a permanent, perfectly good destination.

Exercise: pick two modules, list every cross-module table access and function call, and remove or wrap each with an interface.

Step 3: Prepare the platform

Before the first service goes live, you need:

  • Automated build, test and deployment per service.
  • Centralised logging, metrics and distributed tracing.
  • Configuration and secrets management.
  • A standard service template with health checks and resilience defaults.

Skipping this stage is the most common reason migrations go badly: teams end up debugging production blind.

Step 4: Extract the first service

Choose a module that is loosely coupled, has clear data ownership and delivers visible value. Avoid starting with the most tangled core.

Use the strangler fig pattern:

Clients -> Gateway --+--> New Notification Service
                     +--> Monolith (all other traffic)

Process:

  1. Build the new service with the same behaviour behind the gateway.
  2. Run in parallel or shadow mode and compare outputs.
  3. Shift traffic gradually, for example 1, 10, 50, then 100 percent.
  4. Keep the old code path until confidence is high, then delete it.

Exercise: extract a low-risk capability such as notifications or reporting. Record effort, incidents and benefits.

Step 5: Migrate the data

Data is the hard part. A safe sequence:

  1. The new service reads and writes the monolith database through its own tables only.
  2. Copy data to a new store with change data capture or dual writes, while the monolith remains the source of truth.
  3. Switch reads to the new store, verify, then switch writes.
  4. Remove the old tables after a safe waiting period.

Transactions that spanned modules now become sagas with eventual consistency. Identify each such transaction early, and decide what the user sees in partial failure.

Step 6: Repeat and review

After each extraction, review: did lead time improve, did incident rate change, what did it cost in infrastructure and effort? Extract the next service only if the evidence supports it. It is legitimate to stop with a handful of services around a still-solid core.

Pitfalls

  • Big-bang rewrite promising a clean slate, which routinely takes longer than planned and loses features.
  • Sharing the database long term, producing a distributed monolith.
  • Extracting by technical layer rather than business capability.
  • Underestimating the operational load on a small team.

Key takeaways

  • Migrate for a specific, measurable problem, not for fashion.
  • Modularise and enforce boundaries inside the monolith first.
  • Build deployment, observability and platform foundations before extracting.
  • Use the strangler pattern with parallel runs and gradual traffic shifts.
  • Treat data migration and cross-module transactions as the primary risk.
  • Measure after every step and stop when the benefits no longer justify the cost.