SemVer Falsehoods
Updated on 07 Apr 2024 permalink
Programmers have a lot of beliefs that are mismatched with reality. You’d think that we wouldn’t do it to ourselves, and generally build a solid foundation for each other, but no: even SemVer suffers from this.
So let’s talk about SemVer falsehoods:
- A public API will never have contradicting requirements with SemVer.1
- Everyone agrees on the public API if it isn’t explicitly specified.2
- Tooling won’t add things to your public API by “accident”.3
- You will catch incompatible changes when reviewing code.4
- Tooling will catch incompatible changes.5
- It will be easy to upgrade to a new major version.6
- Every minor version will get fixes back-ported to them.7
- Maybe not every minor version, but every major version will get fixes back-ported.7
- Maybe not every major version, but at least the last two?7
- Anything that claims SemVer is using version X.8
-
This can happen if “be secure” is an expectation of your tool, and fixing a vulnerability requires breaking compatibility. Ex: removing an insecure primitive, or changing cryptography requirements. Like attempting to version TLS, or upgrade a hashing algorithm.
But we have cryptographic agility.
You say.
TLS downgrade attack.
I say. ↩
-
This is why the spec says you MUST declare a public API, because it’s so easy to misunderstand what the public API of a particular piece of software covers when it’s not explicitly communicated. Is it everything that’s publicly available, or some subset? Declare it.
Folks depending on Python’s
cryptography
got a rude awakening when they disagreed with the maintainers about the build chain being part of the API. That’s not the end of it, because Hyrum’s law says that any change that you make to observable behaviour will break someone! XKCD 1172 ↩ -
Who is right when tools automatically add extra functionality that you don’t want to promise as part of your API? Like when Rust automatically derives
Send
andSync
for your public types? ↩ -
Not always… There’s a plethora of ways to make an incompatible change that seem inocuous. Including ones that strictly add new functionality: oh no, you broke people’s tests because their test mock didn’t implement your new functionality properly. But wait, is that covered by their API2? ↩
-
Tooling is getting better here, so it may be possible to notice incompatible API definitions. But Hyrum says that everything is in scope, even list sorting and the other things your tools don’t yet automatically detect. ↩
-
No, of course it isn’t. There’s a long list of incompatible changes that developers want to make that has grown month by month since the last major version bump. And of course releasing them all at once is the best strategy that SemVer gives you, technically and culturally. For example: there were lots of changes between Python2 and Python3, and some were much more difficult to manage than others (str/bytes). Had they all been spread out over many more version changes (python 3, 4, 5, maybe even 6) it would have been much easier to manage each successive set of breaking changes.
We don’t deploy software all at once, so why would we version it that way? ↩
-
Unless you’re paying for support, who knows if the maintainers have time to back-port fixes at all. XKCD 2347 ↩ ↩2 ↩3
-
There’s two major versions of SemVer and the spec provides no way to differentiate between them. Who knows if you’re getting version 1.0.0 or version 2.0.0, or another later major version? ↩
Thanks to Lenny Morayniss for comments/corrections/discussion.
Previously:
- I Don't Like Getopt
- SemVer Falsehoods
- Postel was Wrong
- Discord Webrtc Logging
- Keybase
- Public Key Change
- A Journal Of History
- Collapsing Contexts
- Cryptography And Backdoors
- Online Personas
- Life On The Internet
- Real Time
- Erlang And Reltool
- Memoization And Meemo
- UW Course Search
- OS 161 Retrospective
- Totp Authentication
- This Site