Each subject has a compatibility mode (globally BACKWARD by default); a breaking version is rejected with HTTP
The /compatibility endpoint runs the check without registering, so CI can test a change before merge:
# Test four candidate versions against version 1 under two modes, registering nothing
R=localhost:33081; S=booknest.order-events-avro-value
check() { # $1 = mode, $2 = jq edit applied to version 1
curl -s -X PUT -H 'Content-Type: application/json' -d "{\"compatibility\": \"$1\"}" \
$R/config/$S >/dev/null
jq -c "$2" schemas/order_event.avsc | jq -Rn '{schema: input}' |
curl -s -X POST -H 'Content-Type: application/vnd.schemaregistry.v1+json' -d @- \
"$R/compatibility/subjects/$S/versions/latest?verbose=true" |
jq -r '"\(.is_compatible) \(.messages[0] // "" | capture("errorType:.(?<t>[A-Z_]+)").t // "")"'
}
add_default='.fields += [{name: "channel", type: "string", default: "web"}]'
add_required='.fields += [{name: "channel", type: "string"}]'
drop_optional='del(.fields[] | select(.name == "customer_id"))'
new_symbol='(.fields[] | select(.name == "type") | .type.symbols) += ["order_refunded"]'
printf '%-14s %-9s %-6s %s\n' change BACKWARD FULL "first error"
for c in add_default add_required drop_optional new_symbol; do
read -r b be <<< "$(check BACKWARD "${!c}")"; read -r f fe <<< "$(check FULL "${!c}")"
printf '%-14s %-9s %-6s %s\n' $c $b $f "${be:-$fe}"
doneOutput
change BACKWARD FULL first error add_default true true add_required false false READER_FIELD_MISSING_DEFAULT_VALUE drop_optional true true new_symbol true false MISSING_ENUM_SYMBOLS
Under BACKWARD the new schema must read old data, so a new field needs a default; dropping an optional field or adding an enum symbol is safe. FULL also makes old readers read new data, and an old consumer has no symbol for order_refunded unless the enum declares a default. Upgrade consumers first under BACKWARD, producers first under FORWARD; _TRANSITIVE modes check every earlier version.