What is a data contract? Schema as a promise, enforced in CI
What a data contract is, what it must cover, where it lives so it is enforced, which changes break it, and how to roll a breaking change out without a 3am page: one page.
Get the free PDF
One page, print-ready, free to share. No signup needed.
Upstream renamed a column. You found out at 3am. A data contract is the file that would have failed their pull request at 3pm instead: schema, semantics, SLA and PII, versioned, owned by the producer, and checked in CI. One page on what it covers, where it lives, and which changes break it. The print-ready A4 PDF is at the bottom.
What it is
- A contract is schema plus rules, versioned.
- The owner is the producing team, not the consumer.
- It is enforced, or it is a wiki page.
What it covers
- Schema: columns, types, nullable.
- Semantics: what
amountmeans, and in which currency. - SLA: fresh by 06:00, daily.
- PII: flagged and masked.
Where it lives
- In the repo, as yaml next to the producer code.
- A CI check: a breaking change is a red build.
- In the catalog: discoverable or dead.
Breaking or not
- Rename or drop a column: breaking, new version.
- Add a nullable column: safe, notify.
- Type change: breaking, always.
Rollout
- Publish v2 beside v1; consumers migrate on their own schedule.
- Set a deprecation date, then remove v1.
- Tests at the edge: dbt tests, Great Expectations.
A contract is a file, not a meeting
-- contracts/orders.yml, owned by team-checkout
version: 2
schema:
order_id: {type: string, required: true}
amount: {type: decimal, currency: EUR}
placed_at: {type: timestamp, tz: UTC}
sla: {fresh_by: "06:00", cadence: daily}
pii: [customer_email]
breaking: rename, drop, type change -> v3
-- CI fails the producer PR when the schema drifts
Twelve lines next to the producer code. The file is the enforcement point: a schema test in the producer CI, a freshness test on the consumer side, and a catalog entry generated from the same yaml. CI reads it, consumers read it, nobody is surprised.
Gotchas
- A contract with no tests is good intentions.
- Consumer-defined contracts do not hold: producers must own it.
- One giant schema: split it per table, per product.
The trap: the same change, with and without
| The change | Without a contract | With one |
|---|---|---|
| amount → amount_cents | revenue x100 on Monday | CI blocked the PR |
| user_id string → int | joins returned 0 rows | v2 published, v1 kept |
| file lands at 09:30 | dashboard empty at 08:00 | SLA alert to the producer |
Interview phrasing worth memorizing: a data contract moves the failure from my dashboard at 3am to their pull request at 3pm.
Frequently asked questions
What is a data contract?
What should a data contract include?
Which schema changes are breaking?
Who should own a data contract, the producer or the consumer?
Get the free PDF
One page, print-ready, free to share. No signup needed.