Randomize What You Do Not Guarantee
Your API's bugs are someone's features. So break them on purpose.
Every API has two surfaces: the contract you documented, and the much larger set of behaviors that just happen to be true today. Result ordering, timing, the exact wording of a message, whether a list has duplicates. You never promised any of it, but the moment a caller notices it working a certain way, someone writes code that leans on it. Then you can't change it without breaking them.
Hyrum's Law
Hyrum Wright's observation is blunt: with a sufficient number of users of an API, it does not matter what you promise in the contract — all observable behaviors of your system will be depended on by somebody. The documentation is irrelevant to the failure. If a behavior is reachable, at scale it is load-bearing.
The classic example is iteration order. A hash map or a SELECT without ORDER BY returns rows in some order. It is not part of the contract, but it is stable enough day to day that callers quietly assume it. Change your storage engine, upgrade a library, and the order shifts. Their code breaks, and the bug report lands on you.
Randomize the unguaranteed
The fix is counterintuitive: make the unguaranteed behavior visibly unreliable, in dev and test, so no one can couple to it.
import os, random
def list_items(rows):
# Ordering is NOT part of this API's contract.
# Shuffle in non-prod so callers can't depend on incidental order.
if os.environ.get("ENV") != "production":
rows = list(rows)
random.shuffle(rows)
return rows
If the response has no guaranteed order, shuffle it. If a field is optional, sometimes omit it. If a set has no defined sequence, vary it. The caller's incidental dependency now fails fast in their own test suite instead of silently ossifying into a contract you never signed.
Go's map iteration is the canonical implementation of this idea: the runtime deliberately randomizes map iteration order so that no program can depend on it. The randomization is the feature.
What to randomize vs guarantee
| Behavior | Guaranteed in contract? | Action |
|---|---|---|
| Documented sort order | Yes | Keep it stable; test it |
| Incidental row order | No | Shuffle in dev/test |
| Pagination cursor format | No (opaque) | Rotate/obfuscate the token |
| Presence of optional fields | No | Occasionally omit |
| Timestamp precision | No | Vary within the promised bound |
The rule: anything inside the contract, lock down and test hard. Everything outside it, jitter it so it can't quietly become a contract.
Power moves
- Make the promise explicit. If ordering should be guaranteed, document it and add
ORDER BY— then callers can rely on it legitimately. - Opaque tokens. Return pagination cursors and IDs as opaque blobs. If callers can't parse the format, they can't depend on its internals.
- Chaos in staging. Extend the idea beyond ordering: inject small latency and occasional retries in non-prod so callers build in resilience instead of depending on today's speed.
- Version before you break. When you must change a real guarantee, ship a new version rather than mutating the old one.
Resources
- Hyrum's Law
- Go maps in action — iteration order is randomized
- Google's "API Deprecation" and "Backwards Compatibility" chapters — Software Engineering at Google
- Semantic Versioning
Shipping AI or cloud systems? Yeda AI audits and hardens production LLM and API pipelines. Talk to us · Read the blog