This episode covers building data contracts for event schemas, managing schema versioning and compatibility rules, handling schema evolution on Debezium payloads, and using Avro and Protobuf for consumer validation.

When data flows between teams, the event structure is the unwritten agreement that gets violated most often. One team adds a column, another team is left confused. Episode 9 covers how a schema registry turns that agreement into an enforceable contract, complete with versioning, compatibility rules, and automatic validation.
In episode 5 you already saw the schema registry's role in payload formats. Now we step up a level: not just storing schemas, but building a data contract — an explicit agreement about the shape and meaning of every event consumed by many parties.
A data contract answers three questions: what the event structure looks like, who's responsible for it, and how the event may evolve. In Debezium practice, this contract takes the form of an Avro or Protobuf schema registered in the schema registry.
Start by enabling the schema registry in your compose stack:
schema-registry:
image: confluentinc/cp-schema-registry:7.8.0
ports:
- "8081:8081"
environment:
SCHEMA_REGISTRY_HOST_NAME: schema-registry
SCHEMA_REGISTRY_KAFKASTORE_BOOTSTRAP_SERVERS: kafka:9092
SCHEMA_REGISTRY_LISTENERS: http://0.0.0.0:8081Once the worker uses the AvroConverter and points to schema.registry.url, every Debezium event automatically registers its schema the first time the connector runs.
The schema registry stores the history of every schema per subject. Debezium subjects are usually named topic.prefix.database.table-key or -value:
curl -s http://localhost:8081/subjects | jq
curl -s http://localhost:8081/subjects/dbserver1.inventory.customers-value/versions | jqThe compatibility rules determine how freely a schema can evolve:
| Level | Meaning |
|---|---|
BACKWARD | New consumers can read data produced by older versions |
FORWARD | Old consumers can read data produced by newer versions |
FULL | Combination of backward and forward |
NONE | No checking |
Set compatibility per subject with:
curl -s -X PUT http://localhost:8081/config/dbserver1.inventory.customers-value \
-H "Content-Type: application/vnd.schemaregistry.v1+json" \
-d '{"compatibility": "BACKWARD"}'With BACKWARD, adding a column must provide a default — this forces teams to think twice before changing an event that many consumers already use.
Database table changes are translated by Debezium into new schema versions. Common scenarios:
Debezium produces a value schema that merges all table columns. So before changing a table schema in production, first check its compatibility with the related subject:
curl -s -X POST http://localhost:8081/compatibility/subjects/dbserver1.inventory.customers-value/versions/latest \
-H "Content-Type: application/vnd.schemaregistry.v1+json" \
-d '{"schema": "{...skema baru...}"}'Validation on the consumer side is the last layer of the data contract. Avro consumers load schemas from the registry, while Protobuf uses generated .proto files. Both give benefits:
Tip
Keep production key subjects away from experiments. Create dedicated subjects for experimental events so trial schema changes don't disturb running consumers.
Debezium registers two subjects for every topic: one for the key and one for the value. The key subject usually contains the primary key schema — for example dbserver1.inventory.customers-key — while the value subject contains the schema of all table columns. This separation matters because keys and values often evolve at different speeds.
When you add a new column to a table, only the value subject changes. Consumers that rely only on the key can keep reading without issue as long as the key subject is untouched.
Because Debezium generates schemas from database tables, choose a compatibility level aligned with how your tables change:
BACKWARD when column additions happen often and should be given defaults.FULL when you don't want to worry about the direction of evolution.NONE on production subjects unless you fully understand the risks.Remember that Debezium sends the complete schema with every event in JSON mode, but with Avro or Protobuf the schema is sent once and events only carry the schema ID. This difference is why Avro mode is far more bandwidth-efficient on high-traffic topics.
Episode 9 turns event schemas from a footnote into a contract: the schema registry stores versioning, compatibility rules enforce safe evolution, and Avro or Protobuf validate events on the consumer side.
The key takeaways:
topic-key and topic-value pattern with version history.BACKWARD, FORWARD, and FULL compatibility levels control schema evolution.In the next episode, episode 10, we'll discuss security and data privacy — securing database connections with TLS, authentication and service accounts, payload encryption in Kafka, and GDPR compliance and data masking.