skip to content

When may a JSON response field be removed if some clients can never be upgraded?

level: principalimportance: nice to knowfreq 30%

answer

  1. no signal on either side of the wire
  2. grep is not evidence
  3. count who is still calling you
  4. what does emitting it keep alive
  5. the rule is yours, the veto is theirs

basics

~20 s

Only when evidence, not a code search, says nothing in the wild still reads it: traffic by client version measured against the release that stopped needing it. Until then keep emitting the key, and decide by what emitting it forces you to keep alive.

solid answer

~50 s

Deleting a field from a wire struct is a one-line diff with no compiler signal, no test signal and no runtime signal: old clients that wanted the key render blanks rather than raising errors, so nothing in your dashboards moves and you learn about it from support. That means a grep of your own code is not evidence — only the distribution of client versions still calling the endpoint is, read against the release in which clients stopped needing the field. The decision is almost never about payload bytes. It is about what continuing to emit the key forces you to keep alive: an upstream service, a column, a migration you cannot finish. Name that cost, weigh it against the tail of un-upgradable installs, and if it cannot wait, negotiate a minimum supported client version with whoever owns the app. I set the payload policy; the client owner can veto a specific removal, because they carry the users who cannot move.

go deeper

for a junior

Be ready to say that deleting a key from a shipped response is a contract change rather than a cleanup, and that an old client sees a blank value rather than an error.

for a middle

Explain the mechanics that make removal silent — an unmatched field is left at its zero value — and describe a deprecation window: stop reading, keep emitting, measure, then delete.

for a senior

Bring the evidence and the safety net: traffic by client version, a staged rollout, and a way to restore the key without waiting for a client release.

for a principal

Own the policy and name the real tradeoff — what emitting the field forces the organisation to keep alive, who sets the minimum supported client, and who is entitled to overrule you on a specific removal.

## Why removal is a different kind of decision Adding to a payload is a technical judgement you can make alone. Removing from one is not, and the asymmetry is worth stating precisely: a removal has **no failure signal on either side**. Deleting the field from the wire struct compiles. The tests pass, because they were written against the current struct. The server's error rate does not move, because the server is behaving correctly. The client's decode does not fail, because a key with no match is simply never assigned and the field keeps its zero value. The whole event is a blank in somebody's interface, discovered days later through a support ticket, in a client release you cannot recall. So the question is never "is this field still used?" as a code question. It is "what is still out there, and what does it do without this key?" ## What counts as evidence **A code search does not.** Finding no reader in your repositories tells you about the code you own, and the readers that matter are compiled into installs on devices you do not control. **A deprecation note in the documentation does not.** It records an intention. It does not measure who acted on it. **The absence of complaints does not.** The failure mode is a blank field, and a blank field mostly produces a shrug, not a ticket. What does count is traffic segmented by client version, read against a specific fact you have to establish first: *which client release stopped needing this field?* Once you know that, the question becomes arithmetic — how much traffic still comes from versions older than that release, and what is the shape of the tail. On a mobile backend with clients that cannot be force-upgraded, that tail is measured in months and quarters, and it never quite reaches zero; part of the decision is choosing the percentage at which you stop caring, and being able to say why that percentage is acceptable to the business. When you cannot even measure that — no version in the request, no telemetry — the honest answer is that the field is permanent until you can, and the first piece of work is the instrumentation, not the deletion. ## What the decision is actually about The reason to remove a field is almost never the bytes. A key nobody reads costs a rounding error of bandwidth and one line in a struct. The reason that genuinely forces the issue is what *populating* it costs: it keeps an upstream service on life support, holds a column in a table you want to drop, blocks a migration, or requires you to keep computing something whose source of truth has moved. State that cost concretely, in the currency of the team's roadmap, and the conversation stops being about tidiness. That framing also reveals the cheaper options. If the expense is computing the value, you may be able to freeze it — emit a stable, honestly interpretable placeholder such as an empty string or an empty list, which an old client renders as nothing while the expensive source goes away. What you must not do is emit a *plausible but wrong* value: a blank field leaves an old client visibly empty, while a stale one leaves it confidently incorrect, which is strictly worse and much harder to trace. If several removals or a reshape are due at once, version the whole payload instead: a new wire struct served under a new route or media type, with the old one retired wholesale. One retirement you can schedule beats a dozen half-deprecated keys each with its own timeline. ## The deprecation ladder A workable sequence, and the one to describe when asked: 1. Mark the field deprecated where clients will actually see it, and record the client release that stopped needing it. 2. Stop *reading* it server-side and stop requiring anything of new clients — the field becomes emit-only. 3. Keep emitting, and measure traffic from versions older than that release. 4. When the tail is inside your policy, remove behind a flag or a staged rollout so the change can be reversed without a client release. 5. Ship it alone, early in a release train, never alongside other payload changes — a rollback should restore one variable. 6. Delete the field, its conversion code and its stored test payloads together. ## Who decides The payload policy belongs to whoever owns the API: what may change, what evidence a change carries, and how long the deprecation window is by default. A specific removal is different, because the cost lands on the client owner, whose users are the ones who cannot move. The healthy arrangement is that the API owner sets the rule and can be overruled on any single application of it by the person who carries that consequence — and that the overrule comes with a date and a minimum supported version, not an indefinite veto. Where a team has no minimum-supported-client policy at all, that is the missing artefact, and pushing for one is a better use of the argument than winning this particular field.

  • What if you cannot instrument which fields a client actually reads?
    You usually cannot — a response field carries no read signal back to you. Fall back to client version plus release history: identify the first client release that stopped needing the field, then measure traffic from versions older than that. If even the version distribution is unknowable, treat the field as permanent and spend the effort on getting a version into the request instead.
  • Is freezing the field to a placeholder a reasonable compromise?
    Only when the placeholder is honestly interpretable — an empty string or empty list that an old client renders as nothing. That lets you retire the expensive source of truth while keeping the key. Emitting a stale or invented value is worse than removal: the client is confidently wrong instead of visibly blank, and nothing in either system can tell you it happened.
  • When should you version the whole payload instead of removing one key?
    When several removals or a reshape land together, or when the old and new meanings cannot coexist in one document. A second wire struct served under a new route or media type gives you one scheduled retirement rather than a dozen independently deprecated keys, and it keeps the old payload exactly as it was for clients that will never move.
  • How do you make the removal reversible?
    Put it behind a flag or a staged rollout so the key can come back without a client release, and ship it alone, early in a release train, so a rollback restores a single variable. Also delete the field, its conversion code and its stored test payloads in the same change, so a revert brings the whole contract back rather than a half of it.

It is like closing a station's last platform. Nobody sends you a complaint from the train they could not catch; you only learn about it from the people who eventually arrive somewhere else.

saying these in an interview costs you the question

  • Removes a field because a code search finds no server-side reader
  • Assumes old clients will error loudly when a key vanishes
  • Counts saved payload bytes as the reason to remove a field
  • Ships the removal alongside other payload changes
  • Emits a stale plausible value instead of a blank one
  • Treats a minimum-supported-client policy as somebody else's problem