CI/CD for Legacy Modernization
CI/CD for legacy modernization builds an automated pipeline around a system that often had none, integrating, testing, and delivering modernized slices continuously while the legacy system stays in production. The parity comparison runs as a pipeline gate, so no slice reaches the strangler facade or its feature flag without proving equivalence first.
Part 5 treated the feature flag as the control surface that promotes a proven slice. But the flag only has something to point at because a slice was built, validated, and packaged for delivery, and in a legacy modernization that is rarely a solved problem. The system you are modernizing may have had no automated build, no test suite, a release process measured in scheduled change windows and manual checklists. CI/CD for legacy modernization is about constructing the engine that turns each slice into a validated, deployable artifact, continuously, while the old system keeps running.
The starting condition is the hard part
Greenfield CI/CD assumes a system designed for it: fast tests, reproducible builds, deployable units. Legacy systems routinely violate every assumption. The build may be a developer’s workstation and tribal knowledge. The “test suite” may be a QA team and a spreadsheet. Deployment may be a quarterly event with a runbook and crossed fingers. You cannot wave a pipeline into existence over that.
The move that makes it tractable is the same move the whole methodology rests on: you do not pipeline the legacy monolith. You pipeline the slices. Each modernized slice is new code, written for a modern stack, built to be tested and deployed automatically from day one. The pipeline grows around the modern side of the strangler facade, slice by slice, and the legacy system is left as it is until each piece of it is retired. The pipeline does not precede the migration as a giant prerequisite project. It accretes alongside it.
What you do need early, on the legacy side, is enough automated grip to make change safe, and that is a characterization harness, not a full retrofit.
Greenfield CI/CD vs a modernization pipeline
The stages look familiar, but the meaning of almost every one shifts when the system you are validating against is already in production. The differences are what make a modernization pipeline its own discipline rather than a normal one pointed at old code.
| Greenfield CI/CD | Legacy modernization CI/CD | |
|---|---|---|
| Source of truth for “correct” | The specification and its unit tests | The running legacy system, captured as characterization tests |
| Test baseline | Written before the code | Recorded from a system already in production |
| The defining extra gate | None beyond the tests | Parity comparison against legacy output, on every change |
| What “delivered” means | Often deployed straight to users | Delivered behind a flag at zero traffic, promotion kept separate |
| Rollback target | The previous release | A known-good, parity-validated prior artifact |
| Scope | The whole system at once | One slice at a time, accreting across the migration |
Characterization tests: pinning down what legacy does
You cannot safely change behavior you cannot observe. Before a slice is built, the legacy behavior it will replace is captured as characterization tests, tests that assert what the system currently does, right or wrong, with no judgment about whether it should. This is the automated, repeatable form of Part 1’s central idea (hold the new slice to the running system, not the spec), wired into the pipeline.
Characterization tests give the pipeline three things:
- A baseline: a recorded, replayable description of legacy behavior for the slice’s scope.
- A regression net: if work on the slice changes behavior you meant to preserve, a test goes red.
- A comparison oracle: the recorded legacy outputs become reference data the pipeline diffs the slice against (Part 4’s comparison, run automatically).
This is also exactly where AI-accelerated discovery earns its place in the pipeline. Reading a large legacy codebase to find every behavior worth characterizing, the rounding rules, the edge-case handling, and the undocumented branches, is enormous manual effort. ModernLift’s proprietary AI toolchain reads the code, data, and APIs end to end and surfaces those behaviors as living specs, under senior-engineer review, which seed the characterization harness. In ModernLift’s own engagements, AI-accelerated discovery runs about 10x faster than manual review, a typical analysis of 12 weeks compressed to about two. (That is an analysis-time figure, not a general productivity claim.) The engineers still own every test. The toolchain accelerates finding what to pin down, not deciding what is correct.
The pipeline, stage by stage
A slice’s pipeline looks broadly like any modern one, with parity validation wired in as a first-class gate rather than a manual afterthought.
| Stage | What runs | Gate |
|---|---|---|
| Integrate | Build the slice, run static analysis and dependency checks | Compiles clean, no known-bad dependencies |
| Unit & contract test | The slice’s own tests plus API contract checks | All pass |
| Characterization | Replay recorded legacy cases against the slice | Behavior matches baseline (Part 3 triage applies) |
| Parity comparison | Output comparison + reconciliation vs. recorded shadow/replay data | No unexplained divergence |
| Package | Build the deployable, versioned, reproducible artifact | Artifact is immutable and traceable |
| Deliver | Push to the environment, behind the flag at 0% | Ready for ramp, not yet serving |
The line worth underlining: the parity comparison is a build gate. Part 3 described equivalence triage as the human judgment of which divergences matter. Part 4 described the comparison machinery. CI/CD is where that machinery runs automatically, on every change. A slice that introduces an unexplained divergence fails the build the same way a failing unit test does. Equivalence stops being a milestone you reach once and becomes a property every change must continuously hold. That is the difference between proving a slice was equivalent in March and knowing it still is in June.
What the pipeline is built from
CI/CD for a migration is a set of capabilities, not a fixed toolset. ModernLift is stack-agnostic by design, so what follows is the capability each stage needs and the class of tool that provides it. The point is coverage, not any particular brand.
| Capability | What it does in a migration | Tooling class |
|---|---|---|
| Build orchestration | Runs the stages and gates on every change | Any modern CI/CD runner |
| Artifact registry | Stores the immutable, versioned slice artifact | Any container or package registry |
| Slice test frameworks | Unit and contract tests for the new code | Standard frameworks for the target stack |
| Characterization harness | Captures and replays legacy behavior | Record and replay tooling plus recorded fixtures |
| Traffic capture and replay | Feeds real inputs to the shadow comparison | Traffic mirroring at the facade |
| Comparison and reconciliation | Diffs slice output against the legacy baseline | The Part 4 comparison, scripted as a gate |
| Feature-flag control | Gates promotion, enables ramp and rollback | Any flag service or config-driven toggle |
| Observability | Watches the live comparison during ramp | Any metrics, logging, and tracing stack |
Most teams already own several of these for their greenfield work. The migration-specific pieces are the characterization harness, the traffic capture, and the comparison gate. Those three are what turn a normal pipeline into one that can prove a slice equivalent to a system it is quietly replacing.
Delivery is not promotion
A point that keeps teams safe, and that connects directly back to Part 5. “Continuous delivery” in a migration means the pipeline continuously produces a validated, deployable artifact and pushes it to the environment, behind the feature flag, at zero traffic. It does not mean new behavior flows unattended to users.
The split is deliberate:
- Delivery is automated and continuous. Every passing change yields a deployable slice, sitting ready behind the flag. This keeps integration debt from building and means a slice is never far from promotable.
- Promotion is deliberate and gated. Moving traffic onto the slice is the ramp from Part 5, meaning flag-controlled, watched, and reversible. It runs on human-judged green signals and the live comparison, not on a green build alone.
Unattended continuous deployment of new behavior straight to production users is the right default for many SaaS products. It is the wrong default for a migration, where the whole promise is that no behavior change reaches users until it has been proven and ramped under control. The pipeline gets you to the door instantly. Walking through it stays deliberate.
Build the artifact once, promote it everywhere
A discipline that pays off doubly in a migration: the pipeline builds a slice artifact once, as an immutable, versioned, traceable unit, and that same artifact is what flows through every environment and ultimately serves production. You do not rebuild for prod. The thing you validated is byte-for-byte the thing that ships.
This matters more here than usual because parity evidence is attached to a specific artifact. If production runs a different build than the one your comparison validated, the evidence is about a system you are not actually running. Immutable artifacts with a clear version trail keep the chain honest, and they feed Part 7’s rollback story directly, because rolling back is then just promoting a known-good prior artifact, not reconstructing one under pressure.
Schema changes need their own gate
The one thing a slice pipeline cannot treat as immutable is the database. Code artifacts are versioned and identical from test to prod. A schema change is a one-way transformation of live data, and getting it wrong is not a flag flip back but a data-recovery incident. Migrations belong in the pipeline, but under their own discipline.
The safe shape is expand, migrate, contract, run as ordered, reversible steps:
- Expand. Add the new columns, tables, or structures without removing anything. Old and new code both keep working against the same database.
- Migrate. Backfill and dual-write so the new shape is populated and kept in sync while the slice ramps behind its flag.
- Contract. Only once the legacy path is fully retired and nothing reads the old shape, remove it.
Each step is a versioned migration the pipeline applies and can verify, and the expand and contract phases are deliberately separated by the entire ramp. That separation is what keeps a schema change from becoming the one part of the migration you cannot reverse. It is the same rule as the code side, held to the harder standard the data deserves.
Where the pipeline thins out
CI/CD is powerful here, but a few limits deserve plain statement.
- Some legacy behavior resists characterization. Time-dependent jobs, externally-driven state, anything that touches a partner system you cannot stub, these are hard to capture as a fast, repeatable test. The harness covers what it can. The remainder is an explicit gap covered by staged rollout and tight rollback, not a green checkmark that pretends otherwise.
- A pipeline tests what you told it to. An automated gate only catches the divergences your comparison rules look for. It is exactly as good as the characterization and comparison behind it. Garbage baselines produce confident, wrong greens. The pipeline industrializes the judgment from Parts 3 and 4. It does not replace it.
- The pipeline is itself software to maintain. It needs ownership, or it rots into flaky tests everyone ignores, at which point the gate is theater. A gate people route around protects nothing.
None of these argue against the pipeline. They argue for being precise about what a green build means: every change, automatically validated against the behavior you captured, no more and no less.
Where this leads
The pipeline delivers a validated slice behind a flag, ready to ramp. Part 5 made the flag the trigger for rollback. This part made immutable artifacts the thing you would roll back to. But neither is enough on its own. Rollback is only instant if the slice was architected to be reversible, with its data and dependencies designed for it. Part 7, Per-Slice Rollback Architecture, is about engineering that reversibility into each slice from the start, so the flag flip you read about in Part 5 actually delivers on its promise.
Frequently asked questions
- How do you add CI/CD to a legacy system that was never built for it?
- You don't retrofit the whole legacy system at once. You build the pipeline around the modernized slices as they are created, which are written test-first for automation, and add a characterization-test harness against the legacy system so its behavior is captured. The pipeline grows slice by slice alongside the migration, not as a big-bang prerequisite.
- How are characterization tests different from unit tests?
- A unit test asserts what the code should do, written from the specification. A characterization test asserts what the legacy system actually does right now, right or wrong, captured from its real behavior with no judgment about whether it is correct. In a migration the legacy behavior is the target to match, so characterization tests, not unit tests, are what the parity gate compares each slice against. Unit tests still cover the new code's own internal logic.
- Does the parity comparison belong inside the CI/CD pipeline?
- Yes. The output comparison and reconciliation from Part 4 run as automated pipeline gates, against recorded shadow traffic and replayed cases, so a slice that diverges fails the build. Equivalence stops being a manual sign-off and becomes a gate every change must pass, every time, automatically.
- How do you handle database schema changes in the pipeline?
- Under their own discipline, because a schema change transforms live data and cannot be undone like a feature flag. The safe pattern is expand, migrate, contract: add the new structures without removing anything, backfill and dual-write while the slice ramps, and only remove the old shape once nothing reads it. Each step is a versioned migration the pipeline applies, with the expand and contract phases separated by the full ramp so the change stays reversible.
- Can you do continuous deployment to production during a migration?
- To the pipeline's end, meaning building and delivering a deployable, validated artifact continuously, yes. But the final promotion to live traffic stays gated behind the feature flag and parity evidence, ramped deliberately. Continuous delivery of validated slices is the goal. An unattended push of new behavior straight to users is not.