Under a data contract, how does a producer safely drop a field consumers still read?
answer
- removal is an event, not a commit
- mark it, don't delete it
- both fields live at once for a while
- watch who still reads it
- major version after the window closes
basics
~20 sNever delete in place. Publish a contract version that marks the field deprecated with a removal date and a replacement, keep emitting it through the agreed window, watch lineage and query logs for remaining readers, then remove it in a major version once usage reaches zero.
solid answer
~50 sRemoval is a process, not a commit. The producer publishes a minor version that marks the field `deprecated` in the spec, naming the reason, the replacement and a `remove_after` date drawn from the contract's deprecation window. The new field, if there is one, ships alongside — both are populated for the whole window, which is what lets consumers migrate one at a time rather than in a coordinated big bang. Meanwhile you *measure*: query logs and column-level lineage tell you who still reads it, and that list — not the calendar — is what actually gates removal. Near the end of the window you contact the remaining readers directly, and if any cannot move you renegotiate the date rather than break them silently. When usage is zero, a major version drops the field and CI stops blocking the removal. The one thing you never do is delete first and find the consumers from the incident channel.
code
yaml · 13 lines# minor version 2.4.0 — deprecation is a COMPATIBLE change
fields:
- name: coupon_code
type: string
required: false
deprecated:
since: 2.4.0
remove_after: 2026-06-01
replacement: promotions[].code
reason: an order can now carry several promotions
- name: promotions # ships now, populated for the whole window
type: array<struct<code:string, amount_cents:int>>
required: falsego deeper
Know that under a data contract a field is first marked deprecated and kept in place, and only deleted later — never removed the moment the producer stops needing it.
Explain the sequence: deprecate in a minor version with a removal date and replacement, ship both fields in parallel, announce, then drop in a major version once the window closes.
Show that usage telemetry, not the calendar, gates the removal, and name the blind spots — query-history retention shorter than a quarterly job, consumers outside your lineage graph — plus what you do when a reader remains at the deadline.
Own the window length and the policy behind it: how long the slowest consuming team needs, when you extend versus force, and how enforcing a deprecation over a live dependency once costs you every future deprecation's credibility.
## Removal is the hard direction Adding to a dataset is cheap; taking away is where contracts earn their existence. A removal is invisible to the producer — their tests pass, their service is healthier without the field — and catastrophic to whoever was reading it. The contract's job is to convert that invisible act into a scheduled, announced, measurable one. ## Step one: deprecate in the spec, do not delete The first change is additive metadata, not a removal. In the contract, the field gains a deprecation block: **since** which version, **remove_after** which date, **replacement** (a field, or none), and **why**. This is a compatible change, so it passes the producer's CI gate without a major version bump and without consumer sign-off — deliberately, because you want deprecation to be frictionless while deletion stays expensive. If a replacement exists, it ships in the same version and is populated from that moment. The overlap period is the whole mechanism: while both fields carry correct data, each consuming team migrates on its own schedule, and no coordination meeting is required. ## Step two: announce on the channel the contract names A change nobody saw is not a change that was announced. The contract's change policy should name where announcements land — a catalog entry that surfaces the deprecation to anyone browsing the dataset, plus a push into whatever channel consuming teams actually read. Publishing a new contract version on merge is what makes this automatic rather than dependent on somebody remembering. ## Step three: measure who is still reading This is the step that separates a real process from theatre. The calendar does not tell you it is safe to remove a field; **usage telemetry** does. Two sources are typically available: - **Query history / access logs** from the warehouse, parsed for references to the column. Most engines record executed SQL, and column-level parsing turns that into a reader list with team attribution. - **Column-level lineage** from your transformation tooling or catalog, which finds models and downstream tables that select the field even if no human queried it recently. Both have blind spots. Query logs miss a monthly or quarterly job that has not run inside the retention window — a real cause of "we checked, nobody used it" incidents. Lineage misses consumers outside your lineage graph entirely: an application reading the table directly, an exported extract, a notebook. So combine both, extend the observation window past your longest known job cadence, and treat *silence* as weaker evidence than an explicit confirmation from an owner. ## Step four: converge, then remove As the date approaches, the remaining readers should be a short list of named teams, and you talk to them. Two outcomes are legitimate: they migrate, or the window extends. What is not legitimate is removing on schedule while a known reader is still reading, because the whole social contract collapses the first time a deprecation window is enforced over a live dependency. When usage is zero, the removal ships as a **major version**. The producer's CI gate — which blocks field removals — is satisfied by the major bump plus whatever sign-off the change policy requires, and increasingly by an automated assertion that the deprecation existed for the required window and that usage telemetry is clean. ## Renames are removals A rename is an add plus a remove, and it must be run as one: introduce the new name, populate both, deprecate the old, wait, then drop. Teams that treat a rename as a single atomic edit are the origin of most contract incidents, precisely because it feels harmless in the producer's diff. ## Choosing the window length The window is a property of the contract, not of the individual change, and it should be long enough to cover your slowest consumer's release cadence plus their queue — a quarter is a common choice for shared datasets and a fortnight for datasets read only inside one team. Publish it in the contract so that when the moment comes, nobody is negotiating a timeline under pressure. ## The failure modes The recurring failures are: deleting first and discovering consumers through the incident channel; announcing but never measuring, so the removal is a coin flip; measuring only recent query history and missing the quarterly job; leaving the field deprecated forever because nobody owns closing it out, which trains everyone to ignore deprecation notices; and dual-writing two fields with subtly different semantics during the window, so consumers who migrate early get different numbers than those who have not.
- Query history shows nobody has touched the field in 30 days. Is that enough to remove it?No. Thirty days misses anything running on a longer cadence — a monthly close, a quarterly regulatory extract — and query logs never see consumers outside the warehouse, such as an application reading the table directly. Extend the observation window past your slowest known job, cross-check column-level lineage, and get explicit confirmation from the owners you can identify.
- How do you run a field rename under a data contract?As an add plus a deprecated remove, never as one edit. Introduce the new name, populate both fields for the full window, mark the old one deprecated with a removal date, let consumers cut over individually, then drop the old name in a major version. Treating a rename as atomic is the single most common source of contract incidents.
- The window expires but one team still reads the field. What do you do?Extend it and say so. Removing over a known live reader destroys the credibility of every future deprecation notice, and the cost of a few more weeks of a redundant column is trivial next to that. Escalate through the owning teams if the migration is stalled, and record the new date in the contract rather than letting it drift informally.
It is the same discipline as retiring a public API endpoint: mark it deprecated with a sunset date, keep serving it, watch the access logs until the callers are gone, then delete in a major version.
saying these in an interview costs you the question
- Deleting the field and waiting to see who complains
- Treating the calendar date as sufficient reason to remove
- Running a rename as one atomic change to the schema
- Checking only 30 days of query history for readers
- Leaving fields deprecated indefinitely with no owner closing them out