What is ODCS? The Open Data Contract Standard, explained

What the Open Data Contract Standard is, what a v3 contract actually contains, why an open standard beats a vendor's rule format, and how importing and exporting ODCS keeps your data quality rules portable.

· 8 min read

The Open Data Contract Standard (ODCS) is an open specification for describing what a dataset promises: its schema, its owner, its service levels and — the part most teams care about — its data quality rules. It is a YAML document, versioned like code, readable by a person and executable by a tool. ODCS is developed in the open as part of the Bitol project under the Linux Foundation's AI & Data umbrella, which means no single vendor controls it and no single vendor can strand your rules.

The problem it solves

Every data quality tool invents its own rule format. dbt has tests in a schema.yml. Great Expectations has expectation suites in JSON. Soda has SodaCL. Monte Carlo has monitors in its UI. Each format is reasonable on its own and none of them travel.

That has three practical consequences:

  1. Your expectations are locked to a runner. Moving from one tool to another means rewriting every rule by hand, which is why teams don't move, which is why the format keeps its captive audience.
  2. Producers and consumers disagree about what was promised. The rules live in the pipeline repository, in the pipeline team's format. The analytics team that depends on the table cannot read them, let alone review a change.
  3. Nothing is reviewable. A rule changed in a vendor UI leaves no diff. Whether the threshold moved because the business changed or because someone got tired of the alert is unrecoverable.

A data contract addresses all three by making the promise itself the artefact — separate from the tool that enforces it, versioned in the same repository as everything else, and written in a format both sides of the interface can read.

What is actually in an ODCS contract

A v3 contract is a YAML document with a small number of top-level blocks. The essentials:

apiVersion: v3.0.0
kind: DataContract
info:
  title: orders
  version: 1.2.0
  owner: data-platform
schema:
  - name: orders
    physicalName: orders
    physicalType: table
    properties:
      - name: order_id
        logicalType: string
        physicalType: uuid
        required: true
        primaryKey: true
        description: Business key, stable across restatements.
        quality:
          - rule: nullCount
            dimension: completeness
            severity: error
            mustBe: "0"
          - rule: duplicateCount
            dimension: uniqueness
            severity: error
            mustBe: "0"
      - name: status
        logicalType: string
        quality:
          - rule: validValues
            dimension: conformity
            severity: error
            mustBe: "['pending', 'paid', 'shipped', 'refunded']"
      - name: created_at
        logicalType: timestamp
        quality:
          - rule: freshness
            dimension: timeliness
            severity: error
            mustBe: "<= 24h"
    quality:
      - rule: rowCount
        dimension: consistency
        severity: warning
        mustBe: "> 0"

Reading it top down:

The anatomy of a quality rule

Four fields do the work:

FieldWhat it means
ruleThe check to run — nullCount, duplicateCount, validValues, between, regex, freshness, rowCount, referentialIntegrity, customSql
dimensionWhich quality dimension it belongs to — completeness, uniqueness, conformity, accuracy, timeliness, consistency
severityerror fails the run; warning records the violation and continues
mustBeThe threshold, in a small readable grammar

mustBe is the part that makes contracts pleasant to read. It is not a boolean expression language; it is a per-rule shorthand:

Someone who has never used the tool can read mustBe: "<= 24h" on a freshness rule and know exactly what will happen. That is the whole point.

Dimensions are not decoration

The dimension field looks like taxonomy for its own sake until you have a hundred rules. Then it becomes the only way to answer the questions that actually get asked: *are we covered on timeliness across our critical datasets, or only on completeness?* Rules grouped by dimension turn into a coverage matrix, and gaps in that matrix are usually more informative than any individual failing check.

The six dimensions in common use — completeness, uniqueness, conformity, accuracy, timeliness, consistency — are conventional enough that a coverage report means the same thing across teams.

Why an open standard is worth the trouble

Portability is the obvious one. The same contract describes the same logical dataset whether it lands in PostgreSQL, BigQuery or SQL Server. The runner compiles nullCount to that engine's SQL. A warehouse migration stops being a rules migration.

