It has become difficult for me to avoid the suspicion that this class of complaint is another way of saying that semver often doesn’t work very well in practice.
I think it does work well in practice, for packages that practice it. I think a lot of people still have this “only want to increment the major version for big stuff” mindset as opposed to “major version just denotes breaking changes and it’s okay if we’re releasing version 105.2”.
And for packages which can practice it. Many packages can’t change anything without altering previous behavior. It’s hard to think “people might depend on this bug, so it’s a breaking change.”
I was thinking about this recently too… normally you would think of adding a new function as being a minor change - not breaking compatibility but not just an internal fix either..
But, on the other hand, if you add a new function, it might conflict with an existing name in some third party library the user also imports and then boom they have a name conflict they must resolve.
So you could fairly argue that all changes are potentially breaking changes…
They’re a thing but there are frequently ways to have this problem anyway
E.g.
from dependency import *
in python. “Don’t do that” is fair to say, but if somebody downstream already has they have to deal with fixing the ambiguity.
You can have subtler versions of this for example in C++ ADL can bite you:
int foo(tpnamespace::Type v) { ... }
if your dependency later adds a function named foo in their namespace the meaning of
foo(...)
in your program may change.
A world where every identifier is fully qualified to avoid running into this after an upgrade starts to look similar to a world with no namespaces at all.
This is precisely it, you can import all and get conflicts. In the D language I use, you can do it with decent confidence too: the compiler automatically detects it and offers ways to very easily specify it (you can just be like alias foo = mything.foo; to give it priority in this scope, among other things).
But nevertheless, if the conflict happens in a dependency of a dependency because one of its dependencies added something… it can be a pain. Some unrelated change caused a name conflict compile error that is halting your build.
(of course personally I say the real wtf is using dependencies with dependencies on dependencies. but meh)
I think a lot of people still have this “only want to increment the major version for big stuff”…
This has been my experience as well. Forcing a major increment for breaking changes has a secondary benefit in that it encourages developers to think hard about whether they actually need to break an API, or can judiciously deprecate to provide a smooth path forward.
I would like to point out that you’re far less likely to come across a blog post that says “I’ve been using SemVer for the past several years and it’s been working very well in practice”. SemVer is probably one of those things that, when it works, you get to not think about it much and carry on with whatever it was you were actually trying to do.
This class of complaint is part of how the system works in practice.
Semver is basically a way of managing expectations between API producers and consumers. Complaining when API produces don’t follow the stated guidelines is part of the feedback loop to maintain consensus about what changes are allowed.
Exactly. The only other thing I would add is something about scale in terms of the number of independent people working on independent projects that can be combined together in any one of a number of ways. Or in other words, a lack of centralization.
If the scale problem didn’t exist, then I absolutely would not want to deal with semver. But to some extent, the problems with semver are the problems with communication itself.
I am 100% over versioning. I have never seen an implementation that doesn’t suck. It’s miserable. Something is fundamentally wrong with the whole model, whatever methodology you use for tagging won’t fix that.
There could be different ways:
Google has run decently internally by building everything from HEAD. It’s not easy, and it requires a monorepo, but it does work. Could this work in the real world? Probably not. But what if you say “GitHub is a monorepo”? What if when the dependency author uploads a breaking change, GitHub can say who they broke and how it broke, prompt the dependency author to document what the remediation for that pattern of breakage is, and just let people be broken until they upgrade? Maybe this is pushed to per-language registries like crates.io or the Go proxy.
Unison tries to sidestep versioning entirely at the language level.
Stop trying to meld dependencies together across packages. Every package and its dependencies are treated separately, and binaries just include every version that is depended on. Hard drive size is a trivial concern, binary sizes when you’re building binaries into Docker containers means the image size almost certainly dominates.
I can’t wait for some of the ideas from Unison to permeate into more mainstream ecosystems. Lots of great ideas (globally accessible CAS, AST storage etc.) stuck behind a Haskell-like syntax.
I sort of agree because I don’t think there’s a perfect versioning system, but I think semver2 may be as good as it gets.
I like it because it’s more functional than the marketing driven “versions don’t matter, we’ll just call it version 2.0 to sell more” and all the alternatives get into too much time spent on perfecting versioning systems to diminishing returns.
I use it just so we have something, it saves time from deciding what to do, and it helps denote drafts or breaking changes. I use it even for stupid stuff like the “enterprise policy on bathroom breaks.” If it’s version 0.4.77 then it’s still in progress and could change any time. If it’s 1.1.16 then I mean it’s probably approved by someone. If I use 1.1.16 and see version 2.0 then it probably means I should read it because now it means I can only go to the bathroom on even hours or something that disrupts or may disrupt me.
In addition to the complaint about not following the breaking change requirement, I also dislike when packages spend years with tons of production users but refuse to reach “1.0” because they don’t want to commit to the semantic versioning requirement (lookin’ at you Hugo and Buffalo).
By leaving things at 0.x.y, users have to assume that every single 0.x change could break them, and that’s annoying.
I’ll add Terraform to this list. It’s otherwise a great tool, but version upgrades from 0.n to 0. n+1 have been a pain. That said, I believe the developers think this is the best way to maintain the project at the moment.
I think it’s less annoying than companies locking into something arbitrarily. I prefer this honesty in projects because, hey, maybe they will break stuff whenever they want. I want to know that a project might do this.
I usually interpret this as the company not taking time to commit to not breaking. With projects like Hugo that’s perfectly fine as I get what I pay for. I’d much rather them take this approach than releasing a new major version every month and not actually breaking anything (lookin’ at you Firefox). Functionally, it’s the same as 0.x.y, but it’s hard or even impossible to tell when they really release breaking stuff.
Over the past few years, I’ve had a decent number of bad versioning experiences where either:
A minor version increment of my dependency resulted in a breaking change for me, or
I made a “non-breaking” change and discovered that someone downstream was relying on my software remaining bug-compatible
(I decline to comment on whether I was the one depending on bug-compatibility in any of those cases…)
So at this point, I’m honestly not much of a fan of SemVer. The entire point is to provide a way to communicate changes to the user, and I don’t think it’s a rich enough mechanism to account for the wide variety of human experience involved in doing that.
Instead, I prefer to either use:
A monotonic version number (v1234), if the software will only ever have a single release branch, or
Calendar-based versioning (e.g. versions like 20.04 for software released in April 2020), if I have users who might need bugfixes on a past branch (so I can have 20.04.1)
And then, instead of thinking deeply about version numbers for my own dependencies, I prefer to rely on a careful upgrade policy, a good suite of integration and unit tests, and a working mechanism for rollbacks in the case of catastrophe. ;-)
Looks like author never maintained large projects. Semver is great, but sometimes unexpectedly patch will broke someone’s workflow. Just incrementing major all the time is not a solution. Designing robust API can help with part of the problem. But no silver bullet in semver.
I’m not sure what you’re taking issue with. I’ve couched the whole purpose of Semantic Versioning as supporting the design of a robust API. I also made no claim that it’s a silver bullet, but it’s valuable enough to me that if you’re going to ignore it, then I’m not going to use your libraries.
It’s true that I don’t maintain any large open source projects. Professionally, does >2M LOC count?
It has become difficult for me to avoid the suspicion that this class of complaint is another way of saying that semver often doesn’t work very well in practice.
I think it does work well in practice, for packages that practice it. I think a lot of people still have this “only want to increment the major version for big stuff” mindset as opposed to “major version just denotes breaking changes and it’s okay if we’re releasing version 105.2”.
And for packages which can practice it. Many packages can’t change anything without altering previous behavior. It’s hard to think “people might depend on this bug, so it’s a breaking change.”
I was thinking about this recently too… normally you would think of adding a new function as being a minor change - not breaking compatibility but not just an internal fix either..
But, on the other hand, if you add a new function, it might conflict with an existing name in some third party library the user also imports and then boom they have a name conflict they must resolve.
So you could fairly argue that all changes are potentially breaking changes…
Isn’t this why namespaces are a thing?
Not in languages like C, which still does have libraries.
They’re a thing but there are frequently ways to have this problem anyway
E.g.
in python. “Don’t do that” is fair to say, but if somebody downstream already has they have to deal with fixing the ambiguity.
You can have subtler versions of this for example in C++ ADL can bite you:
if your dependency later adds a function named foo in their namespace the meaning of
in your program may change.
A world where every identifier is fully qualified to avoid running into this after an upgrade starts to look similar to a world with no namespaces at all.
This is precisely it, you can import all and get conflicts. In the D language I use, you can do it with decent confidence too: the compiler automatically detects it and offers ways to very easily specify it (you can just be like
alias foo = mything.foo;
to give it priority in this scope, among other things).But nevertheless, if the conflict happens in a dependency of a dependency because one of its dependencies added something… it can be a pain. Some unrelated change caused a name conflict compile error that is halting your build.
(of course personally I say the real wtf is using dependencies with dependencies on dependencies. but meh)
This has been my experience as well. Forcing a major increment for breaking changes has a secondary benefit in that it encourages developers to think hard about whether they actually need to break an API, or can judiciously deprecate to provide a smooth path forward.
I would like to point out that you’re far less likely to come across a blog post that says “I’ve been using SemVer for the past several years and it’s been working very well in practice”. SemVer is probably one of those things that, when it works, you get to not think about it much and carry on with whatever it was you were actually trying to do.
This class of complaint is part of how the system works in practice.
Semver is basically a way of managing expectations between API producers and consumers. Complaining when API produces don’t follow the stated guidelines is part of the feedback loop to maintain consensus about what changes are allowed.
Exactly. The only other thing I would add is something about scale in terms of the number of independent people working on independent projects that can be combined together in any one of a number of ways. Or in other words, a lack of centralization.
If the scale problem didn’t exist, then I absolutely would not want to deal with semver. But to some extent, the problems with semver are the problems with communication itself.
I am 100% over versioning. I have never seen an implementation that doesn’t suck. It’s miserable. Something is fundamentally wrong with the whole model, whatever methodology you use for tagging won’t fix that.
There could be different ways:
I can’t wait for some of the ideas from Unison to permeate into more mainstream ecosystems. Lots of great ideas (globally accessible CAS, AST storage etc.) stuck behind a Haskell-like syntax.
Compare-And-Swap? Content-Aware Scaling? Close Air Support? Computer Algebra System? Content-Addressable Storage?
Content-Addressable Storage. Check it out! https://www.unisonweb.org/
I sort of agree because I don’t think there’s a perfect versioning system, but I think semver2 may be as good as it gets.
I like it because it’s more functional than the marketing driven “versions don’t matter, we’ll just call it version 2.0 to sell more” and all the alternatives get into too much time spent on perfecting versioning systems to diminishing returns.
I use it just so we have something, it saves time from deciding what to do, and it helps denote drafts or breaking changes. I use it even for stupid stuff like the “enterprise policy on bathroom breaks.” If it’s version 0.4.77 then it’s still in progress and could change any time. If it’s 1.1.16 then I mean it’s probably approved by someone. If I use 1.1.16 and see version 2.0 then it probably means I should read it because now it means I can only go to the bathroom on even hours or something that disrupts or may disrupt me.
In addition to the complaint about not following the breaking change requirement, I also dislike when packages spend years with tons of production users but refuse to reach “1.0” because they don’t want to commit to the semantic versioning requirement (lookin’ at you Hugo and Buffalo).
By leaving things at 0.x.y, users have to assume that every single 0.x change could break them, and that’s annoying.
I’ll add Terraform to this list. It’s otherwise a great tool, but version upgrades from 0.n to 0. n+1 have been a pain. That said, I believe the developers think this is the best way to maintain the project at the moment.
I think it’s less annoying than companies locking into something arbitrarily. I prefer this honesty in projects because, hey, maybe they will break stuff whenever they want. I want to know that a project might do this.
I usually interpret this as the company not taking time to commit to not breaking. With projects like Hugo that’s perfectly fine as I get what I pay for. I’d much rather them take this approach than releasing a new major version every month and not actually breaking anything (lookin’ at you Firefox). Functionally, it’s the same as 0.x.y, but it’s hard or even impossible to tell when they really release breaking stuff.
Over the past few years, I’ve had a decent number of bad versioning experiences where either:
(I decline to comment on whether I was the one depending on bug-compatibility in any of those cases…)
So at this point, I’m honestly not much of a fan of SemVer. The entire point is to provide a way to communicate changes to the user, and I don’t think it’s a rich enough mechanism to account for the wide variety of human experience involved in doing that.
Instead, I prefer to either use:
v1234
), if the software will only ever have a single release branch, or20.04
for software released in April 2020), if I have users who might need bugfixes on a past branch (so I can have20.04.1
)And then, instead of thinking deeply about version numbers for my own dependencies, I prefer to rely on a careful upgrade policy, a good suite of integration and unit tests, and a working mechanism for rollbacks in the case of catastrophe. ;-)
Looks like author never maintained large projects. Semver is great, but sometimes unexpectedly patch will broke someone’s workflow. Just incrementing major all the time is not a solution. Designing robust API can help with part of the problem. But no silver bullet in semver.
I’m not sure what you’re taking issue with. I’ve couched the whole purpose of Semantic Versioning as supporting the design of a robust API. I also made no claim that it’s a silver bullet, but it’s valuable enough to me that if you’re going to ignore it, then I’m not going to use your libraries.
It’s true that I don’t maintain any large open source projects. Professionally, does >2M LOC count?
edited to remove ad hominem