Your migration passed. Will the version that is still running?
During a rollout, the previous application version keeps serving traffic against the new schema. Skewcheck runs that version's own tests against the proposed PostgreSQL schema, compares them with a run on the current schema, and blocks the merge only when it can show a reproducible failure tied to a specific change.
The workflow in GitLab
A developer proposes a migration
Shop v2 renames
customers.email. v2's own tests pass. The gate reports Unsafe: v1 now fails with SQLSTATE 42703 on that column.GitLab Duo proposes a repair
The developer mentions the custom Duo flow on the merge request. It reads the evidence from the gate's job log and opens a merge request on a
skewcheck-repair/branch. It cannot mark its own change as safe.The same gate judges the repair
The engine and policy come from the target branch, not the repair branch. A defective repair is rejected; an expand/contract repair passes and v2 still works.
A human merges
The reviewer sees the original failure, the proposed change and the independent result side by side in the merge request.
Five verdicts, not a green tick
No supported incompatibility under the conditions tested. Not a universal guarantee.
The previous version fails in every candidate run, and a deterministic rule ties the SQLSTATE to a changed object.
Something changed (an assertion, an unrelated error, a flaky result) but not enough to block.
The experiment worked, but no test touched a risky change. Blocks by default.
Missing baseline, broken migration, database or harness failure. Never reported as a pass.
Example scenarios
Each is a committed fixture with real engine output. Open one and run it again live on this server.
| Scenario | Verdict |
|---|---|
| Additive change Adds a nullable column, a new table and a plain index. Nothing the old version does can notice. | Safe |
| Column rename v2 renames customers.email to email_address. v2 is fine; v1, still serving traffic during the rollout, is not. | Unsafe |
| Changed default New orders default to "pending" instead of "new". The schema still accepts v1, but v1 now sees different data. | Advisory |
| Untested column drop Drops customers.legacy_notes. No v1 test touches it, yet v1 admin code still does. | No evidence |
| Missing baseline The pinned previous version cannot be found. Without it there is nothing to compare. | Inconclusive |
| Migration fails to apply The proposed migration has a typo and does not apply. That is not a compatibility pass. | Inconclusive |
| Already-broken test One v1 test already failed before this migration. It must not be blamed on the migration. | Safe |
| Defective repair A "repair" for the rename that adds email_address as NOT NULL. v1 never writes it. | Unsafe |
| Expand/contract repair Keeps email, adds email_address, syncs both with a trigger. The destructive step is deferred. | Safe |
| Type tightened to enum orders.status becomes an enum without "shipped", a value v1 still writes. | Unsafe |
| New CHECK constraint Totals must now be positive. v1 still creates fully discounted orders. | Unsafe |
| Table rename orders is renamed to purchase_orders for v2. | Unsafe |