Hyrum’s Law in Golang

Historical compatibility anecdotes

  • Windows 3.x vs. SimCity: game had a use‑after‑free bug that happened to “work” on Windows 3.x; Windows 95 added a special allocator mode triggered only for SimCity (and similar apps) to preserve behavior.
  • This is cited as a classic Hyrum’s Law case: undocumented quirks become de‑facto API that must be preserved for compatibility.

Go’s compatibility culture

  • Go maintainers emphasize very strong backward compatibility; even subtle behaviors (e.g., rand.Rand output, crypto key generation details, map iteration order) are treated as part of the contract once widely used.
  • They sometimes add deliberate behavior (e.g., reading an extra random byte, randomized map iteration) to prevent future dependence on specific internals.
  • GODEBUG flags are used to roll out behavior changes more safely and measure impact.

Stringly errors vs. typed errors

  • Core example: a specific HTTP error string (“request body too large”) cannot be changed because many projects compare the string.
  • Earlier Go versions only exposed this as a plain error with that message; checking the string was effectively the only way. A dedicated MaxBytesError type came much later.
  • Some argue this is misuse by consumers and shouldn’t be supported; others say the stdlib’s original string‑only design forced this, so blame lies with the API.

Debate: who should adapt?

  • One side: code that parses error strings is “just bad” and should be allowed to break; developers must accept risk when depending on undocumented details.
  • Other side: real‑world constraints often force hacks (“ship now, fix later”), and Go’s promise is precisely that such working code will keep working; otherwise software is never “finished.”
  • Some would tolerate more breaking changes to enable improvements; others say Go’s stability is its core value and would switch languages if guarantees were relaxed.

Fighting Hyrum’s Law

  • Suggested techniques:
    • “Greasing”/randomization (e.g., QUIC/TLS unused fields, randomized hashes/map iteration) so consumers cannot safely rely on specifics.
    • Avoid being “liberal in what you accept” (Postel’s Law) to prevent ossification.
    • Static analysis or lint rules to flag string comparisons on errors.

APIs, semver, and emergent dependencies

  • Many note that semver can’t fully solve Hyrum’s Law: you often don’t know what people rely on, so you won’t bump a major version.
  • Tests are a frequent place where unintended dependencies surface: order of maps/sets, exact error messages, timezones, gzip bytes, and performance characteristics.