Treat cutover as an evidence-backed, reversible release.

Build a Migration Acceptance Plan Covering Data, Query Results, Performance, Cost, Security, and Rollback

Build a migration acceptance gate spanning data, queries, performance, cost, security, and rollback.

Intermediate → Advanced145–190 minutesacceptance + rollback labmigration + semantic preservationLast reviewed: September 2026

Learning outcomes

01

Build an acceptance matrix spanning data, history, query results, performance, cost, security, SLOs, consumers, and rollback.

02

Use explicit pass/fail evidence rather than subjective “looks good” cutover decisions.

03

Separate measured local performance from modeled cost assumptions.

04

Execute and verify a reversible routing cutover.

05

Define retirement criteria for the legacy EDW only after rollback risk is acceptably reduced.

Continuity: migration may move implementation, not meaning

Chapter 29 begins from the governed AtlasMart state through Chapter 28: 10 current paid lines, 8 orders, 12 units, 820 USD gross revenue, 495 USD cost, and 325 USD gross profit, with source waterline 208. The sales fact grain remains one paid order line; gross_revenue_usd.v1 remains paid line amount in USD under its governed filters and UTC time semantics. A platform migration is not permission to redefine those contracts.

Executed local harness and assumptions

The mandatory lab is free/local/synthetic and was executed with Python 3.13.5 and SQLite 3.46.1. SQLite supplies tables, views, transactions, UPSERT and query-plan evidence, but it does not implement stored procedures, cloud roles, CDC connectors, or managed-warehouse cost controls. Stored-procedure behavior and access roles are therefore represented as explicit metadata/policy fixtures, while SQL correctness and restart/idempotency behavior are executed locally. Time zone is UTC; no production credentials or personal data are used.

1. Problem frame: cutover is a release decision

AtlasMart has discovered hidden logic, chosen a mixed replatform/refactor strategy, backfilled history, replayed the CDC boundary, and preserved metric v1. None of those steps alone authorizes cutover. The final decision is a release gate with evidence across all consumer-critical dimensions. A team that validates only data can regress performance or security; a team that validates only dashboards can miss historical drift.

2. Acceptance matrix

Dimension Fixture acceptance evidence Pass condition
Atomic controls 10 / 8 / 12 / 820 / 495 / 325 exact match
Historical fidelity six daily control rows + SHA-256 daily rows exact
CDC/restart E207/E208 applied once; duplicate replay ignored state unchanged after replay
Metric semantics gross_revenue_usd.v1 old/new 820 = 820
Security finance report reader cannot query raw sales_line no bypass
Performance same 180k-row query and result hash new p95 <= defined threshold
Cost explicit synthetic rate model within approved model threshold
SLA/consumer critical reports and owners inventoried acknowledged and smoke-tested
Rollback route modern -> injected failure -> legacy tested before retirement

3. Measured performance: same result before speed

The executed benchmark holds data, SQL semantics, warm-up policy, cache mode, and concurrency constant. The old table has no supporting index; the modern fixture has (product_id, order_date). SQLite's plan evidence changes from SCAN old_fact to SEARCH new_fact USING INDEX idx_new_product_date. The result hash is identical. On this generation environment, p95 was 8.0677 ms old versus 0.1669 ms new. Timing values are environment-dependent; the reproducible obligation is same result plus disclosed method.

text
Environment: Python 3.13.5 / SQLite 3.46.1Dataset: 180,000 deterministic benchmark rowsCache: warm SQLite page cache after 8 warmupsConcurrency: 1Legacy p50/p95/p99: 6.847 / 8.0677 / 8.9842 msModern p50/p95/p99: 0.0983 / 0.1669 / 0.2661 msLegacy plan: SCAN old_factModern plan: SEARCH new_fact USING INDEX idx_new_product_date ...Result checksum: bfb516aa49a62401ec32c3a301f5bd2bb08054ce768d8195bd067953fc4fdc77

4. Cost comparison: assumptions are data too

The cost gate uses fictional teaching rates, not current vendor pricing: old fixed compute is modeled as 730 hours × 0.50 USD/h plus storage/egress; modern compute is 180 hours × 1.25 USD/h plus its storage/egress assumptions. That yields 370.50 USD versus 232.25 USD in the model. The only valid conclusion is “under these stated assumptions.” A real migration must substitute current region, pricing mode, discounts, storage class, egress path, concurrency, and observed workload.

5. Security acceptance: intended view plus effective privilege

The initial modern policy is deliberately unsafe: finance_report_reader can query both the governed report and raw sales_line. The gate fails even though the dashboard itself is correct. After repair, the role can query modern_finance_report but direct raw access is denied. This reuses the Chapter 22 principle that secure views are ineffective when a bypass path remains open.

6. Rollback is an executable path, not a document heading

json
{  "before_cutover": "legacy",  "after_acceptance": "modern",  "after_injected_smoke_failure": "legacy",  "reason": "rollback simulation only; both data copies retained"}

