skip to content

A platform team says its central schema registry enforces compatibility, yet a breaking record-schema change reached consumers. What must hold for the registry to gate a publish?

level: seniorimportance: must knowfreq 66%

answer

  1. a library is called, a gate is unavoidable
  2. server-side decision, not client courtesy
  3. a name without a mode judges nothing
  4. count the write paths that skip it
  5. who may relax the rule under pressure

basics

~20 s

A registry gates a publish only when the decision is server-side, the name being published to has a mode configured, no write path reaches the bytes without registering, and the mode itself is not freely editable by the team being gated. Miss any one and it is a library.

solid answer

~40 s

A library is something you call; a gate is something you cannot avoid calling. Four properties turn one into the other. The comparison must run on the registry, not in client code that a producer can decline to use. The name being published to must have a compatibility mode set — storing versions without a rule records history nobody checks. Every path that writes these bytes must go through registration, including backfills, archival writers and one-off scripts from teams you did not brief. And the mode must not be editable by the team it constrains, or the gate is advisory. Registration-on-publish does not bypass the check, but it moves the failure into a running producer and lets a brand-new name be created silently, which is the cheapest way around any rule.

code

pseudocode · 14 lines
pseudocode
on register(name, candidate):
    history = registry.versions_of(name)
    if history is empty:
        return registry.store(name, candidate)      # baseline, nothing to compare

    mode = registry.mode_of(name)
    if mode is none:
        return registry.store(name, candidate)      # stored, never judged

    for each old in versions_in_scope(mode, history):
        if not relates(old, candidate, mode):
            reject(name + ' breaks version ' + old.version)

    return registry.store(name, candidate)

go deeper

for a junior

Recall that a registry can only refuse a schema it is actually asked about, and only when a rule has been configured for that name.

for a middle

Explain the difference between storing versions and judging them, and why a decision made inside client code binds nobody.

for a senior

Enumerate the write paths in a real pipeline and say which ones reach the bytes without a verdict — backfills and migration scripts are the usual answer.

for a principal

Own the placement question: the same comparison costs minutes in a change, hours in a deploy, days in a consumer, and the editable mode decides which one you actually get.

Most 'the registry did not catch it' incidents are not bugs in the comparison. The comparison worked; the change simply never had to face it. ## Library versus gate A **library** is something a well-behaved client calls. A **gate** is something no client can get past without a verdict. A registry is shipped as the first and only becomes the second through deliberate configuration, and the difference is invisible on the happy path — both look identical until the day somebody ships a break. ## The four properties 1. **The decision runs on the server.** If the compare happens inside client code, the verdict is a suggestion: a client can be an old version, a different implementation, or a program that simply does not perform the check. Only a decision made by the registry, as a precondition of storing a version, binds every caller equally. 2. **The name has a mode configured.** Storing versions is not judging them. A name left without a rule accepts every candidate and accumulates a history nobody is checking. This is the most common single cause of the incident in the question: the registry was up, correct and consulted, and had been asked to enforce nothing on that name. 3. **No write path skips registration.** The gate protects the bytes that pass through it. Backfill jobs replaying corrected history, archival exporters, a partner integration, a migration script writing directly with a hand-rolled encoder — each is a lane with no turnstile, and each has put incompatible data on a stream that had a green registry. 4. **The mode is not editable by the team it constrains.** A rule anyone can relax under release pressure is advisory. This is a governance property, not a technical one, and it is the one most often missing. ## Where registration-on-publish really hurts A client permitted to register whatever it is about to write does **not** escape the check — a configured mode still refuses an incompatible candidate. Two other things change, and both are bad: - The failure surfaces **in a running producer**, at deploy or at traffic time, instead of in a change under review. - A **new name** can be created silently, with no history to be compared against, so its first document becomes an unreviewed contract. Renaming a record to dodge a rejection is the cheapest escape route the model permits. ## The cost ordering of where a break lands | Where the break surfaces | Who notices | Cost | |---|---|---| | A change under review | The author, immediately | Minutes; nothing shipped | | A producer's publish call | The producing team, in their own deploy | Hours; a rollback, no bad data | | A consumer's decode | Another team, often at night | Days; poison records, dead letters, backfills | The registry's comparison is identical in all three rows. Only the placement differs, which is why 'shift the same check left' beats any improvement to the check itself. ## Auditing the claim 'the registry enforces it' Ask five questions in order: 1. Which names carry a mode, and which carry none? Expect the list of unconfigured names to be longer than anyone believes. 2. Can a client register a name that did not exist yesterday, and who reviews the first version under it? 3. Which processes write to this stream, and can you name one that does not consult the registry? 4. Who can change a mode or remove a version, and is that action recorded anywhere? 5. Where does a rejection surface today — in a change, in a deploy, or in a consumer? ## What a gate still does not give you Even with all four properties in place, the verdict is structural. It does not know the meaning of a field, the deployment order of producers and consumers, or whether a reader parses records by hand. A registry raises the floor from 'anything can ship' to 'nothing structurally incompatible can ship *through this path*'. Contract tests and rollout ordering are what sit above that floor, and a team that believes the gate replaced them has just moved its outages rather than removed them.

  • Why is a new name the cheapest way around a rejection, and what stops it?
    A name with no history has nothing to compare against, so the candidate is accepted unconditionally. Nothing technical stops it; what stops it is review over the creation of a name and ownership of the stream, so a second lineage for the same record type is a visible decision rather than a silent one.
  • The registry was reachable and the mode was set, yet bad records appeared. Where do you look first?
    At writers that never call it: replay and backfill jobs, archival exporters, migration scripts, partner integrations with hand-rolled encoders. A gate covers the path through it, and these are the lanes without a turnstile.
  • Does moving the check into a change-review step make the server-side check unnecessary?
    No. The early check is advisory by construction — it can be skipped, run against the wrong document, or bypassed by a writer that never opens a change. It buys a cheap failure, not a guarantee; the server-side decision remains the thing nobody can avoid.

A turnstile only controls a station if every entrance has one; the polite passengers were never the problem, and the unstaffed side door is.

saying these in an interview costs you the question

  • Assumes buying a registry is the same as enforcing anything
  • Thinks a name with no mode still refuses incompatible schemas
  • Forgets backfill and archival writers bypass the gate entirely
  • Believes client-side checking is as binding as a server verdict
  • Says renaming a record type cannot smuggle a breaking change
  • Treats the ability to relax a mode as an implementation detail