Remix.run Logo
kibwen 5 days ago

Hyrum's Law is one of those observations that's certainly useful, but be careful not to fixate on it and draw the wrong conclusions. Consider that even the total runtime of a function is an observable property, which means that optimizing a function to make it faster is a breaking change (what if suddenly one of your queues clears too fast and triggers a deadlock??), despite the fact that 99.99999999% of your users would probably appreciate having code that runs faster for no effort on their part.

Therefore it's unavoidable that what constitutes a "breaking change" is a social contract, not a technical contract, because the alternative is that literally nothing is ever allowed to change. So as a library author, document what parts of your API are guaranteed not to change, be reasonable, and have empathy for your users. And as a library consumer, understand that making undocumented interfaces into load-bearing constructs is done at your own risk, and have empathy for the library authors.

materielle 5 days ago | parent | next [-]

I think everything you said is totally correct for open source library owners.

But let me offer a different perspective: Hyrum’s law is neither a technical contract nor a social contract. It’s an emergent technical property in a sufficiently used system.

How you respond to that emergent property depends on the social context.

If you are a FOSS maintainer, and an optimization speeds up 99.99% of users and requires 0.01% to either fix their code or upgrade to a new API, you ship it.

If you are working at a big tech company, you need both the optimization and breaking 0% of the company. So you will work across teams to find the sweet spot.

If you are an enterprise software company, and you change breaks 0.1% if users, but that user is one of the top 5 contracts, you don’t ship.

kmacdough 4 days ago | parent | next [-]

Seems like you're saying the same thing, just using "social contract" differently. I think they use social contract not to mean binding, but to highlight the fact that Hyrums Law must be taken in the social context of the project. In the case of large SW company, the social contract would be to not break services, even when folks are misusing an API. And for a popular open source project, it would mean not breaking a widely used behavior, even if it isn't specified or officially supported. Determining the social contract seems to be precisely what you describe as "not a social contract".

uoaei 5 days ago | parent | prev [-]

> It’s an emergent ... property in a sufficiently used system

This is also a sufficient description of "social contract" for this context.

SatvikBeri 4 days ago | parent | prev | next [-]

I once sped up a very suboptimal routine that from ~100s to ~.1s (it was doing database lookups in a for loop), and that broke the reporting system because the original author had made several asynchronous function calls and assumed they would have all finished running by the time the (formerly) slow routine was done. Figuring out exactly what happened took forever.

bluGill 5 days ago | parent | prev | next [-]

That used to be a problem in the 1980s. Thus PCs came with a turbo button to slow them down, and 8 bit computers went the entire decade without upgrading their speed even though faster CPUs were available. These days nearly everything runs on more than one CPU and so nobody relies on function runtime (other than is if fast enough). Even in embedded they have been burned by their one CPU going out of production and so try to avoid that dependency because it cannot be relied on anymore.

thayne 4 days ago | parent | next [-]

> nobody relies on function runtime

Maybe not intentionally.

But there have been several times where I've seen bugs where two tasks are done concurrently, but task A always takes longer than task B, then someone makes A faster, and that exposes some race condition or deadlock that only occurs if A completes before B.

hinkley 4 days ago | parent | prev | next [-]

I found a used copy of Warcraft III and found it was unplayable because the scrolling algorithm ran as fast as possible with no minimum time. Any map bigger than 2x2 screens you could not scroll to the middle.

marcus_holmes 4 days ago | parent [-]

I used to enjoy Wing Commander back in the 90's. Then I upgraded my PC and it became unplayably fast - 1 second after I took off the "you died" screen appeared.

outworlder 4 days ago | parent | prev [-]

In the 8 bit computer era, we knew exactly how much time any given instruction took. Retrieving some precision clock (not available!) and computing the time delta between runs - as is trivially done today - would probably be more computing power than they had at the time. Every cycle counted. Not very surprising that it wasn't done at that era. Also, there wasn't a "winning" instruction set or compilers able to target different architectures, so there was far more at stake than just clock speeds. If they changed the processor, you lost all your software.

DOS didn't have any precision clocks either as far as I know (it seems that there's interrupt 1A but it only updates 18 times a second, which is an eternity). Apparently there's 8254 based timer code after a few PC generations.

Windows 95 came up with QueryPerformanceCounter() and that simplified life quite a bit.

rjst01 5 days ago | parent | prev | next [-]

One day I will give a lighting talk about the load bearing teapot, or how and why I made HTTP Status 418 a load bearing part of an internal API, and why it was the least bad option considering the constraints.

renewiltord 4 days ago | parent [-]

It’s a classic. Binance will give you 429 errors to back off then 418s to tell you you will be IP banned and then they’ll ban you.

hinkley 4 days ago | parent [-]

Google’s spiders will punish you for giving them too many 429 responses. It’s hell for hosting sites with vanity urls. They can’t tell they’re sending you 50+ req/s.

It’s practically a protection racket. Only AWS gets the money.

wbl 4 days ago | parent [-]

50 requests/sec? Did you forget a few zeros?

hinkley 4 days ago | parent [-]

Little’s law is a bitch, and you can get away with a little throttling but not much.

Also, that’s a bit dismissive for HN.

ljm 4 days ago | parent | prev | next [-]

I feel like this is approaching absurdity, if only because something like the total runtime of a function is not under the control of the author of the function. The operating environment will have an impact on that, for example, as will the load the system is currently experiencing. A GC pass can affect it.

In short, I wouldn't consider emergent behaviours of a machine as part of an intentional interface or any kind of contract and therefore I wouldn't see it as a breaking change, the same as fixing a subtle bug in a function wouldn't be seen as a breaking change even if someone depended on the unintentional behaviour.

I think it's more of a testament to Go's hardcore commitment to backwards compatibility, in this case, than anything else.

skybrian 4 days ago | parent | next [-]

Yes, it’s an absurd example to make a point. We don’t normally consider performance in scope for what’s considered a breaking API change and there are good reasons for that, including being non-portable. Performance guarantees are what hard real-time systems do and they’re hardware-specific. (There is also the “gas fee” system that Ethereum has, to make a performance limit consistent across architectures.)

But there are still informal limits. If the performance impact is bad enough, (say, 5x slower, or changing a linear algorithm to quadratic), it’s probably going to be reverted anyway. We just don‘t have any practical way of formalizing rough performance guarantees at an API boundary.

kibwen 4 days ago | parent [-]

> If the performance impact is bad enough

Even worse, it's possible to select a new algorithm that improves the best-case and average-case runtimes while degrading the worst-case runtime, so no matter what you do it will punish some users and reward others.

AlotOfReading 4 days ago | parent | prev [-]

It's quite common in cryptography for the runtime to be important. For example, password verification time shouldn't depend on the value of the key or the password. Systems have been broken because someone wrote a string compare that returned early.

ljm 4 days ago | parent [-]

And, since most languages short circuit on basic string comparisons, you'd have some form of `secure_compare` function that compares two strings in constant time, and that behaviour is contracted in the name of the function.

Nobody is rewriting `==` to compare strings in constant time, not because it breaks some kind of API contract, but because it would result in a massive waste of CPU time. The point is, though, that they could. But then they are deciding to sacrifice performance for this one problem.

Crypto is obviously a case of it own when it comes to optimisations and as much as I called out the parent for approaching the absurd, we can pull out many similar special cases of our own.

tester756 3 days ago | parent | prev | next [-]

>which means that optimizing a function to make it faster is a breaking change (what if suddenly one of your queues clears too fast and triggers a deadlock??), despite the fact that 99.99999999% of your users would probably appreciate having code that runs faster for no effort on their part.

I agree with your point, but that's poor example because you can't rely on function's speed reliably and easily.

Timing differs between hw, OS, OS updates, whatever.

Meanwhile it is trivial and easy to rely on error messages.

tshaddox 4 days ago | parent | prev | next [-]

> Consider that even the total runtime of a function is an observable property, which means that optimizing a function to make it faster is a breaking change

Well yeah, that's pretty much the textbook example of Hyrum's Law (or some funnier variation like "I was relying on the heat from the CPU to warm my bedroom, can you please revert your change that improved CPU performance").

brundolf 4 days ago | parent | prev | next [-]

Reminds me of: https://xkcd.com/1172/

skovati 5 days ago | parent | prev [-]

reminds me of: https://xkcd.com/1172/

citizenpaul 4 days ago | parent [-]

That one always fell flat for me, but I get it. The idea that an emacs user would communicate with another human rather than tinker with their config to deal with the change is unrealistic. /s /sorta