See It Flow

Architecture

Architecture Evolution & Migration

Most production systems accumulate legacy characteristics quickly. The key architectural question is how to evolve them safely under live traffic, real data, and existing downstream dependencies.

Mangesh MandlikLast updated:

What Is Architecture Evolution?

Architecture evolution is the practice of improving, migrating, or decomposing a system that is already serving live traffic — without breaking existing behaviour. Unlike greenfield design, evolution must account for production data, downstream dependencies, and the need for continuous delivery throughout the migration.

The primary constraint is not only “what is the best target architecture?” but also “what is the safest path from here to there?”

Strangler Fig
Define a routing boundary, run old and new side by side, and move traffic incrementally instead of cutting over in one shot.
Routing proxy
Sits in front of both systems. Traffic shift becomes a configuration change, which can make rollback much faster.
Dual-write
Useful in some data migrations, but only with explicit idempotency, reconciliation, and a clear source of truth.

Visual Story

Monolith → constraint hit → strangler extraction → cut-over

Architecture Evolution

Architecture Evolution
Healthy Monolith
0 / 0
👥Up nextOrg Growth

⤢ Expand for synchronized narration timeline

The proxy (Scene 3) enables incremental migration with safer rollback. Traffic is shifted gradually, often through canary steps, rather than in one irreversible cut-over event.

Problem It Solves

Why safe migration is harder than greenfield design

The instinct to rewrite from scratch is frequently wrong, but not universally. Some systems really do need a more invasive rewrite when the existing architecture cannot support new requirements or has become too unsafe to change incrementally. The point is that rewrite vs migration is a decision to evaluate explicitly, not a slogan.

Greenfield architecture is easier because there are no existing users, no production data, and no downstream dependencies. Real architecture evolution happens in a live system. Incremental and reversible approaches are often the safer default because they preserve institutional knowledge and validate assumptions under real traffic, while data ownership and source-of-truth decisions still have to be reasoned through carefully in Data Architecture.

Migration Patterns

Patterns for architecture migration, coexistence, and cut-over

Strangler migration: routing boundary, coexistence, observability, rollback

A strangler migration needs five things defined up front: the routing boundary that determines which requests go to the old vs new path, the coexistence plan for how long both systems will live together, the observability needed to compare behaviour, the rollback mechanism and expected speed, and the completion criteria that justify deleting the old path.

Strangler FigRisk: Low–Medium
When: Extracting a domain or capability from a monolith or replacing a live subsystem.
How: Put a routing layer or feature flag in front of old and new. Use shadowing and then canary rollout to shift traffic gradually, watch errors/latency/correctness, and roll back by routing traffic back if needed. Declare complete only after sustained production success and cleanup of the fallback path.
Dual-writeRisk: High
When: Migrating a source of truth, schema, or downstream dependency where both old and new systems need fresh writes during a transition.
How: Use only with a declared authoritative system, idempotent writes, retry rules, reconciliation, and verification. Naive application-level dual-write is correctness-sensitive; CDC or a transactional outbox can be safer ways to move changes into the new system.
Expand/ContractRisk: Low
When: Zero-downtime schema evolution where old and new code paths must coexist for a period.
How: Add the new shape first, backfill historical data, update readers and writers, verify compatibility, and remove the old shape only after the old path is no longer needed. Avoid collapsing expand and contract into one deploy.
Backfill + Cut-overRisk: Medium–High
When: Historical data can be migrated ahead of time and the business can tolerate a clearly planned cut-over step.
How: Backfill old data, compare old/new outputs, define the point of no return, and rehearse rollback before switching reads or writes. Simpler than long coexistence, but the cut-over moment carries concentrated risk.

Why dual-write is harder than it looks

Dual-write is not “write to both places and call it done.” If the write to system A succeeds and the write to system B fails, you have an immediate partial-failure problem. Retries can then apply writes twice unless the operation is idempotent. Ordering can diverge across systems, which means the same business entity may end up with different final state in each store.

That is why dual-write needs active reconciliation: detect divergence, decide which system is authoritative during the migration window, and repair mismatches. Many teams prefer change-data capture or a transactional outbox over naive application dual-write because those patterns can reduce some of the partial-failure and ordering risks while still feeding the new system.

How Architects Think

The discipline of reversible migration

Every migration phase needs a tested rollback plan before it begins. Shadowing, verification, and canary rollout exist to make rollback a practical mechanism, not a comforting document. If rollback depends on emergency code changes, it is probably too slow for the cut-over risk you are taking.

Data migration is often the hardest part. Backfill moves historical records, dual-write or CDC handles fresh changes, verification compares old vs new outputs, and reconciliation catches drift. Architects should name the point of no return — the moment after which rollback stops being realistic because old data has been deleted, the old schema removed, or consumers fully switched.

