Hacker News new | past | comments | ask | show | jobs | submit login
What's next for SemVer (steveklabnik.com)
280 points by steveklabnik on Feb 11, 2019 | hide | past | favorite | 222 comments



The absolute most important thing they could do is add a fourth number, at the front, that is meaningless - the vanity number. vanity.major.minor.patch. There has been so much wasted time, and incorrect versioning, just because people have not "felt" that breaking changes constituted "major" semver updates - just to then break many production systems due to semver ranges only working iff major, minor, and patch actually attempt to mean anything.

This is a losing battle -- we are never going to convince people of this. So let's just acquiesce. Let's allow the first number to advance at whatever rate you like. It's the "human" portion of the version number. That way, everything to the right of it, no one will care about and can serve its actual purpose. Ultimately this isn't even a new idea, its just "the PR version" vs. "the build number" just like native apps have had for ages.


Not sure if it's a good idea.

A better idea in my opinion would be to have 2 versions:

* an "API" version that specifies technical changes.

* a public version more representative of big milestones in the project.

It's roughly what is done in the C/C++ ecosystem with sonames.

But I agree, that's indeed a flaw of SemVer IMHO. According SemVer, even if you are adding tons of new cool and useful features, while maintaining compatibility of existing one, you are not supposed to increase the major number.

From a "social" point of view, it gives the feeling a project is not really active.

And even from a technical perspective, bumping the major in case of significant changes can actually be a good idea. When I see an X.0.0 release, I kind of expect major changes inside the project, and even if the project remains compatible, significant changes generally bring bugs and instability, in some context it might be a good idea to wait for a version like X.1.4 to remain stable.

With semver, 1.42.0 -> 1.43.0 can be a small add on of one method or tons of new methods. And even 1.2.2 -> 1.2.3 can be an entire rework of the internals, while the API stays exactly the same.

Don't get me wrong, SemVer is great, it has enabled far less painful dependency management and better tooling, but some human and qualitative aspects are lacking in this versioning scheme.


There's no difference between our two ideas except that I have put both "versions" together in one string (please correct me if I'm wrong here).

The reason I've done this is because the second you make one version the "real" version that developers will see in their config files, that number will once again take on "emotional" value and lead to resistance to updating it.

There is theoretically nothing stopping node developers from using the system you describe today -- except that the "API" version is the one that people see in their files, and so naturally becomes the "spoken" version. If the "public version" isn't represented anywhere meaningful, then it requires a tremendous amount of 1) work on the developer's part to communicate its existence, and 2) restraint to maintain the spirit of the semver that users will actually repeatedly see. This is certainly something that a big corporation like Google is capable of, but small package developers in the ecosystem have shown that they are unwilling to do. The simple hack is to just append the "public version" to the front of the "API" version, and now everyone is happy.


I can't find any reference to not being able to increment the major version if you maintain compatibility. Only the reverse is specified, you must increment it if it is broken. https://semver.org/#spec-item-8


I'd think the problem with bumping the version number if you're remaining compatible is that it signals to the end-user that you're not compatible. i.e. when I start using 2.x the constraints I'll put in my list of libraries is < 3.0 (because otherwise my project could randomly break upon 3.0's release, depending on whether it's compatible or not).


The issue with the present system is that we have one numbering scheme trying to satisfy two very different roles:

- automated dependency management (“can I safely update to this version?”)

- marketing (“do I care about this new version?”)


I’m running into this issue with “prettier”, a JavaScript pretty printer. If we were to follow semver, we’d have to always bump the major version because every version changes in some way or form the output of some construct.

Instead what we opted to do is to have the major be 1. to signify that the project is mature, the minor for roughly quarterly big updates and the patch for hotfixes right after we ship a new update.


But at that point what you're doing has no resemblance to semver. And there is nothing at all wrong with that!

Just don't call it semver, because if you do, people will expect it to be... y'know... semver.

The same applies to putting your not-semver version number into places where people or tools expect a semver number to be placed. If you encounter those places (like npm) please put an actually-semver number in them.


I don’t call it semver, I publish it to npm with this version number.

The fact that npm install automatically adds a ^ next to the version number has caused so many issues in the past where some deep dependency would push a patch, not following semver, and break a huge part of the end users.

Thankfully, yarn lockfiles were introduced and solved the problem. Only people adding the dependency for the first time would see the new behavior, which is usually fine.


Looking at the definition from Steve's post:

> For this system to work, you first need to declare a public API. ... Once you identify your public API, you communicate changes to it with specific increments to your version number.

If you don't have a public API in Prettier, SemVer doesn't actually apply. If you do have a public API, only changes to that API (meaning the types, arities, functions, names, modules etc.) qualify for version number bumps. Everything else (like changing the output format) does not qualify.


I’m no expert in semver, but I’m fairly confident that updates which change outputs absolutely count as breaking changes. Surely it isn’t just the signatures of public APIs that are considered to be breaking changes.

It seems to me that the semver concept of “breaking changes” by definition applies to almost all changes to a code formatted like Prettier, except for updates which truly don’t affect the output of any given input (like performance improvements or support for additional platforms).

Most pieces of software can have non-breaking changes like adding new features, but if you add a new feature to Prettier (like specific formatting rules for React hooks [0]), you of course change the formatted output of code that uses React hooks.

https://github.com/prettier/prettier/blob/master/CHANGELOG.m...


Looking at the actual specification ( https://semver.org/ ):

> 1. Software using Semantic Versioning MUST declare a public API. This API could be declared in the code itself or exist strictly in documentation. However it is done, it should be precise and comprehensive.

I don't believe we can make such a claim about Prettier's output format. For example, how would you denote a backward-compatible change to the output format?


Prettier has a clear API.

That’s my point: there by definition cannot be backwards-compatible changes to the output format.


> there by definition cannot be backwards-compatible changes to the output format.

In that case we can conclude that it's incompatible with SemVer! SemVer is all about trying to convey what changes to a package are backward-compatible and thus are safe to upgrade to new versions. The whole point is that you can say something like "that-package": "^3.9.1" and mean that you're OK to upgrade up to anything less than 4.0.0. If you can never do that, the package is obviously not using SemVer.


It's as simple as adding to the README 'do not depend on specific output as it will change between versions' and then you're doing server...


Well, sure, you could always just lie. But in practice, Prettier updates would literally be breaking changes, as in, if you have your CI verifying that code conforms to Prettier's formatting, and you're using a new version of Prettier locally, your commits will literally break the tests. Which is, of course, what "breaking change" means in practice.


Since prettier isn't using Semver (I don't think it can either), maybe instead of Semver, you can experiment with alternate schemes which do provide more clarity. Example: an `YYMM.patch` style version, something like "1903.01"


I'm kind of convinced that this is one of those times where doing anything is worse than doing nothing.

Do we really have a real problem here deserving of all the costs that come with more change?


Exactly. Leave what works. The emperor doesn't need new clothes and we don't need the pain of fashionable, consumerist, job-security churn.


In the case of a non-breaking "new version" release would you go directly from, say, 1.4.6.0 to 2.4.7.0 then?

And for a breaking "update" release you' go from 1.4.6.0 to 1.5.0.0?


Yes, precisely. You could have big flashy releases with "2" branding that as far as automatic updates are concerned are non-breaking and non-conceptual (new logo! we got acquired! better docs! whatever). You can also have important bug fixes that will break existing clients and thus should not auto-update, but under the "idea" of 1.0.


That sounds awful. It makes it even harder to make decisions about whether to accept a new version because it is tangling different concerns into a single knot and saying "here, we might mean all of these things, you figure it out!"

Just change the name.


The name doesn't show up anywhere

Also, there is too much focus on the least important of the two scenarios: wanting to increase the version number with no meaningful changes. The actually important scenario is the opposite: when the major version SHOULD be upgraded but people don't want to give the impression that this is a "major release" (in the colloquial sense). This is why the name is irrelevant: this is a situation where you'd want to "keep the name the same", and we already see that not working.

Perhaps you'd be happy with making the vanity portion I suggested a "name"? mojave.1.0.3? This is 100% equivalent to what I was already proposing (the vanity section is pure vanity and is meaningless in package resolution), but seems to also satisfy your concern that it would be confusing if it were a number. Does that satisfy your concerns? My concern is simple: unless he "name" shows up in config files, it will not have the power of the "major" version and will thus inevitably result in people not properly upgrading the major version when necessary.


Honestly, I agree with you.

A current client has been trying to dictate our version numbers purely for marketing purposes and we've been pushing back since the start of the project. They originally wanted us to release 1.0 as 1.5, literally so they could imply that the software was further along than it actually was at the time (software marketing people sure seem to love lying to their own potential clients).

At least we no longer tie our sprints to version numbers (also at the behest of the client). Our scrum master eventually decoupled them unilaterally because the workload was getting ridiculous, and our internal project management was ending up as simply a formality because of it. We're all grateful for that one.


Clients don't always help themselves here though with blanket policies against deploying 1.x or x.0 releases.


> Let's allow the first number to advance at whatever rate you like

This is what I feel like Pip did. https://pip.pypa.io/en/stable/news/

it went from 1.5.6 to 6.0, and later from 10.0.1 to 18.0.

Ok.


I don't know anything about the 1 -> 6 jump, but the jump to 18 was because they switched to time-based versioning last year (2018). First release of 2019 was 19.0.


the 1 -> 6 jump was because they decided to drop the first number. Yes, they had reasons, but changing version schemas twice in a couple years is a great way to confuse people in the interest of just doing whatever the author likes.


This is called explicit versioning [1].

But in my opinion the correct fix is the one suggested in this comment: https://news.ycombinator.com/item?id=19138143

Windows is a great example: the number behind the name (7, 8, 8.1, 10, etc.) is just marketing gobbledygook. It doesn't even have to be a number (XP, etc.). The real version number of Windows is hidden from regular users---though they don't follow semver so I'm not sure it has much value per se. But they could plausibly adopt semver and regular users wouldn't need to know.

