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

Could we clarifies better in the semver doc PATCH releases and initial development? #915

Open
camilamacedo86 opened this issue Feb 8, 2023 · 33 comments · May be fixed by #923
Open

Could we clarifies better in the semver doc PATCH releases and initial development? #915

camilamacedo86 opened this issue Feb 8, 2023 · 33 comments · May be fixed by #923

Comments

@camilamacedo86
Copy link

By reading the doc https://semver.org/ is clear for me that:

  • PATCH releases should not contains api changes and they are reserved to CVEs and bug fixes.
  • If the project is not a stable API yet. That means the project will not have MAJOR bumps and because of it we could expected any breaking changes into the MINOR bumps.

However, the text:

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

Brings the idea that new features (api changes) would be fine in PATCH releases with Anything MAY change at any time.

@camilamacedo86
Copy link
Author

Proposed suggestion to bring clarifications to do the doc: #916

@ljharb
Copy link
Contributor

ljharb commented Feb 8, 2023

What it means is that in 0.y.z, everything is major, and there is no patch. "patch" isn't "the third number", it's "the number that indicates non-additive non-breaking changes", which doesn't exist in a 0.x version.

@camilamacedo86
Copy link
Author

camilamacedo86 commented Feb 9, 2023

Hi @ljharb,

The whole point of the convention is to have clear and defined criteria/rules to address the expectations of those that will consume the API and keep the solution maintained.

Sorry, but your comments and arguments do not seem consistent with the info provided in the doc https://semver.org/ and real scenarios. Therefore, I hope we agree that the https://semver.org/ shows need to adequately cover the stage where the project version is < 1.0.0 at least. Otherwise, we would not have this discussion with very different points of view.

Now, following some comments inline about your replies so far:

What it means is that in 0. y.z, everything is significant, and there is no patch. "patch" isn't "the third number"; it's "the number that indicates non-additive non-breaking changes", which doesn't exist in a 0. x version.

That seems inaccurate and contrary to the points described in this doc. If so, why do we have in the doc:

Screenshot 2023-02-09 at 08 03 30

Screenshot 2023-02-09 at 08 23 57

If you are right here, then:

  • Why the document says for projects < 1.0.0 to do releases 0.y.z and should explicitly describe to use only 0.y?
  • Why start from 0.1.0 and not from 0.0.1 ? Would it not be because the changes will be incremental ones?
  • What would be the point of describing that initial development also has 0.y.z and 0.y releases if you can add ANY thing in both scenarios?

Ideally you'd start the project with 1.0.0 and avoid v0 versions entirely :-)

Sorry, but that does not make sense to me.

Before becoming stable, an API must have many lifecycles and interactions with those users, resulting in incompatible changes. I can share with you that, for example, many Kubernetes projects are < 1.0.0 for those reasons.

This convention could not be valid and valuable to many factual scenarios if you think this way. In the real world is a big deal to have so many releases and become 100.0.2 very soon. Indeed that is also a point highlighted in the document.

PS: The doc highlighted this point and considered this real case scenario and requirements.

v0 versions have no patch number.

At the top of the document, we have the following:

Screenshot 2023-02-09 at 08 06 22

Also, we have:

Screenshot 2023-02-09 at 08 22 55

  • Why are the superset rules not applied to initial development releases? What would be the reason for that? Where is it described in the documentation?
  • Why can we not say that a release is 0.Y.Z is a PATCH release for a project that has not yet reached the stable version?
  • Why can we not say that a release 0.Y is a MINOR release for the same scenario with the intuitive caveat that also might contain incompatible changes since the project will not provide MAJOR releases in this case?

It is clear to me that we cannot expect a project that is < 1.0.0 need to make a MAJOR bump release to breaking changes because no MAJOR releases will be produced at all in this case. However,

USE CASE: I am as a consumer of any API that did not reach the stable version yet I would expect that the rule PATCH version when you make backwards-compatible bug fixes would still be respected and see bug fixes ONLY in the 0.y.z releases and not in 0.y.0 ones so that I have the same clarity about what expected when I see the release versions.

I hope that the comments and arguments above make sense.
Also, I would love to get others' input here as well.

@ljharb
Copy link
Contributor

ljharb commented Feb 9, 2023

I agree there's confusion there - i think the document mistakenly conflates, at times, first/second/third for major/minor/patch, since those are only the same at v1+.

@jwdonahue
Copy link
Contributor

jwdonahue commented Mar 7, 2023

@camilamacedo86

Reading and writing technical specs is as much an art form as it is an engineering practice. The language can be particularly terse with respect to "normal" communications. The goal is to produce a document that can hold up to formal logic and the later requires extra attention on the part of the reader. The SemVer spec does a pretty good job of this on whole, but doc is also cluttered with some non-formal language that tends to be the prime source of confusion. It's also attempting to simultaneously codify and extend a certain pre-existing convention that was also likely in use right here at github before the author checked in the original proposal.

Why are the superset rules not applied to initial development releases? What would be the reason for that?

Because during initial development, lots of things are broken and unpredictable and the standard would not have been adopted if it did not include the prerelease loop holes. First, the 0.y.z form was a common convention prior to the writing of SemVer and second, there had to be a get out of jail free card for already released software, to allow for additional prerelease cycles. If not for the allowance of these two conventions, SemVer would never have been adopted, as it would be deemed too restrictive.

There's nothing in the spec that prevents you from using the full major.minor.patch-prereleaseTag semantics for initial development. The 0.y.z prerelease form is there because some developers and probably some marketing departments didn't like the idea of their first official non alpha/beta/dog-food release being very much higher than 1.0.0. In the CI/CR we live in today, best engineering practices mostly prevail, so marketing and developer predilections and superstitions are a little less dominate, and we're seeing a lot more of those higher major version numbers than in the past.

If you start at 1.y.z-pr, you should use an informative tag of the form -a.Dev or my favorite -a.Experimental. Note the 'a.' prefix on the tag. That's there so my Experimental tag doesn't sort higher than -Beta. Alternatively you could use -alpha.

Nothing in the SemVer spec prevents you from layering your own compatible conventions over the top of it.

Where is it described in the documentation?

There's six parts to the SemVer doc:

  1. Summary
  2. Introduction
  3. Semantic Version Specification (SemVer)
  4. BNF Grammar
  5. Why Use Semantic Versioning
  6. FAQ

The spec and the BNF grammar say everything you need to know. The rest is there for for anyone initially unable to grok those two parts or unwilling to read them carefully enough. There is much that can be derived from them. Almost as important as what they say is what they don't say. The spec is not over-engineered to the point of being difficult to implement or use. If you spend some time researching through some of the discussions that have taken place here (closed as well as open), you will get a better impression of how we interpret the spec and why some of it's apparent vagaries are allowed to stand without comment in the FAQ.

Why can we not say that a release is 0.Y.Z is a PATCH release for a project that has not yet reached the stable version?

Why indeed? Where in the spec does it say you cannot do that?

There's two common conventions allowed under the initial prerelease phase, either drop the major version entirely and continue using the minor and patch as intended (0.minor.patch), or shift the meaning of the triple to the right (0.major.minor). And even though the spec doesn't say anything about the 0.m.y convention, it is certainly allowed due to the phrases "Anything MAY change at any time. The public API SHOULD NOT be considered stable".

One problem I have with these to two initial release conventions, is that there is no formal way to distinguish between them, but then we don't have to because we have the phrases "Anything MAY change at any time. The public API SHOULD NOT be considered stable".

In this case the publishers intent is not relevant. The consumers of the initial release had better take head of the phrases "Anything MAY change at any time. The public API SHOULD NOT be considered stable".

Why can we not say that a release 0.Y is a MINOR release for the same scenario with the intuitive caveat that also might contain incompatible changes since the project will not provide MAJOR releases in this case?

Unless I am misunderstanding the question, I think the spec says exactly that. The Y part of the triple is the Minor field and the fact that it's a prerelease version means that breakage is allowed.

So now we come to the realization that the spec never says "MAJOR.MINOR.PATCH" (it's in the summary, not the spec), it uses the "X.Y.Z" notation everywhere and applies the labels Major (X.y.z), Minor (x.Y.z), Patch (x.y.Z) instead. Why is this? Because the original convention that was being extended with the proposed semantics commonly used the X.Y.Z pattern (there were others, but they weren't triples). In some cases X was the "major" thing that marketing was promoting as the next best thing, and the Y.Z parts were left for whatever willy-nilly thing the developers wanted to cram into them (like dates!). In other cases it was X => Year, Y => Month, etc. All of those other conventions are still quite commonly used, but many of them already have their own set of explicit or implied semantics.

@jwdonahue
Copy link
Contributor

I should note that I have also seen the 0.YYYYM.DRR convention is used internally in many places. You can't have leading zeros in the triple, so the year and revision parts must be fixed length so you can drop the leading zeros for month and day fields as needed and still parse the thing in your head.

I don't see any reason why you couldn't release those to early adopters. The semantics are obvious when you see them in the wild and the Y and Z fields will get reset when you finally get around to bumping the major version. Since both prerelease forms absolve the publisher from semantic correctness with regard to the triple values, they lose their intended meanings from the perspective of the consumer anyway, so why not lay in some useful information instead?

You may encounter some tooling that won't accept large numeric field values because the implementers either misinterpreted the spec to mean integer in whatever their programming language was (8, 16, 32, 64 bits, etc) or they were just to lazy to figure out that an integer string without leading zeros will sort correctly if you take account of the string length (simple algorithm takes less time to sort than converting to int and then comparing, in the vast majority of real world version strings).

@steveklabnik
Copy link
Member

For whatever it's worth, I see this line as the worst line in the semver spec

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

And would like to see it removed. Nobody who implements semver actually respects this; they all implement the same ranges as everyone else, some of which have semantic meaning for the relationships between x=0 versions.

I just have not had the energy to try and get it done. Maybe I will.

@ljharb
Copy link
Contributor

ljharb commented Mar 7, 2023

@steveklabnik do you mean to enshrine the way npm treats v0 versions? If that's the case and you'd accept the PR, I'm happy to take a crack at authoring it.

@steveklabnik
Copy link
Member

I think "the way npm treats v0 versions" is too broad for me to know what specifically you mean; I'm referring to the "the ^ matcher treats y as x and z as y when x = 0" semantics.

But part of the reason that I haven't done it, and maybe you're interested in this too, is that IMHO the spec is poorly organized. That's nobody's fault, really. But at the very least, the "what versions mean" bits and the "how versions relate to each other" bits should be separated out cleanly. Part of the issue here is that #584 has still not landed; I think ideally this would all be under a "how versions relate to each other" bit.

But that's not all fully thought through, and a bit rambly. Needs more work, heh.

@ljharb
Copy link
Contributor

ljharb commented Mar 7, 2023

Gotcha, I think that aligns :-) I'll see if I can find some time this week. While I'd love to see #584 land, that seems like a larger change and i don't think this needs to wait on it.

@steveklabnik
Copy link
Member

Cool cool. I agree that it's not blocking, for sure.

@jwdonahue
Copy link
Contributor

Reducing to only one prerelease form would help. But that's a breaking change, so how do you disambiguate between SemVer 2 and 3? I strongly recommend that you resolve that problem first.

@steveklabnik
Copy link
Member

Reducing to only one prerelease form would help.

That is not what I would advocate. Pre-release versions are still pre-release versions.

@jwdonahue
Copy link
Contributor

@steveklabnik I meant that the 0.y.z form of prerelease is redundant. I had thought you were in agreement with that, or were you objecting to the "Anything MAY change at any time. The public API SHOULD NOT be considered stable" phrasing of the 4th clause?

@steveklabnik
Copy link
Member

I had thought you were in agreement with that

I am not. People use this, massively, in the real world. There's no advantage to getting rid of this. In fact, in some ways, the point is the opposite: people don't necessarily view x=0 as a prerelease.

were you objecting to the "Anything MAY change at any time. The public API SHOULD NOT be considered stable" phrasing of the 4th clause?

That is correct. Things like the ^ operator define stricter semantics than MAY, which still technically fits the spec, but the split between real-world usage and the 4th clause causes endless online arguments. The 4th clause should be brought in line with real-world usage.

@jwdonahue
Copy link
Contributor

Things like the ^ operator define stricter semantics than MAY

I am not familiar with how that operator is applied to version ranges in whatever tool chain(s) you are referring to. Stricter in what way?

@ljharb
Copy link
Contributor

ljharb commented Mar 9, 2023

@jwdonahue in npm, ^ includes only nonbreaking changes. so in ^x.y.z, the y and z may change. Similarly, in v0.x.y and v0.0.x, only the y and z may change - because the x is always the "major", for breaking changes.

(Additionally, semver ranges by default ignore prereleases, unless the root version of the range is itself a prerelease)

@steveklabnik
Copy link
Member

steveklabnik commented Mar 9, 2023

(not only npm, but cargo, and iirc bundler with ~>.)

@jwdonahue
Copy link
Contributor

Ah, the never defined in the SemVer spec convention of slipping the Major version number to the right. Ya, I would object to making that part of the standard, but it's not my call.

I see that as pointless because by definition, breaking changes can and often are, slipped into a prerelease code base. So why pretend otherwise? I don't object to the practice however, I think it's a perfectly valid workflow that is well within the bounds of the spec, along with a couple others I've seen in use.

I was also one of three creators of a packaging tool, built on the same tech that Microsoft's Universal Packages are based on, that is used in the Windows build system that uses proper range notation of the form (VL, VH), (VL, VH], etc. Not my tool anymore since I moved on, but last time I was there, they were still using it to populate tooling and other binary bits that are used in the Window build.

I agree the "0.y.z" form is commonly supported by all semver compliant tools. That's why I think mucking around with it is going to break somebody.

ljharb added a commit to ljharb/semver that referenced this issue Mar 9, 2023
@ljharb ljharb linked a pull request Mar 9, 2023 that will close this issue
@ljharb
Copy link
Contributor

ljharb commented Mar 9, 2023

@steveklabnik filed #923, lmk your thoughts!

@jwdonahue
Copy link
Contributor

I just stumbled across a related comment of old: #127 (comment)

We now have multiple PR's that are related to this topic.

ljharb added a commit to ljharb/semver that referenced this issue Apr 21, 2023
ljharb added a commit to ljharb/semver that referenced this issue May 15, 2023
@jwdonahue
Copy link
Contributor

jwdonahue commented Jun 12, 2023

@ljharb

^ includes only nonbreaking changes. so in ^x.y.z, the y and z may change. Similarly, in v0.x.y and v0.0.x, only the y and z may change - because the x is always the "major", for breaking changes.

If that is true, then NPM is not SemVer compliant if the ^ operator accepts any 0.x.y versions. The 0.y.z form comes with the caveat that "Anything MAY change at any time. The public API SHOULD NOT be considered stable", therefore the ^ operator should exclude all 0.y.z forms if it claims to be SemVer compliant. But NPM is free to innovate however it sees fit to satisfy it's customers. If the ^ operator behaves as you say it does, then it is innovating beyond the bounds of the SemVer spec and should make that clear in the documentation.

As I stated earlier, the 0.major.minor convention is compatible with SemVer because consumers and publishers understand that any 0.x.y form or N.x.y form come with the prerelease caveat that "Anything MAY change at any time. The public API SHOULD NOT be considered stable".

@ljharb
Copy link
Contributor

ljharb commented Jun 12, 2023

@jwdonahue npm isn't semver v2 compliant, because npm existed before the current version of the semver spec. #932 and #584, however, would change the semver spec so that it fully matches npm.

@jwdonahue
Copy link
Contributor

@jwdonahue npm isn't semver v2 compliant, because npm existed before the current version of the semver spec. #932 and #584, however, would change the semver spec so that it fully matches npm

Are you saying that NPM cannot be used in its current form in a SemVer compliant way? Does it not have a -SemVer mode? That would be a sad situation to be in. I am sure there are other packaging tools out there who's owners wish they could bend the SemVer spec to validate their current product behaviors.

This is one reason why I have always argued that the spec should steer clear of the range specification and dependency tree problems. SemVer as it is, is a simple convention to follow. Adding range spec behavior and dependency tracking to it, will constrain innovation and severely complicate the spec. Better to layer-on whatever flavor of range spec you prefer to use in a separate document. In the case of the ^ operator, it sounds like you also have non SemVer compliant semantics of your own.

I don't see any problem with the various packaging schemes applying their preferred semantics, provided they are clear that theirs are not SemVer semantics. SemVer is a specific kind of semantic versioning that is very simple, easy to implement and programming language/tool-chain agnostic. Other than specifying how to sort a list of SemVer version strings, it is entirely agnostic on the subject of range specifications and other implementation details.

Tool owners and development communities may choose to support SemVer or any other form of semantic versioning that they see fit. Claiming to use a form of semantic versioning is not the same thing as claiming SemVer compliance. A tool need not promote any of the semantic forms it supports. I know there are many that predate SemVer which also currently support it while allowing for other forms to be used. It sounds like NPM promotes another form by implication of its range operator specifications.

@ljharb
Copy link
Contributor

ljharb commented Jun 12, 2023

I'm saying that "what semver is" changed after npm had already cemented its semantics, and no, there couldn't possibly be any mode to give it different semantics. Your argument already doesn't work because this spec already changed (widening what changes are expected in v0) in a way that conflicted with existing implementations. #923 fixes that.

@jwdonahue
Copy link
Contributor

Emphasis added by me. Also, that's not a valid link at the end:

I'm saying that "what semver is" changed after npm had already cemented its semantics, and no, there couldn't possibly be any mode to give it different semantics. Your argument already doesn't work because this spec already changed (widening what changes are expected in v0) in a way that conflicted with existing implementations. #932 fixes that.

Hence the SemVer version bump from 1.0.0 to 2.0.0. My argument holds in any case, in fact, I think we should have a 3.0.0 of the spec where we finally deal with the version string disambiguation problem, and only that problem. Some form of VersionMeta would provide a means to disambiguate between the various semantic forms currently in existence. That would give us a solid base to work from, that would not stifle innovation or break existing conventions.

And a quick check of the v1 and v2 specs yields exactly the same language wrt to the 0.y.z form:

Other than the added emphasis in v2, they look the same to me. Notice that nowhere in the history of SemVer does it refer to the 0.x.y form?

If NPM cannot support SemVer in it's current or past forms, then why are they even involved in maintaining this spec? I would be surprised if NPM is truly non-SemVer compliant. It may have non-SemVer features, but those don't have top be used.

@ljharb
Copy link
Contributor

ljharb commented Jun 12, 2023

The purpose of specs is to document reality, not to dictate it. The spec should match what major implementations are already doing.

@jwdonahue
Copy link
Contributor

Then we'd need 10 of them.

@ljharb
Copy link
Contributor

ljharb commented Jun 12, 2023

Or, we'd need one with sufficient minimal union semantics that all 10 can be considered compliant.

@jwdonahue
Copy link
Contributor

There are literally thousands of bespoke tools out there that implement SemVer 2.0.0 as it is. Why break it for a handful of tools that are getting by just fine without it?

@jwdonahue
Copy link
Contributor

The purpose of specs is to document reality, not to dictate it. The spec should match what major implementations are already doing.

No, that's what your product docs are for. This spec was intended by mojombo to be a proposal for a specific form of semantic versioning. I think NPM predates the spec and since https://semver.org/spec/v1.0.0-beta.html, the language regarding the 0.y.z form has been very clear that "The public API should not be considered stable" and was then changed to "Anything may change at any time. The public API should not be considered stable" in the v1 spec..

I don't see anything in Preston's LinkedIn profile indicating that he's ever had anything to do with NPM. I have always suspected that he wrote it in response to dependency issues experienced during the development of GitHub, caused by the subjective use of major.minor.patch versioning that was still common at the time. It's obvious the proposal simply layered tighter semantics on meanings of each of those fields.

I would add that most specifications are written prior to implementation. The general purpose of writing specs is to make it clear how the implementation should behave or what it should look like. I would also add that there are packaging and other versioning related tools out there that do adhere to the spec as written (Nuget for instance).

@ljharb
Copy link
Contributor

ljharb commented Jun 15, 2023

Originalism isn't really useful or realistic - the value of the spec now is that it's what people look to when they want to mirror existing implementations, and if it doesn't match those, it's not doing its job.

@jwdonahue
Copy link
Contributor

There are plenty of implementations that hone to the current spec. Many that are mostly compatible, plus support for other semantics as well. Tweaking the spec to match NPM behavior will break it and render many tools incompatible. From that point on, the spec won't be relevant to anybody but the NPM community and yet another fork will follow.

I think the main variance between all of the tool chains is in their range specifiers and selectors used for defining dependencies. It's already pretty obvious that not even the tools represented by the maintainers can agree how those should operate and they may represent a majority of the package repositories, they are a minority in terms of CI/CD tooling however. There are literally thousands of other tools, some of them representing large vertical segments that reference this spec.

I think the whole point of having a very narrowly defined spec is to clarify the gist of semantic versioning (not really invented by mojombo btw) and leave some space to innovate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
7 participants
@steveklabnik @ljharb @camilamacedo86 @jwdonahue and others