beginner 3 min answer

Your API returns a JSON object. Which of these changes can break a client you have never met: adding a field, removing a field, changing a field's type, adding a value to an enum, reordering keys, or making an optional field always present?

backward compatibilityjsonenumsparsersapi evolution
Show the full answer Hide the answer

The rule that decides every case

A change is safe only if every program that correctly handled the old response still handles the new one. You do not control that code, you cannot enumerate it, and you must therefore assume the strictest reasonable parser rather than the most forgiving one. Most compatibility arguments are really arguments about which parser the speaker is imagining.

Change by change

  • Removing a field is always breaking, for anyone who reads it. There is no version of this that is safe, and no way to know who reads it without telemetry you probably do not have.
  • Changing a field's type breaks every statically typed client at deserialisation, and breaks dynamically typed ones silently through comparisons. The most damaging variant is numeric: an identifier that was a string becoming a number loses precision above 2^53 in any JavaScript client, and the corruption is silent.
  • Adding a value to an enum is the change everyone files as additive and it is not. A client with an exhaustive switch, or a strict enum deserialiser, fails on a value it has never seen. This breaks clients written before the value existed, which is all of them.
  • Reordering keys is meaningless in JSON semantics and breaks anything comparing bytes — which is exactly what signature verification over a serialised webhook body does.
  • Making an optional field always present is safe for readers and breaks clients that use absence to mean something, which is more common than it sounds in APIs where null, absent and empty were never distinguished.
  • Adding a field is usually safe, because unknown keys are ignored by default in most parsers, and it is the one change a versioning policy can permit freely.

Why even "adding a field" has conditions

It breaks when the client uses a strict deserialiser configured to fail on unknown properties, which is the default in some libraries; when the client hashes or signs the payload; and when the response is stored in a schema-validated store that forbids additional properties.

This is why publishing a tolerant-reader expectation matters as much as the change policy itself. Tell integrators, in writing and from version 1, that they must ignore unknown fields and map unknown enum values to a documented default. You cannot enforce it, and it converts a class of future breakage from your fault into a documented expectation.

The safe set

Add optional fields. Everything else needs a version or a deprecation path. That is a short list and it is the honest one.

When this is the wrong framing

For an internal API with a known, enumerable set of consumers, "breaking" is a scheduling problem rather than a permanent constraint. With contract tests covering every consumer and the ability to deploy them, removing a field is a two-week coordination exercise. Applying public-API discipline there produces seven live versions of an interface used by four teams, which costs more than the coordination ever would.

Common weak answers

  • "JSON is flexible, so additive changes are always fine." Flexibility is a property of the parser, not the format, and you do not choose the parser.
  • "We will announce it in the changelog." Nobody reads a changelog for a dependency that is working.