The pipeline runs every night at 2 a.m. Nobody fully understands it. The original author left in 2019. It is part SAS, part shell, part stored procedures, and part a spreadsheet someone emails in. It produces the numbers finance signs off on every month, and nobody wants to be the person who touches it.
If you are an operations or data leader in a mid-market company, this is the pipeline you are living with — and the one you know you have to modernize before the next audit, the next acquisition, or the next AI initiative that needs clean, timely data. This post is a practitioner’s guide to modernizing that pipeline without a rewrite. We cover the strangler-fig pattern, zero-downtime migration tactics, validation gates, and rollback strategy. We close with two real-world migration patterns we have used repeatedly with mid-market clients.
The goal is not a greenfield rebuild. It is to retire the legacy system one safe slice at a time while keeping the business running.
Why Full Rewrites Fail
The instinct on most legacy pipelines is to start over. A new team, a new stack, a new architecture. Six months later, the new pipeline is half-built, the old pipeline is still running, and the team is now maintaining two systems — one they trust and one they do not. The rewrite stalls because every migration attempt surfaces a new edge case the legacy system silently handled. Meanwhile the business keeps depending on the old numbers.
This diagram requires JavaScript.
Enable JavaScript in your browser to use this feature.
Full rewrites fail because they bet everything on a single cutover. They require you to fully understand the legacy system before you replace it, which is the one thing you cannot do. The legacy pipeline is the documentation. Its behavior, quirks, and silent corrections are the spec. A big-bang rewrite throws that spec away and hopes the new system reconstructs it. It rarely does.
We watched this play out at a manufacturing company that spent fourteen months and two million pounds building a replacement pipeline. The new system was architecturally superior — cloud-native, properly orchestrated, well-documented. But every time they tried to cut over, they discovered another silent correction the legacy SAS code had been making. A rounding rule that applied only to intracompany transfers. A deduplication step that fired only on the last day of the quarter. A timezone adjustment that nobody remembered but that finance had been relying on for years. After three failed cutover attempts, they reverted to the legacy system and started the modernization over — this time incrementally.
Incremental modernization inverts the bet. Instead of replacing the system, you replace slices of it, one at a time, while the whole keeps running. Each slice is small enough to understand, validate, and roll back. The legacy system keeps producing correct output until you have provably replaced each piece. This is the strangler-fig pattern, and it is the only approach we have seen consistently succeed on pipelines that matter to the business.
The Strangler-Fig Pattern Applied to Pipelines
The strangler-fig pattern, named by Martin Fowler, describes incrementally replacing a legacy system by wrapping it. New functionality goes into the new system. The old system keeps running. Over time, the new system grows until it strangles the old one, which is then retired. Applied to data pipelines, this means routing data and computation through a dispatcher that can send work to either the legacy or the modern pipeline, comparing results, and gradually shifting traffic.
The key enabler is a routing layer between the source systems and the downstream consumers. In its simplest form, this is a thin orchestrator that decides which pipeline produces a given table, dataset, or report. Early in the migration, the orchestrator sends almost everything to the legacy pipeline. As you modernize each piece, the orchestrator sends that piece to the new pipeline. Consumers keep hitting the same interfaces; they do not know or care which pipeline produced the output.
Three properties make this work. The routing layer must be the only path to downstream consumers, with no direct legacy writes that bypass it. Both pipelines must read from the same sources, so they are operating on identical inputs. And you need a comparison mechanism, which is where validation gates come in. Without these three, you do not have a strangler-fig migration. You have two pipelines and a prayer.
The pattern gets its name from the strangler fig tree, which grows around a host tree, gradually replacing it until the host is no longer needed. The metaphor is apt for data pipelines. The new system grows around the old one, taking on more responsibility slice by slice, until the old system is serving nothing and can be safely retired. The key insight is that at no point during the migration is the business without its numbers. The old system is always there as a fallback until you have proven the new one works.
Zero-Downtime Migration: Run Both, Compare, Then Cut
Zero-downtime does not mean no cutovers. It means no moment where the business cannot get its numbers. The tactic is parallel run with shadow comparison: the new pipeline runs alongside the legacy one, consuming the same inputs and writing to parallel outputs. A validation step compares the two. When the comparison is clean for long enough, you switch downstream consumers to the new output. The legacy pipeline keeps running silently in the background as a fallback until you are confident enough to retire it.
The shape of the comparison matters. Naive equality — asking whether these two tables match row for row — almost never holds, because the new pipeline will legitimately differ from the old one. Floating-point ordering, timezone handling, deduplication rules, and rounding all produce differences that are technically valid but break exact comparison. Instead, build a comparison that knows what counts as equivalent. Aggregate-level checks such as totals, counts, and distributions are more useful than row-level matching. Key-level checks ensure every primary key is present and every business-critical field is within tolerance. Reconciliation against an authoritative source catches drift that internal comparison misses.
A data engineering lead at a financial services firm described their parallel run as the most valuable two weeks of the entire project. In the first three days, the comparison caught four silent transformations the legacy system had been applying that nobody had documented. In the second week, it caught a timezone edge case that would have shifted quarter-end numbers by a day. None of these were found during the rewrite’s testing phase, because the tests were written against assumptions that turned out to be wrong. The parallel run against production data was the only thing that surfaced them.
Time-box the parallel run. Two to four weeks of clean comparison on production data is usually enough for a single slice. Shorter, and you have not seen the weekly and monthly cycles. Longer, and you are paying double compute without learning anything new. Define the exit criteria before the run starts: which checks must pass, on what cadence, signed off by whom. A migration slice without exit criteria tends to run forever.
Validation Gates: Prove Equivalence Before You Trust It
Validation gates are the checkpoints that decide whether a migrated slice is safe to promote. They sit between the parallel run and the cutover. A gate is not a vibe check. It is an explicit, automated set of assertions about the new pipeline’s output, run against production-scale data, with a recorded pass or fail.
A useful gate has four layers. The first is structural: does the output have the expected schema, the expected row count range, and no nulls in non-nullable fields? The second is reconciliative: do totals tie back to source systems and to the legacy pipeline within a stated tolerance? The third is semantic: do the numbers mean what the business expects, checked against known business rules and a curated set of golden examples? The fourth is performative: did the pipeline finish within its SLA window, and did it handle a failure injection gracefully?
Each layer catches a different class of problem. Structural checks catch the obvious breakages — a missing column, a truncated table, a silent failure that produces empty output. Reconciliative checks catch subtle drift — a rounding change that moves pennies across thousands of rows, a deduplication difference that drops a handful of legitimate records. Semantic checks catch meaning changes — a transformation that produces correct numbers but assigns them to the wrong business unit. Performative checks catch operational issues that would surface as user complaints if they reached production.
Automate the gate. If the validation is manual, it will not happen consistently, and the migration will accumulate silent drift. Make the gate output a single binary decision — promote, block, or hold for human review — with the evidence attached. Store the results so that when a stakeholder asks six months later how we know the new numbers are right, you have an audit trail.
The gate also serves a political function. In most organisations, the people who built the modern pipeline are not the people who own the business numbers. The gate creates an objective standard that both sides can agree on. It removes the subjective debate about whether the new system is good enough and replaces it with a checklist that either passes or does not.
Rollback Strategy: Make Reversal Cheap and Expected
The single most underrated property of a safe migration is cheap rollback. Every slice should be promoted with the assumption that it may need to be reverted within hours. If rollback is expensive or embarrassing, the team will rationalize forward progress through problems and ship a worse outcome.
For pipelines, the cheapest rollback is routing-based. Because the orchestrator decides which pipeline serves each consumer, rollback is a configuration change: point the consumer back at the legacy output. The legacy pipeline is still running in shadow, so the data is fresh. This works only if you keep the legacy pipeline alive during the parallel-run period, which is the whole point of running both.
The riskiest moment is the window after you retire the legacy pipeline. Once it is gone, rollback to it is no longer possible. Treat legacy retirement as a separate, deliberate decision made weeks or months after the final cutover, not as part of the cutover itself. Until then, the legacy pipeline runs quietly as your insurance policy. It costs some compute. It is worth it.
Document the rollback procedure before you need it. The rollback runbook should be a one-pager: who declares rollback, what command flips the routing, how downstream consumers are notified, and how long recovery takes. Drill it once on a non-critical slice so the team has done it before the stakes are high. The first rollback should be boring, not dramatic.
A team that has drilled rollback approaches cutover with confidence rather than anxiety. They know that if something goes wrong, they have a tested procedure to revert in minutes. That confidence is what allows them to migrate aggressively — to take on the harder slices, to move faster — without taking on reckless risk. Teams that have never rolled back are paralysed by the fear of it. Teams that have rolled back successfully treat it as a routine tool.
Two Real-World Migration Patterns
The principles above play out differently depending on pipeline shape. Here are two patterns we have used repeatedly with mid-market clients.
The Warehouse-First Lift
The legacy system is a tangle of nightly jobs that load an on-prem database from operational systems, transform the data, and serve reports. The modernization target is a cloud warehouse. The mistake teams make is trying to migrate the transforms and the storage simultaneously. The lift works better when you sequence it.
Step one is to stand up the cloud warehouse and replicate the raw source data into it, unchanged. No transforms, no business logic. Just land the same inputs the legacy pipeline reads. Step two is to rebuild the transform logic in the warehouse, one table at a time, comparing output against the legacy pipeline’s results. Step three is to repoint reporting consumers at the warehouse version once a table is validated. Step four is to decommission the legacy load jobs once all tables are repointed and validated over a full reporting cycle.
This pattern works because it separates the two hardest problems — moving the data and changing the logic — into independent slices. The replication in step one is mechanical and low-risk. The transform rebuild in step two is where the real equivalence work happens, and it happens one table at a time with the legacy system as a live oracle. We have used this pattern to move mid-market finance and operations reporting stacks to the cloud without a single missed month-end close.
A retail client used this pattern to modernise a pipeline that had been running on the same on-prem SQL Server since 2014. By the time they finished, eighteen tables had been migrated individually over six months, each with its own parallel run, validation gate, and cutover. Not once during the migration did finance miss a reporting deadline. The legacy server was decommissioned quietly on a Friday afternoon, and by Monday nobody noticed it was gone. That is what a successful migration looks like.
The Orchestrator Swap
The legacy system is a scheduler — cron, Airflow 1.x, or a commercial tool — with hundreds of jobs whose dependencies are implicit and partially broken. The modernization target is a modern orchestrator with explicit DAGs. The temptation is to rebuild every DAG from scratch in the new tool. Do not.
Instead, wrap each legacy job in a thin shell that the new orchestrator can invoke. Run the wrapped jobs in the new orchestrator in shadow mode, with the legacy scheduler still the source of truth for production triggers. Compare start times, completion times, and exit codes job by job. As each job proves stable in the new orchestrator, switch its production trigger over. Once a full dependency cluster has moved and held for a cycle, retire that part of the legacy scheduler.
This pattern works because it preserves the legacy job’s behavior while migrating only the orchestration. The messy business logic inside each job is untouched; only the scheduling and dependency graph changes. Dependencies that were implicit become explicit in the new orchestrator’s DAG, which is often the actual goal of the migration. We have used this pattern to migrate teams off end-of-life schedulers without rewriting a single job.
What to Do Next
Start by mapping your legacy pipeline into slices you could plausibly migrate independently — by table, by job, by consumer. Pick the smallest slice that, if it went well, would give you and the business confidence in the approach. Stand up the routing and comparison infrastructure for that one slice. Run the parallel run. Hit the validation gate. Cut over. Roll back if the gate fails. Repeat.
The discipline matters more than the tooling. Modernizing a legacy pipeline without rewriting it is unglamorous, incremental work. It is also the only path we have seen reliably deliver. Ship one safe slice this quarter, build the evidence, and let the measured result fund the next slice.
By the time the legacy pipeline is finally retired, no one will remember it was ever a risk. That is exactly the outcome you want. A successful migration is one that nobody notices — except the engineering team that no longer carries the burden of a system they do not understand and are afraid to touch.