[HN Gopher] Hyrum's Law
___________________________________________________________________
Hyrum's Law
Author : andsoitis
Score : 85 points
Date : 2025-08-01 14:58 UTC (8 hours ago)
(HTM) web link (www.hyrumslaw.com)
(TXT) w3m dump (www.hyrumslaw.com)
| azhenley wrote:
| Discussion last year:
| https://news.ycombinator.com/item?id=39401973
| dang wrote:
| Thanks! Macroexpanded:
|
| _Hyrum's Law in Golang_ -
| https://news.ycombinator.com/item?id=42201892 - Nov 2024 (183
| comments)
|
| _Hash Ordering and Hyrum 's Law_ -
| https://news.ycombinator.com/item?id=41673295 - Sept 2024 (41
| comments)
|
| _Hyrum 's Law_ - https://news.ycombinator.com/item?id=39401973
| - Feb 2024 (66 comments)
|
| _Git archive generation meets Hyrum 's law_ -
| https://news.ycombinator.com/item?id=34631275 - Feb 2023 (76
| comments)
|
| _Hyrum 's Law_ - https://news.ycombinator.com/item?id=33283849
| - Oct 2022 (52 comments)
|
| _Hyrum 's Law_ - https://news.ycombinator.com/item?id=29848295
| - Jan 2022 (36 comments)
|
| _Hyrum 's Law_ - https://news.ycombinator.com/item?id=27386818
| - June 2021 (5 comments)
|
| _Hyrum 's Law: An Observation on Software Engineering_ -
| https://news.ycombinator.com/item?id=21515225 - Nov 2019 (6
| comments)
|
| _Hyrum 's Law_ - https://news.ycombinator.com/item?id=19249199
| - Feb 2019 (1 comment)
| AdamH12113 wrote:
| From an API designer's standpoint (especially if that API has
| paying customers), Hyrum's Law is something that has to be taken
| into account. But from a user's standpoint, it is engineering
| malpractice, plain and simple. At the very least, relying on
| quirks of someone else's implementation is a risk that should be
| understood and accounted for, and no one has any _reasonable_
| grounds for complaint if those quirks suddenly change in a new
| version.
| kccqzy wrote:
| Malpractice? It's usually just plain old bugs unintentionally
| written that way.
| breppp wrote:
| Depends on the product. Sometimes you are completely dependent
| on an API ecosystem (iOS, Android, Windows) where the only way
| to achieve something is a quirk
| Ygg2 wrote:
| > But from a user's standpoint
|
| Not true generally. One man's engineering malpractice is
| another man's clever hack.
|
| Users of Windows 95 complained that Windows 95 broke SimCity.
|
| What did Windows 95 break? It fixed an obscure allocator bug
| SimCity was relying on.
|
| Users loved Windows 95, for ""fixing"" this. How was it fixed?
| By introducing an obscure switch to old allocator if it
| detected SimCity in the app name.
|
| https://arstechnica.com/gadgets/2022/10/windows-95-went-the-...
| bigstrat2003 wrote:
| Different users. The users that GP was accusing of
| malpractice would be the Maxis devs in this case, not the end
| users who were trying to install SimCity on their Windows 95
| machine.
|
| Microsoft has a commitment to backwards compatibility that I
| think is going too far, but I understand why. Raymond Chen
| has explained that if a user buys the new version of Windows
| and their programs stop working, they will blame MS
| regardless because they don't have any way to know it's the
| program's fault. So MS is incentivized to go out of their way
| to enable these other programs' bad behavior, because it
| keeps their (Microsoft's) customers happy.
| patrickmay wrote:
| Exactly. Hyrum's Law should always be paired with Postel's Law:
| Be conservative in what you do, be liberal in what you accept
| from others.
| chuckadams wrote:
| Being liberal in what you accept also leads to users
| depending on you accepting marginal input that exploits
| implementation quirks, either because the quirks get the job
| done or for more nefarious reasons.
| bigstrat2003 wrote:
| Yeah, I definitely don't agree with the "be liberal in what
| you accept" paradigm. It's worth it to force users to send
| correctly-formed data, IMO.
| don-code wrote:
| I don't think such usage is malicious, so much as ignorant -
| it's sometimes hard to know that a behavior _isn't_ part of the
| API, especially if the API is poorly documented to begin with.
|
| I maintain a number of such poorly-documented systems (you
| could, loosely, call them "APIs") for internal customers. We've
| had a number of scenarios where we've found a bug, flagged it
| as a breaking change (which it is), said "there's _no way_
| anybody's depending on that behavior", only to have one or two
| teams reach out and say yes, they are in fact depending on that
| behavior.
|
| For that reason, we end up shipping many of those types of
| changes ship with a "bug flag". The default is to use the
| correct behavior; the flag changes the behavior to remain
| buggy, to keep the internal teams happy. It's then up to us to
| drive the users to change their ways, which.. doesn't always
| happen efficiently, let's say.
| twodave wrote:
| Hard disagree. If my users are exploiting some unintended,
| unannounced part of my API then me patching that out is
| something they're just going to have to deal with. In well-
| described systems these sorts of behaviors lead to nasty bugs
| down the line, sometimes months in the future (e.g. "Huh, why
| aren't my tax reports tying out?").
| partdavid wrote:
| I think you're agreeing with GP, not disagreeing.
| twodave wrote:
| I was disagreeing with the notion that this law has to be
| taken into account. I suppose that's true for certain
| software, but if e.g. Apple can get away with breaking
| these use cases then I don't see why, as an API designer, I
| should care either.
| bryanrasmussen wrote:
| I think this really depends on who your customers are and
| how they pay for your services.
| RyanCavanaugh wrote:
| The problem is that people commonly don't even realize they're
| depending on implementation quirks.
|
| For example, they write code that unintentionally depends on
| some distantly-invoked async tasks resolving in a certain
| order, and then the library implementation changes performance
| characteristics and the other order happens instead, and it
| creates a new bug in the application.
| throw0101c wrote:
| > _From an API designer 's standpoint (especially if that API
| has paying customers), Hyrum's Law is something that has to be
| taken into account._
|
| How good-of-an-idea / best practice is API versioning?
| /api/v1/foo /api/v2/foo
|
| What are the pluses and minuses?
| detaro wrote:
| you end up with a lot of versions if you version everything
| that could change some non-guaranteed behavior in some corner
| case.
| ad_hockey wrote:
| A couple of considerations are:
|
| - You have to decide whether to bump the entire API version
| or only the /foo endpoint. The former can be a big deal (and
| you don't want to do it often), the latter is messy.
| Especially if you end up with some endpoints on /v1 (you got
| it right first time) while others are on /v4 or /v5. Some
| clients like to hard-code the URL prefix of your API,
| including the version, as a constant.
|
| - You still have to decide what your deprecation and removal
| policy will be. Does there come a time when you remove
| /api/v1/foo completely, breaking even the clients who are
| using it correctly, or will you support it forever?
|
| It's not easy at all, especially if you have to comply with a
| backwards compatibility policy. I've had many debates about
| whether it's OK to introduce breaking changes if we consider
| them to be bug fixes. It depends on factors like whether
| either behaviour is documented and subjective calls on how
| "obviously unintended" the behaviour might be.
| cogman10 wrote:
| Plus, easy to see that you might have to do something
| different to move over to v2 as client.
|
| Minus, You will support v1 forever. It's almost impossible to
| make it go away.
| gwd wrote:
| > At the very least, relying on quirks of someone else's
| implementation is a risk that should be understood and
| accounted for, and no one has any reasonable grounds for
| complaint if those quirks suddenly change in a new version.
|
| It's almost always unintentional. Someone wrote some code, it
| works, they ship it, not realizing it only works if the list
| comes back in a specific order, or with a specific timing. Then
| a year or two later they do some updates, the list comes back
| in a different order, or something is faster or slower, and
| suddenly what worked before doesn't work.
|
| This is why in Golang, for instance, when you iterate over map
| keys, it purposely does it in a random order -- to make sure
| that your program doesn't accidentally begin to rely on the
| internal implementation of the hash function.
|
| ETA: But of course, that's not _truly_ random, just
| _pseudorandom_. It 's not impossible that someone's code only
| works because of the particular pseudorandom order they're
| generating, and that if Golang even _changes the pseudorandom
| number generator they 're using to evade Hyrum's Law_ that
| someone's code will break.
| RyanCavanaugh wrote:
| There's probably at least one game out there somewhere that
| uses Go's map iteration order to shuffle a deck of cards, and
| would thus be broken by Go removing the thing that's supposed
| to prevent you from depending on implementation details.
| deathanatos wrote:
| Intent enters into it when someone complains about something
| that is obviously out of the specification breaking.
|
| Prior that, yeah, that's just a bug.
|
| > _This is why in Golang, for instance, when you iterate over
| map keys, it purposely does it in a random order_
|
| It could be that Go's intentions are different here, but IIRC
| languages will mix randomization into hashtables as it is
| otherwise a security issue. (You typically know the hash
| function, usually, so without randomization you can force
| hash collisions & turn O(1) lookups into O(n).)
| CobrastanJorji wrote:
| A counterexample would be Python, where dictionaries
| maintain their insertion order.
| deathanatos wrote:
| Python does the same hash randomization, but yes, it
| _also_ maintains the insertion order. This is more
| expensive, obviously, as additional data has to be
| tracked.
| porridgeraisin wrote:
| > mix randomisation into hash tables.
|
| I believe you don't understand.
|
| In go, they literally randomly permute the iteration order
| of the map each time you iterate over it.
|
| e.g for x in map { }
| for x in map { // different order }
|
| Now, the fact that they randomize means people use it as a
| cheap "select random item from map" function :D, which is
| hyrums law all over again. var randomUser
| User for userId, user in usersMap {
| randomUser = user break }
|
| Funny isn't it.
| deathanatos wrote:
| Well ... it seems like you're right. (A playground --
| https://go.dev/play/p/OHQTIuDWicd -- if anyone is
| curious.)
|
| That's ... pretty surprising, since that would seem to
| imply that iteration would require a O(n) sized chunk of
| memory somewhere to reify the order into. (And probably
| O(n) time to do the shuffle, or at least, a copy of the
| ordering; we should shuffle as we go, I suppose, but we'd
| need to track what we've emitted & what we've not, hence
| O(n) time & space to populate that scratch...) That seems
| undesirable.
| nvader wrote:
| It seems to me that there's some advantages to undertaking
| "Freedom of Navigation Operations" by randomizing implementations
| from time to time to discourage any dependence on internal
| behaviors.
|
| For instance, traversal order of maps in Go is always randomized,
| to prevent subtle bugs caused by depending on the order.
|
| As AI generated code becomes cheaper, it may be worthwhile to
| change some subset of your internal behaviors from release to
| release, so that users don't become too complacent.
| avidiax wrote:
| There's a corollary: Even if you explicitly deny
| a guarantee of a certain behavior in your contract, if you
| usually deliver that behavior, most of your customers will
| depend on it.
|
| Some examples:
|
| If you make a queueing system, it's impossible to guarantee
| anything other than delivery "at most once" (some loss occurs),
| or "at least once" (some duplication occurs), but if you usually
| provide "exactly once" in practice, most of your customers will
| depend on this.
|
| If you provide a data bucket service, and guarantee availability,
| but not performance, and you usually provide 100MB/s throughput,
| your customers will have major problems if you only provide
| 10MB/s throughput in some cases.
|
| If you make a self-driving car, and it requires human monitoring,
| but it's really good, say one intervention per year of driving .
| . . your customers will die because they aren't paying attention.
| dongkyun wrote:
| This is explicitly recognized in contract law: course of
| performance / dealing is a factor courts will consider in
| evaluating the nature of a deal. (Most contracts will try and
| carve it out).
| chuckadams wrote:
| Can't disagree with anything you said, though I think there are
| steps to address at least some of them: for queueing systems,
| testing with a chaos monkey isn't a bad idea... you'd want a
| test environment representative of production workloads, which
| is hard to do, but anything should be better than nothing.
|
| In the self-driving car scenario, you'd probably go with cold
| statistics: is it killing fewer people than ones that need more
| interventions? Just like queueing though, experiments in
| production could be problematic.
| worik wrote:
| > In the self-driving car scenario, you'd probably go with
| cold statistics
|
| No. There is a big difference in an accident caused by human
| error and an accident caused by machine failure.
|
| We tolerate much more of the former than the latter.
|
| This feels like a cognitive failure, but I do not think it is
| weinzierl wrote:
| "Die normative Kraft des Faktischen" or "the normative force of
| the factual" is a thing and usually not seen as necessarily
| bad.
|
| It recognizes that legitimacy often emerges organically from
| social acceptance rather than top-down imposition. In
| technology we often see that evolving reference implementations
| work better than elaborate specifications.
| leoc wrote:
| In its form as 'the normalisation of deviance' it's generally
| recognised as bad.
| Animats wrote:
| > If you make a queueing system, it's impossible to guarantee
| anything other than delivery "at most once" (some loss occurs),
| or "at least once" (some duplication occurs), but if you
| usually provide "exactly once" in practice, most of your
| customers will depend on this.
|
| That's only a condition at termination. For ongoing
| communication, you can guarantee exactly once delivery. When
| communication ceases, the final state between the ends is
| indeterminate. If you can keep talking, or resume after breaks,
| it's a solvable problem.
| doormatt wrote:
| We ran into this at SNS (AWS) all the time.
| rossant wrote:
| If I'm not mistaken, CPython's dict preserved insertion order
| as an implementation detail at first, but because too many
| users came to rely on it, it was made part of the language
| specification starting in Python 3.7.
| TehCorwiz wrote:
| XKCD always has a relevant comic: https://xkcd.com/1172/
| xatax wrote:
| That's linked in the OP.
| amelius wrote:
| This is why an API should always have an
| "assertions": true
|
| option. Why should normal function calls have assertion/invariant
| checks, and not API calls?
| porridgeraisin wrote:
| This idea looks good. Have you used it in practice? Can you
| share how?
| amelius wrote:
| Yes, you basically use the option whenever you have
| assertions turned on in your code.
|
| Then the service running the API will do extra checking when
| the assertions option is true, basically making it less
| forgiving and following the specification closely.
| prats226 wrote:
| This is super interesting to think about in LLM world where lot
| of software is getting replaced with LLM calls.
|
| In terms of output of an LLM, there is no clear promise in the
| contract, only observable behaviour. Also the observable
| behaviour is subject to change with every update in LLM. So all
| the downstream systems have to have evals to counter this.
|
| One good example is claude code where now people have started
| complaining them switching models effecting their downstream
| coding workflows.
| worik wrote:
| Yes.
|
| This is the unfortunate thing about wrapping LLMs in API calls
| to provide services.
|
| Unless you control the model absolutely (even then?) you can
| prompt the model with a well manicured prompt on Tuesday and
| get an answer - a block of text - and on Thursday, using the
| exact same prompt, get a different answer.
|
| This is very hard to build good APIs around. If done expect
| rare corner case errors that cannot be fixed.
|
| Or reproduced.
| fsmv wrote:
| A good example of defence against this is go maps randomize
| iteration order just so that people don't rely on it being
| consistent.
| kiitos wrote:
| > if an interface has enough consumers, they will collectively
| depend on every aspect of the implementation ...
|
| Yep!
|
| > [and that] constrains changes to the implementation, which must
| now conform to both the explicitly documented interface, as well
| as the implicit interface captured by usage
|
| Nope!
|
| Software authors define the rules for the software that they
| author. I understand it's a spectrum and the rules are different
| in different circumstances but at the end of the day my API is
| what I say it is and if you rely on something that I don't
| guarantee that's on you and not me. Hyrum's Law describes a
| common pathology, it doesn't define an expected rule or
| requirement.
| cafard wrote:
| Actually, I think that in _The Mythical Man-Month_ Brooks
| mentioned users depending on nominally undefined, practically
| consistent behavior, e.g. what was left in some part of a
| register.
___________________________________________________________________
(page generated 2025-08-01 23:00 UTC)