# Schema compatibility checking

Schema compatibility checking compares a new version of a shared schema with the
version before it, and rejects the change when a reader on one version could no
longer read data written under the other. The schema is the contract between a
producer and consumers it may not know: the messages on an event topic, the
operations of an HTTP API, the columns of a table other teams query. Producer and
consumers deploy on their own schedules, so for a while old readers meet data
written under the new schema, and new readers meet data written under the old
one. The check decides compatibility from the two schemas alone, for every record
they admit, before any such data exists.

## Compatibility rules

A rule names which side may upgrade first:

- **Backward** — consumers on the new schema can read data written with the
  previous one, so consumers upgrade first. It is the default in Confluent Schema
  Registry.
- **Forward** — consumers on the previous schema can read data written with the
  new one, so producers upgrade first.
- **Full** — both directions.

Each rule also has a **transitive** form, which checks the new version against
every earlier version rather than only the latest. Compatibility does not chain:
three versions can each be compatible with their predecessor while the third
cannot read data written under the first. A topic that keeps months of events, or
a consumer several versions behind, needs the transitive form.

What counts as breaking depends on the format. Protobuf identifies a field on the
wire by its number, not its name, so renaming a field breaks the code generated
from the schema but not the encoded bytes; buf's rule sets therefore range from
`FILE`, which protects generated code, down to `WIRE`, which protects only the binary
encoding.

## What it catches

- **Removed fields and columns.** A consumer that reads a field the producer
  stopped writing fails, or reads nulls where it expects values.
- **Changed types.** A column changed from integer to text, or a field from a
  number to a string, breaks the readers that decode the old type.
- **New requirements on input.** An API parameter or a message field made
  required rejects requests and messages from clients that do not send it.
- **Removed operations.** Deleting an HTTP endpoint fails every client that still
  calls it.
- **Breaking changes shipped as minor releases.** The check runs on every change,
  so it does not depend on the producer judging the change correctly or choosing
  the right version number.

What compatibility checking does **not** catch:

- a change in meaning that keeps the shape, such as a price that switches from
  cents to euros or a status value reused for a new state;
- data written without going through the check: a producer that bypasses the
  registry, or a subject whose rule is set to none;
- a change in behavior behind an unchanged schema.

## Where the check runs

A schema registry checks each new version when a producer registers it, and
refuses a version that fails the subject's rule, so producers that go through the
registry cannot write data under it. A CI check compares the schema on a
branch with the version on the main branch or the deployed one, and fails the
pull request. The CI check reaches the author while an additive change is still
an option; the registry check also covers producers whose schemas never pass
through that pipeline.

## Tools

- **[buf](https://github.com/bufbuild/buf)** — `buf breaking` compares a Protobuf schema with a past
  version from a Git repository, a Buf Schema Registry module or a tarball, under
  rule sets from `FILE` (generated code) to `WIRE` (binary encoding).
- **[Apicurio Registry](https://github.com/Apicurio/apicurio-registry)** — open-source schema registry
  that applies a backward, forward or full rule, transitive or not, to each new
  Avro, Protobuf or JSON Schema version. Confluent Schema Registry and AWS Glue
  Schema Registry apply the same rules.
- **[oasdiff](https://github.com/oasdiff/oasdiff)** — compares two OpenAPI descriptions; its `breaking`
  command lists only the changes that break existing clients, and it runs in CI
  through a GitHub Action.
- **[dbt](https://github.com/dbt-labs/dbt-core)** — model contracts: with a contract enforced, dbt compares
  the project with its previous state and raises an error when a column is
  removed, a column's type changes, or a constraint is removed or changed.
- **[Data Contract CLI](https://github.com/datacontract/datacontract-cli)** — `datacontract breaking` fails
  when a change to a data contract breaks a guarantee its consumers rely on.

## When to use, when not

**Use:**

- On every schema whose consumers deploy separately from its producer: event
  topics, internal and public HTTP APIs, warehouse tables other teams query.
- As a CI gate on the pull request that changes the schema, where the author can
  still turn a removal into an addition.
- With a transitive rule where old data stays readable, such as a topic that
  retains events for replay.

**Don't:**

- On a schema read only by code released in the same deploy as its producer. No
  window exists in which the two sides run different versions.
- As the only check on a change in meaning. A shape check passes a field whose
  values changed units; [consumer-driven contract
  tests](https://quality.stereobooster.com/snapshot-testing.md) replay what consumers actually expect.
- By setting a subject's rule to none to push one breaking change through. The
  registry then accepts any later version as well, until someone restores the
  rule.

## Evidence

The support for the check itself is definitional, not statistical: a consumer on
a schema that passes a backward rule can read every record written under the
previous version, for the change kinds the tool's rules model.

- **Producers ship breaking changes without signaling them.** Of 3,075 OpenAPI
  histories mined from GitHub, 2,487 APIs introduced breaking changes, and in the
  best case only 517 of them consistently released those changes as major
  versions (Serbout and Pautasso 2024)[^serbout2024].
- **Breaking changes slip past manual assessment and break consumers.** In interviews
  with 17 practitioners at 11 companies evolving HTTP and message-broker APIs, 9
  said developers sometimes overlooked that a change was breaking and published it
  without versioning or notice, and 10 reported that their system or a consuming
  one had broken because the other side did not correctly assess or announce a
  breaking change (Lercher et al. 2024)[^lercher2024].

## Classification

- **Quality dimensions:** Functionality: compatibility (a consumer built against the earlier schema can still read what the producer writes), Reliability: reliability (a breaking change is refused before it ships, rather than failing in a consumer in production).
- **Area:** Schemas that systems deployed separately depend on: event and message schemas (Avro, Protobuf, JSON Schema) held in a schema registry, HTTP API descriptions (OpenAPI), and the published columns of warehouse tables (dbt model contracts, data contracts).
- **Guarantee:** Exhaustive within the tool's rules — the verdict covers every record the two schemas admit, not a sample of them; a change the rules do not model passes.

## Referenced by

- [Data quality](https://quality.stereobooster.com/data-quality.md) · Data quality
- [Snapshot and approval testing](https://quality.stereobooster.com/snapshot-testing.md) · Methods
- [Static analysis](https://quality.stereobooster.com/static-analysis.md) · Methods

## References

[^serbout2024]: Serbout, Souhaila, and Cesare Pautasso. 2024. "[How Many Web APIs Evolve Following Semantic Versioning?](https://souhaila-serbout.me/pdfs/2024_APIACE_ICWE_VersioningVsChange.pdf)" *Web Engineering (ICWE 2024)*, Lecture notes in computer science. [https://doi.org/10.1007/978-3-031-62362-2\\\_25](https://doi.org/10.1007/978-3-031-62362-2\_25).
[^lercher2024]: Lercher, Alexander, Johann Glock, Christian Macho, and Martin Pinzger. 2024. "[Microservice API Evolution in Practice: A Study on Strategies and Challenges](https://arxiv.org/pdf/2311.08175)." *Journal of Systems and Software* 215. <https://doi.org/10.1016/j.jss.2024.112110>.
