
What's next for SemVer - steveklabnik
https://words.steveklabnik.com/what-s-next-for-semver
======
tolmasky
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.

~~~
kakwa_
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.

~~~
titeipa
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](https://semver.org/#spec-item-8)

~~~
mrgriffin
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).

------
spenczar5
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](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.

~~~
steveklabnik
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!

~~~
spenczar5
The language implements semver directly in the Go language source:
[https://github.com/golang/go/blob/47d5a4433513c3870a9994b50e...](https://github.com/golang/go/blob/47d5a4433513c3870a9994b50ed4fa2af628c742/src/cmd/go/internal/semver/semver.go)

This is used to resolve module loading, according to this logic:
[https://github.com/golang/go/blob/aab0b704d88b7dad10c767ad94...](https://github.com/golang/go/blob/aab0b704d88b7dad10c767ad941619d59887203c/src/cmd/go/internal/modload/query.go#L17-L35)

~~~
steveklabnik
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.

~~~
puddums
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](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](https://github.com/semver/semver/blob/master/semver.md)

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

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

~~~
steveklabnik
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.

~~~
puddums
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...

------
ruuda
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](https://www.youtube.com/watch?v=oyLBGkS5ICk)

~~~
badsectoracula
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.

~~~
slowmovintarget
SemVer bit starts at
[https://youtu.be/oyLBGkS5ICk?t=825](https://youtu.be/oyLBGkS5ICk?t=825)

Have to agree, excellent talk.

------
munificent
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.)

~~~
slobotron
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.

~~~
DougBTX
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.

------
pjtr
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"](https://semver.org/spec/v2.0.0.html")
}

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

{ "version"="1.0.0.0", "version_scheme":
"[https://altver.example.org/winver/1.0.0.0.html"](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").

------
jt2190
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?

~~~
hluska
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!

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

~~~
dagenix
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.

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

~~~
dagenix
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.

------
ccleve
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".

~~~
steveklabnik
The SemVer spec does have pre-release versions [https://semver.org/#spec-
item-9](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?

~~~
ivan_gammel
“-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).

------
ilovecaching
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.

~~~
steveklabnik
[https://semver.org/#how-do-i-know-when-to-
release-100](https://semver.org/#how-do-i-know-when-to-release-100)

------
trias
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.

~~~
steveklabnik
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.

~~~
slowmovintarget
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](https://youtu.be/oyLBGkS5ICk?t=1189)

------
mitchtbaum
I would like to work similarly to how WHATWG makes HTML a "living standard",
with rolling releases tracked by [http://caniuse.com/](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](https://gist.github.com/dominictarr/7c3319a0b0bad22bde2c)),
[Towards "annex", a Fact Based Dependency
System]([https://speakerdeck.com/markhibberd/towards-annex-a-fact-
bas...](https://speakerdeck.com/markhibberd/towards-annex-a-fact-based-
dependency-system)), ["Towards "annex", a Fact Based Dependency System" by
Mark
Hibberd]([https://www.youtube.com/watch?v=JjYAnBhF2JU](https://www.youtube.com/watch?v=JjYAnBhF2JU))

~~~
spenczar5
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.

~~~
mitchtbaum
> 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

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

~~~
mitchtbaum
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_as_data_\(disambiguation\))

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

\- [https://en.wikipedia.org/wiki/Round-
trip_engineering](https://en.wikipedia.org/wiki/Round-trip_engineering)

\- [https://en.wikipedia.org/wiki/Model-
driven_engineering](https://en.wikipedia.org/wiki/Model-driven_engineering)

further, I believe:

> code is unrefactored config

> config is immature content

> content is unrefined code

------
nathanaldensr
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...](https://docs.microsoft.com/en-
us/azure/devops/artifacts/quickstarts/universal-packages?view=azure-
devops&tabs=azuredevops)

~~~
thesunny
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`

~~~
nathanaldensr
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.

------
Navarr
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.

~~~
steveklabnik
[https://github.com/semver/semver/issues/495](https://github.com/semver/semver/issues/495)

------
brudgers
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](https://en.wiktionary.org/wiki/MINASWAN)

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

~~~
joemi
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.

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

~~~
pryce
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?

~~~
brudgers
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.

------
transfire
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".

~~~
zozbot123
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?

~~~
dtech
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](https://github.com/etorreborre/specs2/releases)

------
z3t4
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)

------
Perceptes
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.

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

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

~~~
steveklabnik
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!

~~~
jonahx
Could you elaborate on this?

~~~
steveklabnik
“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.

~~~
jolmg
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.

~~~
munificent
That's an easy one. Try:

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

Justify your answer. :)

~~~
merlincorey
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.

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

~~~
merlincorey
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](https://semver.org/#spec-item-11)

------
maltalex
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.

~~~
Amelorate
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.

~~~
steveklabnik
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.

~~~
lmkg
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.

------
garmaine
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?

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

~~~
steveklabnik
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!

------
jancsika
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?

~~~
Skunkleton
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?

~~~
jancsika
> 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

~~~
mrgriffin
> 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](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.

~~~
jancsika
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

------
jaydenseric
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.

~~~
jessaustin
_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?

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

~~~
steveklabnik
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 :/

------
1wd
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>](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: ..."

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

------
k__
ComVer is what's next.

[https://github.com/staltz/comver](https://github.com/staltz/comver)

~~~
quickthrower2
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.

------
kuon
I love how elm enforces semver versioning.

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

------
FichteFoll
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/](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.

------
int_19h
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.

~~~
steveklabnik
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.

~~~
int_19h
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.

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

------
sifoobar
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](https://github.com/ajalt/fuckitpy#versioning)

