When it comes to software behavior, developers often rely on what they get, and not necessarily what they’re promised. The more developers you have, the more likely it is that somebody depends on your API’s implicit behavior — an observation known as Hyrum’s Law. In this article, we’ll discuss what exactly this observation means, why it can be a problem for you and your developers, and what to do about it.
What Is Hyrum’s Law?
Hyrum’s Law is a phenomenon in software engineering whereby developers come to depend on all observable traits and behaviors of an interface, even if they are not defined in the contract. Google engineer Hyrum Wright originally described the phenomenon, wording it as follows:
“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.”
In other words, developers will build against whatever you give them, regardless of whether the contract explicitly defines it, resulting in what’s called an implicit interface.
For example, in a real-world setting, REST APIs are often described with an OpenAPI Specification file. The specification file itself and any documentation generated from it should define what resources exist, what parameters they take, and what data they expose. However, this may leave out many other important API behavior traits, like how large the response will be, how it is formatted, pagination, or performance and latency.
The Law of Leaky Abstractions
Before we go any further, it’s worth mentioning that the “observable behaviors” referenced in Hyrum’s Law may stem from another software engineering phenomenon — The Law of Leaky Abstractions — which states that:
“All non-trivial abstractions, to some degree, are leaky.”
Since APIs almost always involve some amount of abstraction away from the complexity of internal implementations — to provide targeted, useful functionality — it follows that some traits and behaviors of the internal implementations themselves will leak through.
In other words, this means that the implicit interface that users come to rely on as a result of Hyrum’s Law may embody the traits and behaviors of internal systems. We’ll come back to this idea (and why it’s a problem) later on.
Examples of Hyrum’s Law
Here are some examples of observable traits and behaviors that developers might come to depend on, regardless of whether or not the contract defines them:
- Ordering or lack thereof of lists in responses
- File types returned from specific endpoints
- Content of responses (e.g., the format of returned URLs)
- Status codes and reference numbers of error messages
- Large or small objects or payloads
- Fast or slow response times
Why Is an Implicit Interface Bad?
The problem with implicit interfaces is that developers come to rely on them, as described in Hyrum’s Law. When developers come to rely on traits and behaviors that aren’t defined in the contract, it becomes much easier for API owners to introduce breaking changes accidentally. In other words, every change they make to the interface now has the potential to break their developers’ applications.
Even if API owners take every possible precaution to prevent breaking changes, there’s still one big fundamental pitfall. If developers can rely on whatever traits and behaviors they observe, how are API owners supposed to know what parts of the interface they can and can’t change? They’re not; instead, they have to rely on reasoning and intuition to predict what developers are counting on.
And now we arrive at yet another threat. Recall The Law of Leaky Abstractions: the traits and behaviors of the interface — which itself is usually an abstraction — may well be traits and behaviors of internal systems. In other words, developers may have come to rely on your implementation’s inner workings, meaning that you can now break their applications without even changing the interface!
Some might argue that none of this is the API owners’ problem: the contract is a two-way agreement, and developer consumers must hold up their end of the deal — i.e., only depend on what is explicitly promised. The reality is that developers do depend on observable traits and behaviors, often out of necessity.
“And whether you can tell someone who was relying on unspecified behavior to go pound sand is a business decision, rather than a technical one.” – PaleCommander on Reddit
How to Negate Hyrum’s Law
We’ve looked at what Hyrum’s Law is and why it’s a problem for everyone involved, but what can you — the API owner — do about it? Below, we discuss a few possible solutions.
If developers are predominantly relying on traits and behaviors they observe instead of those defined in the contract, that might be a sign that your contract is overly vague. After all, if you don’t tell developers what to expect, they can only go on what your API gives them! Comprehensive documentation is one way to remedy this: by describing your API in greater depth, you give developers more surface area to build against reliably.
Drawing from our previous list of implicit traits and behaviors, it’s easy to see how beneficial it can be you to tell developers:
- whether or not you will order lists in responses
- what file types specific endpoints might return
- what the content of responses might be (e.g., how you’ll format URLs)
- how big or small objects or payloads might be
- how fast or slow your API might respond
2. Bug Compatibility
Suppose you know some developers absolutely depend on a specific API trait or behavior that you intend to change. It’s too late to describe it in your documentation; although that might help new developers, existing developers are unlikely to be reviewing the documentation for functionality they’re already integrating with successfully. In this case, bug compatibility might be a valid solution.
Bug compatibility is the idea of purposefully replicating a previous version’s behavior, even if that behavior was unintentional or undesired. For example, if a particular endpoint used to take fifteen seconds to respond, and a new backend overhaul means it’ll now take fractions of a second, you might consider emulating the delay — just in case anyone depended on it.
3. Chaos Mocks
If you and your developers have the resources and resolve, you might also consider a chaos mock. This funky solution involves varying non-contractual traits and behaviors — like response speed or size — so that developers can build more resilient applications:
“Coined by Microsoft architect Gareth Jones, the chaos mock is an API virtualization that purposefully embodies variability. The goal of a chaos mock is to enable developers to code against all sorts of weird and wonderful API behavior, so they can be confident their integrations will survive under all circumstances.” – 7 Best Practices for API Sandboxes
Learning From Hyrum’s Law
Hyrum’s Law is an astute observation of an inevitable and inconvenient phenomenon in software engineering: developers will depend on the traits and behaviors they can see. This can lead to several problems, which revolve around accidentally breaking your developers’ applications. However, with some detailed documentation, a dose of bug compatibility, and perhaps even a chaos mock, you can minimize the negative impacts of Hyrum’s Law.
Source: Nordic APIs