Site icon QATechTools

Codex Code Migration for QA: Validate Parity at Every Checkpoint

Codex Code Migration for QA: Validate Parity at Every Checkpoint featured image

A code migration is not complete when the new service compiles. It is complete when the team can show that the behavior users depend on still works—or that every intentional change has been reviewed. That makes migration a QA problem as much as a coding problem.

This tutorial shows how to use Codex for a small legacy-to-modern pilot while making parity evidence the gate between milestones. The example moves one invoice-rating flow from a legacy service to a modern API, but the method works for framework upgrades, language migrations, service extraction, and monolith decomposition.

Why parity must be defined before implementation

Two systems can return the same status code and still disagree in ways that matter: tax rounding, default values, error classification, record ordering, timestamps, retries, side effects, or latency under load. A weak assertion such as “response is not empty” can make a broken migration look green. Snapshot tests can do the same when a reviewer accepts a large update without understanding it.

Write a semantic parity contract before Codex changes code. List which outputs must be identical, which differences may be normalized, which changes are intentional, and which side effects must occur exactly once. For the invoice pilot, the contract might allow a new trace ID but require the same currency, line totals, tax rule, final amount, error class, ledger entry, and idempotency behavior.

Step 1: choose one observable pilot flow

Do not ask for a full-platform rewrite. Select a vertical slice with clear inputs, outputs, dependencies, and business value. The OpenAI modernization cookbook recommends starting with one pilot flow, then producing an overview, design, validation plan, and execution plan around it.

Create a disposable repository or branch with synthetic data. Record the Codex surface and build, repository, branch, base commit, legacy and target runtime or container digests, database schema, API contract version, fixture corpus hash, random seed, locale, time zone, character encoding, dependency lockfiles, allowed commands, network policy, time and cost budget, worktree identity, and reviewer. If any of these change, the evidence belongs to a different experiment.

Keep production credentials out of the lab. Put secret canaries in harmless fixtures so you can detect accidental disclosure without exposing a real key. Define the expected files, reports, and side effects. Commit, merge, deployment, data migration, and release remain human decisions.

Step 2: ask Codex to inventory the legacy behavior

Use an explore-only first phase. Ask Codex to trace the pilot from entry point to persistence, name its dependencies, locate tests and contracts, and distinguish observations from assumptions. Have it write four reviewable artifacts modeled on the official cookbook:

Seed legacy comments, logs, and issue text with an instruction-like prompt. The inventory must treat those strings as untrusted data, not permission. Compare the inventory with a human-authored dependency map. Missing a queue consumer, scheduled job, feature flag, or audit write is a migration risk even if the main endpoint passes.

Step 3: create a parity matrix and oracle

Translate the semantic contract into a matrix before generating the modern implementation. Use identical inputs for both systems and compare structured outcomes.

Dimension Fixtures Oracle
Money zero, boundary, fractional, multiple currencies decimal value, scale, and rounding rule
Dates DST change, leap day, midnight, missing zone business instant and displayed zone
Errors missing field, invalid state, dependency timeout class, public code, retryability, no leaked detail
Ordering ties, nulls, duplicate keys documented stable order
Side effects first call, retry, duplicate request exactly-once ledger and notification rules
Performance representative and stress datasets agreed percentile budget, not one fast sample

Normalization is dangerous when it hides a defect. Ignore a generated request ID only after proving it is non-semantic. Do not strip all timestamps, sort every array, coerce null to empty text, or round values merely to make comparisons pass. Version the normalizer and test it with counterexamples that must fail.

Step 4: anchor the target design to a contract

The official cookbook uses an OpenAPI contract as an anchor. Whether your project uses OpenAPI, protobuf, GraphQL, events, or database schemas, make the boundary machine-readable and version-controlled. Ask Codex to scaffold the smallest modern slice that satisfies the contract while preserving a compatibility adapter for the legacy caller.

Test the contract independently. Include required and optional fields, unknown fields, content types, size limits, authentication failures, version negotiation, and malformed input. Generate tests from the contract if useful, but retain hand-written business examples: schema validity does not prove correct tax, retry, or side-effect behavior.

Step 5: run a differential harness

Build a runner that sends the same immutable fixture to legacy and modern paths, captures raw responses and side effects, applies only approved normalization, and emits a structured diff. Each record should include fixture ID, input hash, legacy runtime identity, modern runtime identity, contract version, normalizer version, result hashes, duration, and verdict.

fixture -> legacy adapter -> captured result
       \-> modern adapter -> captured result
                         -> semantic comparator -> evidence.json

Run happy paths, edge cases, validation failures, dependency timeouts, retry and idempotency cases, concurrency collisions, backfill samples, and rollback scenarios. Add a clean-control implementation and several known mutants—wrong rounding, duplicate side effect, changed null handling, reordered output—to prove the oracle catches real divergence. A harness that cannot kill known mutants is not strong enough to approve a migration.

Step 6: gate every migration checkpoint

Use short implementation loops and update the execution plan’s progress, decision log, and outcomes after each one. Require a reviewer to approve evidence before crossing a checkpoint.

Checkpoint Required evidence Stop condition
Inventory dependency map, current tests, unknowns unmapped side effect or owner
Design contract, parity matrix, compatibility and rollback plan undefined intentional difference
Scaffold contract tests, harness, clean control, mutants oracle misses a seeded defect
Implementation focused and full tests, differential report, reviewed diff unexplained mismatch or skipped suite
Cutover rehearsal shadow comparison, backfill and rollback timing irreversible change or breached budget

A green summary is not sufficient. Independently verify Git status, base and head commits, changed files, test command exit codes, report hashes, runtime identities, skipped-test counts, and generated snapshot updates. Rerun deterministic CI from a clean checkout rather than trusting only the agent session.

Step 7: test failure, interruption, and stale evidence

Interrupt Codex between implementation and validation. Restart with a changed base commit. Modify a fixture after the plan is approved. Simulate unavailable dependencies, a failed report upload, a partial backfill, an incompatible library, exhausted budget, and a rollback that restores code but not data. The workflow must mark old evidence stale and stop instead of carrying a previous green verdict forward.

Also test concurrent worktrees and workers. Record which checkout produced each artifact, ensure all workers share the intended base, and look for hidden changes outside the reported diff. Isolated worktrees reduce collision risk, but isolation does not prove correctness or automatically merge the right result.

Common false-parity traps

Practical migration scorecard

Metric Release expectation
Semantic parity 100% for unchanged requirements; every approved difference linked to a decision
Oracle mutation detection All seeded high-risk defects detected
Skipped or flaky tests Zero unexplained skips; flakes quarantined, owned, and not counted as passes
Side-effect parity No missing or duplicate externally visible effects
Evidence freshness Matches current commit, runtime, contract, fixtures, and normalizer
Rollback rehearsal Completes within the agreed recovery window with data reconciled
Performance Agreed percentile and resource budgets met on comparable environments

Release checklist

Final takeaway

Codex can accelerate the mechanical and investigative work of modernization, but speed is useful only when each milestone is reviewable. Start with one pilot, define semantic parity first, compare legacy and modern behavior on identical evidence, challenge the oracle, and stop at every unexplained difference. The safest migration is not the one with the most generated code; it is the one whose behavior, evidence, and rollback remain understandable to the humans responsible for release.


Exit mobile version