Let the marketing people choose the branding and the developers choose the version numbers. Keep those concerns separate, don't force them to be conflated.

[1]: https://medium.com/sapioit/why-having-3-numbers-in-the-versi...


Yea I don't get why you can't do this already, effectively. Just hide the real semver in the place it'll be consumed, with a comment tying it to the branded version if you want to reduce confusion.


By numbers NPM is probably the biggest ecosystem using semver but the config files are JSON which don’t allow comments. Bit of a PITA generally but also makes this tougher to do consistently.


A somewhat off-the-wall solution to this problem would be to require major versions to start at some arbitrary lower limit much greater than 1. For example, imagine every semantic version started at 123.0.0. It's less of a big deal to go from version 123 to 124 than it is to go from 1 to 2.


I recently learned that a system like this is already described by Haskell’s Package Version Policy[1]. They call the vanity digit the “epoch”[2]. Basically the way the formalize it is that the first two components specify the major component, and a breaking change must correspond to an increment where the new major version is lexicographically greater than before. Depending on the whim of the maintainer this can be by incrementing the first or second number.

[1] https://pvp.haskell.org [2] https://pvp.haskell.org/faq/#semver


I don't see anything wrong with something like "Angular 2 (v1.3.6-a)".

It's not a new version of the same software -- it's new software.


Just to clarify, the real concern is the opposite of what you are showing, when the semver version advances faster than the "desired" version. So for example, something like Angular 1 (v2.5.6), because it contains a change that breaks 1.x.x users.

