Flyway / Pact
- Looked at checksum, version, signature
- Old binary still reads not in scope
Topic · Schema and API migration
Gist
Flyway records that V42 applied. Pact records that the consumer still signed. Neither is a shall that old readers still work after the drop. Proof fails the merge when a signed migration rule has no witness, with the same command on every push.
proof audit --fail-level warn
Keep Flyway. Keep Pact. Same-input same-output is proof differential fuzz on a declared transform, not the schema file.
01 · The version
The version table moved. Half the fleet still runs the previous binary. That binary reads a column the migration just dropped.
The rewrite that already has a ledger lives on characterization testing and mirrors. The empty start lives on replace a component with no specs. This page is the migration job: schema or API, Flyway or Pact already in the tree, and nobody can point at a check that the sold behavior survived the cutover.
Flyway, Liquibase, Alembic version files. They do that job. They do not compile “old application-version N-1 still reads every column this migration leaves behind.” Pact versions a consumer contract. It does not compile “range scans still return the same projected keys after the engine swap.”
-- V42__drop_settled_at.sql
ALTER TABLE invoices DROP COLUMN settled_at;
Checksum matches. Version table is 42. Tests against the new binary pass, because the new binary never selected that column. Workers still on N-1 raise “column does not exist.” Rollback is already blocked: the data is gone. The catalog name for the missing shall is forward_compatible. List the family with
proof catalog list --domain db_migration.
proof audit --fail-level warn fails when that shall has no witness on this change.
02 · The exhibit
The version is a row in a table. The shall is this push. Click the tabs.
Flyway / Pact
The shall
When ApplyMigration deploys schema-version N, it shall not drop a column application-version N-1 still reads. Expand, then contract.
Not in the version tableFlyway / Pact
Still a checksum. Still a signed contract. Still no N-1 reader.
Still the versionProof
Same cutover. A version, or a shall. Click the tabs.
| Who | What they notice | What they lose |
|---|---|---|
| Flyway / Liquibase / Alembic | The file applied. The checksum matches. The version table moved. | They version SQL. They do not compile expand-then-contract. Keep them. |
| Pact / contract tests | Whether the consumer still signed the shape it calls | A signed shape is not range-scan order after an engine swap. Keep Pact. |
| Golden master / characterization | Observed bytes from the old system | Useful when you can record I/O. That job lives on characterization. |
| Proof | The migration shall still has a witness, or the merge stays red | Proof does not apply SQL. It does not prove identical rows by itself. Jama still authors the programme. |
The three classes on a schema change, from the catalog:
proof catalog list --domain db_migration
proof catalog show forward_compatible
proof catalog show reversible_or_documented
proof catalog show locks_bounded
proof audit --fail-level warn
forward_compatible: deployable while old code still runs. Expand, then contract, never a single PR that drops a column the previous binary reads.
reversible_or_documented: a tested down-migration, or a recovery runbook when reverse is impossible.
locks_bounded: the lock window is named, not “it was fast on staging.”
If the job is two implementations of one transform, and you need them to agree on concrete inputs, that is
proof differential fuzz <transform-key>.
Mirror coverage parity is not that. Mirror proves the ledger moved. Differential proves the two shims disagreed on an input, shrinks it, and files an unpromoted known issue. We have not run Flyway against that gate on a frozen corpus of invoice schema. The loss is named, not scored.
03 · The honest loss
If nobody signed “old readers still work,” there is nothing for proof audit --fail-level warn to fail on except the suite you already have.
Proof does not apply migrations. Flyway still does. Proof does not own consumer-driven contracts. Pact still does. Proof does not prove identical input-output by installing the catalog. That is
proof differential fuzz
on a declared transform, with two complete shims. A transform with fewer than two shims is skipped as information.
Mirror coverage parity is not behavioral equivalence. Both sides can carry a negative cell and still reject different inputs. Never describe a completed mirror as “the new API matches the old one.” Jama still wins at programme authoring. 100% MC/DC on the written decision still misses a partition that was never a condition.
The rewrite H1 lives on characterization testing and mirrors. The empty-start H1 lives on replace a component with no specs. The money-path H1 lives on fintech transaction logic.
04 · Nearby questions
How do I rewrite a legacy system without introducing regressions? A rewrite with a ledger. Characterization testing and mirrors.
How do I safely replace a legacy component that has no specs and no tests? There is nothing honest to characterize. Empty-start replacement.
How do I verify a rewrite behaves identically to the old system? Identical output is
proof differential fuzz
on a declared transform, not mirror_complete. FAQ on the characterization URL. Do not mint a twin.
What's the safest way to modernize legacy software that nobody fully understands? The unread tree you cannot throw away yet. Unknown-system modernize.