What's next for SemVer
words.steveklabnik.com
words.steveklabnik.com
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.
It's not a new version of the same software -- it's new software.
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.
And for a breaking "update" release you' go from 1.4.6.0 to 1.5.0.0?
Just change the name.
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.
- automated dependency management (“can I safely update to this version?”)
- marketing (“do I care about this new version?”)
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.
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.
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.
> 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.
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...
> 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?
That’s my point: 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.
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...
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.
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.
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.
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 --versionDo we really have a real problem here deserving of all the costs that come with more change?
People deserve to be reminded that the entire thing is silly ego. Every single time.
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.
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.
[1] https://pvp.haskell.org [2] https://pvp.haskell.org/faq/#semver
> 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.
This is used to resolve module loading, according to this logic: https://github.com/golang/go/blob/aab0b704d88b7dad10c767ad94...
What would you have them do differently to be described as semver compliant without the -"ish"?
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.
In any event, it is exciting to see more energy going into the semver spec!
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...
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.
Again, happy to learn otherwise...
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.
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?
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.
Have to agree, excellent talk.
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.)
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.
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.
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").
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?
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.
If you can figure this out, it would be an absolutely amazing feat. Best of luck!
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
(edit: change video link to start right at the ranty part about Semantic Versioning)
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".
But, I guess you're saying that certain kinds of names in the pre-release space should have defined semantics?
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.
https://xenforo.com/xf2-docs/dev/add-on-structure/#recommend...
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.
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.
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
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.
That's more for HTTP communication between services but I wonder if you could build something similar for libraries.
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)
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
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
https://developer.mozilla.org/en-US/docs/Web/CSS/@supports
https://developer.mozilla.org/en-US/docs/Web/CSS/@media/hove...
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...
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`
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.
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.
[0] https://en.wiktionary.org/wiki/MINASWAN
[1] A breaking change looks like this, https://itnext.io/a-tiny-disastrous-ecmascript-change-fadc05...
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?
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.
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.
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.
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.
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.
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.)
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.
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
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.
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.
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.
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.
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.
> 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.
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.)
Does "<=2.3.4" include "2.3.3-some.prerelease"?
Justify your answer. :)
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.
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.
the "no way to determine" is the point here, not the details of the example that was chosen.
For a better example, how about version 1.9 vs 1.10? Or 1.9rc1 vs 1.9?
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.
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.
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.
For example, the creator of Backbone: https://github.com/jashkenas/backbone/issues/2888#issuecomme...
I say: why not both.
{marketing}v{major}.{minor}.{patch}
> 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.
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.
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.
If you maintain software that only humans interact with and does not need to be programmatically read, there is no reason to use semver.
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 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.
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.
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.
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...)
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.
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 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.
And BTW, in case of Java since 9, it's the opposite, each new version is a major version.
Issues with delimiters in release versions on Linux
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.
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.
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.
Adding another number for this purpose still does not tell you whether your particular use case of a library received breaking changes or not.
As I said in my other comment, a lack of spec for ranges is a key addition that'll be coming.
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!
* 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?
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.
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.
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
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.
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?
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
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.
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
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.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?
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 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.
When you write a library, it will detect API changes and upgrade the version accordingly.
[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.
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.
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.
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.