Hyrum's Law
also called Law of Implicit Interfaces, Implicit Contract
The observation that with enough consumers every observable behaviour of an interface becomes a dependency, so undocumented details such as error text and timing are part of the contract in practice.
An API has never made a breaking change by its own definition. One release rewords an error message, changes the order of JSON object keys, and makes a lookup 300 ms faster. Four integrators open tickets. "We only changed undocumented behaviour" is not a defence here; it is a description of how the outage happened.
The observation is Hyrum Wright's, the name is Titus Winters's, and it is set out in Software Engineering at Google (2020): with a sufficient number of users, what you promised stops being the interface and what you do becomes the interface. The related phrase, bug-for-bug compatibility, names the consequence.
Why it matters
Compatibility policies are written against the documented surface, and incidents happen on the observable one. The gap is not a failure of discipline by your consumers; it is arithmetic. A thousand integrators will between them depend on error prose, field ordering, pagination stability, default sort, rounding behaviour, timing and the precise shape of your 500s, because each of those is the cheapest way for somebody to solve a real problem your documented contract left open.
Treating the gap as the consumers' fault leads to repeated self-inflicted incidents, because you keep classifying changes as safe and keep being wrong in the same way.
Implementation patterns
- Give consumers something better than the thing you do not want them to depend on. A stable machine-readable error
typeorcode, documented and versioned, with the humandetailfield explicitly marked non-contractual. Then changing the prose is genuinely safe. - Signal deprecation in the protocol. The
Deprecationresponse header (RFC 9745, Standards Track, March 2025) says a resource is being phased out;Sunset(RFC 8594, Informational, May 2019) says when it is expected to stop answering. Both are machine-readable, so an integrator's monitoring can warn them before their customers do. - Measure usage per consumer, per field, per version. You cannot assess the impact of a change if you cannot name the four accounts that will feel it, and this telemetry has to exist before you want it.
- Run a dated brownout, not a cliff. Fail the deprecated behaviour for one hour a day for the fortnight before removal, with the deadline in the error body, so the hidden dependency becomes a support ticket at a time you chose.
- Freeze the properties you intend to keep, in tests. A golden-file test over response bytes, field order and error codes turns an implicit promise into an explicit one you can change deliberately.
- Document non-guarantees. Saying "key order is insignificant and may change" does not stop someone depending on it, and it does change who owns the breakage.
Industry example
Published examples of the law in action are easiest to find in long-lived platform APIs, where deprecation schedules slip repeatedly because real consumers turn out to depend on more than the specification. Salesforce's Platform API retirements, announced years ahead and then postponed, are a documented case of the economics: a vendor with enough integrators cannot simply remove a version on the date it chose, and the mechanism it needs is telemetry plus brownouts rather than a louder announcement.
Failure scenarios
- Error prose as a control plane: a client branches on a message string because the codes were too coarse, and rewording the message stops their retry logic working.
- Response-body hashing: a client canonicalises and hashes the body for change detection, so a key-order change marks every record modified and the nightly diff reports the whole dataset as changed.
- Latency as a lock: a client relies on a slow read to hide a race, and making it faster exposes the race in their code.
- Pagination stability: a client resumes from a stored cursor after you change the default sort, and either skips records or loops.
- Bug-for-bug dependence: a client works around a rounding error by applying the inverse, so fixing your bug breaks their total.
Trade-offs
Accepting the law costs freedom. A stable error taxonomy constrains internal refactoring, golden-file tests make some legitimate changes noisy, field-level telemetry is real engineering, and brownouts mean deliberately failing production traffic. What you buy is the ability to change anything at all without discovering the dependency at 3 a.m. The alternative is not lower cost, it is the same cost paid in incidents and goodwill.
When not to use it
For an internal API with three consumers in one repository, the response is a grep and a conversation; building deprecation infrastructure for it is waste. The investment scales with the number of consumers you cannot telephone. And some implicit dependencies should be broken on purpose: if a client depends on a race window or on a bug, preserving it forever is the wrong call — give them a deadline and a migration path rather than a permanent promise.
Interview question
Q: You want to change your API's default sort order from newest-first to relevance, which is a strict improvement for most users. Nothing in the specification promises an order. Tell me how you would find out who depends on the current behaviour, and what you would actually ship.
What a strong answer covers: naming the implicit-contract problem and refusing to treat silence in the spec as permission · instrumenting cursor reuse and result-position dependence per consumer · shipping the new order behind an explicit parameter first, so dependence becomes opt-in and measurable · a dated brownout before flipping the default · Deprecation and Sunset on anything being retired · and documenting the sort as explicit contract afterwards, so the next change is governed rather than discovered.
Quick check
Quiz: Why does improving an endpoint's latency count as a behaviour change? — Because some consumer's correctness depends on the old timing, typically to hide a race in their own code, and the faster response exposes it.
Flashcard: What is the cheapest structural response to Hyrum's Law on a public API? — Publish a stable machine-readable error taxonomy and mark human-readable fields non-contractual, so clients have no reason to parse prose and you can change it freely.