The most expensive review miss isn't a bug — it's a breaking change that ships to a hundred consumers you didn't know about.
Student edition · understand, apply, verify
Review binary, schema, and semantic compatibility across old/new readers and writers.
What you should be able to do: Identify field meaning and migration/rollback requirements, not just whether a change is optional.
A context-dependent practice guide, not a universal policy or verified personal history. Examples and numbers are illustrative. Use genuine, authorized evidence; personnel/privacy/legal decisions follow applicable organization policies and qualified review.
Concepts explained in this guide
Read these definitions first, or use the linked terms as you go. The examples and limitations are local to this guide; companion pages are optional.
Schema compatibility and semantic contracts
Backward compatibility lets new readers read old data; forward lets old readers read new data. Field identity/default rules differ by format.
Example. Milliseconds changed to seconds under an unchanged integer field is a breaking semantic change.
Boundary. Optional is not universally safe. Registry checks are configured structural rules, not business semantics; schema-ID headers are format/vendor conventions, not Kafka itself.
Expand-contract migrations
Change a schema in stages that allow old and new software to coexist: add a compatible representation, deploy readers/writers, backfill and reconcile, switch authority, then remove old fields only after the rollback horizon.
Example. Rename full_name by adding display_name first. Both app versions remain valid until backfill, dual-write consistency, and rollback checks are satisfied.
Boundary. Two application writes are not automatically atomic. Prefer one transaction/change-log authority and detect divergence. Dropping the old field too soon makes code rollback unsafe.
Ownership, accountability, and decision rights
Ownership names who drives follow-through; accountability names who answers for outcomes. Decision rights specify who chooses/approves and who is consulted.
Example. A migration has a coordinating owner, service owners, and named release authority.
Boundary. DRI/RACI/DACI are local conventions, not guarantees. Role labels do not replace authority, capacity, and follow-through.
Preconditions and postconditions
A precondition states required inputs/state before an operation. A postcondition states the result promised when those requirements hold and execution terminates normally. Together they make requirements testable.
Example. For sum_first(b,n), require integer 0≤n≤len(b) and numeric elements; promise the sum of b[0:n].
Boundary. State exceptions, mutation, numeric domains, and empty cases. A mathematical array range does not automatically validate a runtime input.
Outputs, outcomes, and measurement
Outputs are produced things; outcomes are changes for users/business/team. Useful metrics state definition, denominator, population, period, and attribution limits.
Example. 'Shipped a tool' is output. 'Median onboarding fell ten to six days in this cohort' is an outcome with sample/context caveats.
Boundary. Before/after does not prove sole causation. Avoid gaming, selection bias, hidden costs, and unlike-team comparisons.
In a large system, interfaces are contracts, and a diff that changes a contract can break producers and consumers that the author never sees. This question tests whether you review schema and API changes with the whole ecosystem in mind: who reads this field, who writes it, what happens during the window when old and new code both run. For an events-infra engineer this is bread and butter — Thrift/protobuf/Avro schemas flow through pipelines and land in tables that dozens of teams query, so a careless field change is a silent data-corruption or outage event, not a local bug.
Read every interface change as a contract: additive vs breaking, wire-format field rules, defaults, forward/backward schema evolution, a real deprecation path, the full set of downstream consumers, query semantics, and long-term data correctness.
What this question is really testing
Whether you think in terms of rollout windows and independent deployment. The core insight is that producers and consumers are never upgraded atomically, so every change must be safe when old and new versions coexist. The tell is that you reach for “additive and backward-compatible” by default, know the concrete wire-format rules that make a change safe or breaking, and always ask “who are the consumers?” before approving.
How to answer
Adding an optional field can be structurally compatible under a format/reader policy, but defaults, presence, JSON/text names, enum handling, and semantics still need tests.
Binary tag-based identity differs from JSON/text/reflection naming. Renaming can break those supported surfaces even if binary tags are unchanged.
Defaults must satisfy the actual writer/reader compatibility direction and semantic contract. Presence versus default value is format/version-specific.
Schema evolution: forward and backward. Backward = new code reads old data; forward = old code reads new data. Pipelines usually need both, because data written yesterday is read by code deployed today and vice versa. Confirm the change survives both directions.
Deprecation path. To remove or change a field, do it in stages: add the new, dual-write, migrate readers, verify no one reads the old, then remove. Never a big-bang rename. The diff should show which stage it's in.
Downstream consumers. Ask explicitly: who consumes this schema/endpoint? Pipelines, dashboards, other services, external partners? Use code search and lineage to find them. “I couldn't find a caller” is not the same as “there is no caller.”
Query-language and semantic changes. A change that keeps the schema but alters semantics — a filter's default, a join's cardinality, a null-handling rule, a unit or timezone — is a silent breaking change. It compiles, tests pass, and downstream numbers quietly shift.
Data correctness and schema stability over time. The table or topic is a long-lived asset. Ask whether the change preserves the meaning of historical data, whether backfills are needed, and whether the schema stays stable enough to trust in year-over-year analysis.
What the interviewer is looking for
“Additive and backward/forward compatible” as the default posture.
Precise knowledge of wire-format rules — tag identity, no type changes, reserve removed fields.
“Who are the consumers?” asked before approval, with lineage/code search to find them.
A staged deprecation path instead of a big-bang rename or removal.
Awareness that semantic changes are breaking even when the schema is unchanged.
The classic wire-format catch — a protobuf change that looks harmless and silently corrupts every consumer:
// WRONG: reused tag 2 and changed a type -- old readers misparse the bytes
message AdEvent {
string ad_id = 1;
int32 region_id = 2; // was: string campaign_id = 2; BREAKING
}
// RIGHT: reserve the old tag/name, add the new field with a fresh tag
message AdEvent {
reserved 2; // never recycle tag 2
reserved "campaign_id";
string ad_id = 1;
int32 region_id = 3; // additive: new tag, optional, defaulted
}
Common follow-ups
You can't find any consumers of a field. Safe to delete?
How to answer
Absence of evidence isn't evidence of absence. Reflection, dynamic queries, external partners, and archived jobs won't show in a grep.
Deprecate, then delete. Mark it deprecated, add usage logging, wait a cycle with zero reads, then remove.
Reserve after removal. Keep the tag/name reserved so it's never recycled.
How do you review an API versioning strategy?
How to answer
Prefer evolution over versioning. Additive, compatible changes avoid a version fork entirely; version only for genuinely breaking redesigns.
Explicit lifecycle. A new major version needs a migration guide, an overlap window, and a deprecation/sunset date for the old one.
Don't strand clients. Confirm every consumer has a path and time to migrate.
Schema unchanged, but the diff changes what a field means. Your call?
How to answer
Treat it as breaking. A semantic change (units, timezone, null handling, dedup rule) breaks consumers silently — worse than a compile error.
New field or new version. Encode the new meaning separately rather than mutating the existing one in place.
Backfill and communicate. Decide what happens to historical data and notify every downstream owner.
What's specific about Avro/schema-registry compatibility?
How to answer
Compatibility mode matters. Backward, forward, or full — the registry enforces different rules; know which the topic is set to.
Defaults enable evolution. Adding a field requires a default for backward compat; removing one requires the field had a default for forward compat.
Reader vs writer schema. Correctness depends on the pairing, not just the latest schema.
Evidence to collect with your approved tools
A decision/review record with the problem, constraints, options, and your contribution.
Relevant tests, dashboards, or measurements with definitions, denominators, dates, and uncertainty.
Incident or delivery records showing actual outcomes and unresolved limitations.
Stakeholder agreements and follow-up actions with owners and review dates.
Only authorized, minimized, safe-to-share evidence. Do not copy confidential personnel notes into a public story.
Worked example: an additive field is semantically breaking
This is an illustrative situation. The point is the reasoning and evidence, not copying the numbers or first-person story.
Sequence for this example. The table explains the evidence and decision at each step.
Step
Evidence, decision, and expected result
Describe old meaning
duration is milliseconds and readers depend on that unit.
Keep the same integer field but change units
New code emits seconds. Structural checks pass, but old consumers silently compute wrong results.
Introduce an explicit new representation
Use a new field/version with documented units, defaults, reader/writer compatibility, and migration scope.
Preserve rollback/history
Backfill/reconcile under one authority, verify consumers and archived data, and retain the old meaning through the supported rollback horizon.
Check your understanding: Are Protobuf field names always cosmetic?
No. Binary identity uses tags, but JSON/text mapping, reflection, APIs, and tooling can depend on names. Check every supported representation.
Run and change the example
Download guide_labs.py. Python 3.10+, standard library only. In its directory, run:
python3 guide_labs.py compatibility
python3 guide_labs.py test
The lab checks old/new reader fixtures and demonstrates a units change that passes a shape-only check.
The program tests its stated finite contracts. It is an educational model, not an implementation of the named production product or a proof of unmodeled distributed faults.