What Are Data Contracts? Definition and Example (2026)
A data contract is a versioned, machine-readable agreement between the team that produces a dataset and the teams that consume it. It fixes the schema and the quality rules and sets service levels. It also names an owner and is checked automatically, so that a breaking change fails in the producer's pipeline instead of in a consumer's dashboard.
What are data contracts?
A data contract is an agreement between the team that produces a dataset and the teams that read it, written as a versioned file that a tool can check. It fixes what the data looks like, how good it must be, how fresh it must arrive and who answers when it breaks. The producer owns keeping the promise, and consumers can build on it without asking.
The idea comes from API contracts. A service publishes an interface and does not change it without a new version, and a data contract gives a table or a topic the same treatment. Without one, a producer team can rename a column in a normal refactor and break a dashboard it has never heard of. With one, the rename fails the producer's own pull request.
Contracts differ from documentation in 1 way that matters: they are enforced. Confluent, whose Schema Registry stores them, puts it as a formal agreement between an upstream and a downstream component and notes that a schema is only one element of it. The rest is metadata and rules, plus an owner.
A data contract example in YAML
apiVersion: v3.2.0 kind: DataContract id: 7c1f3a52-9d0e-4b8a-8f26-5a1e0c6d2b14 name: orders version: 1.1.0 status: active domain: checkout description: purpose: One row per customer order, for finance and growth reporting. limitations: Not for real-time use, loads land in batches. schema: - name: orders physicalType: table properties: - name: order_id logicalType: string primaryKey: true required: true - name: amount logicalType: number required: true - name: customer_email logicalType: string classification: restricted
The Open Data Contract Standard layout: identity and version, then the schema
# under the customer_email property quality: - metric: nullValues mustBeLessThan: 1 unit: percent dimension: completeness # at the top level of the contract slaProperties: - property: latency value: 4 unit: h element: orders.order_id
A completeness rule on 1 column and a latency promise for the table
models: - name: orders config: contract: enforced: true columns: - name: order_id data_type: string constraints: - type: not_null - type: primary_key - name: amount data_type: numeric constraints: - type: not_null - name: customer_email data_type: string
The same shape as a dbt contract, checked when the model builds
What goes in a data contract
The 4 required fields in every ODCS contract are apiVersion and kind, then id and version. Everything else is optional, but a contract that stops there promises nothing.
Identity and purpose: a name, a status that runs from proposed and draft through active to deprecated and retired, an owning domain, and a plain statement of what the data is for and what it must not be used for. Schema: each column with a logical type, whether it can be null, whether it is part of the primary key and how sensitive it is. Quality rules: nullValues and duplicateValues are the standard metrics, along with invalidValues and rowCount. Each is compared with a limit such as mustBeLessThan, and tagged with a dimension like completeness or uniqueness.
Service levels: ODCS names properties such as latency, frequency, availability and retention, plus timeToDetect and timeToRepair. Prefer latency to a freshness figure, since the standard does. Ownership and support come down to who is on call for the table and where consumers report problems. A contract with no named owner is documentation under another name.
Keep it small. Contract only the columns and rules a consumer actually depends on, since every line is a promise someone must keep. The full standard is maintained by Bitol under the Linux Foundation's AI & Data umbrella, and its JSON Schema lets a CI job validate that the file itself is well formed.
Know Data Contracts the way the interviewer who asks it knows it.
How a contract catches a bad load
Take the orders contract version 1.1.0. Its quality rule allows fewer than 1% null values in customer_email, and its SLA promises each load within 4 hours. A load arrives with 20,000 rows, 340 of them with a null email. That is 340 / 20,000 = 1.7% null.
1.7% is over the 1% limit, so the rule fails, and the load is quarantined instead of published. The same load landed 6 hours after the cut-off, 2 hours past the 4 hour latency promise, so the SLA is also breached. Both failures route to the producer, because the contract names the producer as the owner.
The point of the arithmetic is who finds out. Without the contract, the 1.7% sits in the table until an analyst sees a drop in email campaign reach. With it, the producer sees a failed check at load time and fixes the cause before any consumer reads the data.
Where a data contract is enforced
A contract that nobody checks is documentation, and the checks can sit at 3 points on the way from producer to consumer. The earlier the check, the fewer readers a break can reach.


