Regarding Semantic Versioning
danielmoch.com
danielmoch.com
I am much more persuaded by the argument (not mine, i get this from more experienced maintainers) that semver is a social contract, because what constitutes a bugfix vs a new feature vs a breaking change is exactly as well defined as "what is this piece of code really supposed to do"? aka it's not well defined at all. all software "contracts" are underspecified, given enough users and scale.
remember Hyrum's Law: "With a sufficient number of users of an API, it does not matter what you promise in the contract - all observable behaviors of your system will be depended on by somebody." Even bugs will be relied on and people get pissy about semver when you break them!
Breaking compatibility with customers that are depending on your bugs to remain unfixed is both good (insofar as it removes bad things) and bad (insofar as things that were working work no longer).
Another angle on the social contract is visible in that, when we accidentally introduce a major-version change in a patch-version, we don't retroactively change the version number to a major version bump, we just release another patch. :)
The documentation is the contract that I use for semver. If something is documented, I can't break it without bumping the major version number. If it's an undocumented internal class or function I can change it any time I like.
For example, imagine you expose a "map" API and you explicitly don't mention it's a hash map or some tree. You also don't claim the the iterator will be in order, but you fail to document that you explicitly don't promise any order at all.
Some users will not read the documentation and say "wait a minute, it doesn't claim whether any ordering is guaranteed nor it claims the contrary, hence I must assume it's undefined behaviour thus I can't rely on any ordering".
Instead some users will look at the behaviour and assume the documentation just forgot to mention that aspect (and if the implementation indeed iterates the map in order they'll just assume that's what the documentation should have written).
After all, just as code, documentation can have bugs and will be amended in subsequent releases.
Now, you finally realize that you forgot to say "the iteration order is undefined" and make a new release with that documentation fix. The question is: would you consider this a major "breaking" change or a minor point release?
If the library follows the rules, then...
Separation of minor and patch versions doesn't make sense. Both in the case of a minor or patch version change the client should be able to upgrade to the latest version.
A major version change means that backwards compatibility is likely broken. In that case, we're actually talking about a new library.
Therefore you can just use a single integer as the version number, and if you break backwards compatibility, then you change the library name. If you had a library called MyLib with version 1.0.0, then you can just go with version 1 instead. In case of a patch (no functional change), you go to version 2, and all clients can immediately upgrade. Then if you add a new feature, but backwards compatibility is preserved, you can go with version 3, and all clients can immediately upgrade. If you break backwards compatibility, then you rename your library to MyLib2, so it's clear to everyone that this is a new library (with similar functionality).
The real reason for minor and patch versions is that libraries actually don't follow semver, and patch version change means 'small change, don't worry about re-testing', minor version change means 'not that small change, you should re-test' and major version change means 'we've very likely broken most dependencies'.
In real life, you need to re-test anyway.
Yes, it does.
> Both in the case of a minor or patch version change the client should be able to upgrade to the latest version.
Yes, but if it's a minor version change then, as a developer, I also want to review the changelog for new features I should consider whether we should add backlog items for refactoring to take advantage of, while a patch doesn't create that need. There is more space for SemVer-aware tooling than “is this safe to update”, though that's obviously the most important single piece of information.
> The real reason for minor and patch versions is that libraries actually don't follow semver, and patch version change means 'small change, don't worry about re-testing'
No, you always worry about retesting because SemVer is a statement of API intent, but does not include a guarantee against bugs, including new or modified bugs.
That's what I do, yes. What I tried to explain is how library devs generally update versions.
> Yes, but if it's a minor version change then, as a developer, I also want to review the changelog for new features I should consider whether we should add backlog items for refactoring to take advantage of, while a patch doesn't create that need
This sounds like a good argument; although in my professional experience I found that 1) I need to re-test anyway 2) I need to read the change description anyway (as bugfixes most of the time mean behavioral changes, so I want to understand why the patch was necessary).
So overall I understand the theoretical argument behind semver, but I still don't see a very strong argument behind it. At least not as strong argument as popular semver has become.
Collecting evidence for bold claim is left as an exercise to the reader.
And before anyone claims one shouldn't use stuff on versions below 1.0, consider that very popular projects like React were widely deployed in production before they decided to bump themselves out of 0.x...
> And before anyone claims one shouldn't use stuff on versions below 1.0, consider that very popular projects like React were widely deployed in production before they decided to bump themselves out of 0.x...
I agree.
It's pretty easy to advise people not to use library with low version numbers, but then what do you use instead? Not every ecosystem out there has libraries for all common task, and if you need to, say, fill out a PDF form in language $X, and your choice is either a v0.5.22 library or writing it from scratch, I know I'd go for the v0.5.22... (especially with deadlines looming, and me having other things to do than reinvent the wheel).
I think SemVer would be improved if 0.x versions were treated exactly like 1.0+ versions except that all minor versions had the semantic guarantees (none, that is) of major versions (but patches still had patch semantics.)
> Library authors often stay in 0.x ranges "just in case the API needs to change".
It's a problem that library authors don't want to commit to API stability and clearly differentiating breaking changes, but it's not a problem with SemVer that it makes it easy for them to notify of this intent in an unambiguous manner.
Its been done as a research project [1] but I don't think it has been done in any major language or package manager.
[1] https://drops.dagstuhl.de/opus/volltexte/2018/8456/pdf/OASIc...
In terms of "this set of semvers can be satisfied in a way that the project will always build", yeah, it'd be nice if semver could be used like that rather than as a rule-of-thumb.