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
[spec] document real-world v0 semantics #923
base: master
Are you sure you want to change the base?
Conversation
I am unsure how I feel about this personally, we can sort it at some point. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you so much for this.
I am unsure how I feel about it right this moment, but I deeply appreciate starting to tackle this. You took a different approach than maybe I was thinking of, I am unsure if that's good or bad! I am unlikely to be able to devote significant time on this until next week.
@@ -1,4 +1,4 @@ | |||
Semantic Versioning 2.0.0 | |||
Semantic Versioning 2.1.0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you could argue this means 3.0.0. I dunno. Something to think about. Semver has never really been super consistent with versioning itself. Maybe it's a good time to start that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, i figured we'd iterate on this line :-) you could argue this is technically loosening constraints on v0, or increasing them. Making it v3 would certainly be a safer approach.
I'd love to hear more about what approach you were thinking of. No rush; happy to talk about it next week. I'm also available for a video call if that's easier for you. |
This is definitely a breaking change. Your favorite packaging tools are not the only bits that depend on this spec. If you are going to ensconce just one convention that was traditionally an allowed overlay on top of the spec, I think you should use mine You seem to be trying to say that 0.y.z is no longer a prerelease form on its own, which was not the original intent of versions 1 & 2 of the spec. Version 2 of the spec and a reading of the FAQ indicate that the 0.y.z form precedes the 1.y.z initial release form. I am sure there are many explanations of this in semver/semver issues and probably StackOverflow showing typical version histories of the form: 0.1.0 // implied prerelease as the API is not defined until 1.0.0. As far as I can recall nobody ever raised any objections to this in the past. Nothing about the original wording precludes you from adopting your proposed workflow oriented overlays, but no sane external consumer of your product would expect a 0.1.0 version to be complete, build or runtime safe. |
That's correct, and has always been the case, since you can have a prerelease of a v0. v0 versions are and have always been full releases. |
Which is it? A prerelease or a full release? I do agree that you can also add a -pr tag as well, but that doesn't change the fact that a 0.x.y without the -pr tag is in fact a prerelease. According to the v2 spec:
Sounds like a prerelease to me. Initial develop precedes release, so clearly this one form of a prerelease version. Notice there is no 0.x.y in this spec, the author(s) clearly intended 0.minor.patch. The mere presence of the 0 in the major field literally means any publication can contain breaking changes so the same rules associated with the pr tag apply. But then we get 5 (emphasis is mine):
So now we have the definition for a release version which follows the initial pre-release definition. Yet another indication that 0.x.y really is something that comes before the first release, clearly making it a prerelease. Hence this proposed change is breaking and I see no language therein which provides a means for implementors to determine which semantics apply to a particular version string. Then we eventually get to 9:
This is the second definition of a "prerelease" version and it allows us to go back into development mode without having to drop back to the 0.x.y series. I should point out that having a dev branch perpetually churning out 0.x.y prereleases that land in a series of release branches: 1.0.0 // After 0.7.23 was tested. Isn't prohibited by the spec. While this may not be sound, there's many other potential permutations on this for organizations that do lots of pre-flighting of prereleases in various A/B scenarios. Fixing that loop-hole is technically a breaking change, but unlikely to have widespread impact, especially among the current set of package types represented by the maintainers of SemVer. I've certainly never seen anybody doing this, but there must be tens of thousands of bespoke and ad-hoc implementations in the world today. I think the spec should stick to the minimum required to define a common syntax and semantics, and leave the tool makers and workflow designers with as much freedom innovate as possible. |
A prerelease is only something with the prerelease suffix - it has no other requirements. It may sound like a prerelease, but it’s still not one. That the things that a prerelease indicates may also apply elsewhere doesn’t mean it’s a two-way implication. |
Looks like a duck, sounds like a duck, it's a duck. |
Unfortunately, the map remains not the territory. |
Ya I was just trying to find Preston's original repo. I remember reading through all of the issues before he handed it off to Phil and they moved the official site here. |
@steveklabnik gentle ping, since it's been a few weeks :-) |
I did not manage to get to this this weekend but I do care about it, my intention is to merge something semantically the same as this, or maybe possibly this, but I need some more time to get my full thoughts down but also didn't want to continue radio silence :) |
Thanks for the update :-) I'd be more than happy to make any changes whenever you get to describing them. |
@steveklabnik another gentle ping, if you want to take some time away from governance drama <3 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Apologies, once again, for taking forever. Sadly the governance drama doesn't let me get distracted with other work, it just robs me of the desire to do anything :(
Okay. After sitting on this for a while, and even passing it by @indirect a while back, I am comfortable with the way that you changed this.
The only real question to me is the "semver of the spec itself" problem. Which is its own can of worms. I am not sure the proper way to move forward on that, but I also don't want to block this on solving a bigger issue. Do you have any thoughts there?
Arguably this is a patch in that it loosens the semantics of v0 versions from “always breaking” to “only sometimes breaking”. You could also interpret it as major because it applies semantics that weren’t there before. Are there any important ecosystems that don’t follow this already, where this change would break them? |
As far as I know, no, it is purely a "bring the spec in line with practice" thing. but i wish we had better test coverage here. So I think this implies we should do it. |
It may be reflective of what is used in practice, but semver isn't about "how an API is used". It's about "how an API is defined" and if we consider the rules that semver defines to be the API, then this is definitely a breaking change that requires a major bump. Example: With semver 2.0.0, you may include breaking changes in the update from 0.1.0 to 0.1.1. With this change, that would be illegal as any breaking change for a 0.X.y (X > 0) MUST increment X. |
compatible functionality is introduced to the public API. It MUST be | ||
incremented if any public API functionality is marked as deprecated. It MAY be | ||
incremented if substantial new functionality or improvements are introduced | ||
within the private code. It MAY include patch level changes. Patch version | ||
MUST be reset to 0 when minor version is incremented. | ||
|
||
1. Major version X (X.y.z | X > 0) MUST be incremented if any backward | ||
1. Major version X (0.0.X | 0.X.y | X.y.z) MUST be incremented if any backward |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In order to prevent overlaps between 0.X.y
and 0.0.X
, a requirement for X > 0 should be added here as well. Note that this would not cover 0.0.0
, which … doesn't really fit into any bucket here anyway. Maybe it should be disallowed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that this would not cover
0.0.0
, which … doesn't really fit into any bucket here anyway. Maybe it should be disallowed?
In practice I think I've only seen this used for reserving a name in a registry, not for any actual release. In other words 0.0.0
releases typically don't even have an API yet to version. So I guess the general usage already assumes that 0.0.0
is invalid?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure why 0.0.0 would be invalid, it's a valid starting point afaict.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Other than it being far more likely, that an existing feature could be removed or substantially broken without notice in a 0.y.z publication, I have never treated them any differently than any other prerelease.
I do find it useful that 0.y.z preserves the "feature added" and "fixed bug" semantics, rather than the 0.x.y form that munges patch and feature semantics in y, while giving me zero useful information in x. Our pre-ingestion tests are going to tell me whether something is broken, that's why we run them before we ingest them. We assume breakage, but that doesn't stop us from running whatever tests we can, to continuously track our progress.
Given that this proposed change still allows breakage on the y bump, as consumers, we still have to assume that all prereleases may be broken, so this change in the spec is not an improvement. The fact that we would lose the feature/patch signals, implies a loss of data, hence a step backwards, at least for some of us who pay attention to these sorts of things.
Under the current interpretation of the spec, I know that a passing ingestion test on a y bump is meaningless until I add tests for whatever new features were added or removed. I also know that a z bump should have fixed at least some of the test case failures. I also know that when a non-prerelease 1.x.y is finally released, it's highly unlikely to be a breaking change from whatever the last 0.x.y was. Under the current spec, we don't have to have special logic for two classes of prerelease.
I think that the 0.major.minor|patch and 0.0.major schemes simply allows publishers to claim semantic versioning while never really committing to keeping un-signaled breakage to a minimum, or really signaling anything useful at all. It also introduces the weird inconsistency that the value of major can go from x > 1 to x = 1 with each shift to the left.
I also think that we should not consider breaking SemVer without first establishing how to disambiguate between SemVer 2 and all future versions, including patches and etc.
Even setting aside how much transition confusion this would introduce, I think this PR would cause far too much cognitive load when you consider how millions of people use this concept of semver MAJOR.MINOR.PATCH. Even one second extra is thousands of extra hours spent decoding what semver a package is using, maybe guessing based on it's release date and then trying to understand what changes have occurred. Also, having to communicate about changes in a <1.0.0 version Perhaps the problem statement and intent could be clarified, but I suspect the intent is to reduce ambiguity on whether a release is stable or in initial development, and that would be a good idea, but I don't think this is the way to go about it. One idea might be that the Development Status of the product could be more clearly separate from semver altogether. For example Pypi has the underused ability to mark Development Status: 1 - Planning Note decisions regarding this are often not made WITHIN a release, the clearest example would be deprecating a package, but you can apply it to the others. You wouldn't cut a new release to mark something as deprecated, you would flag it as deprecated and archive the repo. I think it would be very neat if this was used more ubiquitously while semver eventually removed the notion of <1.0.0 being initial development. Think of packages that need to make a breaking change but don't consider themselves beyond a alpha status for their product overall. And of course as you illustrate, there are many sub 1.0.0 products that have become defacto Production/Stable, cutting a new release just to indicate the product is stable seems strange. |
@Lxstr this is already how npm (and possibly bundler?) has always operated, and the number of people using npm is quite significant compared to all other users of semver, so I think this would reduce, not increase, confusion. The intent is to update the spec to match what npm does. |
That is exactly why this PR should not be accepted. It would impact a large number of valid implementations. There are many other implementations that do not and probably will not conform to how NPM does it. Keep in mind that SemVer predates NPM. There is nothing in the spec that precludes NPM's behaviors, so why change the spec to match what they are doing? |
@jwdonahue what highly adopted implementations would it impact? |
That opinion is reasonable to express about ranges, but I'm not sure why it applies here - again, if you can help me understand concretely how this PR - and only this PR - would negatively impact other adopted ecosystems, such as NuGet, I'd be happy to work on bridging that gap. |
I did a search on github for SemVer implementations a few years back. There were over several hundred of them at that time. I would estimate that there are perhaps as many as 10K, or more, SemVer implementations being used in production systems around the world today, mostly in the form of scripts. Most of us do not do SemVer the MPM way. |
It's clear this change would be a "breaking" change in the spec. That's ok tho, because the long tail of implementations are best served by following what the most popular implementations do - that's how standards work, in practice. |
SemVer semantics influence baseline selector logic. Last few times I read your proposed changes, they effectively modify the semantics of the version triple. It is a breaking change that further restricts the range of conformant selector logic. |
Yes, that is the point, and the improvement. This PR would need to be part of a "v3" of the spec, surely. |
The 0.y.z form, uses the major field to signal that a new version might break something, without requiring the major version bump on each release and without losing the distinction between non-breaking additions and bug fixes. The SemVer spec is built around the mapping between major field indicates breakage, minor field indicates additions and patch indicates bug fixes and the syntax is major.minor.patch. The meaning of each field is fixed, the values are used for sorting and selecting. |
Might as well call it the NPM version of the spec. |
npm (and that sounds like a good thing, given that it's the largest ecosystem by any available metric) |
Here's a metric for you: It's just 1 to maybe 5 in thousands of implementations, hardly dominant. |
Number of implementations is irrelevant, anyone can make a hundred new implementations that nobody uses - what matters is the number of users (individually or in aggregate). |
Not relevant to you perhaps, but to all of us implementers that gleaned the beauty of the fixed syntax and the specific semantics of SemVer, NPM's barely conforming implementation is of little concern to us. All I personally care about here, is preserving the ability to innovate while honing to the semantics of the SemVer spec. I do not intend to modify any code to cover any version of SemVer that does not offer an enrichment of my own products and processes, any more than NPM is likely willing to adopt any breaking spec changes that do not enrich them. Since they already have this particular feature, it's a wash for them if it is not adopted. One key feature of SemVer is that it is about as tool-chain agnostic as it can be, while remaining relevant. The spec will become irrelevant to all but NPM users, if this change is accepted. Since you brought up size repeatedly in this thread, I would point out that the most widely adopted and documented versioning scheme is the version quad that Microsoft uses, and other similar schemes. In Microsoft's case, they are gradually moving towards SemVer for many things, but even there, they do not hone to the NPM scheme for anything but Node packages, that I am aware of (FYI: I am not currently employed or contracted at Microsoft). NPM doesn't give us the full richness available from the current spec and it's selector logic is complex, so we generally use their SemVer tool for that purpose and just assume that all 0.y.z or 0.x.y or whatever might be in them, are breaking changes and we can't know whether any bug fixes are in it, or whether it's a pure feature addition, so... way more attention has to be paid to the release notes, because the NPM tools don't give us a rich enough selector (despite its complexity). I suppose the later might be automatable eventually, given the recent advances in AI, but not all NPM packages have accurate release notes. The point is, our tooling works as-is and nobody really needs this spec change, not even NPM. The shifting fields scheme is well documented here and elsewhere, it should not be included in the spec. |
I am all in favor of these changes being made as a new "npmver" spec, but it would be wrong to eliminate a clear distinction followed by real package authors (myself included) from the semver spec on the meaning of 1.0.0. Changing the semver spec to make 1.0.0 a meaningless distinction is a regression for this project: it offers no guidance at all on how to handle certain critical decisions in versioning instead offering the choice to either follow the religion of two-digit semver (0ver) or the religion of three-digit semver. Please please please do not subject your versioning scheme to schism because of React. |
This is the kind of change that destroys meaning that did exist. I created meaning when I authored libraries in accordance with the actual guidance given in the spec, and since packages generally do not declare (in a consistent manner) which version of semver they target, the majority of distinctions between 0.x and 1.x APIs made in good faith with regard to the 2.0 spec would be cast into doubt. In the future it will become hard to know whether a package author was aware of the 3.0 specification eliminating the 0ver distinction, especially given that the 3.0 specification must be applied retroactively in order for it to have the benefit of describing what npm has always been doing. |
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. 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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the other changes are OK but the removal of these sections would, in my view, be a serious error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's basically the entire point. The distinction of "v1.0.0" isn't valuable or meaningful, and in practice having it has been harmful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It isn't meaningful to you, but I'd argue that there are plenty of people to whom it is a meaningful distinction. To me it's the keystone to the philosophy of semver. The spec hints at this heavily when in the FAQs:
If even the tiniest backward incompatible changes to the public API require a major version bump, won’t I end up at version 42.0.0 very rapidly?
The way I interpret the answer is "if there are so many APIs in your package that you're realistically in danger of ending up at v42, you really should have split apart multiple packages with more focused responsibilities".
I think that by making 0.42.0 the same thing as 42.0.0 you're removing the key force the agreement exerts that tends to create strong well-factored ecosystems of code, which in my mind was the largest part of the purpose of this document
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's just a number, it doesn't matter if you have v42 or v153. Breaking changes should be avoided in every case regardless, but that's an opinion that doesn't belong in this document. (and that doesn't match my understanding of its purpose, which is to safely convey breakage, not to inhibit it)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But v153 does inhibit breakage. It usually means that there are several unrelated APIs inside, e.g. all the helper methods from a utility library, or all the components in a whole design system. Most of the time a bump from v152 to v153 means that 49 of the components in a design system had no breaking changes and one of them did. This is not a situation where semver has failed to protect the publisher but it is a situation where the it has failed to protect the user, who got 49 false positive reports of changes alongside one real change. This is no longer a valuable tool for helping the user understand what has not changed about their own software.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If v3 of the spec is breaking, why not just outlaw versions starting with 0? Wouldn't that solve everyone's problems to their satisfaction? It would be clear that any triplet starting with 0 is not semver v3.
The only problem with it is that it isn't a change that can be retroactively applied without opt-in from the users, but I see that as ideal. The great promise of semver is that it allows you to make breaking changes because people who don't want to be broken have the chance to opt out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The benefit is that it requires no changes at all. Semver already requires versions to be >=1
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it doesn’t require that.
Outlawing versions that already exist would be incredibly harmful; that should be a nonstarter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm saying the spec exactly as written is already correct. These versions are not outlawed in must-not language, the breaking.adding.fixing
triplet pattern just doesn't apply to them. To offer that protection you have to opt in by making at least one official release.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The fact that semver explicitly doesn't say what semantics apply to any version starting with 0.x is exactly the freedom you need to be able to solve your problem: defining a meaning for those 0.x version numbers that is consistent with the npm ecosystem's historical expectations (roughly 0.changing.fixing
). The only problem I have here at all is the idea that the two things are one document: that your changes overwrite the underlying spec instead of coexisting with it as they easily could.
So my argument is that there are two versioning schemes: three-digit semver and two-digit semver. They are compatible with each other (mostly) because both agree on using a triplet, it's just that in the two-digit scheme the first digit is always 0. For this reason I think it's more likely that users will know them as semver and 0ver. 0ver is currently a joke, but if 0ver.org proves anything it's that there's plenty of projects for which the two-digit semver model is appropriate and its usage ongoing. The main problem here is that the site describing this versioning system is a joke, a mean prod at legitimate engineering. All the projects listed on that site need the ability to have a semver.org-like document that describes the semantics of triplet-using projects that never intend to go to |
There’s also one-digit, 0.0.x, where every change is breaking. All three are part of semver in the npm ecosystem. |
Makes sense, yep. There should be a spec documenting one-digit triplets too, and preferably each package should be able to declare (in a discoverable way) whether its intention is to use 1-digit, 2-digit, or 3-digit version numbers. If this was implemented even one-digit semvers could be made to behave correctly in npm, since the default behavior of prepending |
why would it be valuable to have three distinct specs that nobody uses in isolation, and all combine to form what the world actually uses? (as opposed to one spec containing all three forms) |
Are you saying this is something you'd agree with as long as the one spec (semver.org) simply defines one-digit, two-digit, and three-digit variants? |
I must not be clear on what you're suggesting. I think the only thing that matters is having a single spec that defines how npm works, which this PR plus the ranges PR would do. I see no value in having any spec for "semver but v1+", or for "v0.x.y" or for "v0.0.x" in isolation. |
My confusion is that to be able to say what constitutes a breaking change you have to know what the package author intended. I thought that we were in agreement on that. Each of the versioning systems in real use use a different digit to denote breaking changes. If you say "the spec doesn't differentiate" then it seems to me you're saying "it will never be possible for users to be protected from breaking changes in all real versioning scenarios." |
Yes, but this specification doesn't address that - something external to this spec would have to point to it, like package.json in npm, or the equivalent in other ecosystems. I would support that if you pursued it elsewhere, after a spec exists that matches what npm has always done, of course. |
npm has always presented the semver spec as its own ideal -- the idea that packages people depend on should be 1.0.0, that was and currently is npm's stated ideal. How would that idea survive these changes? |
That wasn't always the case, and just because it's the default in Thus, these changes would have no impact on anything except to bring this specification into alignment with its most widely used implementation. |
"It has no impact on anything" is wearing a bit thin when I am describing the impact. You could earn my endorsement though by creating explicit guidance in the language of the specification that describes when it is appropriate to use one digit vs two digit or three digit versions. |
To be clear, all of them are three digits - it's just that the first one or two is zero. Adding "should" guidance doesn't really make sense tho when the guidance is "use whatever version you feel like using" - it's not this spec's place to editorialize about the appropriateness of version number choice, as long as breakage and additions are conveyed correctly by transitions. |
I said my piece. I believe you should write up a spec that has no editorial intent. This spec has editorial intent, and if it stops having that intent it will need to be replaced with (or at least augmented by) one which does. |
This PR does not add editorial intent - it has precisely the same amount as the current spec. |
My problem is removing editorial intent. Your starting point is a spec with an opinion ( |
Adding my two cents: I would argue that the V2 spec explicitly defines 0.Y.Z as developmental, and not to be considered stable in any event. By explicitly defining the rules of 0.Y.Z, I think you're shooting yourself in the foot, as least as written currently. Just because the npm matcher treats 0.Y.Z as 0.X.Y is not a fault of the spec, but a decision within NPM's implementation. NPM should follow the V2 as outlined, in that every bump to an 0.Y.Z version, as long as Y or Z have incremented, are potentially breaking changes. If the author of that software doesn't wish to declare an official release as 1.Y.Z, then it is up to them to relay to their user base that there are no breaking changes - because as per the spec, they should consider every bump as potentially breaking. Additionally, according to the V2 spec itself, the changes listed in the document would constitute a Major bump as you are explicitly defining (in essence) new ways to handle behavior which would be potentially incompatible with the existing V2 spec. |
Per #915 (comment)
Fixes #915. Closes #916. Closes #127.
Additionally, I notice the document often, but not consistently, hard-wraps at 80 chars. While I find soft-wrapping immeasurably superior for diff clarity and editability, consistency is also important, so I'd be happy to make a separate PR that updates the entire document to consistently soft-wrap or hard-wrap.