Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Problems with the Magic Zero #221

Closed
isaacs opened this issue Sep 4, 2014 · 52 comments
Closed

Problems with the Magic Zero #221

isaacs opened this issue Sep 4, 2014 · 52 comments

Comments

@isaacs
Copy link
Contributor

isaacs commented Sep 4, 2014

The "anything may change at any time" nature of a leading zero major version is somewhat disruptive for authors who may feel intimidated at the perceived commitment involved in a 1.0.0 release.

We may all have see The Light, and realized that the number 1 is in fact a perfectly fine place to start counting our releases, and casually increment the major version with every breaking change if that change provides some value. But many are timid, and talking them out of their timidity is very difficult.

Yet, the language in section 4 makes it very difficult to rely on pre-1.0 code. I believe that this increases, rather than decreases, the trepidation at marking something 1.0. It would be better with a slight increase in commitment; a stepping stone from the wild world of 0.0.4 up to the public scrutiny of 1.2.3.

This would also allow project authors to communicate non-breaking additive changes in the 0.x.y version families. In practice, many seem to treat the transition from 0.9.2 to 0.9.3 as a non-breaking change, whereas they'd treat 0.9.2 to 0.10.0 as potentially introducing breaking changes. (Node, for example, operates this way, though that is a pretty egregious example of Sentimental Versioning.)

In effect, the left-most non-zero version number is the "effective major version", and incrementing it indicates a breaking change. The next place is the "effective minor version", and the third is the "effective patch version". This allows authors to more gradually shift from "anything goes" to "grownup".

The ^ operator in https://github.com/isaacs/node-semver used to operate this way, until some users requested that it instead follow the SemVer specification. However, shifting to ^0.x.y meaning "exactly equal to 0.x.y" has proven very problematic in practice.

I haven't wordsmithed this very much, but this is a first-pass at some language we could use to express this. If others on this repo feel it's worthwhile, I'd be happy to send a proper pull request.

diff --git a/semver.md b/semver.md
index 0c060db..8dfdc06 100644
--- a/semver.md
+++ b/semver.md
@@ -68,8 +68,14 @@ Each element MUST increase numerically. For instance: 1.9.0 -> 1.10.0 -> 1.11.0.
 1. Once a versioned package has been released, the contents of that version
 MUST NOT be modified. Any modifications MUST be released as a new version.

-1. Major version zero (0.y.z) is for initial development. Anything may change
-at any time. The public API should not be considered stable.
+1. Major version zero and minor version zero (0.0.z) is for initial
+development. Anything may change at any time. The public API should
+not be considered stable.
+
+1. Major version zero and minor version greater than zero (0.y.z) is
+for beta development. Additive changes may be introduced by
+incrementing the patch version. Breaking changes may be introduced by
+incrementing the minor version.

 1. Version 1.0.0 defines the public API. The way in which the version number
 is incremented after this release is dependent on this public API and how it

cc @haacked @mojombo @othiym23 @domenic @ceejbot

@FichteFoll
Copy link

You should consider that the FAQ suggest to use 0.1.0 as first development release.

(this was edited)

@haacked
Copy link
Contributor

haacked commented Sep 4, 2014

It basically makes 0.1.0 equivalent to 1.0.0 then, right? Will this cause people to then face 0.1.* with trepidation and keep versions at 0.0.*?

I wonder if it would be better to say this is a recommendation for 0.*.* releases as a means of communicating intent, but be aware that as it's still pre-release software, the versioning scheme could be violated.

By that I mean the change from 0.1.0 to 0.1.1 is intended to be additive, but because of bugs in the pre-release code, there's still the risk of it being a breaking change.

Of course the same is true with released software. The move from 1.0.0 to 1.1.0 could inadvertently introduce a breaking change. The difference is that when you remove the pre-release label, you're communicating that you did some amount of due diligence testing. Whereas with 0.* releases, you might not have done any testing to verify you're compliant with the versioning claims.

@ceejbot
Copy link

ceejbot commented Sep 4, 2014

I love this suggestion very much. I think it accurately captures the distinction in my mind between 0.0.z and 0.x.y version patterns: alpha vs beta. It also describes the real-world behavior I've seen developers use when incrementing version numbers. The strictness increases, and the contract is fully in force once the major number is > 0.

@othiym23
Copy link

othiym23 commented Sep 4, 2014

