The Accidental Interface
The interface you documented is not the interface you have. The interface you have is everything about your system that a consumer can observe and build on — and that set is always larger than what you meant to expose. The exact wording of an error message. The order that results come back in when you never specified an order. The fact that a field is always present even though the schema marks it optional. The timing: how long an operation usually takes, and what a client does when it takes longer. None of these were designed as promises, and all of them are things someone can and will depend on.
This is Hyrum’s Law, and the thing that makes it uncomfortable is that it doesn’t care about intent. It doesn’t matter that you never documented the ordering; if the ordering has been stable for two years, some consumer has code that assumes it, and when you change it, that code breaks. The consumer will be technically in the wrong — they depended on something you never promised — and being technically right will not make their outage less real or the conversation less painful. Observable behavior becomes a contract by usage, not by declaration, and usage is not something you get to approve.
The practical implication is not that you must preserve every observable behavior forever — that would freeze the system completely. It’s that you should know the difference between the promises you made and the behaviors you’re merely exhibiting, and be deliberate about which of them you’re willing to break. Some accidental dependencies are worth breaking: the consumer parsing your error strings should have been reading the error code, and if it costs them a day to fix, that’s the right trade. Others aren’t worth it: if half your consumers depend on the incidental ordering, “we never promised that” is a true statement that will not save you.
The lever you have is to reduce what’s observable in the first place. Behaviors that consumers can’t see can’t be depended on. Randomizing the order of results that have no defined order — deliberately, from the start — means nobody can build on an ordering that was never meant to hold. Structured error codes alongside human-readable messages give consumers a stable thing to branch on so they don’t have to parse prose. Explicit nulls rather than omitted fields prevent “this field is always there” from calcifying into an assumption. Each of these makes the accidental surface smaller by making the real contract more obviously the thing to use.
This is the third face of the same problem: an interface is a promise, deprecation is how a promise ends, and the accidental interface is the promise you made without noticing. The unifying discipline is to be conscious about the boundary — to know what you’ve exposed, decide deliberately what’s guaranteed versus incidental, and make the guaranteed part the easy path for the people building on you. What you can’t do is assume the boundary is wherever the documentation says it is. Consumers build on what they can see, and what they can see is the interface, whether you designed it or not.