Skip to content

Shadow Run or Parallel Run

A live parallel-run method — instantiates Behavior-Preserving Refactoring

Runs the new implementation alongside the old on live traffic — old system serving, new system shadowing — and compares their outputs and real-world side effects before trusting the new one to take over.

Offline tests only ever contain the cases you thought to record; some behavior only appears under real data, real volume, and real timing. Shadow Run or Parallel Run is the mechanism that tests against reality itself. It keeps the old system authoritative — still serving users — while the new implementation processes the same live requests in the shadow, and it compares the two before any cutover. Its defining stance is that the running old system is the live oracle: the comparison happens on production traffic and production state, so it surfaces correlated, volume-, and calendar-dependent differences that a captured-case gate structurally cannot. Being wrong is safe, because the new system's outputs are observed, not acted on. Where a Behavioral Diff Gate diffs captured cases offline before merge, the shadow run is the online, production-fidelity comparison you reserve for the risky cutover.

Example

A bank is replacing its overnight interest-accrual engine, where a wrong figure is real money moved into real accounts. It cannot simply be switched on, so for two months it runs in parallel: each night the legacy engine computes and posts the actual interest (authoritative), while the new engine computes the same accruals in shadow against the same live account data, and a reconciliation compares the two sets of figures every morning. The vast majority match. The handful that don't are exactly the production-only edge cases no test batch held — a rounding rule on a leap-year day-count, the handling of accounts that closed mid-cycle. Each mismatch is investigated and fixed. Only after the parallel run reconciles clean across a full monthly cycle, including month-end, does the bank cut over. The old engine's live output was the standard the new one had to match, on the real world's own inputs.

How it works

The new system is deployed beside the old and fed the same live traffic, but its outward effects are suppressed or diverted so it changes nothing — it computes as if real while the old system remains the one that actually acts. A comparison (a reconciliation, or a live diff) checks the two streams of outputs, and increasingly the side effects and downstream calls, under genuine load. Cutover is gated on the comparison staying clean for long enough to span the real operating cycle. What distinguishes it from every offline mechanism is fidelity: it is validated on real inputs, real concurrency, and real integrations, which is the only place production-only behavior shows itself.

Tuning parameters

  • Traffic fraction — shadowing a 1% sample versus 100% full parallel. More coverage catches rarer divergences; full parallel doubles compute and reconciliation cost.
  • Duration — how many cycles it runs. It must span the real calendar — month-end, quarter-end, the leap day — or it certifies only the ordinary days it happened to see.
  • Side-effect handling — how rigorously the shadow's outward effects are suppressed or mirrored to a sandbox. This is the dial that keeps a test from becoming an incident.
  • Comparison scope — outputs only, versus outputs plus timing, resource use, and downstream calls. Wider scope catches subtler divergence at more cost.
  • Cutover criterion — how clean, and for how long, before switching authority. A quantitative reconciliation bar beats a gut call about "close enough."

When it helps, and when it misleads

Its strength is catching the production-only failures no offline set contains — real data distributions, real concurrency, real integrations — while the old system still protects users, so being wrong during the run costs nothing. It is the highest-fidelity equivalence evidence available and the safe on-ramp to a risky cutover.[1]

It misleads mainly through cost and through side effects. Running two systems with reconciliation tooling is expensive and slow, so it is wasted effort on low-risk changes. The hardest engineering is isolating the shadow's outward effects: a shadow that actually sends the email, hits the payment API, or writes the ledger has turned a safe test into a live incident. And small, tolerable-looking reconciliation mismatches can mask a systematic bug that only bites a rare segment. The classic misuse is declaring victory on a parallel run too short to span the edge-case calendar and cutting over straight into the gap it never saw. The discipline is to run across at least one full business cycle, hold the side-effect boundary rigorously, and set a quantitative clean-reconciliation bar for cutover.

How it implements the components

  • side_effect_boundary — its central engineering problem: the shadow must compute as if real while its outward effects — writes, sends, charges, downstream calls — are suppressed or diverted, so running it changes nothing in the world.
  • external_relation_contract — because it runs on live traffic against real integrations, it validates that the new system's behavior toward downstream systems and partners holds under production conditions, not merely against a stub.

It is the live comparator but does not author the comparison corpus (Characterization Test Harness) or act as the fast pre-merge gate on captured cases (Behavioral Diff Gate), and it does not restructure the code itself — that is Small-Step Refactoring Workflow and Automated Refactoring Tool.

  • Instantiates: Behavior-Preserving Refactoring — it is the production-fidelity comparison that certifies equivalence before a risky replacement takes over.
  • Consumes: the running legacy system as its live oracle, and a Deployment Rollback path to fall back on if a cutover misbehaves.
  • Sibling mechanisms: Behavioral Diff Gate · Deployment Rollback · Characterization Test Harness · Small-Step Refactoring Workflow · Backward Compatibility Test

Notes

The shadow run and the diff gate are the same idea at two fidelities: the gate is cheap, offline, and runs on every change; the shadow run is expensive, live, and runs on the risky cutover where production-only behavior is the entire worry. Use the gate for everything and reserve the parallel run for the replacement you cannot afford to get wrong — spending its cost on a routine refactor is as much a mistake as skipping it on an engine that moves real money.

References

[1] The "parallel run" is long-standing practice in finance and actuarial migration — operate the new system alongside the old and reconcile before trusting it. It pairs naturally with Martin Fowler's strangler fig pattern, in which a replacement is grown around a legacy system and traffic is shifted over incrementally rather than in one big-bang switch.