For a JSON REST API, which changes to a request or response are backward-compatible (additive) and which break existing clients? Give concrete examples.
answer
- would an un-redeployed client still work?
- add is safe, remove/rename/retype is not
- optional param + old-behaviour default
- units and meaning break silently
- status codes and error shape are contract too
basics
~20 sAdditive: new endpoints, new response fields, new optional request parameters, relaxed validation. Breaking: removing or renaming a field, changing its type, units or format, making a parameter required, tightening validation, changing status codes or the error body shape.
solid answer
~40 sThe test is whether an already-deployed client, unchanged, still works. **Additive:** a new endpoint; a new field in a response; a new optional request parameter whose default reproduces old behaviour; accepting more input than before. **Breaking:** removing or renaming a response field; changing a field's type (`"42"` to `42`, scalar to array); changing units or format (seconds to milliseconds); making an optional parameter required; tightening validation; changing which status code an outcome returns; changing the error body shape; changing defaults such as page size or sort order. **Grey zone:** new enum values, new fields inside array elements — safe for tolerant readers, breaking for clients that validate strictly or branch exhaustively. Semantics count as much as syntax: same field, same type, new meaning is a break, and the worst kind, because it fails silently.
go deeper
Recall the two lists with examples and state the un-redeployed-client test.
Add the grey zone — enums, nested fields, defaults — and explain why the answer depends on client parsing style.
Emphasise silent semantic breaks, request-vs-response asymmetry, and enforcing the taxonomy in CI rather than in review.
Frame compatibility as a published policy: define what the organisation guarantees, make it machine-checkable, and accept the modelling cost that guarantee implies.
## The single test A change is **breaking** if a client written against the old contract, and not redeployed, stops working correctly. "Correctly" includes being silently wrong, not only crashing. Every rule below is a corollary. ## Additive changes A new endpoint is invisible to existing callers. A new response field is ignored by parsers that map only the fields they know. A new request parameter is safe **only if it is optional and its default reproduces the old behaviour** — an optional `?include=` that quietly changes the default payload is not additive. Relaxing validation (accepting a longer string, a new currency) is additive in the request direction, because no existing valid call becomes invalid. ## Breaking changes **Removal and renaming.** Deleting `customer_name`, or renaming it to `customerName`, blanks the field in every client that reads it. Renaming is removal plus addition, never a rename on the wire. **Type and representation.** `"42"` to `42`, `42` to `42.0`, a scalar to an array, `null` where the client never saw `null`, an ISO-8601 timestamp to an epoch integer. Strongly typed clients throw; weakly typed ones corrupt data. **Units and semantics.** `timeout` in seconds becoming milliseconds, `amount` in cents becoming decimal currency, `status: "active"` narrowing to exclude trial accounts. Nothing in the schema changes, so no tool catches it, and money or timing goes wrong in production. **Requiredness.** Making a formerly optional request field mandatory rejects every existing caller. Conversely, making a response field that was always present now optional breaks anyone who assumed presence. **Protocol-level contract.** Changing 200 to 202 for the same operation, changing 404 to 200-with-empty-body, restructuring the error envelope, or changing a default page size or sort order all change behaviour clients depend on. ## The grey zone Adding an enum value (`status: "paused"`) or a nested field breaks only clients that reject unknown values or validate responses against a strict schema. Whether you treat these as breaking is a **documented policy decision**: publish the rule ("we may add enum values and object fields at any time; clients must ignore unknown ones") and then adding is additive by contract. ## Practice Evolve by adding: introduce `amount_minor` alongside `amount`, dual-write both, migrate consumers, remove the old one only under a deprecation programme. Treat request and response directions separately — request rules loosen, response rules tighten. Encode all of this in machine-checkable schemas so CI, not review, catches violations.
- Is adding a field to a response ever breaking?Yes, when clients validate responses against a closed schema (`additionalProperties: false`), deserialize with fail-on-unknown-properties enabled, or hash/sign the whole payload. It is also breaking if the field's presence changes meaning — for example adding `discount` that consumers must now subtract. Publish an explicit tolerant-reader rule so additions stay safe by contract.
- Is changing an HTTP status code from 200 to 201 for a creation endpoint breaking?Yes. Clients branch on status codes, and many only special-case exact values or treat anything non-200 as a failure path. Even within the 2xx class the change alters observable behaviour, so it belongs in a new version or behind a deprecation cycle rather than shipping as a fix.
saying these in an interview costs you the question
- Believing anything that keeps the schema valid is non-breaking, ignoring semantic changes like units
- Treating a rename as safe because the data is unchanged
- Adding a required request parameter and calling it additive because the endpoint is unchanged
- Assuming only response changes can break clients
- Judging compatibility from the server's tests instead of the clients' actual usage