What would change your decision? If the existing system's architecture is fundamentally incompatible with new requirements, or the codebase is too unsafe to evolve incrementally, a larger rewrite may be justified — but it still needs a migration plan for data, users, and rollback boundaries.

Illustrative Scenario

Extracting an orders service from a Rails monolith (illustrative scenario)

Context: a marketplace runs on a Rails monolith. The order management module has grown into the primary deployment bottleneck, and a bug anywhere in that code path requires full monolith redeployment.

Phase 1 — Modularise in place: before extraction, explicit boundaries are drawn around the orders module inside the monolith. Direct model reach-through is removed so the future routing boundary is clear.

Phase 2 — Shadowing and backfill: the new orders service is built, historical order data is backfilled, and selected requests are mirrored to the new path without serving its result. Output comparison is used to verify correctness before live traffic depends on it.

Phase 3 — Canary traffic migration: a proxy and feature flag route 1% → 10% → 50% → 100% of order creation to the new service over several weeks (an illustrative duration; real migrations are paced by error rates and reconciliation results, not a calendar). During coexistence, the team uses an outbox-driven integration to keep downstream consumers updated, watches latency and error rates, and runs reconciliation to catch data drift.

Phase 4 — Cut-over and cleanup: once the new path has sustained production success, the old write path is disabled, the remaining fallback is removed, and the team explicitly marks the point of no return before deleting the old orders code.

Trade-offs

Migration is a prolonged constraint, not a clean break

Big-bang rewrite vs incremental migration

A full rewrite can deliver a cleaner system, but it concentrates schedule, correctness, and cut-over risk into one programme. Incremental migration adds coexistence overhead, yet it creates more checkpoints for validation and rollback.

Coexistence complexity vs migration safety

Running two implementations creates routing, data, and operational complexity. That complexity is often the price of a safer, more reversible migration under live traffic.

Data migration risk vs deferred cut-over

Migrating data early gives the new system longer live-data validation. Migrating data later shortens the coexistence window but concentrates more risk near cut-over.

Feature parity vs opportunity cost

Waiting for perfect parity can delay the benefits of the new architecture. Cutting over too early can push missing edge cases into production. Architects need explicit completion criteria, not hope.

What Can Go Wrong

Common migration failure modes

🔄Untested rollback

The rollback plan exists on paper but was never rehearsed. Under pressure, the supposed fallback is slower or less complete than expected.

💥Big-bang cut-over

The team skips incremental validation and moves all traffic at once. One defect now affects every request immediately.

⚡Naive dual-write

One write succeeds, the second fails, or retries apply out of order. Without idempotency and reconciliation, old and new silently diverge.

🗑️Premature deletion

Old code or data is removed before the new system has proved itself and before the point of no return is understood. Fallback disappears too early.

♾️Indefinite coexistence

Migration starts but never reaches cut-over and cleanup. Both systems run for too long, increasing operational cost and cognitive load.

🗄️Schema incompatibility

The migration plan ignores how old and new schemas coexist. Backfill stalls, readers break, or cut-over is blocked late in the programme.

When To Use / When Not To Use

Choosing the right migration strategy

✓ Use incremental migration when
  • → System has active users and live traffic
  • → Rollback must be fast and operationally simple
  • → Team wants production evidence before full cut-over
  • → Data migration is complex, high-volume, or correctness-sensitive
✗ Avoid big-bang rewrite when
  • → System is business-critical and rollback must be quick
  • → The old system contains years of implicit edge-case handling
  • → The team cannot yet explain what the current system actually does
  • → Requirements are likely to keep moving before the rewrite would land
Architect's Challenge

A monolith is being decomposed. The team wants to extract the Orders domain first. Which approach has the lowest risk and the fastest rollback?

Remember· 30-second mental model

Prefer migration paths that create checkpoints for verification, rollback, and cleanup while the old system is still available.

  • 1A strangler migration needs an explicit routing boundary, coexistence plan, observability, rollback path, and completion criteria.
  • 2Shadowing, canary rollout, backfill, verification, and reconciliation reduce migration risk by creating production evidence before full cut-over.
  • 3Dual-write is correctness-sensitive: plan for partial failures, ordering, retries, idempotency, divergence detection, and an authoritative source of truth.
  • 4Expand / contract for schema changes: add the new shape, backfill, verify, then remove the old shape after consumers have moved.
  • 5Cleanup matters. A migration is not complete until the old path, fallback assumptions, and temporary coexistence machinery are intentionally removed.

Key lesson

Incremental migration is a strong default because it turns one dangerous leap into a sequence of reversible decisions — but it still requires disciplined data, routing, and rollback design.