A CI job in the producer's pull request compares the new schema with the contract and fails on a breaking change, so the rename never ships. At the write, Confluent's domain rules run in the serializer, and a failed rule with the ERROR action throws, so the message is never sent. At the load, a warehouse job validates the batch against the contract before consumers query it, and a dbt build does the same.
Most teams cover the third point first because it needs no change in the producer's code, and then move left. Moving left is the real goal, since a check at the load only tells you that bad data already exists.
Data contracts in dbt and Kafka
dbt calls them model contracts. Setting contract: enforced: true makes every column declare a name and a data_type, and before building the model dbt compares the query's output with that list. A mismatch stops the build with an error that lists each column and the reason, along with its definition type and its contract type. A model contract covers the shape of the output, while dbt data tests check its content after the build.
Constraints depend on the platform. Postgres enforces not_null and primary_key as well as foreign_key and unique, and it handles check too, but most cloud warehouses enforce only not_null and record the rest as metadata. A contract that lists a unique constraint on a warehouse that ignores it promises something nothing checks, so read the adapter's table before relying on a constraint. Contracts also do not apply to Python models or ephemeral models, and they skip materialized views. Sources and seeds are skipped, and so are snapshots.
Kafka has the same idea on the write path. Confluent Schema Registry attaches metadata and rules to a schema: domain rules for integrity checks at serialization and deserialization, and migration rules that transform old messages so producers and consumers on different versions still agree. On the write path it is the only check that blocks bad events before they exist.
What counts as a breaking change
A contract is only worth having if consumers can trust it to stay put. Use semantic versioning: a change that breaks any consumer needs a new major version, an additive change needs a minor one, and wording fixes need a patch.
For the orders contract at 1.1.0, adding an optional coupon_code column is additive and moves it to 1.2.0. Dropping customer_email, renaming amount, changing amount from a number to a string, or making a nullable column required moves it to 2.0.0. Each of those fails a consumer's query or silently changes its result.
Run the old and new major versions side by side for an agreed notice period, mark the old one deprecated in its status, and retire it once every consumer has moved. dbt follows the same logic: since version 1.6 it treats removing or changing a column's constraints as a breaking change, and since 1.9 it does the same for removing a contracted model.
Data contract vs documentation vs data test
- States expectations
- Machine readable
- Stops bad data
- Agreed with producer
- States expectationsYes
- Machine readableRarely
- Stops bad dataNo
- Agreed with producerSometimes
- States expectationsYes
- Machine readableYes
- Stops bad dataAfter the load
- Agreed with producerRarely
- States expectationsYes
- Machine readableYes
- Stops bad dataBefore or at write
- Agreed with producerYes
Who owns a data contract: producers and consumers
The producer owns the contract and the promise behind it. Consumers do not write it, but they shape it: the contract lists what they depend on, and a rule nobody reads is dead weight. Without contracts, the consumer's tests detect the producer's changes after the damage. With them, the producer is accountable for the change before it ships.
That ownership is why contracts show up in data mesh designs, where each domain publishes data as a product. They overlap with data quality work, since the rules are quality checks, and with data observability, which watches for the failures nobody wrote a rule for. A contract covers what you can predict, and observability covers what you cannot.
How to introduce data contracts
- 01
Pick 1 table that has already broken
Choose a dataset with several consumers and a recent incident, so the first contract answers a pain that people remember.
- 02
Write down the current shape
Generate the schema from the table, mark which columns consumers use, and drop the rest from the contract.
- 03
Add 2 or 3 rules and 1 service level
Start with nulls and uniqueness on the keys consumers join on and a latency promise, since those fail most often.
- 04
Check it in the producer's pipeline
Run the check in CI and at load time, and route failures to the producer named in the contract.
- 05
Agree a change process
Version the file in Git, require consumer review for a major version and set the notice period.
Data contracts in data engineering interviews
Interviewers rarely ask for the definition. They ask what happens when an upstream team renames a column, how you would stop a bad batch reaching a dashboard, or how you would version a shared table. A contract answers all 3, because it puts the check at the producer and makes every change a versioned event with a named owner.
Practise saying where each check would sit in a pipeline you design. The data pipeline interview questions and data pipeline practice problems cover the design side, the system design round guide shows how the loop frames it, and dbt interview questions cover the model contracts and tests described here.
Data contracts FAQ
What are data contracts?+
What is a data contract example?+
Are data contracts the same as schemas?+
What is the difference between a data contract and a data test?+
What format do data contracts use?+
What counts as a breaking change to a data contract?+
Do data contracts need a data mesh?+
The candidate who gets the offer
- 01
Reading a solution is not the same as writing one
Every engineer who has frozen on a query they had read a dozen times knows the gap. The only preparation that closes it is producing the answer yourself, under time, before the interview does it for you
- 02
76% of hiring managers reject on the coding task, not the resume
From HackerRank's 2024 Developer Skills Report. Candidates who look strong on paper still fail the live screen if they haven't done timed, executable practice
- 03
System design comes down to the calls you defend out loud
Ingestion, batch vs streaming, the bronze/silver/gold layers, idempotency, backfill and replay. Sketching the pipeline and naming the failure modes is the signal, not the boxes
Related guides
Data pipeline interview questions
The design and operations questions interviewers ask
Data pipeline practice problems
Pipeline design problems to practise before the loop
Data engineering system design round
What the design round asks and how to prepare for it
Data mesh
The architecture where domain teams publish data as products
Data quality for data engineers
The checks that contract rules are made of
Compete in the Weekly
Dirty, production-shaped data, scored blind each week.