Data and migrations
Persistent data is not regenerated. It evolves through explicit migrations. Saying “infrastructure handles migrations” is correct only when the responsibility is divided precisely: product intent, artifact generation, execution strategy, and live verification are different jobs.
Schema manifests and diffs
Section titled “Schema manifests and diffs”Each supported revision should produce a normalized physical schema manifest. Comparing it with the parent revision classifies change:
- no-op: no physical change;
- safe additive: new table, nullable column, or compatible index;
- contractive: removal, narrowing, or new non-null requirement;
- transformational: stored values must change;
- ambiguous: the physical diff cannot reveal product intent.
For example, removing surname and adding last_name may be a rename, a replacement, or two distinct decisions. Infrastructure must not guess. The model history needs explicit rename intent.
Responsibility split
Section titled “Responsibility split”| Responsibility | Owner |
|---|---|
| Express rename, split, merge, or transformation | Model revision/history |
| Translate intent into migration artifacts | Generator |
| Choose locks, batches, concurrency, and window | Migration planner/orchestrator |
| Execute with least-privilege credentials | Infrastructure |
| Verify row counts, checksums, and invariants | Data verification |
| Decide product correctness | Human approval + model contract |
Infrastructure can execute a migration safely; it cannot decide business meaning absent from the model.
Forward-only history
Section titled “Forward-only history”An applied migration is durable history. Avoid relying on destructive down migrations as the primary recovery mechanism. Instead:
- keep application releases temporarily compatible with old and new shapes;
- switch traffic back only when compatibility is proven;
- correct bad data or schema with a new forward migration;
- restore from backup only through an explicit recovery procedure.
Expand, migrate, contract
Section titled “Expand, migrate, contract”Large or high-availability changes use staged evolution:
- Expand: add new compatible structures without removing old ones.
- Dual compatibility: deploy code that can operate across the transition; use dual write only when required and bounded.
- Backfill: copy or transform values in resumable batches.
- Verify: compare counts, nullability, checksums, and domain invariants.
- Cut over: switch reads/writes to the new shape.
- Observe: preserve a compatibility window.
- Contract: remove the old shape in a later release.
This is how a “two-hour migration on a live table” is represented. The model captures semantic evolution; the migration plan captures the operational shape.
Online-migration requirements
Section titled “Online-migration requirements”A production-grade planner should specify:
- transaction and lock behavior;
- batch size and checkpoint key;
- maximum runtime and pause/resume controls;
- retry and idempotency rules;
- replica lag or load guardrails;
- verification queries;
- compatibility floor for rollback;
- cleanup in a later release.
Blue-green and databases
Section titled “Blue-green and databases”Blue-green isolates application instances, not the database by default. Both blue and green may observe the same schema. Therefore the migration must be compatible with the old release until traffic rollback is no longer required.
A traffic switch is atomic; a schema rollback usually is not.
Data repairs
Section titled “Data repairs”Not every data problem is a schema migration. Corrupt or inconsistent rows may require:
- idempotent repair jobs;
- event replay;
- quarantine and operator review;
- restore into an isolated environment, verify, then promote data;
- compensating business actions.
Record evidence and make repair operations observable. Never hide them as edits to generated application files.
See Releases and rollback for compatibility and Infrastructure for the runtime boundary.