Routing rollback does not undo source events. It restores consumers to a known-good serving surface while the new platform is diagnosed. Therefore the migration plan must state which system continues ingesting, how dual-load divergence is prevented, and how a second cutover will reconcile the interval.

7. Controlled failure: cut over with no rollback criterion

Wrong approach

Switch every consumer Friday evening, immediately stop the legacy load to save money, and define rollback only if someone complains.

Diagnosis: the team has removed its known-good state before the highest-risk observation period. A security regression or history defect discovered later requires emergency reconstruction rather than routing rollback.

Repair: predefine rollback triggers, keep legacy data/load capability for an evidence-based observation window, rehearse route reversal, and require a separate retirement decision after cutover stability is demonstrated.

8. Executed acceptance gate

python
acceptance = {  "data_controls_match": True,  "daily_history_match": True,  "metric_v1_match": True,  "security_no_finance_raw_bypass": True,  "performance_result_match": True,  "performance_p95_not_worse_than_legacy_125pct": True,  "modeled_cost_not_over_legacy_125pct": True,  "cdc_replay_idempotent": True,  "rollback_route_tested": True,}assert all(acceptance.values())print("acceptance all pass: True")

The 1.25 multipliers are fixture policy, not universal migration thresholds. Production thresholds belong to business and platform owners and must reflect SLOs, risk, budget, and consumer criticality.

9. Legacy retirement criteria

Cutover and retirement are separate decisions. Retirement requires evidence that all consumers moved, required history is preserved, audit/retention obligations are satisfied, rollback dependence has ended, runbooks/ownership transferred, exports and scheduled jobs no longer depend on the old system, and secrets/roles/infrastructure can be decommissioned safely. Keep migration evidence according to organizational policy even after the legacy runtime is removed.

10. Full lab run and expected output

bash
python ch29_lab_script.py# Key expected semantic lines (timings vary by machine):# legacy physical: (11, 9, 13, 860, 515, 345)# legacy governed: (10, 8, 12, 820, 495, 325)# naive modern:    (11, 9, 13, 860, 515, 345)# corrected modern:(10, 8, 12, 820, 495, 325)# cdc: ['applied','applied','duplicate_ignored','duplicate_ignored']# acceptance all pass: True# route: legacy -> modern -> legacy (injected rollback test)

The complete executable fixture used to produce the chapter evidence is standard-library Python plus SQLite. Recreate it from the code patterns in this chapter or your course workspace; do not treat the measured latency numbers above as fixed expected output on another machine.

11. Verification checklist

  • Data and history parity are exact at declared grains.
  • Metric versions and critical query results are unchanged unless separately versioned.
  • Performance evidence uses equivalent data/query/cache/concurrency conditions and preserves results.
  • Cost assumptions are explicit and replaceable with current real pricing.
  • Effective access has no new raw/staging bypass path.
  • Rollback was executed before legacy retirement.
  • Consumer owners know the cutover and incident communication path.
Blast radius and reset

All destructive actions target only a disposable atlasmart_ch29_lab directory. Never point these commands at a production warehouse. Reset with python -c "import shutil; shutil.rmtree('atlasmart_ch29_lab', ignore_errors=True)" and rerun the fixture from a clean directory.

12. Production judgment and bridge to Chapter 30

Modernization succeeds when the new platform can demonstrate preserved meaning, controlled change, adequate service, secure access, and reversible operations. A faster query or successful table copy is supporting evidence, not the decision itself. Chapter 30 now combines the entire course into a production capstone: business processes and bus matrix, dimensional models and history, incremental loading, quality/orchestration, physical design, semantic metrics, marts, security, testing, observability, cloud/lakehouse boundaries, incident recovery, cost evidence, ownership, and an evolution roadmap.

Knowledge check

Checkpoint

What is the difference between cutover and legacy retirement?

Show answer

Cutover routes consumers to the new platform. Retirement removes the old capability only after observation, consumer migration, retention/audit needs, and rollback dependence are resolved.

Checkpoint

Why must performance acceptance include result equality?

Show answer

A faster query that changes filters, joins, grain, or approximation semantics is not a valid optimization of the same workload.

Checkpoint

Are the 370.50 and 232.25 USD values vendor price comparisons?

Show answer

No. They are explicit fictional teaching assumptions used to demonstrate cost-model discipline. Real decisions must use current region/workload/contract pricing.

Checkpoint

Why test a security bypass during migration acceptance?

Show answer

Because a correct governed view does not protect data if the migrated role can directly query raw or staging tables and bypass the policy.

Checkpoint

What makes rollback credible?

Show answer

A rehearsed route/state transition with known-good data and ongoing ingestion/reconciliation rules—not merely a sentence saying “we can roll back.”

Authoritative references

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only Ethereum or ERC-20 compatible assets to this address.