I am also in favor of this change, for reasons specific to my understanding of conventional usage within the Node community (I have no numbers to back any of this up, hence the cagey phrasing). There are several large ecosystems (most notably grunt's) where versions are coupled between plugins and "master" packages (what are called peerDependencies within Node), and the ecosystem as a whole is not yet at version 1.0.

Developers within these communities are using the operators provided by node-semver, ~ and ^, to say "I want nonbreaking changes to the master module on which this module depends". With ~ this isn't a problem, as its intended interpretation is "reasonably close". With ^, the intended interpretation is "compatible", and upon switching the meaning of ^ to be compliant with semver 2.0, we have suddenly rendered any plugins that are using specifiers like ^0.4.2 uninstallable – we have introduced a form of dependency hell where the plugin and the master module can no longer satisfy the version criteria for peer dependencies.

The Node world is particularly rife with 0.x.y-versioned packages, and while the current text (and @haacked's reasoning above) is a reasonable normative interpretation of semver, bringing the community's understanding into convergence with the standard is particularly difficult given the community's size and the inertia already accumulated by current conventional practice. I favor the approach suggested by @isaacs purely out of pragmatism – it matches what I see as the most common practice in the community I support.

@haacked
Copy link
Contributor

haacked commented Sep 4, 2014

it matches what I see as the most common practice in the community I support.

That's interesting and is in line with the philosophy of SemVer. Do you see this practiced in other communities as well?

I'd really like to hear from @mojombo on this as I know he's had strong feels. I still think making it a recommendation rather than a MUST might alleviate that. I'm not sure.

@isaacs
Copy link
Contributor Author

isaacs commented Sep 5, 2014

Do you see this practiced in other communities as well?

I think @othiym23 @ceejbot and I are all pretty full-time on Node and npm these days. Can't speak for other groups, but as npm becomes widely adopted for frontend stuff, we do see a lot of Java and Ruby folks picking it up.

It basically makes 0.1.0 equivalent to 1.0.0 then, right?

Not quite. 1.0.0 says "bugfixes that don't change the API increment the patch, and additive changes increment the minor, and breaking changes increment the major." 0.1.0 says "bugfixes and additive changes increment the patch, and breaking changes increment the minor (or major)."

So, it is a smaller commitment, since additive changes can come along with bugfixes, without having to differentiate between them.

Will this cause people to then face 0.1.* with trepidation and keep versions at 0.0.*?

Not in my experience. It's an intermediate step that "feels" smaller.

@domenic
Copy link

domenic commented Sep 5, 2014

I am against this suggestion, because I think it stems simply from a poor quality of implementation issue in npm that has persisted until recently. That is, I think it does not reflect ipso facto author sentiment, but instead a post facto rationalization in favor of npm's formerly-broken implementation and the ecosystem that grew up around it.

In contrast, from talking to @wycats and his work on the greenfield Rust/Cargo ecosystem, the differentiation that pre-1.0.0 provides seems to have proven useful and to have been accepted in that ecosystem as a good dividing line between "very early development; dependencies should be pinned exactly" and "stable enough to start automatically getting features and bug fixes." Notably the latter category can benefit from deduping, whereas the former demands separate on-disk instances for each instance of a package in the abstract dependency tree. Dividing these clearly between pre-.1.0.0 and 1.0.0 onward is very helpful, and moving that line back to 0.1.0 does not seem like a good idea.

@rlidwka
Copy link

rlidwka commented Sep 5, 2014

The majority of npm users doesn't care about semver. So you suggest to fix this by changing semver to fit existing practices... I suppose next will be breaking post-1.x.x minors, because we have tons of those.

Please, just stop that already. Semver is great as it is. But it can't be used everywhere. It is meant for production-quality libraries with long iterations (like request for example, or express - but there is a talk to change that). But most npm packages are the exact opposite of this.

So, my suggestions are:

  1. Make semver opt-in instead of imposing a standard that people are too afraid to follow (jade reached 0.42.x in a few years before it released 1.0.0!).
  2. Revert Consider ^ over ~ for default --save option npm/npm#4587, changing default version operator to ~. Let people override that if they know what they are doing.
  3. Introduce "we can safely depend on" field in package.json as described in Add property to package.json to change default dependency scheme for --save npm/npm#6075. Add "next surely compatible version" or something like that (if a package says "we break minors", it doesn't make sense to depend with ^).

Let people use versioning system they want to use instead of trying to squeeze semver everywhere. You forcing people to use it, they fail to do so, and they blame the spec (1, 2, 3), while in reality it's npm at fault here.

cc: @jashkenas @balupton @jonathanong @mikesherov @dmethvin

@isaacs
Copy link
Contributor Author

isaacs commented Sep 5, 2014

To be completely honest, my first choice would be to drop the magic zero entirely.

SemVer should start at 0.0.0. The first breaking change should be 1.0.0. If the first "production ready" release is 10.22.0, then so what? Integers are cheap and plentiful.

A period of "anything goes" versioning encourages oversized modules and poorly thought-out designs that should usually be abandoned rather than refactored. It is exactly the reason why semver is a good idea in the first place.

I realize that this is a minority opinion. But that's why npm init starts at 1.0.0 by default now. 0.x versions are too hard to reason about, and encourage bad antisocial practices.

@isaacs
Copy link
Contributor Author

isaacs commented Sep 5, 2014

Also, we already have "anything goes" versions with prerelease tags, which are more orderly anyway.

The next version of node-semver will not let prereleases match on version ranges, so it's just as useful as 0.x is today.

@haacked
Copy link
Contributor

haacked commented Sep 5, 2014

SemVer should start at 0.0.0. The first breaking change should be 1.0.0. If the first "production ready" release is 10.22.0, then so what? Integers are cheap and plentiful.

Not to diverge too much from the discussion, but I wanted to understand how NPM treats 0.* packages.

For example, by default, NuGet only displays stable packages. The non-risk-adverse user can opt into pre-release packages by specifying the -Pre flag. NuGet only uses the presence of the pre-release label to indicate the software is pre-release. It treates 0.* packages as if they were stable and includes them by default (rather than only including them when the -pre flag is in use).

I'd argue that this is a bug in NuGet.

I think it should treat 0.* packages as pre-release. Via the power of defaults, the fact that they're hidden by default would put pressure on authors to get to 1.0.0 and start following SemVer and not stay in 0.* "anything goes" land forever.

Does NPM work in this way? The reason I ask is another option that accomplishes much of what you propose is to allow but hide 0.* packages. This would compel package authors not to stay in 0.* forever as there's a very good reason to get to 1.0.0 - broader distribution.

@haacked
Copy link
Contributor

haacked commented Sep 5, 2014

/cc @jeffhandley for that NuGet bug ^^^. 😄

@domenic
Copy link

domenic commented Sep 5, 2014

I really like the idea of hiding 0.* packages. I don't think it'll work well in the Node ecosystem though---some large-ish percentage of packages would disappear from the npm website.

@balupton
Copy link

balupton commented Sep 5, 2014

I don't really know why I was invited to comment on this. My personal experiences with things like this, is that the public opinion doesn't matter as it doesn't win against conventions, which is the entire point of conventions, which is why I dislike conventions.

I think that the way npm handles things currently is fine. I think complexity is evil.

Semver should be advisory, not enforced, for it cannot be enforced, without a never-ending spiral of complexity, such as this.

Yes. Npm has taken the stance, that npm is pro-semver, and you should follow it (not must follow it). However, changes like this, or the proposed changes in npm, make this silly, as it is we are pro-semver, except in the cases where we accept people don't follow it, so therefore we'll add an exception for that in the semver standard, so that hey, we can be pro-semver again while facing the reality that semver is an idealistic standard that can only be advised and not enforced like we want.

I'd like for a liberal and caring npm (remember when npm's tagline was "npm loves you"), recently this semver debacle is more like a totalitarian npm, where npm controls you.

I really like the idea of hiding 0.* packages. I don't think it'll work well in the Node ecosystem though---some large-ish percentage of packages would disappear from the npm website.

As one who follows the current semver spec, I have published many modules where the initial or a subsequent 0.x.y version was fine and never needed to publish more.

Hiding the 0.x.y releases, means I gotta do unnecessary bumps for following the current semver spec. This also means current package consumers need to update their version ranges too.

Adding this change, also invalidates the integrity of those packages where the 0 versions where fine. For the few people who hadn't caught on to the semver standard yet.

If people want a playground for breaking things, use the git repo.

@rlidwka
Copy link

rlidwka commented Sep 5, 2014

I don't really know why I was invited to comment on this.

Oh, sorry for spamming, I just mentioned people whose posts I was referring to earlier. I'll try to be quieter in the future. :)

@balupton
Copy link

balupton commented Sep 5, 2014

Oh, sorry for spamming, I just mentioned people whose posts I was referring to earlier. I'll try to be quieter in the future. :)

No worries at all, appreciate it even if it wasn't clear why I was invited. Having a question with the cc, or a why I was cc'd, would be useful just to reduce the confusion of such a notification. Hopefully I'm on the right track with my ramblings above.

@FichteFoll
Copy link

@balupton

I really like the idea of hiding 0.* packages. I don't think it'll work well in the Node ecosystem though---some large-ish percentage of packages would disappear from the npm website.

As one who follows the current semver spec, I have published many modules where the initial or a subsequent 0.x.y version was fine and never needed to publish more.

Hiding the 0.x.y releases, means I gotta do unnecessary bumps for following the current semver spec. This also means current package consumers need to update their version ranges too.

First, I really like the idea of hiding 0.* packages too. It gives the author a reason to finalize his product/package so that it can be consumed by the large public. Of course, this has to be treated differently by each package manager because they are all different in a way, but the general idea is good.

Second, you're not doing "unnecessary bumps". If your software is working fine and people are using it in production or anything, you should consider switching to 1.0.0 so that those who depend on it can trust on the conventions that a semantic version follows. By communicating that you're on a major >0 you tell developers that depend on your library that you do not intend to make large changes to the library/API in the same pace as you might have done <1. They can adjust their version range to ~1 (all versions with major 1) and be sure to receive non-breaking updates while this is not the case when <1. Semver is fine with that regard, it's just that people are too hesitant to do the 1.0.0 move and hiding 0.* packages helps with that decision.

@rlidwka

jade reached 0.42.x in a few years before it released 1.0.0!

Why would that be a problem with semver? Atom is already at 0.124.0 and nobody considers it to be out of the initial development phase since they are changing their stuff rapidly (e.g. switching to React recently).
1.0.0 should be used when you think you reached a point where the API (or something similar; that's a topic of a different issue) that you want to – or your clients want you to – continue supporting.

@balupton
Copy link

balupton commented Sep 5, 2014

If your software is working fine and people are using it in production or anything, you should consider switching to 1.0.0 so that those who depend on it can trust on the conventions that a semantic version follows.

That's a lot of notions all based on the premise that 0. means something different than 1+, which for a lot of people, it doesn't, nor does the current semver spec indicate this. This is a new convention that is being proposed here.

By communicating that you're on a major >0 you tell developers that depend on your library that you do not intend to make large changes to the library/API in the same pace as you might have done <1. They can adjust their version range to ~1 (all versions with major 1) and be sure to receive non-breaking updates while this is not the case when <1. Semver is fine with that regard, it's just that people are too hesitant to do the 1.0.0 move and hiding 0.* packages helps with that decision.

Exactly the required changes to a perfectly functioning 0 version that follows the current spec. Unnecessary as without changing the spec, none of the above would be needed.

@domenic
Copy link

domenic commented Sep 5, 2014

That's a lot of notions all based on the premise that 0. means something different than 1+, which for a lot of people, it doesn't, nor does the current semver spec indicate this. This is a new convention that is being proposed here.

False. The current semver spec does indicate this, and it is not a new convention. semver.org:

Major version zero (0.y.z) is for initial development. Anything may change at any time. The public API should not be considered stable.

Version 1.0.0 defines the public API. The way in which the version number is incremented after this release is dependent on this public API and how it changes.

@FichteFoll
Copy link

My statements are solely based on the most recent spec of semver, 2.0.0, and maybe a bit of interpretation in case the initial wording is not 100% clear.

The core difference for clients depending on software 1.* and 0.* is that they can rely on 1.* versions to not break and thus specify a range selector. With 0.* versions this is not feasable because development between minor releases (which could potentially break and thus have to be checked manually for compatability) is too rapid and patch releases could (currently at least, according to the spec) contain breaking changes as well, so you can only select a specifc version as dependency instead of an entire version range.

Imo, the only thing that semver currently needs regarding versions prior 1.0.0 is a mention of 0.major.minor where patch and minor changes get to be grouped. In fact, you can always make a technically only patch update into a minor update because semver does not disallow it, but currently there is just a bit too much of confusion regarding pre-1 releases.

@balupton
Copy link

balupton commented Sep 5, 2014

False. The current semver spec does indicate this, and it is not a new convention. semver.org:

Wow my mistake. Sorry about that. Let me revaluate my position on this.

@othiym23
Copy link

othiym23 commented Sep 5, 2014

some large-ish percentage of packages would disappear from the npm website.

I haven't had time to do an audit of the registry, but I'm guessing it would be a majority, and perhaps even a large majority.

@haacked
Copy link
Contributor

haacked commented Sep 5, 2014

Something that just occurred to me. The problem isn't the "magic 0", but a problem of incentives. There are a group of folks who aren't ready to have their libraries subject to the constraints of SemVer and are skirting around it because it's less of a headache to do so. We have to make the benefits of SemVer outweigh that and provide incentives for embracing the constraints.

Consider this, the intent of 0.* is to indicate the software is pre-release in the same way that the pre-release label does. So if we started applying constraints on 0.*, then folks who want the "anything goes" approach will simply add a pre-release label. 0.0.1-beta to circumvent it (unless we prevent that). Or they'll increment it to 1.0.0-beta and never remove the label (Google! amirite?!). Why should the 0.* packages have a different set of constraints than a labeled pre-release version?

In order to change people's behavior in any given community, we need to give them an incentive to embrace the constraints. Hiding pre-release packages is one possible approach. Maybe that's too drastic for some communities. Other options might be to simply warn users when they install pre-release. Get them to tell the authors to supply a release version. Or show a message to package authors when the package has been pre-release too long asking for commitment (joke).

I think adding more rules increases the complexity without solving the root problem. Especially when there are easy ways to skirt around it.

@FichteFoll
Copy link

👍

@othiym23
Copy link

othiym23 commented Sep 5, 2014

@haacked

I think part of the reason @isaacs opened this issue is that we at npm have maneuvered ourselves into a bit of a tricky position with respect to our community and its behavior. We introduced the ^ operator to node-semver at the beginning of the year as a more semver-compliant equivalent of the ~ operator. In particular, ^1.2.3 does include 1.3.0, but ~1.2.3 does not. Since we made it the default about six months ago (which was probably not adequately evangelized to the Node community, which has led to a lot of additional support work), people have used it pretty widely, and have treated it more or less as equivalent to ~, including for 0.x.y versions.

About a month and a half ago, we collectively noticed that the way ^ worked was out of sync with semver 2.0, and so we decided to "fix" the operator to be more compliant with the standard. At the time, @timoxley suggested the introduction of a new operator with the fixed semantics (because the introduction and defaulting to using ^ as the prefix for saving semver ranges in npm manifests has caused significant support headaches), but my counterargument was that down that road lay an arbirtrarily long trail of semi-broken operators as we try to get this right. So we released node-semver@3 with this change and also did the arduous work of bumping the major versions of all of node-semver's dependents used by npm.

Unfortunately, our community has not really followed our lead (in part because it's very difficult to reach all of them in a consistent way, especially because we are not all of one mind, as this issue shows), and we are now at the point where, if we release npm 2.0.0 with this behavior, we are going to break several plugin-based ecosystems because of how a wide network of distributed developers have decided to implement versioning in their plugins. In particular, this will make it impossible for grunt, broccoli, and gulp users (who are not necessarily Node developers) to install certain sets of plugins that were compatible before, because some of those plugins are (peer-)dependent on grunt@^0.4.2 where others are dependent on grunt@~0.4.3.

The fundamental problem is that

  • educating the npm user community as a whole about this change and convincing them to follow the new rules
  • fixing the peer-dependency problem and getting the community using a new version of npm with the fixes in it
  • creating a consensus around the proper way to use ^ and ~ (or even getting the broader community to fully understand the difference between them – I didn't get it completely until I'd been working at npm for a while, and I had a strong motivation to understand this stuff)
  • convincing people that they "should" be promoting their packages to 1.x.y versions when they're "done"

are all boil-the-ocean problems: they require extensive evangelization efforts, and presume that we even can reach (and convince) enough of our user community to make a significant impact.

As this thread has shown, there are a number of uncompromising, hard-line opinions within the Node community about the significance and proper interpretation of semver with respect to npm (I sort of blame @domenic for getting us in this mess in the first place). I see some value in normative behavior for 0.x.y versions, but I also see strong evidence that, in our community at least, it hasn't achieved a strong enough consensus to warrant how punitive this change is turning out to be. The least painful thing we could do prior to npm 2.0.0's release would be to revert this change to how ^ operates and just accept that we lack the ability to unilaterally impose semver-conformant behavior on our community at a reasonable cost. @isaacs's proposal is an attempt to change semver to make this less of a deviation from the standard, but it's also a suggestion coming from a place of deep experience with a significant community.

/cc @rvagg, who has been known to have strong opinions about how npm deals with semver in the past

@mikesherov
Copy link

My 2 (relatively unexperienced and naive) cents are this:

At this point, the npm use case extends beyond the node community, and even if it didn't, the node community has expanded wide enough that the only imposable behavior is behavior that has far and wide consensus.

Those who remain 0.x.y do so as a (possibly unintentional) protest to semver and the default behavior of npm install --save...

While almost everyone can agree on "no breaking changes in patch releases", some folks operate on the assumption they can do minor breaking changes in minor versions. This is partially because the old default in npm was ~.

On top of that, the semantics of semver are such that major breaking changes can not be distinguished from minor breaking changes at all. Some devs find this undesirable.

On top of that, some devs want their users intentionally (vs. automatically) upgrading when they release new features.

On top of that, some developers believe that requiring a major version bump on every breaking change reduces innovation because projects tend to "save up" their breaks and do them all at once, making the threat of 1.0.0 -> 2.0.0 artificially seem less panic inducing than a bump from 1.0.0 -> 10.0.0 would.

I share several (but not all) of the "problematic" beliefs above, but I'm personally willing to forgo them. However, I whole heartedly agree on the nature of these problems: boiling the ocean. For every me who's willing to bend, there are 10 who aren't.

Therefore, in light of this, it seems that the simplest change that could be made is to return the default behavior of npm install --save to ~ and have it behave the same for both 0.x.y and 1.x.y: latest patch version of this minor.

That'll allow the grunt, gulp, and brocolli ecosystems to once again work, it'll allow authors the freedom they used to have to instrument what they consider major vs. minor, and it'll make devs consider the benefits of upgrading minor versions (which theoretically are only for new features, which perhaps devs shouldn't be blindly upgrading in the first place).

The downside of this approach seems to be more manual updating of libraries. But to prove that, we need hard numbers, not theory:

How often is a major release published compared to a minor compared to patch?
How often before the ^ change?
How often after?

@isaacs
Copy link
Contributor Author

isaacs commented Sep 8, 2014

npm will not be hiding 0.x versions. Interesting idea, but the costs dramatically outweigh any potential benefits.

There are at the current moment, 418,088 releases of 0.x versions, and only 64,536 releases of 1.x versions, 15069 at 2.x, and 5017 at 3.x. (It falls of dramatically from there.)

Most npm modules never are bumped up to 1.x. The reason is most often not a lack of support, but rather a lack of breaking changes. If a module is created, and given a 0.0.0 version, then each release that fixes a bug gets a 0.0.1, 0.0.2, etc., and each release that adds functionality gets 0.1.0, 0.2.0, etc.

Are all of those releases "unstable"? Not fit for public consumption? Let's look at the data.

The most downloaded module on npm is mkdirp, which is version 0.5.0, and as stable and battle-tested as you could possibly want. async, second-most depended-upon module in npm, is version 0.9.0, heavily used everywhere, and only very rarely updated for the occasional bugfix. Grunt, the third-most-starred module in npm, and the fastest growing npm sub-community, used by people who only treat Node and npm as an implementation detail while building sites in Ruby and Java, is version 0.4.5, last updated 4 months ago. Extremely stable, and limited enough in its design to only very rarely require breaking changes.

The spec says that 1.0 is the "first public release", but that's not actually how it people are using versions most often in JavaScript land. x.y.z-alpha.1 and x.y.z-beta.2 and such are used for pre-releases. 0.x.y versions are public releases. Tens of thousands of authors are releasing code to millions of users with 0.x versions. If the spec is intended to be descriptive here, it is incorrect; if the spec is intended to be prescriptive, then it is failing.

npm already is defaulting to 1.0.0 as the first version number in npm init. But that default will take a long time to catch on, if it ever does.

We can either change the spec to reflect what people are doing, or we can change people to reflect what we think the spec ought to be. So, is SemVer a political manifesto, or a reflection of expected practices and reasonable expectations? Are we paving cowpaths or exercising eminent domain to build highways through existing cities?

As it stands, I have been coming to the conclusion that the ^ operator was actually correct in node-semver 2.x. We should have just said that ^ is "what the spec should be" rather than "what the spec is".

@boneskull
Copy link

👍

@FichteFoll
Copy link

As it stands, I have been coming to the conclusion that the ^ operator was actually correct in node-semver 2.x. We should have just said that ^ is "what the spec should be" rather than "what the spec is".

Sounds similar to what I interpreted the situation as. The core problem in the npm situation is very likely:

people have used it pretty widely, treated it more or less as equivalent to ~

and means that the core aspects of semver were not communicated well enough, especially considering that they use it the same way for 0.x.y versions.

So, I think the easiest band-aid fix is to make the ^ operator behave like ~ when used with 0.x.y versions in npm's semver implementation.


Regarding "1.0.0 as the first public release", I think this is wrong. I'm not sure whether this is actually in the spec, but if it is it should be removed. 1.0.0 is for when you reached a point where people depend on your library to "not break", and if they do that while you're still on 0.5.4 without breaking it regularly (~once a month) you should consider bumping to 1.0.0 for that reason. You don't even have to change anything in the code, a simple version bump would be perfectly fine here.

Which brings us to the other problem with people using semver: They don't want to reach the 1.0.0 threshold, but that has been talked about before alread. I think we should arrange an "international 1.0.0 day" where everyone gets to raise their projects' versions to 1.0.0 when he thinks he can. I recently did that, too.

grv87 added a commit to FIDATA/infrastructure that referenced this issue Sep 23, 2017
Also treat zero major version as if Magic Zero rule was dropped.
See semver/semver#221.
Upgrade of major version to 1 means incompatible changes.

Also add version constraints for Terraform providers
implemented in Terraform 0.10.0.
@jwdonahue
Copy link
Contributor

jwdonahue commented Dec 7, 2017

IMO, 0.x.y or 0.0.y should both be banned from the SemVer spec in version 3.0.0. The -prerelease tag effectively has the same default semantics. Virtually all packaging schemes seem to cover the prerelease tag semantics quite well. Dropping the 0.x.y loop-hole would simplify the world and add the ability for developers to tell their customers, here's what I think is just a new non-breaking feature, but I know this technology is immature and we're still receiving surprising feed-back from our customers so I have this prerelease tag on it because I am uncertain that I won't break a significant number of my customers.

Forcing all prerelease versions to have a prerelease tag on them, frees up the entire SemVer triplet for the package producers to advertise their full intent while simultaneously tagging the release as risky. I see no problem with packaging tools refusing to handle this one feature of SemVer, provided they offer proper support for the prerelease tag and semantics.

EDIT: To clarify, 0.0.0-X.Y.Z and x.y.z-anytag would all be valid prerelease forms, but specifically, the leading 0.x.y (no prerelease tag) form would be banned. I think the original 0.x.y bootstrap form was conceived before the full semantics of how prerelease tags might be used was fully understood.

@jwdonahue
Copy link
Contributor

@isaacs, unless you intend to pursue this issue further, please close this issue at your earliest possible convenience.

@elypter
Copy link

elypter commented Feb 7, 2018

couldn't this all be solved if you add the exception that you can bump from 0.x.y to 1.0.0 without needing a breaking change? just allow people to bump when they feel the project is ready but the interface wont change anytime soon. if this is part of the standard then applications will simply ignore the pseudo major bump. the only downside is that you cannot do a major change while jumping to 1.0.0 but then you can still just do it right before.

@jwdonahue
Copy link
Contributor

The standard already specifies that 1.0.0 is the first stable version. Literally every change up to and including the 1.0.0 release is potentially, but not required to be, a breaking change. In other words, risk averse consumers wait for 1.0.0. From that point forward, you should not bump the major version unless you have actually introduced breaking changes.

@elypter
Copy link

elypter commented Feb 8, 2018

maybe make this more clear in the manifest so nobody gets the idea that you cannot just bump to 1.0.0 if you think the time is right.

@jwdonahue
Copy link
Contributor

@elypter,

People get all kinds of ideas in their heads. The spec can't possibly account for every irrational idea that might come into every developer's head. That said, I would personally prefer to see the 0.x.y release mode removed from the spec in vesion 3.x.x. The prerelease tag can be used effectively to cover all prerelease contingencies. The current spec essentially requires that all 0.x.y versions must be treated as prerelease anyway, so why have two forms? Well, it's probably historical, so it was captured in early versions of the spec, but we don't have to continue to perpetuate it.

Related: #287, #333, #238.

@grv87
Copy link

grv87 commented Apr 29, 2018

@jwdonahue, could you clarify your opinion? Does I make these right?

  1. If my library's version is 0.25.47, it becomes not complaint to the standard. I should release 0.25.48-prerelease instead.
  2. I can't increment major version if I don't have breaking changes.
  3. Starting new library I must use 1.0.0-prerelease version.

If these are correct, then it looks OK for new libraries but not for existing. How, having 0.25.48-prerelease, I could make a release not making breaking changes?
Or commit with incremented major version is considered as a breaking change itself?

@jwdonahue
Copy link
Contributor

jwdonahue commented Apr 30, 2018

@grv87 ,

The spec explicitly spells out what you must and should do as a publisher, when you make certain kinds of changes, depending on whether they break back-compat (major bump), add back-compat (minor bump) features or just publish changes with no new features (patch). It does not prohibit version changes in the absence of underlying product changes. In fact, the only way to recover from publishing errors is to remove the erroneously versioned package from public feeds and issue a new package with a correct version number based on the rules (ie, two packages in the world with identical content and two different version numbers is okay, but probably SHOULD be avoided).

The spec also implicitly defines expected risk levels to consumers of your published packages and therefore also guides their policy choices in automatically accepting updates. If you have been publishing a series of 0.y.z releases, your consumers are already treating them as though each release is very risky and also might be valuable to them. They are in a co-development phase with you and are eagerly awaiting completion of a relatively bug free and stable product. They may be taking every update from you, testing it and then deciding whether to publish their own bits that include your updates, or they may be stuck on a more-or-less useful version for their own purposes and waiting for you to complete the project before they devote much more effort into taking a new update. It's actually a continuum between those extremes for most of your consumers.

An arbitrary change on your part from 0.y.z to 1.y.z-prerelease might trigger some of your customers to take a closer look at your new release, and that will likely consume some of their resources. In the absence of guidance from you, they might assume that you are entering a more stable phase than in the past. Most of their tooling however, should distinguish both 0.25.48 and 1.0.0-tag as being of the prerelease risk class, but I don't know that there aren't some tools out there, that break the prerelease category down to higher and lower prerelease subcategories.

No-one has labeled my earlier suggestion as barking mad, yet, so I take that as a good sign, but you have to consider what you know about your customers and make the correct choices for you and them, to the best of your abilities. I can only make some general recommendations how you might convert to the prerelease tag form from the development form. First, taking your example above, your next release would be 0.26.0, whether or not you added new features or breaking changes, because you're about to make a big change in your versioning semantics. You include in the your release notes, a prominent notice of the intended change in versioning and the next package version number, which will be 1.0.0-a.dev (or whatever tag is appropriate for you) and except for a change in the release notes, that package contains the same bits as 0.26.0. Your customers can then fix any knock-on bugs in their own products caused by integrating 0.26.0, review and adjust their intake policies and then verify that the entire publish/consume chain of events works correctly, without having to worry about changes in your code.

Personally, I would only go to all that trouble, if I was going to produce new products. In that case, I wouldn't want to be supporting different workflows for different products. If I had been using the 0.y.z scheme on an earlier product line, I would convert. After working with semantic versioning for several years and having developed tooling around it, I see an advantage to the 1.0.0-X.Y.Z.Tag form of prerelease series, where the X.Y.Z are given full SemVer semantics and it is obvious to everyone that the target release is 1.0.0. It conveys a richer set of information to my consumers and it allows me to more easily support overlapping version 1 and version 2 projects (ie, development on N and maintenance on N-1). I tend to fix bugs first, then do features, so my typical version history might look something like:

1.0.0-0.1.0.dev // Made available to QA and very early adopters (VEA's).
1.0.0-1.0.0.dev // Just broke the world.
1.0.0-1.0.1.dev // Bug fixes.
1.0.0-1.1.0.dev // Making progress on the feature set.
1.0.0-1.1.1.dev // More bug fixes.
1.0.0-1.1.2.flight.0 // Dogfood release.
1.0.0-1.1.2.flight.1 // Wow, good at this, let some outside early adopters have a look.
1.0.0-1.1.3.flight.2 // Fixed bugs, widen audience.
...
1.0.0-Alpha.1 // Earliest generally available release.
1.0.0-1.1.4.dev // Bug fixes for QA and VEA's.
1.0.0-Beta.1 // Getting closer. This is 1.0.0-1.1.4.dev with a new label on it.
1.0.0-RC.1 // Locked down, only bug fixes from here on out.
2.0.0-0.1.0.dev // Moved resources to next generation.
1.0.0-1.1.4.dev // QA & VEA's
1.0.0-RC.2 // Decided to 1.0.0-1.1.4.dev is RC quality, publish it.
... A bunch of version 2 series releases ...
1.0.0 // New cars start showing up in the parking lot :)
2.0.0-1.0.0.dev // Lots of next gen work going on.
1.0.1-1.1.3.dev // Bug fixes in the works for v1.
etc...

The above version history is a plausible SemVer compliant sequence. Some CI tooling already supports it, some don't, in fact, some get in the way of implementing it like that. Obviously, not all of those versions are available to everyone.

Unfortunately, SemVer does not provide the means for defining the semantics for the prerelease tag identifiers (fields), beyond their sort order. Pure numeric prerelease identifiers sort lower than alpha-numeric identifiers. I have been working on and off, over the past few months, on a scheme to provide the means for developers to publish the full semantics of every field in their version scheme. A human and machine readable VersionSchema is required to bring more order to semantic versioning.

See https://VersionMeta.org/ and stay tuned.

@grv87
Copy link

grv87 commented May 4, 2018

IMO, @ronen suggestion to just drop rules 4 and 5 is simpler.
But I can't see any practical difference, turns out it's just personal preference to start major versions from 0 or from 1. When I use semantic release tool it starts versions from 1.0.0 anyway.

When we could expect semver 3.0.0 prerelease with changed magic zero rule?

DavertMik pushed a commit to codeceptjs/CodeceptJS that referenced this issue Jun 2, 2019
Version 0.18.0 has a vulnerability
https://nvd.nist.gov/vuln/detail/CVE-2019-10742

And bumps to x on 0.x.y are considered a major bump
semver/semver#221
@grv87 grv87 mentioned this issue Aug 23, 2019
qub1750ul pushed a commit to qub1750ul/versioning that referenced this issue Jul 10, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests