skip to content

Mechanically, when and how does Schema Registry enforce a compatibility check, and how do you configure or test it via the API/CLI?

level: seniorimportance: should knowfreq 45%

answer

  1. Check is at REGISTER time, not consume time
  2. PUT /config/<subject> sets mode; global via /config
  3. Incompatible → HTTP 409, send() fails
  4. /compatibility endpoint = dry-run, is_compatible
  5. auto.register.schemas=false = prod guardrail

basics

~20 s

When a new schema version is registered for a subject, the registry checks it against prior version(s) per the subject's mode and returns HTTP 409 if incompatible. You set the mode with PUT /config/<subject> and can dry-run a check with the /compatibility endpoint.

solid answer

~40 s

Enforcement happens at registration time. The serializer (or a manual call) issues POST /subjects/<subject>/versions; the registry looks up the subject's compatibility level — subject-specific if set via PUT /config/<subject>, otherwise the global default from PUT /config — and runs the check against the latest version (non-transitive modes) or all versions (_TRANSITIVE modes). If incompatible, it returns HTTP 409 with error 'Schema being registered is incompatible with an earlier schema', and the producer's send fails. To pre-validate without registering, POST to /compatibility/subjects/<subject>/versions/<version> (or .../versions/latest), which returns {"is_compatible": true|false}. Client behavior matters too: auto.register.schemas controls whether the serializer registers new schemas at all, and use.latest.version affects which schema is used. Disabling auto-register and validating in CI is the common production guardrail.

code

bash · 9 lines
bash
# Set per-subject mode
curl -X PUT -H "Content-Type: application/vnd.schemaregistry.v1+json" \
  --data '{"compatibility": "FULL_TRANSITIVE"}' \
  http://localhost:8081/config/orders-value

# Dry-run a candidate schema against the latest version
curl -X POST -H "Content-Type: application/vnd.schemaregistry.v1+json" \
  --data '{"schema": "{\"type\":\"record\",\"name\":\"Order\",\"fields\":[]}"}' \
  http://localhost:8081/compatibility/subjects/orders-value/versions/latest

go deeper

for a junior

Know that a bad schema is rejected with a 409 when registering.

for a middle

Use the /config and /compatibility endpoints and know auto.register.schemas exists.

for a senior

Wire compatibility testing into CI and explain register-time vs consume-time enforcement.

for a principal

Design org governance: disable auto-register, centralize mode policy, gate schema changes in pipelines.

## When the check fires The compatibility check is a **register-time** gate, not a runtime per-message check. The bytes on the wire already carry a schema **ID** (a 4-byte ID in the Confluent wire format after a magic byte), and consumers fetch the writer schema by ID — there is no compatibility evaluation when consuming. The evaluation happens only when a *new schema version* is offered to a subject. ## The registration path 1. A producer's serializer (e.g. `KafkaAvroSerializer`) encounters a schema not yet registered for the subject. 2. If `auto.register.schemas=true` (default), it calls `POST /subjects/<subject>/versions` with the schema. 3. The registry resolves the **compatibility level** for that subject: a subject-level override (`/config/<subject>`) if present, else the **global** default (`/config`). 4. It runs the check: - Non-transitive modes (BACKWARD, FORWARD, FULL): compare against the **latest** registered version only. - Transitive modes: compare against **every** registered version. 5. **Compatible** → assigns a new version + global schema ID, returns it. **Incompatible** → **HTTP 409**, registration refused, producer `send()` fails with a serialization exception. ## Configuring the mode - Global default: `PUT /config` with body `{"compatibility": "FULL_TRANSITIVE"}`. - Per-subject override: `PUT /config/<subject>` with the same body. Override beats global. - Read current: `GET /config` or `GET /config/<subject>`. ## Testing without registering - `POST /compatibility/subjects/<subject>/versions/latest` (or a specific version) with the candidate schema returns `{"is_compatible": true}` or `false`. Newer registry versions also support checking against **all** versions in one call (`.../versions` with a `verbose` option that lists incompatibilities). - This is what CI pipelines and the Gradle/Maven `schema-registry-maven-plugin` `test-compatibility` goal use to fail a build before deploy. ## Relevant client configs - `auto.register.schemas` (default true): when false, the serializer will NOT register new schemas; an unknown schema causes an error instead, forcing schemas to be registered via a controlled process (CI/admin). This is the recommended production posture. - `use.latest.version` / `latest.compatibility.strict`: affect resolving which registered schema the serializer uses, relevant for Protobuf/JSON and reference scenarios. - `normalize.schemas`: canonicalizes schemas so cosmetically-different-but-equivalent schemas don't create needless new versions. ## Edge cases - Setting mode to **NONE** lets any schema register (409 never thrown for compatibility), shifting all responsibility to you. - Lowering strictness (e.g. FULL → BACKWARD) is allowed and takes effect for future registrations; it does not retroactively rewrite history. - Deleting a subject/version and the `DELETE` soft/hard-delete semantics interact with what "all previous versions" means for transitive checks. - In a multi-DC or Confluent Cloud setup, the registry mode is centrally governed, so a single PUT controls the gate for all clients.

  • How do you stop developers from accidentally registering breaking schemas from app code in production?
    Set auto.register.schemas=false on serializers so unknown schemas error out, and register schemas only through a CI step that runs the /compatibility check (e.g. the schema-registry-maven-plugin test-compatibility goal).
  • Does the registry re-check compatibility every time a consumer reads a message?
    No. Consumers fetch the writer schema by its embedded ID and decode; compatibility is only evaluated when a new schema version is registered.

saying these in an interview costs you the question

  • Saying compatibility is checked per-message at consume time.
  • Thinking the mode is set on the topic rather than the subject/global config.
  • Believing changing the mode retroactively re-validates existing versions.
  • Confusing auto.register.schemas with the compatibility mode.

context