Data engineering interview: How would you design schema-compatibility release gates for events?
Prompt and scope
An order event is produced and consumed by many teams. A new version adds an optional field, renames one field, and retires another; consumers cannot upgrade together, and historical messages are replayed. Design gates from proposal and compatibility checks through canary rollout and rollback. Explain how Avro writer/reader schemas, Schema Registry modes, and data-quality checks work together.
This tests data contracts, schema evolution, streaming rollout, and governance. A Schema Registry centralizes versions and compatibility checks, but exact rules vary across Avro, JSON Schema, and Protobuf.
What the interviewer is testing
- Whether you distinguish backward, forward, full, and transitive compatibility instead of treating compatibility as one boolean.
- Whether you derive rollout order from the actual writer/reader deployment window.
- Whether registration checks, runtime schema IDs, consumer lag, and data quality form one release gate.
- Whether you handle renames, defaults, deletions, enums, and irreversible semantic changes with migration and rollback.
Questions to clarify first
- Is the format Avro, JSON Schema, or Protobuf? Parsing and compatibility details differ.
- Are consumers real-time, batch, or replaying history? How long must the oldest writer version remain readable?
- Which side ships first, and are there long-tail versions across regions or teams?
- Are subjects grouped by team, event type, or environment? Is compatibility global or per subject?
- Does rollback restore the old producer, stop rollout, or require dual-writing old and new fields?
A 30-second answer framework
Map versions to a consumer window before choosing a mode: new readers consuming old messages need backward compatibility; old readers consuming new messages need forward compatibility; both directions suggest full, while transitive checks cover retained history. Validate every schema in a registry gate, then roll out readers before writers. Give new fields defaults, migrate renames with aliases or dual fields, and wait out the consumer window before deletion. Monitor registration rejects, deserialization errors, lag, dead letters, and field quality; stop expansion and retain the old version on anomaly.
Step-by-step answer
1. Make the writer and reader matrix explicit
Messages carry or reference a writer schema; consumers resolve it with their reader schema. Draw old/new producer against old/new consumer combinations and mark which must work. A rolling stream upgrade normally makes new consumers read old data before new producers write new data.
2. Tie the mode to the risk
Backward checks whether a new reader can read an old writer. Forward checks whether an old reader can read a new writer. Full checks both. If consumers replay multiple historical versions, use transitive semantics or explicitly test the retained version set. Never assume one registry default is universal across formats.
3. Define change rules
Adding a field requires a safe default whose business meaning is verified. For a rename, use a format-supported alias or dual-write old and new fields before migrating readers. Delete only after the oldest consumer and replay window expire. For new enum values, test how old readers handle unknown values; narrowing a type or changing its meaning or unit is a breaking change.
propose -> lint -> compatibility-check -> consumer-matrix-test
-> register -> canary-producer -> observe -> expand4. Build registration and CI/CD gates
At pull request time run format linting, normalized schema diffs, subject-level compatibility checks, and deserialization against representative historical samples. Keep schema IDs and versions in the registry; production tooling must reject registrations that bypass the check. Manage compatibility per subject when needed, with audited permissions and no unreviewed global override.
5. Roll out readers before writers
Canary new consumers that read the old schema and watch deserialization errors and lag. Then dual-write or release the new producer. Retire old consumers and fields only after the declared window. Long-tail consumers need an owner, version, deadline, and replay plan; “everyone will upgrade” is not a control plane.
6. Measure quality and roll back safely
Track unknown schema IDs, deserialization failures, dead-letter volume, missing-field rate, unit anomalies, consumer lag, replay success, and registration rejects. Roll back by stopping the new producer and restoring a still-compatible writer. If semantics changed, isolate a new topic or versioned subject instead of interpreting old data under a new meaning.
High-quality sample answer
I would draw the writer/reader deployment matrix, identify the oldest producers, consumers, and replay versions, and choose rules for the actual format. A new reader reading old messages is backward; an old reader reading new messages is forward; both directions require full, and retained history may require transitive checks. Every change goes through linting, a subject-level registration check, and historical-sample deserialization.
The rollout order is readers first, writers second: canary new consumers, then dual-write or switch producers, and delete old fields only after the consumer window. Add defaults, migrate renames with aliases or dual fields, and test old-client behavior for deletions and enum changes. Watch registration rejects, deserialization errors, lag, dead letters, and field quality. On anomaly, stop expansion and keep the old schema, returning to the old producer or routing to versioned topics when semantics have changed. Compatibility must be verified for the chosen format; one product’s defaults are not shared semantics for Avro, JSON Schema, and Protobuf.
Common failure modes
- Saying “enable backward” without naming the reader and writer.
- Renaming or deleting a field directly, ignoring aliases, dual writes, and replay.
- Checking only registration, not real historical messages and old consumers.
- Shipping the new producer first and breaking old consumers.
- Treating a global compatibility setting as the subject policy and ignoring permissions or drift.
- Watching Kafka lag only, while missing field loss, unit changes, dead letters, and deserialization errors.
Follow-up questions and reference answers
Why do added fields often need defaults?
Old messages do not contain the field, so a new reader needs a deterministic value to build a record. The default must be semantically valid rather than hiding a required-data defect with null.
Should a renamed field be changed directly?
Usually no. Use an alias or write old and new fields together, migrate readers, and remove the old field only after the replay window.
What is the risk difference between full and full transitive?
Full usually checks both directions against the adjacent version. Full transitive extends checks across retained historical versions, making the gate stricter and the upgrade cost higher.
Why can a registered schema still lose data?
Compatibility covers structural resolution, not units, enum meaning, field quality, authorization, or downstream SQL. Add sample replay, quality rules, and runtime observation.
How do you set an exit condition for long-tail consumers?
Record an owner, version, last-consumed time, and replay need; set a deadline and alert. Offer migration before the deadline, then isolate or reject the old version instead of weakening compatibility forever.
When should you create a new subject or topic?
When semantics, units, lifecycle, or authorization boundaries changed so compatibility cannot express the transition. Isolation lowers misinterpretation risk but adds dual-write, replay, and governance cost.