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.
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?”
Visual Story
Monolith → constraint hit → strangler extraction → cut-over
Architecture Evolution
⤢ 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.
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
The rollback plan exists on paper but was never rehearsed. Under pressure, the supposed fallback is slower or less complete than expected.
The team skips incremental validation and moves all traffic at once. One defect now affects every request immediately.
One write succeeds, the second fails, or retries apply out of order. Without idempotency and reconciliation, old and new silently diverge.
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.
Migration starts but never reaches cut-over and cleanup. Both systems run for too long, increasing operational cost and cognitive load.
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
- → 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
- → 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
A monolith is being decomposed. The team wants to extract the Orders domain first. Which approach has the lowest risk and the fastest rollback?
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.