Regardless, "what's wrong" is that people just don't behave this way, and there's no way to convince them otherwise. For better or worse, semvers have taken on the dual role of "build version" and "public facing version" (probably because that's the only version users actually end up seeing out in the wild). So there is tremendous resistance to advancing the MAJOR portion of the semver faster than their "marketing" desires. I have been in many, many, frustrating arguments on GitHub where there are several users that are begging to simply release a package under a MAJOR update instead of (erroneously) under a MINOR version, because it is incredibly difficult and confusing when a subdependency of a subdependency picks up a seemingly innocuous change which is actually breaking solely because "it still felt like it belonged in this major version", and the "fix" for this on the user's end is super complex - having to dive many layers deep in the dependency tree and wrestle with a lockfile - all because the change "didn't feel 2.0 worthy".

In theory, if you can prove that a change breaks current users, there should be no further discussion, the change merits a major update. But you will be surprised how all of a sudden crazy unrelated arguments start popping up. Ultimately, its all because there is an emotional attachment to the leftmost number, simple as that. Look no further than Underscore's "romantic versioning" (https://gist.github.com/jashkenas/cbd2b088e20279ae2c8e ) drama back when SemVer was first introduced, when it was stated that it would be ridiculous for underscore to be at "version 157" or something had they followed semver to the letter. But again, its only ridiculous because people want to attach PR value to the Major version. It is absolutely not ridiculous from the perspective of knowing that there were 157 breaking changes, and 157.x.y won't work with 156.x.y.


Great idea, but for political purposes, you might not want to call the first position 'vanity'. Perhaps 'symbolic' or 'headline'. (Or even, 'political'.)


No, call it vanity.

People deserve to be reminded that the entire thing is silly ego. Every single time.


But then they'll take it on their ego, thus defeating the purpose


Lecturing people that their preferred "leading version number" practices are "silly ego" won't help this better system attract support.

But also, it's false. It's a legitimate convention, across a bunch of domains that are either analogous or intersecting with software-development, to use increments of the 1st number to signal "big user-meaningful changes" (as opposed to exactly-and-only "interface breaking changes"). It's not just ego, but evolved communication.


That's what Haskell has done for a while now! https://pvp.haskell.org/


Hard disagree here. The tides are finally starting to shift on this and customers are demanding it -- no need to capitulate now.


Your version number is an IP address. Among the many problems that can occur, like leaving out --ppapi-flash-version= or putting a space after it:

  exec /usr/bin/chromium --ppapi-flash-path=/usr/lib/adobe-flashplugin/libpepflashplayer.so --ppapi-flash-version=$(grep version /usr/lib/adobe-flashplugin/manifest.json | cut -d '"' -f 4) "$@"
If it is for vanity then as another comment suggested include it in the name:

  python3 --version


1.1 and 1.1.1 are also valid IP addresses (and http://1.1 even works as a URL) via an obscure shorthand notation. Valid to `inet_aton()` at least, but definitely more recognizable as a version than an IP to humans.


I'm excited to see this. I would hope to see someone from the Go community get involved in the maintainer group, though. Russ Cox's philosophy on versioning is now baked into the Go language pretty deeply, and it's (at least partly) motivated by problems with semver, real or perceived:

> Where I think semver falls short is the idea that “having to bump major versions” is a step that will make you “think through the impact of your changes and evaluate the cost/benefit ratio involved.” Quite the opposite: it’s far too easy to read semver as implying that as long as you increment the major version when you make an incompatible change, everything else will work out. The example shows that this is not the case.

(from https://research.swtch.com/vgo-import, one of the treatises on go versioning)

Despite this criticism, Go is sort of still using semver, at least as a way to compare string version identifiers. I think it's fair to say that Go users form a significant community using semantic versioning. It would be a mistake to leave them out of RFC discussions on the topic.


I didn't previously reach out to anyone from Go specifically because they do not really use SemVer. As a group, we're open to including others than this initial roster, but didn't think it would be particularly relevant to their interest. If that's incorrect, someone please get in touch!


The language implements semver directly in the Go language source: https://github.com/golang/go/blob/47d5a4433513c3870a9994b50e...

This is used to resolve module loading, according to this logic: https://github.com/golang/go/blob/aab0b704d88b7dad10c767ad94...


Thanks for the citations! Reading this, it's semver-ish for sure; that said, the "ish" is exactly why we're doing this. I'll try to figure out how to get in touch with someone.


Excellent, thank you! I would recommend a brief post on https://groups.google.com/forum/#!forum/golang-dev; that's the main hub for people who work on Go itself.


Hi Steve, I've looked at this relatively carefully, and I believe that Go does 100% follow the semver spec (modulo any bugs in the implementation).

However, while that happens to be my personal belief, I am not any type of world-class semver expert, nor am a semver maintainer, and hence always happy to learn more (especially from, say, an actual semver maintainer such as yourself ;-)

I think you are saying "-ish" in your comment above at least partly based the two chunks of linked code above, including this comment there:

  // This package follows Semantic Versioning 2.0.0 (see semver.org)
  // with two exceptions. First, it requires the "v" prefix. Second, it recognizes
  // vMAJOR and vMAJOR.MINOR (with no prerelease or build suffixes)
  // as shorthands for vMAJOR.0.0 and vMAJOR.MINOR.0.
It is true that Go treats the leading "v" as a requirement for VCS tags to be recognized as encoding a semantic version. That said, as far as I am aware, requiring the "v" prefix for VCS tags is allowed by the semver spec, and it might be possible to draw a distinction between a semver version (which does not include a "v") vs. how a given semver version is encoded into a VCS tag (where an encoding is allowed to include a "v", as far as I am aware). For example, there is an FAQ that was added a few years ago to 'master' at github.com/semver/semver that seemed to address this:

prefixing a semantic version with a "v" is a common way (in English) to indicate it is a version number. Abbreviating "version" as "v" is often seen with version control. Example: git tag v1.2.3 -m "Release version 1.2.3", in which case "v1.2.3" is a tag name and the semantic version is "1.2.3".[1]

I am also aware that a leading "v" vs. no leading "v" for VCS tags can trigger some impassioned debate, so I might regret posting this comment. ;-)

Regarding the second piece from that comment above -- that particular Go package does have the ability to parse something like "v1" or "v1.2" (without the three integers required by semver). However, the result is not interpreted as a valid semver version by the overall 'go' tool. For example, a VCS tag such as "v1.2" that the 'go' tool finds on a git repository will _not_ be interpreted as a semantic version (because it does not have the required three integers). However, being able to parse something like "v1" or "v1.2" is used for example as part of a version query mechanism. For example, you can do something like "go get foo@v1.2" as a way of asking "please give me the highest available release version in >= v1.2.0 and < v2.0.0". In other words, it is a short-hand for a particular type of version query, which I would guess would not be in violation of the current semver 2.0 spec? If interested, there is some more information about that query mechanism (which is called a "module query") in the Go doc[2].

Finally, here is a snippet from the Go doc[3] stating semver is used (and there is a link to https://semver.org in that section as well):

The go command requires that modules use semantic versions and expects that the versions accurately describe compatibility

I wouldn't be shocked if you have a different take on some or all of what I said above, but wanted to at least share my personal understanding...

[1] https://github.com/semver/semver/blob/master/semver.md

[2] https://golang.org/cmd/go/#hdr-Module_queries

[3] https://golang.org/cmd/go/#hdr-Module_compatibility_and_sema...


Hey hey!

So, primarily I am saying "ish" for two reasons: one, as I said below, I mis-understood how versions were actually used within Go. There's a lot of stuff out there, and keeping the three or four implementations I do know well is tough enough. Second, I don't want to say, definitively, that any particular implementation "does not implement Semver", because I think the spec is deficient enough that it's really hard to say in general.

On to your specific points:

> That said, as far as I am aware, requiring the "v" prefix for VCS tags is allowed by the semver spec

This is true, SemVer says nothing about VCSes.

> However, being able to parse parse something like "v1" or "v1.2" is used for example as part of a version query mechanism.

Right, so that's what I thought this was getting at, and the general "range" concept isn't in the spec, so all of that is fine, spec speaking.

However, that doc comment describes it a bit differently than you are. It describes them as version numbers. So it's possible that the doc comment is a bit mis-leading, maybe. That's very reasonable! This is why we need to clean up the spec text.


Makes sense. I guess the way I would summarize it is I believe the end-to-end Go system is 100% compliant with semver 2.0 spec. (And that is true to my knowledge even though there happens to be an internal-only 'semver' package that also contains Go-specific functionality related to semver, and that internal 'semver' package both must be used properly and is used properly to keep the overall end-to-end system semver spec compliant, including proper use of functions like isSemverPrefix(version) and similar to differentiate between what is allowed in a semver VCS tag vs. what is allowed for what is effectively a range-based query, etc.).

Again, happy to learn otherwise...


Can you clarify why what Go is doing is only semver-"ish"?

What would you have them do differently to be described as semver compliant without the -"ish"?


https://github.com/golang/go/blob/47d5a4433513c3870a9994b50e... are the two that they identified.

Both of these things are not deal breakers; some semver implementations do allow the prefix "v" (though none require, and some don't allow it too), and their other modification to version numbers is sort of what is done in some parts of the other implementations do. And as I said elsewhere, the matchers aren't part of today's spec, so it feels weird to disqualify them based on a sparse implementation.

My real point is that because the spec is incomplete, many of these kinds of questions cannot be answered, even within the big implementations. So I'm saying "ish" to hedge. Given the anti-SemVer tone throughout the vgo discussion, I thought they had gotten further away from the mainstream than they actually ended up, which is my mistake.


I think the initial vgo discussion and versioned Go modules proposal effectively said "developers expect semver; semver is helpful; Go is officially adopting and requiring semver". At the same time, I think there was also some discussion around "semver alone is not a complete answer". I can see how some of that might have been read as anti-semver, though I suspect that was not the intent.

In any event, it is exciting to see more energy going into the semver spec!


Makes me wonder why this gem is buried under /internal and not exposed publicly. I could have used it in a couple of projects.


> Quite the opposite: it’s far too easy to read semver as implying that as long as you increment the major version when you make an incompatible change, everything else will work out. The example shows that this is not the case.

and if you do that too often then you either end up with a bunch of maintenance on old versions or a use base that hates you.

Isn't that the real disincentive?


Ironically, much of the rest of this thread is people saying they refuse to use or don't like semver _because_ they want to be introducing backwards-breaking changes without incrementing the major version number.

So, while there may be some projects that introduce backwards-breaking changes and increment major versions willy-nilly, I think actual observed developer behavior does not justify Russ Cox's supposition that semver does not serve as a disincentive to introduce breaking changes generally. People are complaining about semver _because_ they find it a disincentive, and don't want it there telling them they can't release backwards breaks without a major version bump they don't want to do :) Of course, you can lead a horse to water...

Perhaps the semver docs should make it clear that frequent backwards-breaking releases _still are likely_ to create developer pain even if you increment the major version release number, I dunno.


For an interesting take on versioning, see the Spec-ulation keynote by Rich Hickey [1]. It specifically touches upon SemVer being itself versioned.

[1]: https://www.youtube.com/watch?v=oyLBGkS5ICk


This is an excellent talk, i found myself fully agreeing and kind of relieved that i'm not the only one thinking that you shouldn't break backwards compatibility and if you do, then rename the API. I'd only add that for libraries renaming "FOO 1" (with "1" often implied) to "FOO 2" isn't a good new name since someone else might want to pick up the slack and continue developing a "FOO 2" that is backwards compatible.

One of my favorite parts of the talk is the X.Y.Z changes in semver essentially translating (from a user perspective) to imscrewed.dontcare.dontcare :-P.

Sadly it seems that this talk has been largely ignored. I'd like everyone who provides any library, framework or anything else that other people are supposed to build on - especially on the perpetually broken Linux desktop - to watch this talk.


SemVer bit starts at https://youtu.be/oyLBGkS5ICk?t=825

Have to agree, excellent talk.


It's a great talk. I can't remember whether he says it directly in the talk, but the upshot of his argument is that the "major version" part of SemVer is pretty much useless information to library consumers, and suggests that rather than using major versions, libraries should just change their names.


This is great!

Natalie Weizenbaum and I used SemVer when we created pub, the package manager for Dart. It was a good choice, but we definitely ran into some subtle problems over the years. Briefly:

* Like Steve notes, Semver doesn't specify any syntax and semantics ranges, constraints, or other "groups of versions". Every package manager just rolls its own thing, hopefully in a mostly compatible way.

* SemVer used to specify an ordering for build versions ("1.2.3+1" versus "1.2.3+2") and then later removed that. Pub still uses the older semantics because a package manager has to do something. If the user requests "foo 1.2.3" and both "1.2.3+1" and "1.2.3+2" exist, what's the package manager supposed to do? Pick randomly? So we still technically use the older SemVer spec where this ordering is defined.

* It's hard for a package to transition into a period of experimental development. Let's say you maintain foo which has lots of users. Foo 1.2.3 is out and lots of people are using it. You want to start working on a major, breaking revision to foo. You need to do that in a way that doesn't spontaneously break existing users, but you also want to ship something so that you can get feedback from early adopters.

Right now, the way you do this is by shipping a series of pre-release versions like 2.0.0-dev.1, etc. That works, but users are often confused that a range like ">=2.0.0" does not include those dev versions, nor does "<2.0.0". It doesn't feel to me that SemVer gracefully handles the idea that previously stable packages may go through periods of instability. Pre-releases are sort of the imaginary numbers of SemVer where they don't really fit the natural linearization of version numbers.

Many projects now have "channels" of development where users can indicate whether they want to be on "stable", "dev", "bleeding edge", etc. When it comes to pre-releases and experimental versions, I think users generally want to indicate whether they want to be on the bleeding edge or not. Today, at least in pub, they have to indicate that using some carefully crafted version constraint that only includes pre-releases. It would be nice if that was a more direct way to express that.

(The fact that SemVer doesn't define ranges compounds this. I think our interpretation of ranges mostly follows npm.)


How pre-release versions are handled, or should be handled is definitely the biggest areas for improvement I've run into.

It's often unclear what one should expect tooling to do, with regard to computing/comparing ranges/versions there. Or even what one should expect such numbers to mean even as a human, with regard to changes from adjacent versions. It's not entirely clear to me even what would be the most useful understanding/requirements to have in this area, what it _should_ be. In practice, there is a lot of confusion, and inconsistency between tools.

Possibly because semver assumes just a one-dimensional list of ordered versions (reasonable, do the stupidest thing that might work and actually be implemented and adopted), but the pre-release issue really introduces a second axis. It's not clear to me what the solution is.


Thanks! These are all great points.

And, if someone from Dart wants to get involved, please open an issue on the repository. We needed to start somewhere, but also don't want to exclude other package managers, either.


One could argue that if you are about to majorly break back compat, that you should just rename your project. Even calling it `foo2` is better than pretending it's `foo 2.0`. It may slow adoption of your new work, but at least it's staying honest.


SemVer doesn’t seem to distinguish between “major” and “minor” breaking changes, instead saying all breaking changes are major version changes. So making new major versions cheap seems like a good way to document any breaking changes, even if they are “minor” breaking changes.


1) In the Windows ecosystem almost every version has 4 parts. (e.g. file headers in exe, dll, assembly etc.) So it's impossible to use semver which only allows 3 parts. This seems like a major unnecessary roadblock.

2) IMO packaging tools should have fields to indicate the versioning scheme they follow, for example:

{ "version": "1.0.0", "version_scheme": "https://semver.org/spec/v2.0.0.html" }

{ "version"="19.02", "version_scheme": "https://calver.org/")

{ "version"="1.0.0.0", "version_scheme": "https://altver.example.org/winver/1.0.0.0.html")

{ "version"="1.0.0.0", "version_scheme": "custom")

Currently the tools pretend to know that "all" versions follow the same scheme. But in reality they just don't and never will.

Maybe also allow a prefix instead, but make it explicit and unmistakable (i.e. not "v1.0.0", but "semver2-1.0.0").


The map is not the territory. Similarly, the semantic version number is not the API.

Why are we relying on fallible humans to examine an API and encode an assertion about its backwards compatibility into a number? Further, why are we then handing this (often dubious) assertion of backwards compatibility to machines to depend on?

Can't we create a set of rules/tests that will actually execute the API to determine whether it's backwards compatible? Why aren't we doing that more?


There are tools to do this! Elm has the best-known implementation.

However, the real reason is exactly why "compatibility" is not defined in the spec. It is really hard to pin this down, and possibly impossible. Machines can help humans check these kinds of things, but they still need humans in the end.


Automatically differentiating between bug fixes and backwards-incompatible changes is, I believe, impossible.


In general, yes. But there are still some things you can do. The Elm package repository, for example, looks at the type signatures of all of the public functions. If there are no changes from the last version, it bumps the patch version. If functions have been added to the public API, it forces a minor version bump. If functions have been removed from the public API, or any type signatures have changed, it forces a major version change. I believe it lets the user force a major or minor bump for changes that don't appear in the type signature. It's been my experience that that is pretty rare though.


Figuring out how to make a computer decide that one API is compatible with another would be bloody hard. I hesitate to say 'impossible' because today, I can subscribe to much of what I deemed impossible two decades ago.

If you can figure this out, it would be an absolutely amazing feat. Best of luck!


When it comes to native libraries at least, API laboratory has some tools to help: https://abi-laboratory.pro/

It isn't perfect, but it can largely show when things break (or might break). As an example here is the report for curl: https://abi-laboratory.pro/index.php?view=timeline&l=curl


Fully typed functional languages (Haskell for example) have this ability.


I'm not sure that is correct. A function that takes no arguments and returns 2 and a function that takes no arguments and returns 3 both have the same type signature. I'm not super strong on the theoretical side of computer science, but, my understanding is that the only general solution to determining if our function returns 2 or 3 (or some other integer) is equivalent to the halting problem.


Yes, obviously. The API is the same though. The interface.


The signature of the interface is the same, yes. But, if the behavior changes it can still break users. I'm fully in support of strong typing and SemVer and all that good stuff - I was just responding to your comment which I interpreted to mean that a language like Haskell has a full solution to the problem of breakage, which I don't believe it does. It's certainly in a better position that many other language in the types of breakage it can detect just from the function signatures, however.


For one, "API" isn't a simply definable thing. It can entail any number of performance constraints and tooling idiosyncrasies. That's why the first step of using SemVer is to declare a public API.


ruuda pointed out an excellent talk/rant by Rick Hickey about the difficulties with versioning: https://youtu.be/oyLBGkS5ICk?t=1792

(edit: change video link to start right at the ranty part about Semantic Versioning)


Rich Hickey, creator of Clojure.


Isn't such a problem at least as difficult a say, the halting problem? There's a lot of implicit contract involved in API's, particularly with languages that aren't theorem proving languages.


I want to see some support for alpha, beta, pre-release, and snapshot versions.

The problem is that everyone does it differently, and build systems have a hard time distinguishing these non-production-ready versions. For example, I type "mvn versions:display-dependency-updates" and I get a suggestion that I use "junit 4.13-beta-2".


The SemVer spec does have pre-release versions https://semver.org/#spec-item-9

But, I guess you're saying that certain kinds of names in the pre-release space should have defined semantics?


“-SNAPSHOT” suffix already has defined semantics in Maven world, which would make sense to replicate elsewhere, since it’s very convenient for CI. Alfa, Beta etc. could be used to improve multi-environment CD (eg UAT envs could accept RCs, production - only stable versions and so on).


Ah yes, didn't know that was there. Would be nice if Maven acknowledged it.

Some kind of standardization would be helpful. In particular, it would be good if a tool could sort on the post-hyphen part so it could know which pre-release version is most recent.



I'm not sure if it's already in the spec, but it would be nice to get a very hard definition of when to call a project 1.0.0.

It would also be nice to see it evolve to handle projects like Docker and Ubuntu, where Docker said goodbye to SemVer because it couldn't fit their use case.

Also, SemVer seems too optimistic about major updates. After being in the industry for several decades, breaking changes are depressingly rare, and SemVer says basically nothing about this, or the fact that major versions include bugs-as-api-contracts.



Is there something like "test-driven versioning"?

It would be rather simple: "Here is the testsuite for our (public) api. We support the use cases tested, but no more.

If you want your use case covered, please submit a testcase.

If we change an existing test, we increase the major version.

If we add tests, we increase the minor level.

If the test suite remains the same and we only change the code, it's a patch-level increase."

This could be verified automatically, increasing trust.


SemVer talks a lot about compatibility, but doesn't define it. Each project is supposed to on its own. You can just say this for any given project, and it would be compatible.


I prefer Rich Hickey's definition of compatibility [1]:

A function is compatible if it requires no more than it did, and provides no less than it did in the previous version. A library is compatible if it provides no fewer functions, and requires no additional context (transitive dependencies) to use it, and all of its functions are compatible as defined.

When you wish to require more, or provide less, you make a function with a new name.

[1] As linked elsewhere: https://youtu.be/oyLBGkS5ICk?t=1189


What happens if you add a testcase that passes in 5.3.2 (current) but fails in 5.3.1?


New feature added!


I really really like this idea. Often I am wondering if something I am doing simply works by chance or if its an actual supported feature and as a developer I wonder if I can change something because someone might be depending on it.

But I am left wondering something. How can you easily let people find out if their use case is included in the tests without having them read 20,000 tests.


Like contract testing? https://docs.pact.io/

That's more for HTTP communication between services but I wonder if you could build something similar for libraries.


I would like to work similarly to how WHATWG makes HTML a "living standard", with rolling releases tracked by http://caniuse.com/

instead of working so hard to name changes with oversimplified numbers, our change management systems could do all this for us with metadata

see also [the package manager we need](https://gist.github.com/dominictarr/7c3319a0b0bad22bde2c), [Towards "annex", a Fact Based Dependency System](https://speakerdeck.com/markhibberd/towards-annex-a-fact-bas...), ["Towards "annex", a Fact Based Dependency System" by Mark Hibberd](https://www.youtube.com/watch?v=JjYAnBhF2JU)


Frankly, I think browser compatibility is one of the nastiest, trickiest bits of web development. I'd want to find any way to avoid that sort of thing. The "living standard" model merely describes underlying complexity without resolving any of it, which puts the burden on individual developers. I think we can do better.


Feature queries help a lot, but seem to slip under the radar for a lot of developers.

https://developer.mozilla.org/en-US/docs/Web/CSS/@supports

https://developer.mozilla.org/en-US/docs/Web/CSS/@media/hove...


> The "living standard" model merely describes underlying complexity without resolving any of it, which puts the burden on individual developers.

Don't blame people when it's their tools that are at fault.

"If you want to teach people a new way of thinking, don’t bother trying to teach them. Instead, give them a tool, the use of which will lead to new ways of thinking."

- Buckminster Fuller


I'm confused. Can you explain which tool is at fault, and for which problem, here?


coding needs to be more automatic

api changes aren't such a big deal

they only seem like it now, because the editor is the primary coding tool

code should be generated from content. roughly speaking:

- https://en.wikipedia.org/wiki/Code_as_data_(disambiguation)

- https://en.wikipedia.org/wiki/Code_generation_(compiler)

- https://en.wikipedia.org/wiki/Round-trip_engineering

- https://en.wikipedia.org/wiki/Model-driven_engineering

further, I believe:

> code is unrefactored config

> config is immature content

> content is unrefined code


Interesting to see this topic coming up. I just recently complained to Microsoft about how Azure Artifacts' "Universal Packages"[1] only supports strict Semantic Versioning with no extras (no additional version number components past patch and no commit hash). This is problematic because there are still several versioning schemes that use four version components, most notably Windows binaries. I was really interested in using Universal Packages to store application binaries as part of a set of new CI pipelines I'm architecting; unfortunately, I have to resort to NuGet, which still supports <major>.<minor>.<patch>.<buildid>+commithash syntax.

I realized the issue was with Semantic Versioning itself. The spec should be changed to mention that any number of additional dotted version components are allowed after <major>.<minor>.<patch> but that they have no semantic meaning. This would help address legacy versioning concerns like mine while not being too prescriptive.

Only having three version components is additionally problematic because there is no place for CI build IDs in the version. Imagine I'm working in a bug-fix branch called 1.5.4. I might have several commits that all trigger CI pipelines. I don't want to increment the patch component until I actually merge the bug fix into master, but I still want my CI system to generate unique version numbers per build. There currently doesn't seem to be a way to do that without being forced to use pre-release identifiers (e.g., 1.5.4-beta.1, -beta.2, etc.), which unnecessarily complicates the CI pipeline.

[1]: https://docs.microsoft.com/en-us/azure/devops/artifacts/quic...


Timely for me as well. Another reason for adding an additional version number component is this: I use an open source library that I need to fork. Since the library uses the `patch` number component, it's hard to release forked versions to NPM without having conflicting patch numbers.

For example, if the library is at `0.15.4` I don't want to release my updates as `0.15.5`, `0.15.6` etc. because when the main library updates to `0.15.5` we have conflict/confusion. Instead, I'd like to `0.15.4.1` to indicate my `.1` update to `0.15.4`. When they update to `0.15.5` I can merge and release a version at `0.15.5.1`


Exactly.

In my case, I'm using Azure Pipelines' "build ID" concept as a globally-unique integer. Given any fourth version component (e.g., the 125 in 1.5.4.125) I can identify the exact unique build that generated that artifact. No two builds across all artifacts will ever share the same build ID. It's a great way of finding the build of any artifact even if all I have is a version number.


SemVer calls it a pre-release identifier but you can in fact put anything you want in there that matches the syntactic rules, so you could just use your build id as a pre-release identifier. Or as the build id field, if you're not already using that.


The issue with doing it that way is package managers like NuGet can "hide" pre-releases--they treat them as different fundamental types of packages. In my case, I don't want that designation. I realize I could use pre-release identifiers but I'd rather have the artifact versioned "first-class," so to speak.


I would suggest putting it in the "build metadata" section then. If you're already putting a commit hash there, you can use the hyphen to separate it, like. 1.5.4+build123-b5bb9d80


The standard separator for build metadata fields is a period, since the section is ignored for version comparison it doesn’t really matter, but the spec does specify it (and some tools may display separate fields differently).


Oh you're right. I glanced at the spec and saw that it defined build metadata as [0-9A-Za-z-] but didn't realize that it was actually defining this as a dot-separated series of components where each component was [0-9A-Za-z-].


I'm surprised there's no involvement from PHP yet (although, who would be involved?)

PHP's renaissance has a lot of thanks to give to Composer - our own package manager that has been fantastic.

Composer should definitely be represented in the maintainer's group.



What's not new: Semantic Versioning is an excuse to break existing code...or more cynically, SemVer is a promise to break your code. The author cites Ruby and Javascript as examples of semantic versioning's success. But trying really hard to not break existing code is a core principle of both languages development. MINASWAN [0] and principal of least surprise in Ruby . A consensus formal specification process for ECMAScript [1] (aka "JavaScript").

[0] https://en.wiktionary.org/wiki/MINASWAN

[1] A breaking change looks like this, https://itnext.io/a-tiny-disastrous-ecmascript-change-fadc05...


Moving into JS development from Windows development was a shock.

APIs have breaking changes?! I get it, open source devs don't have huge corporate resources to ensure back compat forever, but wow, it moves so much burden onto product developers.


It's neither an excuse nor a promise to break code. It's merely a version numbering scheme that makes it explicit which versions will break code. If you're using semver and you don't want your next version to break code, go ahead and release it with an increased minor version. Major version increases aren't for you then.


Semantic versioning reduces the social cost of breaking changes. It makes them more likely.


Suppose we replaced Semantic Versioning with any other approach (of your choice) that still disclosed the same amount of information about whether API was identical, had an additive change, or had a breaking change. Would you object to that model too ?

If so, it seems like you'd be effectively arguing that authors should simply not disclose important changes to the API. It is hard to imagine that position being defensible.

If not, why do you consider Semantic Versioning to be more hazardous?


Additive changes aren't a problem. If I use the method 'spam' from library '{foo : most_recent}' adding 'eggs' to 'foo' doesn't require me to edit my code to {foo : 16.8.37}. The most recent version still works.

If the library author decides to do something that breaks calls to 'spam' (or 'eggs', etc.) then a protocol that calls the new library 'foobar' (not 'foo') means '{foo : most_recent}' still works. I can use continue using 'foo.spam' if it meets my needs.

+ And there's no problem if I want to add '{foobar : most_recent}' to the code if I want to use 'foobar.eggs'.

+ And I can even use 'foobar.spam' alongside 'foo.spam'.

+ And my build system can be simplified to '{foo}, {foobar}' as the ordinary case. With language like '{foo : 23.8.13}' only needed for rare cases. For example when my code depends on the effects of an obvious bug that was patched.

The premise of semantic versioning is that breaking changes for whatever reason are ok. And look, that's fine. "It's your library. It's free. You don't owe me anything." I just prefer not to rely on people who will change "print x" to "print(x)" based on divine revelation rather than the effect it has on everyone who relied on the documentation saying use 'print x'. And no automated migration tools because semantic versioning.


I think the idea (with which i fully agree, btw) is to avoid at all technically possible costs to make backwards incompatible changes in the first place (and when they happen they should be considered as nothing more than bugs to be fixed - ie. restore the backwards compatibility) and the semantics of semver are trying to represent some state that shouldn't be representable at all.

Think of it as a reference in C++ that is null... it can technically happen (in the sense that all bytes that store the reference are, e.g., zero), but the language doesn't really allow for it and if it does happen it is a bug.


You make a design decision and you should be stuck with it forever. You build your own hell one mistake at a time. If you don't like it, quit in shame instead.


In my usage of SemiVer I felt it lacked what I call a "gestalt number" which would be the first number, representing broad changes to the design which (almost always) necessarily include API changes. But API changes can happen within the same "gestalt".


One option is to move the "gestalt number" into the name. It's more common in end user software than APIs since it's more of a marketing thing. Like Mac OS X, Windows 10, Sublime Text 3. It doesn't have to be a number either, like Photoshop CC.


I've certainly encountered this issue.

When I release ConceptNet, I would like to follow SemVer, but it kind of has to be in a way that the second component of the version is the major version, because there are bigger things that happen in a multi-decade project than just API changes.

The project is ConceptNet 5. It is the fifth project named ConceptNet. What it has in common with ConceptNet 4 is some of the data and the spirit of the problem to be solved; the design is quite different.

It's on the sixth major version after the initial release, so I call it ConceptNet 5.6. The current version is 5.6.4. If I wanted to use tools that expect SemVer, I'd have to call it something like "ConceptNet 5 v6.4". I find that confusing, and I think other people would too.


Why does a SemVer spec have to deal with that? Why not have a "gestalt" number/indicator for purely informal use, but keep the SemVer part as is?


That's essentially a 4-part version number. For most projects it's over the top and almost no-one used or uses 1.2.3.4 version numbers. I've seen libraries use "library3 1.6.2" [1] but it feels very strange and confusing.

[1] https://github.com/etorreborre/specs2/releases


And call the project Foo 3 v4.1.9? That seems pretty confusing.


Yes, that is my biggest problem with semver too. Will upgrading 15.1.2 to 16.0.0 be a big upgrade? It could be a single function changing it's signature or a whole different API.

It also encourages version bloat. Previously a 2.8.0 upgrade to 3.0.0 meant a big change. Now you just see versions 1 through 40 with very little usage of minor version numbers.

I don't know of a good way to fix it. At least semver is something everyone more or less agrees on.


I don't get the issue with large major versions. That just means that instead of holding a change for a looong time until it was "acceptable" to create a new breaking version, they released the changes asap for developers to take advantage of, and clearly communicated each time that there was something you'd need to take care of.

For developers who would wait that long anyways, there's no difference between moving from 2.x.x to 40.x.x over 2 year's time than 2.x.x to 3.x.x over the same time period. (Edit to add: except that if you more regularly upgraded versions between 2.x.x and 40.x.x, you don't wind up with needing to make a huge amount of changes in one go from 2.x.x to 3.x.x.)


It's a big question of "am I comfortable updating" when you see a major version increase. If a project is doing a major version update every month, in my mind that's probably not a well designed project, the developers really didn't think their API through, and it's a huge risk to my business trying to integrate that into my product. Every month I'm going to have potentially breaking changes that will suck down developer time.

For projects like Rails, where they optimize for developer productivity, it's good that we've only had 5 versions in 13 years. It's a stable platform to base a business from. When I see projects like React where they've released 16 major versions in 5 years, as a small developer that is a huge risk to take. There should not be that many breaking changes if the project was well thought out from the start, and I can't spend that much time fixing everything that breaks every few months. And if the project was so poorly conceived that it needs 16 major versions in 5 years, there's probably a lot of broken stuff in version 15 that I need to update to version 16 to fix, so I can't just stick with the older version.

On the other hand, I feel Python waited way too long between 2.0 and 3.0 and introduced too many changes all at once, leading to the headache of so many people still using 2.x more than 10 years after the release of 3.0.


> When I see projects like React where they've released 16 major versions in 5 years

They haven't really, they just jumped from 0.14 to v15, because people were referring to 0.x releases as "version x". v15 is basically v1.x and v16 is v2.x


But that's the benefit of semver (or romver like other people have mentioned). It takes the guesswork out of it.

Also can we talk about the silly trend of publicly released software used by thousands or millions of people on production apps and they haven't made it to version 1.0 yet? 0.x is for unreleased software. If you've released it to the public, it's 1.x. As a small developer that's another huge red flag... functionally there is no difference between 0.15 and 15.0. They both indicate poorly conceived projects where the version numbers mean absolutely nothing and there's no guarantee that it will work tomorrow.

And I say that as someone who uses React, I'm not just hating on it from the outside. I'm seriously terrified by the version numbers.


The biggest problem is with an ecosystem of libraries. I avoid libraries that depend on Google's Guava because one uses 21, another 24, another 23.5. There will maybe be a year between 21 and 25. All binary incompatible.

So you either need to stick to 1 version for the whole ecosystem or use tricks to give each library its own exact version of Guava it expects.


That seems like a problem with Java or Maven: it doesn't allow multiple major versions of a library to co-exist within a project. NPM and I believe Cargo both allow this. There's cases where this isn't great (where multiple libraries each need to communicate with each other using a data structure implemented by another library, but they each depend on a different version of the data structure library, or where the library you have multiple versions of is gigantic; this might be a good argument for using many small and focused libraries instead of fewer big ones), but overall allowing multiple versions in one project can save a lot of needless headaches.


The NPM approach does not have that specific downside indeed, but like you said. What are you going to do when both return a different version of a Type to the "main" code?

It's not a Java/Maven problem. Rubygems, Pip and Cabal have the same restriction, and that's only the ones I know that have it.


openssl kind of works like that -- but I believe they are moving to semver


SemVer is great, I think people try to read too much into it. It's there so that your automatic dependency system wont pull a breaking change without you first checking if it actually did break anything. I think waiting for semver major releases is an anti-pattern. I rather have a very small breaking change, rather then a bunch of breaking changes at once. So if you use SemVer, don't be afraid to do a Semver Major, avoid it if possible, but don't make it such a big deal. Ohh and don't use it for marketing, use a cool name instead, like NameOfProduct "ninja tiger" (instead of v9.2.2)


Nice to see this happen. I'm glad that it will get more attention and some possible improvements.

One thing I'd like to see is a specified practice for versioning projects that "wrap" another project where the "inner" project's version is significant to the user.

For example, a long time ago I wrote a Ruby gem to expose jQuery's templating library to the Rails asset pipeline. My gem had a version, but each version also had the jQuery Templates source code packaged within it, which of course was also versioned. The most useful thing I could do for the gem's version was to have it match the version of JQT that it contained, but if I needed to make a new gem release to change some of the Ruby wrapper without changing the packaged version of JQT, there was no "official" or universally obvious way to denote this. I could add an extra number on the end, but it wasn't obvious that this extra number wasn't part of JQT's version just from looking at the four-part number. I believe some package managers use the name of the system in the "wrapper version" to denote this, e.g. "1.0.0.ubuntu4" but even that is lacking because you really need two represent two different fully semantic versions. I'd be great to see a solution for this specified.


That may be more interesting than it looks like at its face, as that give a tie-in to declare dependencies on system libraries.


What are SemVers current flaws? Maybe I missed a link in the article.


Here's the largest, IMHO: none of the matchers are in the spec. It purely talks about versions themselves, not how you specify ranges. It's only really got half of the important stuff in it!


Could you elaborate on this?


“Matchers” or “ranges” (even the terminology is split up thanks to the lack of a spec!) are not defined in semver. It only defines how to version a particular software artifact, it cannot answer the question “does version 1.2.3 fit the constraint > 1.0.0?”

Does that make sense? The “> 1.0.0” part is undefined. That includes things like “is that space required there?”, and so is an interoperabilty issue.


I don't see what's ambiguous in that.

> does version 1.2.3 fit the constraint > 1.0.0?

I would answer, yes, 1.2.3 is greater than 1.0.0. I can't see how anyone would interpret it differently.


How about "does version 1.2.3-alpha.3 fit the constraint "~1.2.3"? If you're asking "what the heck does a tilde mean?" then, yep, that's one of the current problems - it has a de facto meaning because the npm semver package started using it, and now it is adopted by some (but not all) semver implementations in the wild.


I should have used "x.y.z" instead of a hard number, as it kind of obscures the point.

The concept of "this is a thing I use to specify a range of versions", as a concept, does not exist in the spec. This means "> 1.0.0", in the context of the SemVer spec, is meaningless. This means you cannot answer the question, even though, as a human, you can intuit what's meant. Computers cannot.

(Additionally, there are other questions as I mentioned above. Is ">1.0.0" allowed too, or not? Getting parsers correct is important.)


That's an easy one. Try:

Does "<=2.3.4" include "2.3.3-some.prerelease"?

Justify your answer. :)


At least with Gitflow and Githubflow workflows, it is my understanding that we change the version number in the release/feature branch.

With that understanding in mind (regardless of it matching your understanding, I mean), then clearly, "2.3.3-some.prerelease" is in fact less than "2.3.4", or it would have been named "2.3.4-some.prerelease".

Therefore, I believe it is clear that workflow must necessarily be considered to understand SemVer.


So if a user has the version constraint ">=2.3.3 <2.3.4", should they get that pre-release version?


This is covered explicitly in the SemVer specification section 11[0].

The answer depends on if there exists a released "2.3.3" version, in which case, the pre-release is not selected, because pre-release versions have less priority than release versions.

The spec provides the following example:

1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0.

[0] https://semver.org/#spec-item-11


But someone might well interpret it differently. The whole point is to define this stuff. There's no harm in writing down assumptions, even if they're ones everyone is making.


He didn’t say it was necessarily ambiguous, only that the syntax is not part of the spec, and thus someone could define “> 1.0.0” as meaning “redirect to the version 1.0.0” and it would still be SemVer currently. Although I don’t know exactly what that would mean, this choice is foolish given the current implementations of SemVer, perhaps it makes sense from a bash-user perspective.


Yes, but how did you come to that conclusion? Lexical matching, piecewise numeric matching? What's the programmatic way of determining that?

For a better example, how about version 1.9 vs 1.10? Or 1.9rc1 vs 1.9?


Your example does not follow SemVer.

If it did, however, your query would have a straightforward answer described in Section 11 of the Specification[0]

Here is their example for how to handle pre-release versions:

    1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0.
The example shows that "1.9-rc.1" is less than "1.9" and both are less than "1.10".

So while it is true that many parts of SemVer are as yet unspecified, it seems to me (from these comments), that many people do not understand the already specified parts of SemVer, currently.

[0] https://semver.org/#spec-item-11


and yet my matcher disagrees, and there is no way to determine which behavior is correct.

the "no way to determine" is the point here, not the details of the example that was chosen.


I would love an unified matchers syntax. After dealing with ivy matchers, I very much prefer the npm/bundler/cargo style matchers, but would love them to all be exactly the same.


Me too! It's my personal highest priority, so here's hoping!


can you explain that for us?


I think the key flaw is that some people just don't like incrementing the major version number! It's a simple as that.


I propose RomanticSemVer:

{marketing}v{major}.{minor}.{patch}


I second that. The problem is that SemVer is conveying an extremely limited semantic meaning. "This is incompatible with that", yes, good to know. And then?

The derogatorily named marketing number instead used to indicate major changes and important increases in functionality. Now version number are meaningless: there is no way to tell if version 16.0.0 is supposed to be any better, or provide any more functionality, or incorporate any new concept from version 1.0.0. All it says it's that it is incompatible.

Somehow I get that this is the era of "evergreen" software, continuously and incrementally developed, and marketing numbers might be a thing of the past. But sometimes you need to express the concept that something changed radically and that a lot of new things have been introduced at once.


https://github.com/jashkenas/backbone/issues/2888#issuecomme...

> So, as I like to joke — not "semantic" versioning, _romantic versioning_.

> Given a version number MAJOR.MINOR.PATCH, increment the:

> MAJOR version when you make a major new release, or significantly update and/or stabilize the API.

> MINOR version when you add minor new features.

> PATCH version when you make tiny changes, likely to go unnoticed by most.

> This allows folks, immediately upon hearing about a new release, to get a rough sense of its scope.


As I see it, one of the main goals of semver is to allow automated tooling to predict what dependencies can be updated without breaking changes. And specifially in environments where the size of dependency trees was starting to make it really painful to do this manually (to get bugfixes and security patches, which matters!)

It's no coincidence, as the OP notes, that it was invented at about the same time as bundler was being developed -- one of the first of a new generation of dependency management tools that solved other problems in managing very large dependency trees, such that "how do I update to get fixes without breakage, it's too painful to manually figure out what versions for every dependency do that" _becomes_ a salient problem, _and_ can be a tool that can address that problem in an automated fashion, if dependencies use semver.

"romantic versioning" is more or less what everyone was doing anyway/previously. Semver was intended specifically to solve a problem this kind of 'romantic versioning' created, especially with very large dependency trees.

I often find this comes up in discussions of semver. If I'm right about the semver motivations here -- perhaps, if anyone on the new semver team is listening, the semver doc should be more explicit about this motivation. Because I find it often gets misunderstood in discussions of semver. And if you don't understand this goal, you're not on the same page in evaluating semver.


The problem is that in practice patch versions can break compatibility anyway.

On multiple occasions, I have run `npm/yarn update` with only patch upgrades, and had critical bugs.

And even if authors did follow SemVer in practice, fundamentally there is virtually no "safe" change -- bug fix, performance improvement -- that is impossible to produce issues in my code (because I might be holding it wrong).

SemVer would be nice if it worked. Romantic versioning is nice because it makes no false promises.


http://www.hyrumslaw.com/

    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.
It's a nearly insurmountable task to make a change without breaking any users. Semver is an improvement over the status quo, and it at least means that when code breaks someone made a mistake.

I think the next potential step up from semver is a way for a library to run all public tests of its dependencies to look for new breakages. This is not a trivial undertaking, because tests can be slow, resource intensive, and flaky, but it's possible and incredibly freeing for a library or infrastructure developer.

I seem to recall some work in the rust ecosystem like this, but my search-foo is failing me right now.


We do that, yes. "Crater" is the name of the tool (also called "cargobomb" at one point).


In the ruby ecosystem, I think it's indisputable that both bundler and widespread use of semver resulted in huge reductions in pain of dependency management, including improvements in automatic updating to get bugfixes and security patches without breakages.

It's not perfect (not all dependencies even try to use semver, some have bugs in their semver versions), and it doesn't eliminate need for having and running automated tests after dependency updates, or eliminate all surprises or manual interventions -- but I think it's hard to argue that it hasn't been an improvement.

Now, typical "modern" (webpacker etc) Javascript projects can have, it seems, even an order of magnitude larger dependency tree than is common even in post-bundler ruby.

One would think this would make the problem being discussed here _even more painful_. I dabble in JS but am not really expert at it, but I think that's probably so.

So, if semver is not working to lessen the pain -- which is possible, although it's also possible it would be even worse without it -- why not?

I honestly think some of the answer is a lack of understanding of the solution being suggested by semver, which is why I think the semver docs should be more explicit. As I understand it, pre-yarn, JS toolchain didn't even have the equivalent of the `Gemile.lock` for managing dependencies, which to me is pretty crucial. I think there is probably a lack of understanding among some dependency maintainers on what they should be trying to do regarding backwards incompat, the definition of such, it's relation to version numbers, etc. And among dependency consumers on how this system is _meant_ to work, that it could even be _possible_ to automatically update in non-breaking ways, if your toolchain behaves well, if dependency authors behave well, and if you specify your dependency requirements accordingly.

But maybe with all of that, there is just something about JS and the way it is used -- or something about order-of-magnitude bigger dependency trees? -- that makes it much harder.

I'm not sure. You seem to be suggesting that the JS community ought to give up on any automated toolchain assistance in determining updates possible without breakage, and just resign to only updating dependencies after manual human review of changes in each one.

To me, that seems just plain infeasible when you have ~1000+ element dependency trees including many-levels of indirect depenencies you don't particularly want to know about. You're going to end up spending a huge amount of time trying to identify and apply relevant bugfixes and security patches in deeply-indirect dependencies -- or just have buggy and vulnerable software, with huge technical debt if you ever want to update to more recent versions of dependencies after a project has had it's dependency tree stagnant for so long.

On the other hand, the JS developer community seems to be willing to put up with levels of developer inconvenience that seem insane to me, and practically, many people in JS-land seem so far to have been managing their 1000+ element dependency trees entirely manually, somehow. So, I dunno. If this isn't generally seen as a problem in the JS community, then of course there would be no interest in a solution involving semver. I have trouble believing it won't be increasingly seen as a problem, in which case familiarity with solutions that have worked in other ecosystems would be important in working out solutions for JS... but who knows. (There is an npm representative on identified semver maintenance team, so I'd guess at least _some_ JS devs recognize the problem and are interested in a solution involving semver...)


I agree wholeheartedly, and yeah that would make a good FAQ entry. Thanks!


Haha, that's exactly who I was thinking of. I linked to it in a sibling comment.

I agree with him that having a digit that represents the "era" of the software that can spin on its own is useful.

My only problem is that MAJOR is still useful for API changes/breakage, even tiny ones. By adding the ROMANTIC digit, then you, the developer, don't have to be sentimental about MAJOR changes, and your users still know to read the changelog if it changes at all.

Because it's definitely true that sometimes MAJOR changes for massive reasons (e.g. 100% rewrite) and sometimes for trivial reasons, and semver doesn't distinguish them. It's hard to answer questions like "so when was the last big change to this library since my version?" just looking at semver major digits.

Of course, this is just bike-shedding given how entrenched semver has become, and I certainly prefer semver to what we had before: nothing.


I have projects where I intentionally maintain separate semver and user-facing "romver" (I like that name, I may use it moving forward) version numbers. Semver is at something like 18.x.y and Romver in development is currently at 2.z.w, and may jump "soon" to 6.z.w to match/replace an old VB6 app. For a while with 1.z.w, for simplicity to myself z was the semver MAJOR, but at this point they are almost entirely divorced.

ETA: The heavily broadcast user-facing version number of the VB6 app in its usage and marketing being a key reason for needing a "romver" at this point, because users clearly have been trained for decades to expect it.


I do the same thing, I'd assume a lot of people do. My API is at version 87.2.19, while my user-facing app (that nothing programmatically accesses) is 1.3.4.

If you maintain software that only humans interact with and does not need to be programmatically read, there is no reason to use semver.


On the flipside, I've long argued that maybe its time we deprecate/remove user-facing version numbers and there's no reason to use "romver" other than (decades of) momentum.

What does a "romver" really tell a user? "A number incremented from the last time you used this software." That's about it. It doesn't tell the user anything about what new features might have been added or bugs fixed or anything they actually care about. You can get a lot of the basic "number incremented" gut feeling simply from "Release Date", and that at least is something slightly more user concrete because they can look at a calendar and get a feel for how old a version is or how long between two versions development took, which is slightly more interesting.

Really though, "romver" itself is often just as meaningfully useful to users as "random fuzzy animal" or "random sugar snack" keywords, and those are at least more "fun". We can market software versions with other things than dotted sets of maybe sequential numbers.

It's not a battle I often win though, but it is something to think about. Numbers are great for machines, but they aren't very human, and why bother presenting numbers to people?


It's a good point. It really depends on who your users are. The "users" of my apps who would ever see the version numbers are people who need to know version numbers, and romver works fine for that. The actual end consumers of the software don't see version numbers because, like you said, it doesn't matter.

It does bother me that Apple and Google force you to have version numbers shown in their app stores and it's shown to the public. I push OTA updates for my apps quite frequently without going through the stores and I've had some users ask why the app changed without the store version number changing, and I've had users ask why the number jumped from 1.0.7 to 1.3.4 with no updates in between. "Because I didn't have to submit updates to the store for the past two minor changes but I've updated the app icon so now I have to" is difficult to explain to say the least.


This is how software was released up until the late 90s / early 2000s. It's nice for users and system administrators, but it solves a different problem than SemVer is intended to solve (API compatibility).


I agree (mostly). Here's my proposal:

https://gitlab.com/bshanks/sdver

{marketing}v{major}.{patch}

that is, there is no longer any number which distinguishes (non-breaking changes which add API) functionality from (non-breaking changes which do not).

The other main change in my proposal is a restriction of all numbers to be less than 32768, and a restriction of the length of strings. This allows compliant implementations to use fixed-size variable types.


The key value in minor, based on my understanding, is that it's unsafe for a package manager to automatically downgrade a package that was developed against a particular minor version because that would constitute removed functionality.


Are you bemoaning the lack of backward incompatible changes, or the lack of major version numbers because it feels like progress?


Some people just want a leading version number that they can spin manually.

For example, the creator of Backbone: https://github.com/jashkenas/backbone/issues/2888#issuecomme...

I say: why not both.


I'm not bemoaning anything, mate. I'm saying other people bemoan it. If you want to know why you'll have to ask them.


No problem. It remains unclear how other people not wanting to increment a major version is SemVer’s key flaw. Perhaps you could have worded that better?


If people don't want to use SemVer because they don't like incrementing the major component then that's a fundamental flaw. There's no point in it if it's not being used.

There's already a few examples of people's attitudes in sibling comments.

> Perhaps you could have worded that better?

That's a bit snide.


> That's a bit snide.

I've tried twice now to better understand what you're trying to say. In neither case have I been derogatory, mocking, or negative in intent or wording. Yet both times, you seem to have taken offense. You do you, mate, but it is immensely unhelpful to clarifying your point to someone who wanted to understand it.


I think the main issue is that there is no real semantics when incrementing the major version and no word about deprecation.

And BTW, in case of Java since 9, it's the opposite, each new version is a major version.


Here's a flaw I'd love to see fixed (2013) https://github.com/semver/semver/issues/145

Issues with delimiters in release versions on Linux


What I don't like about SemVer is that all breaking changes look exactly the same.

That's why I like to use SemVer with a small twist - a fourth number prepended to the regular three: A.B.C.D.

`B`, `C`, and `D` are the regular SemVer Major, Minor, and Patch. But `A` is "Generation" - something you increment only for fundamental changes to the codebase .i.e. 2.0.0.0 is a different generation of the product than 1.7.45.4, while 1.8.0.0 is the same generation, only with some incompatible APIs.


Rust sort of already has this, though implemented in a worse way. They use 0.[major/minor].[minor/patch]. When the library author wants to indicate that their library is stable, they go to 1.0.0 and promise that they'll never make a breaking change.

The problem with that approach is that you never know if a change is major or minor, since many projects use the 0.A.B differently. Even if projects were standardized, you're missing some information compated to A.B.C or A.B.C.D.


This is an additional thing I'd really like to gain clarity on in the spec itself. Given that ranges have no definition currently....

Oh, also

> they go to 1.0.0 and promise that they'll never make a breaking change.

That's not what 1.0 means; 2.0 can be produced with breaking changes.


I think GP means that as a cultural thing, the Rust/Cargo ecosystem has a strong aversion to breaking changes, and as a result tends not to do anything that would require a bump to 2.0.


To me this is something that a changelog should clarify. SemVer is intended to be used by machines and a change in a major version should prompt a developer to take a look at the actual changes (i.e. the changelog) and whether they are affected by those.

Adding another number for this purpose still does not tell you whether your particular use case of a library received breaking changes or not.


What's the punchline? This post doesn't detail at all what's next for SemVer. What changes are coming? What's wrong with SemVer that needs fixing?


This is an announcement that we have created a mechanism for making changes. It's now up to us (and anyone else submitting RFCs) to decide what changes are coming. I can't tell the future, only lay down those principles.

As I said in my other comment, a lack of spec for ranges is a key addition that'll be coming.


They clearly have some deficiencies in mind; an example or two would go a long way toward helping this post.


I think that getting all of the implementations on the same page is possibly even more valuable than any changes. For example, since ranges aren't in the spec, npm and Cargo treat "1.2.3" very differently. Since we can't break either ecosystem, that means that that specific aspect will have to be either implementation-defined or "choose one of these two ways," but we can at least discover where these kinds of cases are, and possibly fix them, or properly spec them, as needed. This matters a lot for interoperability. For example, Notion is a Rust project that manages JavaScript stuff; for this to work, Rust's implementation will need a "do it like npm" flag. This is really tough when it's not even spelled out anywhere what the possibilities are.

The lack of this causes more issues than real actual problems with the text as it is today, IMHO. For example, the Go code above requires a "v", which is not in the current spec, but some implementations handle it, some don't. Driving these things out is important!


Here's a hypothetical feature of all package managers:

* for all stable packages requested by the user, always upgrade to the most recent stable version that has the same major number as the one currently installed

Can npm, apt, cargo, guix, etc. safely do this presently for the set of packages that use semver?

If the answer is no, then what is the value to using semver?


If I consume library X at version 1.2.3, develop my application, and then library X is upgraded to 1.2.4, should I pull in this new version? What about if it is upgraded to 1.3.1?

The answer is maybe, but there are a few questions that need answers first:

1. The author of the library has stated that there are no backwards incompatible API changes. What is my confidence in this assertion?

2. In asserting that there are no backwards-incompatible changes, they have asserted that there is no new behavior as the result of bugs. Am I sure of that?

3. By stating my application is using v1.2.3, I am stating that I am _correctly_ using the published API. Am I? For example what if an API returned a sorted list, but that was not a guarantee of that API?

4. Are there benefits to upgrading that make the risks above worthwhile?


> 1. The author of the library has stated that there are no backwards incompatible API changes. What is my confidence in this assertion?

Prefix all of my responses with, "If semver is to be meaningful to the developer."

1. Your confidence should be so high that an ongoing pattern of mismatch between statement and reality spells the end of that software project.

2. With the same level of confidence as above, yes.

3. I need clarification. Are you saying that a major version >= 1 of a dependency you rely upon in production returns a sorted list, but then a new bugfix version now outputs an unsorted list?

4. Performance, bugfixes, the ability to use new features with impunity.

And of course my replies are idealistic and cannot work in a large number of extant development contexts.

But let me throw the question back-- given your righteous skepticism, what in the world is the point of having three different version numbers if you cannot trust any of them without further in-depth research? If a bugfix can break one of your dev's assumptions about a poorly documented interface, what meaningful hierarchy could there be among the dots of semver? Why not have a single incrementing number?

edit: clarification


> 3. I need clarification. Are you saying that a major version >= 1 of a dependency you rely upon in production returns a sorted list, but then a new bugfix version now outputs an unsorted list?

Not OP, but I believe they are saying that the API happens to return a sorted list (either for their particular inputs, or in general), but this was never a guarantee of the API (and was presumably not covered by tests), and so the package maintainers changed the implementation to no longer return a sorted list (maybe they found a faster algorithm).

I have to say that I think this is a good case for having tests that are tied to particular versions, like in this comment: https://news.ycombinator.com/item?id=19138853

Or perhaps in this specific case the user of the library will add the test (to their own codebase) that checks the output is always sorted, and it will fail to upgrade to a version where this isn't true. In general I'd be pretty happy to see people write tests for the subset of behavior they require from a library, although I appreciate that's potentially a whole lot of work, particularly in ecosystems without property-based testing.


Attempting to extend the "semver skeptic" OP's original questions: where would such information be represented in semver?

Whatever probabilities you give to it appearing in the major/minor/bugfix slot, the point is that the skeptic dev cannot safely assume that it will appear in the major slot. If it doesn't appear in the major slot, that means there is a mismatch between what the package author/maintainer considers to be backwards-compatibility and what the author assumes it is. Given the sad state of FLOSS documentation, this is a common type of mismatch where bugs hide.

That means the skeptic dev has to pin to some version that they've tested, only upgrading for a critical bug or some feature valuable enough to warrant the increased time building tests for the new version and hunting for "mismatch" bugs.

I think that skepticism characterizes a critical mass of npm usage. If that is indeed the case then those devs do not trust the minor and bugfix slots to convey meaningful backwards-compatibility information. There might as well be a single incrementing version integer with a changelog at that point.

Edit: clarification


> If the answer is no, then what is the value to using semver?

So that humans can get an approximate idea of the pain involved with any particular version upgrade. It doesn't need to be fully automated to be useful. Plus, just because it can't be fully automated now doesn't mean that it won't be in the future.


They... should be able to. Per SemVer rules you should be able to do just this. As an example, you should be able to upgrade from 2.0.0 to 2.25.99 without a single concern, because 2.0.Z is always backward-compatible bugfixes, 2.Y.Z means new features in a backward-compatible manner, and if -anything- ever breaks, then the major version would change, and you wouldn't automatically be upgraded to it.

The only time that the answer would be "no" is if the project advertising itself as using SemVer breaks the expectation, since you have to trust that the developer actually knows how to use SemVer.


Let's say someone releases 1.2.3 and then, later, we realize that there was an obscure breaking change. Is the solution to release 2.0.0 and just hope that people realize the old version was broken? Or to release 1.2.4 with a fix that makes the change non-breaking again?



How am I, that by way of misfortune and bad timing, ended up depending on the obscure "feature" added in 1.2.3, to know that upgrading to 1.2.4 will actually break my thing?


You can't. The situation isn't ideal, of course, but stuff happens. Nobody's perfect.


That's certainly true. But I'd rather have less helpful information than wrong information in a version number.

For example-- a single incrementing version number ostensibly conveys less meaning than semver. If I release 43 after 42, there's no way for the user to tell if the API has broken in between. They must read a changelog. Most importantly, the only way for author/user to make an incorrect assumption about that number is if 43 is _exactly_ the same as 42.

With semver we suddenly must care deeply whether the author got the user's expectations correct with regard to three seemingly separate categories of information. (And this with the current state of FLOSS documentation-- not sure if proprietary is any better in this regard.) What common practice tells me is that very few projects trust an author's assertion to the minimal level needed to automatically upgrade on a non-breaking bugfix release.

Here, we're even discussing the situation where there isn't even an author/user misunderstanding of the API. If the author screws up a release, that wrong information is encoded directly into the semantic version. That's objectively wrong information, where there would have been no wrong information at all if one had only used a single incrementing integer.

I'm not really arguing for a single incrementing integer, that's just an example to explain my point. What I would rather have is version information that conveys meaning reliably within the current universe of developer distrust of packages. I think that means something restrictive and modest like what "patch" does for patching-- e.g., mechanically do a job, warn if something looked slightly out of place, and bail otherwise.

I guess another way to say it is that I do not want to rely upon an author's opinion of their code at all. Instead I want a tool to tell me what it can, when it can, and then goad me into reading the code when it can't tell me any more.

Edit: clarification


What you’re describing does exist, it’s integration and end-to-end testing. It requires no input from the library developers and will always be safe from their mistakes.

SemVer is a simple no frills tool to provide meaningful information in version numbers, but it is not a replacement for proper testing. If SemVer misleading you is that big a deal to your projects, you would already have that testing in place, making any SemVer violations a non-issue for your project.

But in that case SemVer would still be useful because when making any minor-version package updates, you would be able to expect that all your tests would continue to pass, versus upgrading a package to a new major version, where you’d definitely expect to have to refactor.


With cargo:

    cargo add foo
This command will install the current version of foo, and add it to your config file. Then,

    cargo update
will update your package to the highest available version that doesn't bump the major number.


What does "safely do this" mean here?


The clause allowing 0.x releases to contain breaking changes needs to go. Also, the first release should be v1; so many projects never leave 0.x despite being depended on by millions of projects and everytime they push a new minor all hell breaks loose as people have to manually look at the changelog and decide to upgrade.


the first release should be v1

If this were done, how would v1 be any different than v0 is now? Isn't it nice that people who want to avoid all that hell breaking loose can simply not use a package that hasn't gotten to v1? Why would you want to do away with that?


I find it quite useful. I've been bitten before when a system (Carthage) explicitly decides to be 'semver except that part', for some reason.

All projects take a little while to stabilize, and this simply formalizes (the numbering of) that process. Any project can trivially opt out of it by simply starting with "1", as you suggest.


The link to https://github.com/orgs/semver/teams/maintainers/members 404s for me -- I'm guessing due to permissions?


I left a comment below about the problems; the link apparently 404s because of GitHub, see the footnote I added next to the link. Sorry about that! Not under my control :/


Since for SemVer "to work, you first need to declare a public API" maybe tools should require a file VERSIONING (or SEMVER?) containing something like "<PRODUCT> follows SemVer 2.0.0.0 <https://semver.org/spec/v2.0.0.html>. The public API of <PRODUCT> consists of ... We pledge to change the major version when we make incompatible changes to this public API. ... We consider the following as incompatible changes to the public API: ... We do not consider the following as incompatible changes to the public API: ..."


I was curious about the empirical evidence supporting the notion that SemVer is "better" than what came before it. Does anyone have any pointers.


ComVer is what's next.

https://github.com/staltz/comver


I quite like that scheme. It saves all the debate about whether to increment the 2nd or 3rd number.

The downside to such a scheme in terms of production releases (rather than libraries), is I have where 2.3 might need a fix for production, while 2.4 is being worked on. So the natural release number is 2.3.1. With ComVer you are forced to make your production fix 2.4 and rename the WIP version 2.5 which can be a pain depending on your JIRA/CI setup etc.


I love how elm enforces semver versioning.

When you write a library, it will detect API changes and upgrade the version accordingly.


Very exciting to finally see semver get some traction in one way or another! A very welcome decision and announcement, also supported by Haacked's post[1].

[1]: https://haacked.com/archive/2019/02/11/semver-collective/

I have been quite active around the semver repo for a few years after 2.0.0 was released initially and participated in a lot of significant discussions that, to this day, still remain unresolved without either affinity to them or a stated rejection. After many years of seeing the same questions and suggestions pop up with no progress in sight, I just slowly lost hope and once issue updates started piling in to close issues at the initiative of @jwdonahue (thanks, by the way) without advancing any discussion or subject, I just unsubscribed. I am really exited about the things that are to come and will gladly try to participate in discussions to the best of my abilities.

Currently, there are quite a few things that SemVer needs to decide on (not necessarily implement), but imo the important points are:

1. How are versions selected? You mentioned this in the thread already, but this point is hugely lackluster about the current situation. See issue #205 for the biggest discussion to date and many cross-references.

1.1 Build metadata, especially in the context of version selectors, kind of "just exist". They serve mostly as a "catch all" for various things you might want to annotate a version, but there are no rules or use cases for them.

2. (Automatic) development/post-release builds come up frequently when trying to version snapshot builds, however Semver does not have a solution for this. See issue #200.

3. Clarify the "limbo" that are 0.x.y releases. To me, the spec is clear in its semantics for this, but the wording can be misleading to some. Basically, in the current situation semver just does not apply to 0.x releases at all (for the purpose of providing a "stable API") and this should be clearly communicated, especially for version selection.

4. Put more emphasis on what a "Public API" is, either in the spec itself or the FAQ. Many people have problems with this abstract concept and ask about it on the issue tracker.

5. Another topic that comes up frequently is demand for a "generation" or otherwise comparable fourth number to declare a kind of reboot for versioning. Some package managers have this (pacman, for example) and it is sparingly used, but there is no stance on whether or not SemVer would be interested in such an optional component or whether this is to be handled elsewhere. See issue #213.


My biggest problem with SemVer is that determining what is and isn't a breaking change can be very hard in modern languages. Worse yet, if the library is invoked from a different language, some things that weren't breaking can become so (and vice versa). And sometimes, said different language is actually a new version of the same language.

For example, in C#, is adding a new overload to a sealed class a breaking change? The general consensus is "no", but doing that can easily break existing code by making it ambiguous (if there was only one implicit conversion or upcast to consider before, but now there are two).

But what about adding a new method, with a completely different name - surely that's not breaking (to remind, our class is sealed, so there's no descendants; and in C#, methods don't override base class unless explicitly requested)? It wasn't - until C# 3.5 added extension methods. Now, every time you add a new method to your class, you are potentially breaking someone else's extension method with the same signature - and worse yet, it will compile silently, and just call a different thing.

Okay, what about adding a new property? Can't be overloaded, and there's no extension properties (yet; they might still be added). There's still a problem: when calling an overloaded method with a lambda - also added in C# 3.5 - as an argument, one of the criteria for choosing an overload is that the body of the lambda is valid after substituting the types for that particular overload - this includes checking members. For example, suppose we have:

   class Foo {}

   class Bar {
      public void Frob() {}
   }

   class Program {
      static void Qux(Action<Foo> a) {}
      static void Qux(Action<Bar> a) {}

      static void Main() {
          Qux(x => x.Frob());        
      }
   }
This compiles, because only Bar has Frob, and thus Qux(Action<Bar>) is the correct overload. But if Foo also gets some Frob at some future point, this code breaks - the overload is ambiguous now. In more complicated cases with other types involved, this can also result in a silent choice of a different overload.

But, on the other hand, if you were to use the same class from F#, it wouldn't be a problem, because it simply doesn't try to do this sort of type inference on top of overloading. So for F#, this isn't breaking at all... today. It might be tomorrow, though, as it was with the extension method example above (F# didn't add support for those for a while after C# did, so it wasn't broken by that initially).

I guess the TL;DR is that "breaking API change" is an oversimplification of the real thing - in reality, what's breaking depends on the entire toolchain and the way the library is used by the client code, not just the API itself. So our criteria for a major semver increase simply aren't well-defined. And that needs to be done - separately for various languages due to different semantics - before any given version number can be more than just one person's guess.


SemVer itself as a spec expects projects to define exactly what compatibility means, it's true. We went through all of these kinds of cases with Rust, for example, and defined what kinds of changes increase what kinds of version numbers. Given that it's based on language features, it gives a good basis for the ecosystem, but when you don't have that basis, yeah it's much much tougher.


Is there any kind of centralized place where "what does semver breaking change means for language X" can be aggregated? I think it can be useful, at least until this is something that language designers routinely include in specs.

But there's remains the question of what happens if a library is written in one language, but used from many others with different features that can make non-breaking changes breaking, and vice versa. One could say that the owner of the library is generally not responsible for breaking other languages, but in many cases the library is cross-language by design.


Not currently. Rust has done this, but I'm not aware of other languages having done so.


I have a soft spot for Semitic Versioning [0] myself.

I don't know. Great artists know when to stop. I can't help but feel that this discussion crossed that line some time ago.

[0] https://github.com/ajalt/fuckitpy#versioning




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: