mirror of
https://github.com/semver/semver.git
synced 2026-07-11 05:12:48 -05:00
[GH-ISSUE #782] [2.0.0] Make patch optional? #7489
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @JC3 on GitHub (Dec 9, 2021).
Original GitHub issue: https://github.com/semver/semver/issues/782
I think the patch number should be specified as optional, and assumed to be 0 if not present.
I don't have a particularly compelling argument for this other than I very often find that I don't need a patch number, and that I prefer short version identifiers (e.g. "1.0") where possible. Also I think a lot of people already do this, because I see two-component version numbers all over the place. 🤷♂️
If this were to become the case, the following changes would need to be made to the 2.0.0 spec (plus a few others probably):
ac34620593Just a thought!
@ljharb commented on GitHub (Dec 9, 2021):
The lack of precision here (is it an intentional omission? is it a bug?) seems much worse than the mild inconvenience of having to type a dot and a zero occasionally.
@JC3 commented on GitHub (Dec 11, 2021):
Is wondering if something is an intentional omission vs. a bug a productive line of thinking to begin with? I mean, that could be asked about pretty much... everything, ever; not just version numbers. 😄
Still, I think I can make a more compelling argument (I worked way too hard on this, lol... TL;DR just skip to the table below):
Firstly, on the semver parsing / consumption side, here are some notable real-world examples of current behavior:
"="or"v"character is stripped off and ignored." (ref)~and^for comparisons.^and~support in comparisons.^and~support in comparisons. (ref)Secondly, although less compellingly (but maybe still worth noting), on the semver production / software versioning side, I can't be as thorough as above, but just off the top of my head you've got:
Anyways back to the parsers let's break that down:
(in versions)
(in versions)
(in versions)
(in ranges)
(in ranges)
~and^(in ranges)
~* Feature is supported! The default value is indicated.
† Wildcards, tilde, and caret supported in NPM mode. Wildcards and tilde supported in Cocoapods mode.
‡ Not verified.
So what we've got here, essentially, is a standard that does not allow optional version components in a world that clearly wants optional version components. To me, the logical choice, then, is to put it in the spec, because then it's well-defined, whereas currently it's ambiguous and implementations end up with inconsistent support (it's purely coincidence that most of the ones above behave the same).
I mean, it's sort of a classic issue, right:
So it seems to me there is strong motivation to do all of these, beyond my initial proposal of just making the patch optional:
~and^.*,x,X).Possibly (I'm not convinced on this one though) define that a leading(Edit: see https://github.com/semver/semver/pull/584#issuecomment-991823928)vis allowed (or at least, should be accepted but should not be used in new applications). Many implementations strip this out, and many don't -- which is a hint thatvsupport is desired and so a consistent definition would at least ensure that all implementations behave the same when encountering one.So... yeah. A bit more than my original motivation, but it was sort of the implicit reason behind my original suggestion. I do think Semver is rather underspecified in its current form, and that standardizing optional components increases precision in a certain way. I also think that NPM's behavior is fairly ubiquitous and proven, and therefore might serve as a decent basis for expanding Semver as far as ranges go.
@ljharb commented on GitHub (Dec 11, 2021):
Yes, that question should be asked about everything, everywhere, and it's why conveying intention is so important - it's also why omitting curly braces caused a massive SSL bug, for example.
For npm, omitting the patch is already a range, not a version, and this distinction is already coming to the semver spec (see #584) via npm's creator, so allowing
1.2to be a version is already basically a nonstarter.@JC3 commented on GitHub (Dec 11, 2021):
Many (if not most) parsers will treat it as a version. This has and always will be the case. So I think it is a mistake to leave implementors in a position where the details of how partial versions are parsed (and they will be parsing them) is up to the implementor. I think we would want to have a specification that standardizes common use cases; that's what specifications are for, after all.
As I mentioned, I definitely think it is a good idea to expand Semver with more details about ranges. However, I think the change that you describe (assuming that's the full description) would be a mistake, as well. It would make sense if Semver's intent is to limit itself to NPM. In reality, since so many parsers do, and will continue to, allow partial versions, adding that distinction to the spec will drive all partial-version-supporting implementations even further from Semver.
But I think it would be better overall if changes to Semver increased adoption, rather than decreased the applicability of the spec.
Like, I think Semver is a good spec it just needs a couple extra things to make it globally useful -- the fact that so many parsers choose to not strictly adhere to Semver is absolutely a sign that something's missing, I think.
Anyways, I think that's all I got!
@jwdonahue commented on GitHub (Dec 12, 2021):
@JC3 I like your approach to reasoning about these things, but I think you should get your facts straight:
Not true. Most implementers added support for SemVer, in many cases, long after they supported all the non-SemVer behaviors. Even some of the SemVer early adopters, did so grudgingly, and most of them stopped at 1.0.0 or even 1.0.0-beta.
Most definitely. This is always what happens when we try to convert common denominators into a specification. It's always good to delineate the current best practices, particularly when the full range is described (not present in the current context), but when those commonalities are barely significant, as was the case here, it's really hard to drive consensus from it. But SemVer was proposed in addition to those common practices. It was a suggestion that we could all move towards something recognizable and better than the norm, that wouldn't break our existing tools, and that has had the effect of spreading adoption.
What we have now, is a situation where the dominant package tool owners, control the spec (fairly normal), and seem loathe to issue a v3 (also normal). As for whether SemVer should define a range specification for versions, I think it is doomed to fail. Not because there isn't a kernel of commonality out there, but because it will either have the effect of stifling innovation or drive all innovators away from the spec. Since few implementers barely adequately support the full 2.0.0 spec, if at all, I think the later is the most likely outcome.
There is a third possibility that I think is highly unlikely. The stakeholders might decide to develop reference implementations in each of their respective languages that can actually process version and range data interchangeably, without resorting to the lowest common denominator. I would hope that such an effort would include a standard dependency graph notation. I am pretty sure the talent and capital is there in each of their respective organizations, but do they have the will to solve all of the dependency hell problems in an open source environment?
@jwdonahue commented on GitHub (Dec 12, 2021):
@JC3
Back when the original spec was written, the bits that had the widest distribution in the world (on the order of billions), used four version fields, and still do. Most version strings however, were one or two fields publicly and just a build number privately. There were also a lot of Major.Minor.Patch version strings out there, where those field names were taken literally, with no other semantics attached.
I have argued in the past against expanding the SemVer spec to include one or two fields, and specifying some default behavior for the missing fields, on the grounds that they would be ambiguous. After years of following every thread on this site, I have come to the conclusion that we must solve the version ambiguity problem. Current tools, including those that I have written, use a syntactic filter to include strings that should be treated as SemVer. Still, even a version triple is ambiguous! You don't ever really know what semantics were used to generate any particular version string. Even if SemVer was applied, you don't know which version of the spec is adhered to.
I have also argued that while we really need a SemVer 3.0.0, we MUST solve the disambiguation problem first. Allowing short-hand version notation in any formal format (i.e.; not a discussion forum) will only add to the ambiguity problem. It's one thing to reference 'SemVer 3' and quite another to refer to 'Px 3', in the former context, the semantics are obvious, but not in the later.
I am more ambivalent in the case of range specifications. '[3, 4)' seems clear to me that no matter the syntax or semantics of the versions, anything greater or equal to major 3 and less than major 4 is acceptable. That range could just as easily be floats, doubles, decimal or integers and still convey the same meaning.
@wtarreau commented on GitHub (Jul 26, 2025):
For me it's the opposite: I find that "3.0.0-dev1" is not clearly inferior to "3.0.0" for a human (and even in directory listings). And the patch will always remain zero during development. On the contrary, "3.0-dev1" makes it very clear to me that this is a development step of what will become 3.0, and that once you have the second ".", it indicates a stable and maintained version (e.g. "3.0.0"). And it does have the benefit of respecting lexicographic sorting, i.e. "3.0-dev1" < "3.0.0" in ASCII.
@jwdonahue commented on GitHub (Jul 26, 2025):
@wtarreau wrote:
I have never worked for an organization, nor owned one, where that would be an acceptable practice. My professional career in software development, started in 1987.
@wtarreau commented on GitHub (Jul 26, 2025):
What do you mean ? That you're incrementing the 3rd digit during development ? Development nowadays is just tags emitted on a tree at a given date., so it only contains minor updates (including some fixes) but doesn't deserve being incremented. And on the contrary I've yet to see any single project which does increment the patch number during development!
@jwdonahue commented on GitHub (Jul 27, 2025):
Ah, I see our confusion. You probably have one or more files in your repo, that have version strings in them, but you're not publishing the updates, so they never get bumped. Pretty common. Not the best practice, but it's workable.
On my systems, versions strings in source code are held constant (0.0.0-a.Dev). Product versions are tracked in databases, and updated for each build, such that when a dev builds they get bits with something like 1.1.32-a.Dev+DevName.hash.whatever. Publish to test gets 1.1.32-a.Test+BuildMachine.hash.whatever, and then when a choice is made to dogfood or publish to early adopters, you change the tags, etc. Usually the build systems do apply non-dev version tags to the repo, when a proper release is published for milestone or whatever, but we do not trust repo history for purposes of provenance. It's just too mutable.
The practice ensures that a binary will not have a valid version string, if it was not built by sanctioned processes/machines. I got bit once, back in the 90's, when a product manager, got a hold of a dev's test ROM, ran a production run, and shipped product to customers. The QA inspector read a valid version string, and initialed the "inspected by" tag.
@jwdonahue commented on GitHub (Jul 27, 2025):
The rule is simple. No two binaries, with differing source content hash, tool chain versions, or builders, SHALL ever have the same version string, no matter where, when or who produced them.
Example (simplified), global version history for product A:
1.0.0+ReleaseBuilder.hash0 // Release to customers.
1.1.0-a.Dev+JwD.hash1 // built by me for bench testing/debugging a new feature.
1.1.1-a.Dev+JwD.hash2 // built by me for bench testing a fix.
1.0.1-a.Dev+OtherDev.hash3 // built by OtherDev for bench testing a hot fix.
1.0.1-a.Test+CDBuild.hash3 // built on a build box for distribution to QA.
1.0.1+ReleaseBuilder.hash3 // Release to customers.
1.1.2-a.Dev+JwD.hash4 // built by me to test merge of OtherDev's patch with my code.
1.1.2-a.Test+CDBuild.hash4 // built on build box for distribution to QA.
1.1.2+ReleaseBuilder.hash4 // Release to customers.
I have also worked on teams where build numbers are used for version strings during development, with content hash metatags and the release processes would generate appropriate version M.m.p.b or similar things, but the rule is the same.
We also tend to track versions of tooling as well. I've had build lines for up to a dozen different tool version sets, producing bits for test automation. In some cases, the build tools or manifests specifying the build tools to be used, are checked into the repo, so that becomes part of hash.
The rule can be somewhat relaxed where tool chains are deterministic, but those are very hard to come by (embedded user, machine, timestamp, signing strings, etc).
@nngo commented on GitHub (Sep 11, 2025):
I also see value in making the patch version optional. A major.minor version can still be a semantic version following the API breakage policy without the patch version so why make it a requirement in the spec? And as previously stated many libraries/apps start with a 1.0 version and only add a patch version when needed for a bug fix release.
@ljharb commented on GitHub (Sep 11, 2025):
Explicit > implicit, see https://github.com/semver/semver/issues/782#issuecomment-990321899