Data Contracts Guide

Data contract vs schema registry: different boundary, different job

By the engineer who builds Datadef, from client work on data platforms · Reviewed August 21, 2026

A schema registry sits at the write boundary and runs at publish time: it rejects a message whose schema is incompatible with the subject before it reaches the topic. A data contract sits at the read boundary and covers the whole dataset: owner, semantics, quality thresholds, freshness, and the notice period before a breaking change. They are not competing options. The registry stops one class of failure very well, and the contract covers the classes it cannot see.

7 min readFor streaming teams asked whether the registry already gives them data contracts

See it as a diagram

Everything below, as a diagram you can edit. Describe yours and see it in seconds.

227/20003 credits left
Try:

No account needed · Editable canvas, not a picture

What a schema registry does, precisely

It stores versioned schemas per subject (typically Avro, Protobuf, or JSON Schema), hands producers and consumers an id instead of the full schema on every message, and refuses to register a new version that violates the subject's compatibility rule. That refusal is the whole value: an incompatible producer deploy fails at registration rather than at 3am in a consumer.

Compatibility modes are the part worth knowing by name, and the Confluent reference is short enough to read once. BACKWARD, the default, means a consumer on the new schema can read data written with the previous one, which permits deleting fields and adding optional ones. FORWARD means a consumer on the latest registered schema can read data written with the new one. FULL is both. Each has a TRANSITIVE variant that checks against every previously registered version rather than only the immediate predecessor, and NONE turns the check off. Teams that report "the registry did not stop the break" are usually on BACKWARD when they needed FULL_TRANSITIVE.

SR=http://localhost:8081

# What rule is this subject actually enforcing?
curl -s "$SR/config/orders-value"

# Tighten it before you rely on it
curl -s -X PUT -H "Content-Type: application/json" --data '{"compatibility":"FULL_TRANSITIVE"}' "$SR/config/orders-value"

# Dry run a candidate schema in CI, before the producer deploys
curl -s -X POST -H "Content-Type: application/vnd.schemaregistry.v1+json" --data @orders-value.json "$SR/compatibility/subjects/orders-value/versions/latest?verbose=true"

The four things a registry structurally cannot check

Semantics. A field renamed in meaning but not in name passes every compatibility check. If status = 3 stops meaning shipped and starts meaning partially shipped, the registry sees no change at all, and every downstream aggregate is quietly wrong.

Values. Compatibility is about shape, not content. A producer that starts emitting nulls in an optional field, or currency amounts in cents where they used to be units, breaks consumers without breaking a single schema rule.

Timeliness. The registry has no opinion about whether the topic received anything today. Freshness failures are the most common data incident, and they are entirely outside its remit.

Everything not on a topic. Warehouse tables, lakehouse tables, files, and API responses have no registry in front of them. Most analytical consumption happens after the sink has landed the data, which is exactly where a contract check runs and a registry does not reach.

Confluent Data Contracts blur the line, up to a point

Confluent extended Schema Registry with features marketed as data contracts: schema-level and field-level metadata, rulesets whose domain rules are written in Google Common Expression Language and apply on WRITE, READ or both, migration rules written in JSONata that run on UPGRADE, DOWNGRADE or both, and tags that drive downstream policy. That covers value-level validation and some semantics, which closes two of the four gaps above for streaming data.

Check the bill and the version before you plan around it. Confluent documents that schema rules are only available on Confluent Enterprise and on Confluent Cloud with the Stream Governance Advanced package, and only from version 7.4 onwards. On open-source Schema Registry, or on a lower Cloud package, the compatibility check is the whole of what you get.

What it does not change is the boundary. Everything still applies to messages on topics, at publish time, for producers using that registry. The warehouse table your analysts actually query, the daily batch load from the vendor SFTP, and the freshness of both remain outside it. That is why teams running Confluent Data Contracts still write contract files for the landed datasets.

The overlap is worth naming so you do not build it twice: CEL domain rules and a contract quality block are both value-level checks. Put the ones that must reject a single message at the registry, because that is the only place a bad record can be stopped before it lands, and put the ones that judge a whole load, row counts, distributions, freshness, in the contract, because a per-message rule cannot see them.

How to run both without duplicating work

Let the registry own the write boundary for streaming: one subject per topic, compatibility set deliberately (FULL_TRANSITIVE for topics with many independent consumers), and a compatibility dry run in the producer CI pipeline so the failure lands on the pull request.

Let the contract own the read boundary for every published dataset, streaming or batch. Generate the contract's schema block from the registry subject where one exists, so the shape is not maintained twice, and add the parts the registry has no field for: owner, semantics, quality thresholds, freshness, notice period.

Run both checks in the same pipeline. The registry check answers "may this producer deploy". The contract check answers "is what landed still what was promised". A team that runs only the first is protected against exactly one failure mode, and it is not the one that generates most incidents. The section layout for the second file is in what is in a data contract.

FAQ

What is the difference between a data contract and a schema?

A schema describes shape: fields, types, nullability. A data contract wraps that schema in commitments: who owns the data, what the fields mean, which quality checks must pass, how fresh the data will be, and how much notice consumers get before a breaking change. The schema is one section of the contract.

Does a schema registry replace data contracts?

No. A registry enforces schema compatibility at publish time for messages on topics. It cannot detect a change in the meaning of a field, a shift in value distribution, a pipeline that stopped running, or any problem in a warehouse table that has no registry in front of it. Contracts cover those.

Which schema registry compatibility mode should you use?

BACKWARD is the common default and allows deleting fields and adding optional ones. FULL requires both backward and forward compatibility. The TRANSITIVE variants check against every previous version rather than only the latest, which is what topics with many independent consumers usually need. NONE disables checking entirely.

What are Confluent Data Contracts?

An extension of Confluent Schema Registry that adds schema and field level metadata, rulesets with domain rules written in Google CEL, migration rules written in JSONata, and tags for downstream policy. Confluent documents schema rules as available only on Confluent Enterprise and on Confluent Cloud with the Stream Governance Advanced package, from version 7.4 onwards, and they still apply at publish time to messages on topics rather than to landed tables.

Do you need both a schema registry and data contracts?

If you run streaming, yes. The registry protects the write boundary and fails an incompatible producer before deployment. The contract protects the read boundary and covers ownership, semantics, quality, and freshness for every published dataset including batch tables. Generate the contract schema block from the registry subject so the shape is maintained once.