Hyrum's Law
hyrumslaw.com
hyrumslaw.com
You start with:
We followed the robustness principle, which is be conservative in what you do and be liberal in what you accept from others
But then, the 'things you accept' become part of your api: The problem with the robustness principle is a flaw can become entrenched as the defacto standard. Any implementation of a protocol is required to replicate the apparent behavior. This is both a consequence of applying the robustness principle and a product of a natural reluctance to avoid fatal error conditions.
And you arrive at: With a sufficient number of users of an API, it doesn’t matter what you promise. Any observable behavior of your system will be dependent upon by somebody
I have a long rant about this, and how web browser standards fell victim to this. It's why user agent string look like they do and why 'chucknorris' is an HTML color.Without knowing anything, the RNG could generate all 0s one day, and all 1s the next day, which most people would consider a very bad and worthless RNG.
If you want to be helpful to your users, rather than punish them for doing something you didn't explicitly allow them to do, you can communicate. Issue warning messages to people using a behavior that will be deprecated. Keep the old version around for a while for compatibility, but require users to opt into it and tell them when it's going away. Email your users to announce breaking changes. Et cetera.
All of this can take significant effort, and of course some people won't listen, won't check logs, will ignore warning messages and sunset dates. Which is the other side of the problem.
Whether we're changing software or adapting to changes, the message remains the same: software engineering isn't just futzing around with code until something "works", and then anything that breaks after that is someone else's problem. We live in a society where we all depend on each other. We need to communicate, and communicate in a way that values each other's success.
Also, there was another creative exploit of our in-house slab allocator like object pool. The contract was simple; if the client asks, it will return a new object handle, which is just a pointer with some metadata. And every time it needs to access object, it should explicitly fetch the object pointer with some utility function which performs very basic memory safety checks like dangling pointer or double free etc. I wasn't its designer and it's not perfect but it actually caught some potential bugs on earlier stages so probably gave us some value. Anyway these are tangential to the real story.
One day someone found out that its implementation resembled a sort of linked list on a large array and technically you can go through those objects if you're willing to play with reinterpret_cast. They decided to exploit that for "an efficient object query" and did it secretly. Obviously they didn't have all the handles, so safety checks are bypassed. This seemed to work for most cases because the code checked a tombstone bit, a part of object metadata rather than handle metadata. But for some case, an object could be released and a new object takes that memory place during the iteration (it's multi threaded) and this caused a number of subtle memory bugs since the object access was done out of iteration context.
Yeah, library writers might deserve the right to break its clients if they ignores the contract. But I just wanted to say that sometime, it could be inevitable if the job's done as your means of livelihood and in that case you probably want to keep those creative people in your mind when you design your library. That probably won't stop them, but at least you will be less shocked by finding out their sole existence...
Though in practice most software packages are not used extensively to the point that any observable behavior is depended upon (and Hyrum himself opens with this caveat "With a sufficient number of users ...").
Is there intended to be an implicit limit of "all the people in the world who might plausibly use this API"? Then I'm sure the law is false.
If there isn't intended to be such a limit, then the law is unfalsifiable and not useful as a guide to activity.
I think this purported law comes from the sort of advice you can end up giving when you notice that many young programmers err too much in the direction of X, so you make an exaggerated statement in the direction of not-X to correct them. This can be distinctly unhelpful in the long run.
But that's not what he actually wrote, and I can easily imagine a future where this "law" starts to be quoted in contexts where it doesn't turn out to apply.
1. A lot of code 2. The ability to test all of their dependencies code
And so the people making the changes can detect all of the breakages. If it was possible for me to run the CI for every system on GitHub that depended on the library, it might be more obvious, but usually the cost of these unintentional breakages is borne by users not maintainers.
The law applies to anyone who maintains a widely used library, whether that's good, Django, or std::.
And note that many more people in this thread are calling the law "obvious", than thinking it's not true. That should give you some pause.
But the "law" is not literally true, so to make it useful you have to mentally add some reservation: "no, of course I don't mean that someone will be depending on a detail like _that_".
The trouble then is that it doesn't work usefully as a piece of advice: if you're not already experienced enough to know where to draw the line, it doesn't help you get any more accurate.
It's like saying "however big you think X is, it's bigger than you thought".
But maybe I'm wrong about what you believe. Do you actually think the "law" as stated is literally true (without using a quibble like "well, there's no upper bound on what we mean by 'sufficient'")?
Be careful who you pretend to be, because you are who you pretend to be.
Applying the pigeonhole principle here would give "with a sufficient number of users it's guaranteed that some observable behaviour of the system will be relied on by more than one user", which is true (assuming the system has a finite number of observable behaviours) but not very interesting.
Hyrum's law says that with enough users all the observable behaviours are relied on by at least one person. Mathematically it's trivial for that not to be the case regardless of how many users you have. You just need a single behaviour that nobody's relying on. So Hyrum's law isn't mathematical, it's an observation about software development and human behaviour.
EDIT: Upon re-reading it, I'm seeing that Hyrum gave this Titus Winters guy (who I've never heard of), also at Google, credit for giving credit to him. ...what an impressive density of greatness over there.
He's well known from the talks he has given at CppCon.
...but the best name he could come up with is a name only recognized by fellow Googlers and people attending highly specialist conferences, and he's going around telling people "Hey look, this super-smart thing that I once said. It's so cool. Look, this guy over here also thinks that I'm the one who deserves credit for it. This is so smart, it will blow your mind. Now listen up, ..."
If this kind of thing doesn't "pop" to you as a violation of social norms then you know you're living in a subpopulation where, basically, narcissism has gone endemic.
Most HN users do more damage to the world through their adtech careers than any blog post.
Once someone has popularized a law in your name then it’s too late to change the name. For related reasons.
There are good reasons to change undocumented behaviors with each release just to discourage dependence on them. E.g., change hash functions. People will squawk, but will be better off.
Probably the best thing is to throw exceptions from functions that used to not. People who imagine that having searched the whole call tree one time means anything deserve to be surprised. Cleanup code should always be in destructors where it will be exercised properly on every run.
But for a lot of APIs, including nearly all OSS libraries that are only updated by the developer using them, your stance makes a lot more sense.
In other words just test it on every piece of software out there.
I recall a bit of drama in Linux land was when ext4 broke the "write temp file + rename over old one" classic technique for safely rewriting config files. People ended up with data loss and were outraged. ext4 devs said "But POSIX never actually promised this! Doing things our way is much better performance. Add extra fsyncs to actually get things to stick".
The result as I recall was a lot of drama back and forth.
(POSIX was a red herring: Linux has never limited itself to POSIX, and anyway POSIX didn't promise that doing things the way the ext4 maintainer recommended would work either.)
When shells that trace back to v6 Unix depend on a behavior, that being undocumented is a bug in documentation.
What I think you mean is: don't live in fear of Hyrum's Law.
Postel's law (or robustness principle) states that you should be liberal in what you accept, conservative in what you output. Hyrum's law shows the danger in being liberal in what you accept.
(Shameless plug: I've just made public an open source CLI flashcard app where the example deck includes a set of Hacker Laws including the aforementioned ones, you can try it here: https://github.com/SteveRidout/flashdown)
"Be clear about what you will accept", instead. And, make sure there is an easy way to discover why it was not accepted.
How is that not paying attention to Hyrum's Law, something you just told us to not do? Hyrum's Law explicitly does not say to never change undocumented behaviors, and what you propose is a reaction to the observation in it.
arein3slaw.com