Kafka Schema Compatibility Checker
Will the Schema Registry accept this version? Avro's reader and writer resolution, written out from the specification, with the exact path into the schema that the registry's own error message leaves out.
BACKWARD asks whether a consumer on the new schema can read what is already in the topic, so the new schema is the reader. FORWARD asks the opposite. A transitive level checks every registered version rather than only the latest, which is the difference between a safe chain and a safe pair.
Nothing is uploaded: the comparison runs in this tab.
The checks that were run
Common mistakes
These are the ones that fail silently. The config is accepted, nothing raises an error, and the consequence arrives later.
Checking compatibility in the wrong direction
BACKWARD means a new schema can read old data, FORWARD means old readers can read new data. Which you need depends on whether producers or consumers upgrade first.
Instead:Decide the deployment order, then set the compatibility mode to match.
Assuming the default mode is what you want
Confluent Schema Registry defaults to BACKWARD, which permits deleting a field and blocks adding a required one. That is right for consumer-first upgrades and wrong for producer-first.
Instead:Set it explicitly per subject.
Testing only the latest version
BACKWARD checks against the immediately previous schema. BACKWARD_TRANSITIVE checks against all of them, and only the transitive mode protects a consumer reading old data.
Instead:Use the transitive mode where old data is still readable in the topic.
Reader, writer, and which one your new schema is
The compatibility levels are named for what a consumer can do, not for what a producer did. Reading them the other way inverts every answer, and the two directions are symmetrical enough that a wrong reading looks right about half the time.
BACKWARD makes the new schema the reader
It asks whether a consumer that has been upgraded can still read what is already in the topic. So the new schema is the reader and the registered one is the writer, and the reader's defaults are what fill the fields the old data never had. This is the registry's default when nothing is set on the subject, and it is the right default when consumers are upgraded before producers.
FORWARD is the other way round
A consumer still on the registered schema has to be able to read data written with the new one. The old schema is the reader, so its defaults do the work and the new schema's defaults are irrelevant. This is what you want when producers move first, and it is why adding a field with no default passes FORWARD while failing BACKWARD: the old consumer simply ignores a field it does not know about.
A field with no default is the whole of backward compatibility
A reader substitutes a field's default for data written before the field existed. Without one there is nothing to substitute, so none of the existing data can be read. Nothing else about the change matters, and adding the default is the entire fix. For an optional field the shape is ["null", T] with a default of null, and null has to be the first branch or the default itself is invalid.
{"name": "note",
"type": ["null", "string"],
"default": null}
// safe to add. Without the default,
// nothing already in the topic reads Transitive exists because a chain of safe steps is not safe
A non-transitive level checks the latest registered version only, which is the registry's behaviour rather than a shortcut here. So version 1 can hold a field as an int, version 2 can drop it, and version 3 can bring it back as a string with a default. Each step is backward compatible with the one before it, and version 3 cannot read version 1's data at all. Paste every registered version, oldest first, separated by a line of three dashes, and pick a transitive level to check them all.
The namespace move that passes and still breaks everything
Avro's own compatibility check compares the short name of a type and then the reader's aliases, not the fullname. Moving a record from com.acme.v1 to com.acme.v2 is therefore compatible as far as the registry is concerned. Generated classes move package, the canonical form and the fingerprint both change, and any consumer resolving by fullname stops matching. That is reported here beside a passing verdict rather than folded into either answer.
What this cannot see
Your registry, your subject, and which level is actually set on it. It also does not know your subject naming strategy, so it cannot tell you whether these two schemas are even under the same subject. Check the level with a GET on /config/<subject>, which falls back to the global one when nothing is set. Everything here runs in your browser and no schema is sent anywhere.