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.

Last updated: Proudly published by: Jeff Wahl8 min read

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

YMLorders.odcs.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

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.

Prepare for the interview
01 / Open invite
02min.

Know Data Contracts the way the interviewer who asks it knows it.

a Data Contracts query, the same shape a screen would give you.
The diff against expected. Where ties broke. What you missed.
sandbox
1source → bronze → silver → gold
2 ingest : CDC + Kafka
3 transform : dbt + Airflow
4 serve : Snowflake
5
Execute your solution0.4s avg.
SpotifyInterview question
Solve a Data Contracts problem

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 producer and its consumers joined by a contract, with 3 numbered checkpoints in order: 1 pull request, 2 write, 3 load, running from earliest catch to latest catchA producer and its consumers joined by a contract, with 3 numbered checkpoints in order: 1 pull request, 2 write, 3 load, running from earliest catch to latest catch

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

Documentation
  • States expectationsYes
  • Machine readableRarely
  • Stops bad dataNo
  • Agreed with producerSometimes
Data test
  • States expectationsYes
  • Machine readableYes
  • Stops bad dataAfter the load
  • Agreed with producerRarely
Data contract
  • 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

  1. 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.

  2. 02

    Write down the current shape

    Generate the schema from the table, mark which columns consumers use, and drop the rest from the contract.

  3. 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.

  4. 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.

  5. 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?+
A data contract is an agreement between the team that produces a dataset and the teams that consume it, written down in a machine-readable file and checked automatically. It states the dataset's schema, its quality rules, its service levels such as how late it may land, and who owns it.
What is a data contract example?+
A contract for an `orders` table: columns `order_id` (primary key), `amount` and `customer_email`, a rule that fewer than 1% of `customer_email` values are null, and a promise that each load lands within 4 hours. A load of 20,000 rows with 340 null emails is 1.7% null, so it breaks the rule and the producer's check fails.
Are data contracts the same as schemas?+
No. A schema is 1 part of a contract. The contract adds quality rules and service levels, plus ownership and a version policy, and it is agreed with consumers instead of being set by the producer alone.
What is the difference between a data contract and a data test?+
A test is written by the team that wants protection and usually runs after the data is built. A contract is agreed with the producer and is meant to be checked before consumers read the data, ideally in the producer's own pipeline.
What format do data contracts use?+
Most are YAML files. The Open Data Contract Standard, now at version 3.2.0, defines a common layout with a JSON Schema to validate it. dbt keeps its model contracts in the model's YAML, and Confluent Schema Registry stores them beside event schemas.
What counts as a breaking change to a data contract?+
Removing or renaming a column, changing a column's type, and tightening a rule that consumers rely on, such as making a nullable column required. Adding an optional column is not breaking. A breaking change needs a new major version and a notice period for consumers.
Do data contracts need a data mesh?+
No. They are common in data mesh designs because each domain team publishes its own data, but any producer with more than 1 consumer can use one, including a single pipeline team feeding several dashboards.
02 / Why practice

The candidate who gets the offer

  1. 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

  2. 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

  3. 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