Reviewability is the underrated one. Because the contract is a file, a threshold change is a diff in a pull request, with an author and a reason. The most valuable property of a data contract is not that a machine can run it — it is that a person can argue with it before it merges.

Independence is the strategic one. Rules written in an open standard are not an asset of whoever runs them. If you replace the validation tool, the contracts come with you. That changes the negotiating position, and it changes how much you are willing to invest in writing rules in the first place.

Interoperability with catalogs. Because ODCS also carries ownership, descriptions and service levels, the same document feeds a catalog, a lineage tool and a quality runner without three separate sources of truth drifting apart.

Import and export, in practice

A standard is only worth as much as the ease of getting in and out of it. Two rules of thumb:

Import should meet you where you are. Point a tool at an existing table and it should read information_schema, infer the columns and types, propose a baseline contract from what it finds — required columns become nullCount rules, primary keys become duplicateCount rules, timestamps become freshness candidates — and let you edit from there. Starting from a generated draft of thirty rules and deleting ten is a very different experience from starting from an empty file.

Export should be byte-for-byte the thing you edit. This is where most tools quietly fail. If the YAML is a rendering of a database row, every round trip through the UI reformats the file, reorders the keys and drops your comments, and the diff in your pull request becomes unreadable noise. The contract has to *be* the storage format, not a projection of it.

Catalyst takes the second approach literally. The YAML is the source of truth; the visual rule builder edits the document in place, preserving comments and key order, so a rule added in the UI produces a one-line diff. Export is a file download, import accepts any valid v3 document, and nothing is stored in a shape you cannot get back out.

Versioning a contract

Use semantic versioning on info.version, and mean it:

The discipline pays off at the moment someone asks whether they can safely drop a column. With versioned contracts and a diff, that is a five-minute conversation instead of a week of grep.

Where contracts fit alongside dbt tests

They are not competitors, and it is worth being precise about the difference.

dbt tests run inside a dbt build and cover models dbt owns. They are excellent at exactly that, and if every table you care about is a dbt model, they may be all you need.

A data contract sits one level up. It describes the dataset's guarantee independently of what produced it — dbt, Airflow, a Spark notebook, a vendor's replication tool, a hand-written loader — and independently of who runs the check. It is the interface between a producer and a consumer, versioned as a first-class artefact.

In practice, teams that use both put fast, cheap assertions in dbt tests, where they fail the build, and put the durable promises in the contract, where they are reviewed, versioned and monitored on a schedule regardless of which pipeline wrote the table today.

Frequently asked questions

What does ODCS stand for?

Open Data Contract Standard. It is an open specification for describing a dataset's schema, ownership, service levels and data quality expectations in a single versioned YAML document, developed as part of the Bitol project under the Linux Foundation's AI & Data umbrella.

Is ODCS the same as a data contract?

"Data contract" is the concept — an agreed, versioned promise between a data producer and its consumers. ODCS is one concrete, open format for writing that promise down. You can have data contracts without ODCS, in a homegrown format; using an open standard is what makes them portable between tools.

What is the difference between ODCS and dbt tests?

dbt tests are assertions inside a dbt project, scoped to models dbt builds, and they run as part of a dbt build. An ODCS contract describes the dataset's guarantees independently of the tool that produced or checks it, is versioned as its own artefact, and can be enforced by any compatible runner. Many teams use both.

Which data quality rules does ODCS support?

The specification's quality block is open enough to carry any rule a runner understands. In practice the common set is completeness (nullCount), uniqueness (duplicateCount), allowed values (validValues), numeric ranges (between), pattern matching (regex), freshness (freshness), row counts (rowCount), referential integrity, and an escape hatch for custom SQL. Each carries a dimension and a severity so results roll up into a coverage view.

Can I import an existing ODCS contract into Catalyst?

Yes. Catalyst reads any valid ODCS v3 document, validates it against the dataset's imported columns — so a rule referencing a column that does not exist is caught at save time, with a line number — and stores the YAML itself as the source of truth. Export gives you back the same file, comments and ordering intact.