Version incrementing with prerelease #323

Closed
opened 2026-02-17 11:50:43 -06:00 by GiteaMirror · 12 comments
Owner

Originally created by @jadaml on GitHub (Apr 25, 2018).

I do not quite understand how the version numbers shoud be incremented when there is a pre-release node(?) id specified. The ordering of version numbers seem to indicate, that neither major, minor or patch is needed to be incremented, if a new pre-release version is released, yet I somehow feel that at least a patch number must be incremented.

So when 1.1.0-alpha is released, the next version can be 1.1.0-alpha.1 instead of 1.1.1-alpha.1

Maybe I do not fully understand how 1.1.0-alpha.1 is different to 1.1.1-alpha.1 considering that the previous version in 1.1.0-alpha. Can you clarify this for me?

Originally created by @jadaml on GitHub (Apr 25, 2018). I do not quite understand how the version numbers shoud be incremented when there is a pre-release node(?) id specified. The ordering of version numbers seem to indicate, that neither major, minor or patch is needed to be incremented, if a new pre-release version is released, yet I somehow feel that at least a patch number must be incremented. So when 1.1.0-alpha is released, the next version can be 1.1.0-alpha.1 instead of 1.1.1-alpha.1 Maybe I do not fully understand how 1.1.0-alpha.1 is different to 1.1.1-alpha.1 considering that the previous version in 1.1.0-alpha. Can you clarify this for me?
Author
Owner

@klehelley commented on GitHub (Apr 26, 2018):

I am not sure why you feel at least patch number should be incremented.

For starters, 1.1.0-alpha.1 and 1.1.1-alpha.1 are obviously different version, and you have 1.1.0-alpha.1 < 1.1.1-alpha.1. I'll try to detail as much as possible to see if it helps you wrap your head around it (which by my understanding of your post you are pretty close to doing).

  • 1.1.0-alpha is a pre-release version that is directly related to a potential future 1.1.0 stable version.
  • 1.1.1-alpha.1 is a pre-release version that is directly related to a potential future 1.1.1 stable version.

As per the precedence rules described in part 11 of the specification, you have :

  • 1.1.0-alpha < 1.1.1-alpha.1 : not because alpha < alpha.1 but because 1.1.0 < 1.1.1 (the comparison stops there, no need to take the pre-release part of the version into account -- 1.1.0-anything < 1.1.1-anything).
  • 1.1.0-alpha < 1.1.0-alpha.1 : because A larger set of pre-release fields has a higher precedence than a smaller set, if all of the preceding identifiers are equal.

So because of the way the precedence rules are defined, taking all the versions we use as example in both our posts, we have 1.1.0-alpha < 1.1.0-alpha.1 < 1.1.0 < 1.1.1-alpha.1 < 1.1.1.

@klehelley commented on GitHub (Apr 26, 2018): I am not sure why you feel at least patch number should be incremented. For starters, `1.1.0-alpha.1` and `1.1.1-alpha.1` are obviously different version, and you have `1.1.0-alpha.1 < 1.1.1-alpha.1`. I'll try to detail as much as possible to see if it helps you wrap your head around it (which by my understanding of your post you are pretty close to doing). * `1.1.0-alpha` is a pre-release version that is directly related to a potential future `1.1.0` stable version. * `1.1.1-alpha.1` is a pre-release version that is directly related to a potential future `1.1.1` stable version. As per the precedence rules described in part 11 of the specification, you have : * `1.1.0-alpha < 1.1.1-alpha.1` : not because `alpha < alpha.1` but because `1.1.0 < 1.1.1` (the comparison stops there, no need to take the pre-release part of the version into account -- `1.1.0-anything < 1.1.1-anything`). * `1.1.0-alpha < 1.1.0-alpha.1` : because <q>A larger set of pre-release fields has a higher precedence than a smaller set, if all of the preceding identifiers are equal.</q> So because of the way the precedence rules are defined, taking all the versions we use as example in both our posts, we have `1.1.0-alpha < 1.1.0-alpha.1 < 1.1.0 < 1.1.1-alpha.1 < 1.1.1`.
Author
Owner

@jadaml commented on GitHub (Apr 29, 2018):

I am not sure why you feel at least patch number should be incremented.

That is because I'm used another versioning scheme before where there is no pre release notation in the version number, and it would rather increase one of the numbers in a similar scenario. This is the first versioning scheme I see that notes pre releases in the version number.

But then my first hunch was correct.

I think what I miss is the management method of a pre release version of a breaking version, compared to a feature/patch release. A breaking release can have the potential of redefining the API, leaving the possible need of an in-between 0.0 version with an uncertain API-2.0.

Also it feels 0.0.0 should be treated/managed slightly differently from as 1.0.0-alpha, as the prior tells that the API still greatly changes compare to the latter, despite it not being the final either. Is that correct?

@jadaml commented on GitHub (Apr 29, 2018): > I am not sure why you feel at least patch number should be incremented. That is because I'm used another versioning scheme before where there is no pre release notation in the version number, and it would rather increase one of the numbers in a similar scenario. This is the first versioning scheme I see that notes pre releases in the version number. But then my first hunch was correct. I think what I miss is the management method of a pre release version of a breaking version, compared to a feature/patch release. A breaking release can have the potential of redefining the API, leaving the possible need of an in-between 0.0 version with an uncertain API-2.0. Also it feels 0.0.0 should be treated/managed slightly differently from as 1.0.0-alpha, as the prior tells that the API still greatly changes compare to the latter, despite it not being the final either. Is that correct?
Author
Owner

@klehelley commented on GitHub (Apr 29, 2018):

If you know for sure your next version will be breaking compatibility, then there is no issue with having a pre-release related to the expected future version (2.0.0-alpha.1, for example).

However, if you are in a situation where things aren't that clear-cut (for example with breaking compatibility not part of the plans at first, then planned after a few pre-release versions have already been delivered, to finally decide to go back to keeping compatibility...), SemVer does not provide a versioning strategy that makes things clear just by looking at the version numbers for the interface consumer. There are a couple of discussions in other issues about adding a "post-release" versioning scheme to SemVer (see #200), but they've been pretty inconclusive so far as no consensus has been reached.

Regarding the use of the pre-release format, compared to the 0.x.x scheme, the only thing SemVer says in both cases is that the compatibility is not guaranteed. But it advocates no specific scheme regarding the documenting of interface maturity. You are free to use alpha, beta, rc... if you actually have a specific management strategy for the pre-release version and document it on the side. As long as the rules of SemVer (including precedence) are correctly followed, this is valid SemVer as far as the spec is concerned, and thus tools expecting SemVer will work correctly with the version strings you use.

I personally advocate to not just rely on the version string when using SemVer (and also when not using it), be it on the provider or consumer side of the API. Sufficient documentation, in the form of changelog, release note and anything else that would help the use of the interface -- and a proper evaluation of the impact and risk associated with a version upgrade -- should be provided with any version released ; with special care for the ones labelled as stable.

@klehelley commented on GitHub (Apr 29, 2018): If you know for sure your next version will be breaking compatibility, then there is no issue with having a pre-release related to the expected future version (`2.0.0-alpha.1`, for example). However, if you are in a situation where things aren't that clear-cut (for example with breaking compatibility not part of the plans at first, then planned after a few pre-release versions have already been delivered, to finally decide to go back to keeping compatibility...), SemVer does not provide a versioning strategy that makes things clear just by looking at the version numbers for the interface consumer. There are a couple of discussions in other issues about adding a "post-release" versioning scheme to SemVer (see #200), but they've been pretty inconclusive so far as no consensus has been reached. Regarding the use of the pre-release format, compared to the 0.x.x scheme, the only thing SemVer says in both cases is that the compatibility is not guaranteed. But it advocates no specific scheme regarding the documenting of interface maturity. You are free to use `alpha`, `beta`, `rc`... if you actually have a specific management strategy for the pre-release version and document it on the side. As long as the rules of SemVer (including precedence) are correctly followed, this is valid SemVer as far as the spec is concerned, and thus tools expecting SemVer will work correctly with the version strings you use. I personally advocate to not just rely on the version string when using SemVer (and also when not using it), be it on the provider or consumer side of the API. Sufficient documentation, in the form of changelog, release note and anything else that would help the use of the interface -- and a proper evaluation of the impact and risk associated with a version upgrade -- should be provided with any version released ; with special care for the ones labelled as stable.
Author
Owner

@jwdonahue commented on GitHub (Jul 12, 2018):

@jadaml, you are free to increment the version numbers to reflect the relative stability of your changes. There's many ways to look at SemVer's prerelease tags:

They can be phase tags:
1.2.42-beta
1.2.43-beta

Or they can include their own counters:
1.2.42-beta.1
1.2.42-beta.2

Or you can use random alphanumerics:
1.2.42-xyz1
1.2.42-abc2

As @klehelley pointed out, each is a unique version string. As long as their is a prerelease tag in the version string, or the major version number is zero, all of SemVer's semantics around breaking change, new feature or patch, go completely out the window.

@jwdonahue commented on GitHub (Jul 12, 2018): @jadaml, you are free to increment the version numbers to reflect the relative stability of your changes. There's many ways to look at SemVer's prerelease tags: They can be phase tags: 1.2.42-beta 1.2.43-beta Or they can include their own counters: 1.2.42-beta.1 1.2.42-beta.2 Or you can use random alphanumerics: 1.2.42-xyz1 1.2.42-abc2 As @klehelley pointed out, each is a unique version string. As long as their is a prerelease tag in the version string, or the major version number is zero, all of SemVer's semantics around breaking change, new feature or patch, go completely out the window.
Author
Owner

@jadaml commented on GitHub (Jul 13, 2018):

As long as there is a prerelease tag in the version string, or the major version number is zero, all of SemVer's semantics around breaking change, new feature or patch, go completely out the window.

😮 Perhaps this is something that should be pointed out more clearly, as it seems to me that many may not understands this feature.
Wait. Does this means, that none of the pre-release version implementations in a SemVer based software contributes to the 'public release' on which the version number instrumentation specification correlates to?

@jadaml commented on GitHub (Jul 13, 2018): > As long as there is a prerelease tag in the version string, or the major version number is zero, all of SemVer's semantics around breaking change, new feature or patch, go *completely out the window.* 😮 Perhaps this is something that should be pointed out more clearly, as it seems to me that many may not understands this feature. Wait. Does this means, that none of the pre-release version implementations in a SemVer based software contributes to the 'public release' on which the version number instrumentation specification correlates to?
Author
Owner

@jwdonahue commented on GitHub (Jul 13, 2018):

Not exactly sure what you are asking there.

For the 0.y.z prerelease form:

4. 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 for prerelease tags.

9. ... A pre-release version indicates that the version is unstable and might not satisfy the intended compatibility requirements as denoted by its associated normal version. ...

Clearly, the spec allows publishers to make breaking changes with any and every prerelease. While one might think that 1.6.7-ReleaseCandidate indicates a more stable product than 1.6.7-Alpha, the presence of the prerelease tag indicates the product is not stable. The precedence rules say that ReleaseCandidate > Alpha, not because of the apparent semantic meanings of those words, but based strictly on the ASCII sort order of the tags. In terms of risk, both are equal, as are any 0.y.z or prerelease tagged publications. In other words, if it's not in the stable version form x.y.z where x > 0, with no prerelease tag, the release is potentially dangerous to production systems.

There is nothing in the SemVer spec that requires -ReleaseCandidate to be any more stable than -Alpha. Only convention and good manners suggests otherwise, so unless your tooling discriminates based on past history and other side-channel data such as publisher documentation, it can only assume there are four risk buckets:

  • Potentially breaking on every version change (0.y.z and -prereleasetag).
  • Breaking change indicated but not required (major version bump, no prerelease tag).
  • Non-breaking change indicated, new features (minor version bump, no prerelease tag).
  • Non-breaking change indicated fix (minor version bump, no prerelease tag).

SemVer 101, every clause in the spec has purpose. It took me one read to realize I could not grok all of it's implications, but after several passes and some thought, it is quite clear what it says.

@jwdonahue commented on GitHub (Jul 13, 2018): Not exactly sure what you are asking there. For the 0.y.z prerelease form: > [4. ](https://semver.org/#spec-item-4)**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 for prerelease tags. >[ 9. ](https://semver.org/#spec-item-9)... **A pre-release version indicates that the version is unstable and might not satisfy the intended compatibility requirements as denoted by its associated normal version.** ... Clearly, the spec allows publishers to make breaking changes with any and every prerelease. While one might think that 1.6.7-ReleaseCandidate indicates a more stable product than 1.6.7-Alpha, the presence of the prerelease tag indicates the product is not stable. The precedence rules say that ReleaseCandidate > Alpha, not because of the apparent semantic meanings of those words, but based strictly on the ASCII sort order of the tags. In terms of risk, both are equal, as are any 0.y.z or prerelease tagged publications. In other words, if it's not in the stable version form x.y.z where x > 0, with no prerelease tag, the release is potentially dangerous to production systems. There is nothing in the SemVer spec that requires -ReleaseCandidate to be any more stable than -Alpha. Only convention and good manners suggests otherwise, so unless your tooling discriminates based on past history and other side-channel data such as publisher documentation, it can only assume there are four risk buckets: - Potentially breaking on every version change (0.y.z and -prereleasetag). - Breaking change indicated but not required (major version bump, no prerelease tag). - Non-breaking change indicated, new features (minor version bump, no prerelease tag). - Non-breaking change indicated fix (minor version bump, no prerelease tag). SemVer 101, every clause in the spec has purpose. It took me one read to realize I could not grok all of it's implications, but after several passes and some thought, it is quite clear what it says.
Author
Owner

@jadaml commented on GitHub (Jul 13, 2018):

SemVer 101, every clause in the spec has purpose. It took me one read to realize I could not grok all of it's implications, but after several passes and some thought, it is quite clear what it says.

I have read it several times my self, and for me, the more I read it, the less I understood. I still consider this, and will give it another stab.

In fact, now that I have searched normal version and see that it isn't a term that I made up, now I can rephrase better my question! I would like to reply to the rest of your reply before posting the question.

For the 0.y.z prerelease form:

  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.

Of course! But there is no version before 0.y.z, since major version cannot be anything that by the specified values and ordering can come before 0. So in this case, I'm safe to do whatever I want. (Noted in the specification as well.)

And for prerelease tags.

  1. ... A pre-release version indicates that the version is unstable and might not satisfy the intended compatibility requirements as denoted by its associated normal version. ...

I think the highlighted part is my answer here, but it isn't quite clear to me:

  • Does compatibility requirements refers to the breaking-change/feature-update/patch (point 5., 6., 7. and 8.) specifications?
  • Which is considered to be the "associated" normal version for a 'a.b.c-*' version? Is it 'a.b.c'? (This isn't specified in the specification.) By what and how does a normal version denotes anything to a prerelease version?

If I get over these questions however and go with the gut feeling of what this means, then these are the things I conclude, if w, x, y, z are whole non-negative numbers, and y < w:

  • A 'x.w.0-I.Troll.You' version may break compatibility with 'x.y.z' despite it's version numbers indicate otherwise, but 'x.w.0' will retain it's compatibility with 'x.y.z', since it only have updated features.
  • And If a product is at version '1.y.z', and the team starts releasing '2.0.0-I.Troll.You.Hard.#', they could do whatever the hell they want because when deciding compatibility for '2.0.0', only '1.y.z' is considered, and none of the '2.0.0-*' versions between them.

So my rephrased questions (with reference to the specification):
When deciding whether a version number should be incremented considering compatibility (points 6., 7., and 8.), should the prerelease versions also considered (point 9.), or only the normal versions (point 2.)?
After all, ordering includes the prerelease versions between releases.


On a side note, I've just realized: Re-reading the specification again, I don't know how public API declaration in-code is meant. Is this referring to Doxygen-like comments, or like XML documentation for C#? Or perhaps is the code it self is meant to be the API declaration?

  1. [...] This API could be declared in the code itself or exist strictly in documentation. [...]

Edit: Changed x.y+1.z to x.w.0 and described w, x, y, z numbers.

@jadaml commented on GitHub (Jul 13, 2018): > SemVer 101, every clause in the spec has purpose. It took me one read to realize I could not grok all of it's implications, but after several passes and some thought, it is quite clear what it says. I have read it several times my self, and for me, the more I read it, the less I understood. I still consider this, and will give it another stab. In fact, now that I have searched **normal version** and see that it isn't a term that I made up, now I can rephrase better my question! I would like to reply to the rest of your reply before posting the question. > For the 0.y.z prerelease form: > > > 4. Major version zero (0.y.z) is for initial development. Anything may change at any time. The public API should not be considered stable. Of course! But there is no version before 0.y.z, since major version cannot be anything that by the specified values and ordering can come before 0. So in this case, I'm safe to do whatever I want. (Noted in the specification as well.) > And for prerelease tags. > > > 9. ... A pre-release version indicates that the version is unstable and ***might not satisfy the intended compatibility requirements as denoted by its associated normal version.*** ... I think the highlighted part is my answer here, but it isn't quite clear to me: * Does *compatibility requirements* refers to the breaking-change/feature-update/patch (point [5.][p5], [6.][p6], [7.][p7] and [8.][p8]) specifications? * Which is considered to be the "associated" normal version for a 'a.b.c-*' version? Is it 'a.b.c'? (This isn't specified in the specification.) By what and how does a normal version denotes anything to a prerelease version? If I get over these questions however and go with the gut feeling of what this means, then these are the things I conclude, if w, x, y, z are whole non-negative numbers, and y < w: * A 'x.w.0-I.Troll.You' version may break compatibility with 'x.y.z' despite it's version numbers indicate otherwise, but 'x.w.0' will retain it's compatibility *with 'x.y.z'*, since it only have updated features. * And If a product is at version '1.y.z', and the team starts releasing '2.0.0-I.Troll.You.Hard.#', they could do whatever the hell they want because when deciding compatibility for '2.0.0', only '1.y.z' is considered, and none of the '2.0.0-*' versions between them. So my rephrased questions (with reference to the specification): When deciding whether a version number should be incremented considering compatibility (points [6.][p6], [7.][p7], and [8.][p8]), should the [prerelease versions][p9] also considered (point [9.][p9]), or only the [normal versions][p2] (point [2.][p2])? After all, ordering includes the prerelease versions between releases. ----- On a side note, I've just realized: Re-reading the specification again, I don't know how [public API declaration in-code][p1] is meant. Is this referring to Doxygen-like comments, or like XML documentation for C#? Or perhaps is the code it self is meant to be *the* API declaration? > 1. [...] This API could be *declared in the code itself* or exist strictly in documentation. [...] **Edit:** Changed x.y+1.z to x.w.0 and described w, x, y, z numbers. [p1]: https://semver.org/#spec-item-1 [p2]: https://semver.org/#spec-item-2 [p5]: https://semver.org/#spec-item-5 [p6]: https://semver.org/#spec-item-6 [p7]: https://semver.org/#spec-item-7 [p8]: https://semver.org/#spec-item-8 [p9]: https://semver.org/#spec-item-9
Author
Owner

@jwdonahue commented on GitHub (Jul 14, 2018):

A 'x.y+1.z-I.Troll.You' version may break compatibility with 'x.y.z' despite it's version numbers indicate otherwise, but 'x.y+1.z' will retain it's compatibility with 'x.y.z', since it only have updated features.

When you put a plus sign in the middle of your version strings like that, I don't see a legal SemVer string, I see something like x.y+buildMetaBabel. It's easier for everyone if you write out the history you want to talk about in legal SemVer form. So for your "x.y+1.z-I.Troll" example I think you probably meant something like:

1.2.3-I.Troll
1.3.0-I.Troll
...

But I am not clear exactly what you were saying about them. As a consumer of versioned libraries I must treat every prerelease as potentially breaking, whether or not the publisher is trying to indicate that 1.3.0-I.Troll is just a new non-breaking feature addition in the history of the product. In fact, I expect the publisher to try to indicate that fact, because it feeds into my willingness to include it in my next test build and test production systems. While I may have decided to include 1.2.3-I.Troll in my actual production system after testing it, I would not allow an automatic update to a production system from any prerelease feeds. Depending on my history with the producer, I might not allow any auto-update of prereleases to my test beds either.

The producer/consumer dichotomy is very important for anyone working on tooling on either side of that pipeline. Where the producer says something like "bug fix prelease", the consumer hears "potentially breaking change", which is exactly the way the SemVer spec is intended to be interpreted by each side, respectively.

When deciding whether a version number should be incremented considering compatibility (points 6., 7., and 8.), should the prerelease versions also considered (point 9.), or only the normal versions (point 2.)?
After all, ordering includes the prerelease versions between releases.

That last phrase is not necessarily correct. The total release history of a product is just one kind of search result data set. There are others, that do not include any prereleases, or which are limited only to a major version with a particular tag or sub-tag. The possible result sets are literally every unique combination of possible sorted subsets that can be derived from the visible history of the product. For some products, that's a huge number of possibilities. A consumer might want to do a study of the relative stability of every release version compared to; the previous N prereleases in historical order of release, or sorted by one or more prerelease identifiers. Do not attempt to merge producer and consumer business rules!

Now, finally getting to that question. The SemVer spec leaves a lot of wiggle-room for publishers to set their own policies. All eleven SemVer rules come into play, not just the few you mentioned. Here's some possible histories:

H1:
0.1.0 // Initial release to early adopters.
0.1.1 // Bug fix, I don't think I broke anything, but I did not test it.
0.1.2 // Oops! Damn customers always find my bugs before I do.
1.0.0 // I claim this is stable.
1.0.1-alpha // Another bug fix, let the customers test it.
1.0.1-beta // Maybe all I did was test it? Maybe I fixed it? You may never know.

H2:
1.0.0-1.dev // Initial release to early adopters.
1.0.0-2.dev // Bug fix, I don't think I broke anything, but I did not test it.
1.0.0-3.dev // Oops! Damn customers always find my bugs before I do.
1.0.0 // I claim this is stable.
1.0.1-alpha // Another bug fix, let the customers test it.
1.0.1-beta // Maybe all I did was test it? Maybe I fixed it? You may never know.

H1 and H2 are obviously equivalent histories to a consumer, who for the sake of this discussion, can't see the comments. Everything before 1.0.0 was clearly prerelease and that can only mean unstable to the consumer. There's a subtle difference however. The H2 publisher seems less willing to imply any intent of compatibility while H1 publisher seems to be trying to indicate some intent, but the comment indicates they did not actually apply due diligence, so why did they even bother? Now had they tested 0.1.1, their intent would have aligned with their diligence and that would be reflected in their reputation over time, so the answer to your question comes down to what is your intent and how diligent are you willing to be? Another question is, who are your customers? I've seen different policies applied to internal releases than to external releases.

The API is definitely the code, but visible comments can also be part of that interface specification, as can any documentation included with the product. From the introduction:

For this system to work, you first need to declare a public API. This may consist of documentation or be enforced by the code itself. Regardless, it is important that this API be clear and precise. Once you identify your public API, you communicate changes to it with specific increments to your version number.

Lets take two hypothetical API's under consideration:

API.X 1.0.0
// Returns a number.
int GetNum();

API.Y 1.0.0
// Returns randomly distributed results using the Donahue method as documented at www.madeup.com/DonhueMehtod.
int GetNum();

Now, a user of API.X might notice that the GetNum() interface actually returns randomly distributed results that fit the same curve as the Donahue method and take a dependency on it. In fact, maybe 80% of API.X customers rely on this apparent fact. Was it part of the API? I don't think so. I could release API.X 1.0.1 that uses Juhász method instead and it's technically not a breaking change, as I can argue that the GetNum() interface could always return 1 or any other integer and it would not break the intended and stated interface. Now, I could not release an API.Y 1.0.1 based on the Juhász method without rightfully pissing off a lot of customers.

Now, if I am aware that 80% of my API.X customers depend on its close fit to the Donahue method, I probably wouldn't switch methods without issuing substantial warnings. But I might have to if someone else owns a patent on the Donahue method.

Does this help answer your questions?

@jwdonahue commented on GitHub (Jul 14, 2018): > A 'x.y+1.z-I.Troll.You' version may break compatibility with 'x.y.z' despite it's version numbers indicate otherwise, but 'x.y+1.z' will retain it's compatibility with 'x.y.z', since it only have updated features. When you put a plus sign in the middle of your version strings like that, I don't see a legal SemVer string, I see something like x.y+buildMetaBabel. It's easier for everyone if you write out the history you want to talk about in legal SemVer form. So for your "x.y+1.z-I.Troll" example I think you probably meant something like: 1.2.3-I.Troll 1.3.0-I.Troll ... But I am not clear exactly what you were saying about them. As a consumer of versioned libraries I must treat every prerelease as potentially breaking, whether or not the publisher is trying to indicate that 1.3.0-I.Troll is just a new non-breaking feature addition in the history of the product. In fact, I expect the publisher to try to indicate that fact, because it feeds into my willingness to include it in my next test build and test production systems. While I may have decided to include 1.2.3-I.Troll in my actual production system after testing it, I would not allow an automatic update to a production system from any prerelease feeds. Depending on my history with the producer, I might not allow any auto-update of prereleases to my test beds either. The producer/consumer dichotomy is very important for anyone working on tooling on either side of that pipeline. Where the producer says something like "bug fix prelease", the consumer hears "potentially breaking change", which is exactly the way the SemVer spec is intended to be interpreted by each side, respectively. > When deciding whether a version number should be incremented considering compatibility (points 6., 7., and 8.), should the prerelease versions also considered (point 9.), or only the normal versions (point 2.)? > After all, ordering includes the prerelease versions between releases. > That last phrase is not necessarily correct. The total release history of a product is just one kind of search result data set. There are others, that do not include any prereleases, or which are limited only to a major version with a particular tag or sub-tag. The possible result sets are literally every unique combination of possible sorted subsets that can be derived from the visible history of the product. For some products, that's a huge number of possibilities. A consumer might want to do a study of the relative stability of every release version compared to; the previous N prereleases in historical order of release, or sorted by one or more prerelease identifiers. Do not attempt to merge producer and consumer business rules! Now, finally getting to that question. The SemVer spec leaves a lot of wiggle-room for publishers to set their own policies. All eleven SemVer rules come into play, not just the few you mentioned. Here's some possible histories: H1: 0.1.0 // Initial release to early adopters. 0.1.1 // Bug fix, I don't think I broke anything, but I did not test it. 0.1.2 // Oops! Damn customers always find my bugs before I do. 1.0.0 // I claim this is stable. 1.0.1-alpha // Another bug fix, let the customers test it. 1.0.1-beta // Maybe all I did was test it? Maybe I fixed it? You may never know. H2: 1.0.0-1.dev // Initial release to early adopters. 1.0.0-2.dev // Bug fix, I don't think I broke anything, but I did not test it. 1.0.0-3.dev // Oops! Damn customers always find my bugs before I do. 1.0.0 // I claim this is stable. 1.0.1-alpha // Another bug fix, let the customers test it. 1.0.1-beta // Maybe all I did was test it? Maybe I fixed it? You may never know. H1 and H2 are obviously equivalent histories to a consumer, who for the sake of this discussion, can't see the comments. Everything before 1.0.0 was clearly prerelease and that can only mean unstable to the consumer. There's a subtle difference however. The H2 publisher seems less willing to imply any intent of compatibility while H1 publisher seems to be trying to indicate some intent, but the comment indicates they did not actually apply due diligence, so why did they even bother? Now had they tested 0.1.1, their intent would have aligned with their diligence and that would be reflected in their reputation over time, so the answer to your question comes down to what is your intent and how diligent are you willing to be? Another question is, who are your customers? I've seen different policies applied to internal releases than to external releases. The API is definitely the code, but visible comments can also be part of that interface specification, as can any documentation included with the product. From [the introduction:](https://semver.org/#introduction) > For this system to work, you first need to declare a public API. This may consist of documentation or be enforced by the code itself. Regardless, it is important that this API be clear and precise. Once you identify your public API, you communicate changes to it with specific increments to your version number. Lets take two hypothetical API's under consideration: API.X 1.0.0 // Returns a number. int GetNum(); API.Y 1.0.0 // Returns randomly distributed results using the Donahue method as documented at www.madeup.com/DonhueMehtod. int GetNum(); Now, a user of API.X might notice that the GetNum() interface actually returns randomly distributed results that fit the same curve as the Donahue method and take a dependency on it. In fact, maybe 80% of API.X customers rely on this apparent fact. Was it part of the API? I don't think so. I could release API.X 1.0.1 that uses Juhász method instead and it's technically not a breaking change, as I can argue that the GetNum() interface could always return 1 or any other integer and it would not break the intended and stated interface. Now, I could not release an API.Y 1.0.1 based on the Juhász method without rightfully pissing off a lot of customers. Now, if I am aware that 80% of my API.X customers depend on its close fit to the Donahue method, I probably wouldn't switch methods without issuing substantial warnings. But I might have to if someone else owns a patent on the Donahue method. Does this help answer your questions?
Author
Owner

@jadaml commented on GitHub (Jul 14, 2018):

With your response public API is now clear to me. It is also clear to me that it would be generally a better idea to issue a document if possible, as it is more direct.

So for your "x.y+1.z-I.Troll" example I think you probably meant something like:

1.2.3-I.Troll
1.3.0-I.Troll

Indeed, when I wrote x.y+1.z I meant that it is a following version of x.y.z incrementing the minor version (y). I also forgot for in the example, that in such case, the patch version should have been reset. After I post this response, I'll try to edit my response to a format that I believe is more clear; I will introduce w for y+1.

However, utilizing the same format that you have in H1 and H2 my question is more tailored toward a hypothetical version history something like the following:

H3
[...]
1.0.0 // We declare it is stable
1.1.0-dev.1 // We have some great ideas to introduce to the last API
1.1.0-dev.2 // We have introduced the ultimate feature!
1.1.0-dev.3 // We have realized that the ultimate feature is BS and quickly removed it! Luckily we want to release new features anyway, so a new release is inevitable.
1.1.0 // We declare it is stable

Question is, should the hypothetical version 1.1.0 have considered 1.1.0-dev.3's change when choosing the version number, or it did it correctly by only considering 1.0.0, but nothing in between?

Alternatively is the following hypothetical version history correct:

H4
[...]
1.0.0 // We declare it is stable
1.0.1-dev.1 // We couldn't care less what is the next normal version number is, we only know at this point, that it is a followup of 1.0.0
1.0.1-dev.2 // blah blah blah
1.1.0-dev.1 // blah blah
1.1.0-dev.2 // blah blah blah blah blah
2.0.0 // Now that we have declared it stable, we analyzed the changes we stick with at least at this point, and we came to the conclusion that this version must be versioned to 2.0.0. Or 3.0.0. Or 128.0.0 or whatever.

Could have they versioned 1.1.0-dev.2 as 2.1.0-dev.1 and get away with releasing 2.0.0 (stepping back during prerelease but before release) afterwards? Perhaps with a warning?

At this point I also wonder how these are apparent in the specification?

@jadaml commented on GitHub (Jul 14, 2018): With your response public API is now clear to me. It is also clear to me that it would be generally a better idea to issue a document if possible, as it is more direct. > So for your "x.y+1.z-I.Troll" example I think you probably meant something like: > > 1.2.3-I.Troll > 1.3.0-I.Troll Indeed, when I wrote x.y+1.z I meant that it is a following version of x.y.z incrementing the minor version (y). I also forgot for in the example, that in such case, the patch version should have been reset. After I post this response, I'll try to edit my response to a format that I believe is more clear; I will introduce w for y+1. However, utilizing the same format that you have in H1 and H2 my question is more tailored toward a hypothetical version history something like the following: **H3** [...] **1.0.0** // We declare it is stable **1.1.0-dev.1** // We have some great ideas to introduce to the last API **1.1.0-dev.2** // We have introduced the ultimate feature! **1.1.0-dev.3** // We have realized that the ultimate feature is BS and quickly removed it! Luckily we want to release new features anyway, so a new release is inevitable. **1.1.0** // We declare it is stable Question is, should the hypothetical version 1.1.0 have considered 1.1.0-dev.3's change when choosing the version number, or it did it correctly by only considering 1.0.0, but nothing in between? Alternatively is the following hypothetical version history correct: **H4** [...] **1.0.0** // We declare it is stable **1.0.1-dev.1** // We couldn't care less what is the next normal version number is, we only know at this point, that it is a followup of 1.0.0 **1.0.1-dev.2** // blah blah blah **1.1.0-dev.1** // blah blah **1.1.0-dev.2** // blah blah blah blah blah **2.0.0** // Now that we have declared it stable, we analyzed the changes we stick with at least at this point, and we came to the conclusion that this version must be versioned to 2.0.0. Or 3.0.0. Or 128.0.0 or whatever. Could have they versioned 1.1.0-dev.2 as 2.1.0-dev.1 and get away with releasing 2.0.0 (stepping back during prerelease but before release) afterwards? Perhaps with a warning? At this point I also wonder how these are apparent in the specification?
Author
Owner

@jwdonahue commented on GitHub (Jul 14, 2018):

Question is, should the hypothetical version 1.1.0 have considered 1.1.0-dev.3's change when choosing the version number, or it did it correctly by only considering 1.0.0, but nothing in between?

How a version considers another version? What are we talking about here? A tool of some kind perhaps? I kind of get the impression you are working on something that is supposed to decide what version of the package to generate next, but maybe you're interested in which version to consume next?

When defining user scenarios, you have to be very clear about who the user is. Remember we have consumers and producers, and depending on who you ask, you can break those two classes up into a bunch of sub-categories. The producers test automation is a kind of consumer, so it will have different rules regarding which is the next version to update to, than an end user might have, and I could go on for too long.

I think you're focused on how to go about generating the next version?

SemVer gives you lots of latitude. You can go with the target-release scheme as you seem to be doing in H2 and H3, or you can go with the monotonic-version-triple, where every publication applies the full semantic versioning scheme to the version triple, just to name two of the infinite possiblities. The later, tends to lead to faster changing version triples over time and it can be confusing to derive a real history from the visible version strings. Consider this scenario:

H5
[...]
1.0.0
1.1.0-a.dev // New feature
2.0.0-a.dev // Removed feature.
2.1.0-a.dev // Added another feature.
2.1.0-alpha // Early adopter release.
2.1.1-alpha // Bug fix.
2.1.1-beta // Wider audience.
// 2.1.1 or 1.1.0 ?

That's entirely up to the publisher! If you think about the potential consumers at each sequential publication, you are telling them exactly what to expect with regard to back-compat with the previous publication. The terminal release version in question, is not a breaking change relative to the previous release (1.0.0), so 1.1.0 would be perfectly legal as far as SemVer is concerned, as is 2.1.1.

Now I ask you to think about that, and the many other possible histories, in terms of a main release branch and a developer branch, or many branches? What developer scenarios are you trying to support? Don't ask "what can I do with SemVer?", ask "does this scenario violate any SemVer clauses?", because the set of constraints is infinitely smaller than the set of can-do's.

Also keep in mind that producers see the full history unfolding as they go, but consumers generally have a much more limited view of the historical set. For any possible producer scenario, think of the consequences of randomly removing half or more of the history, and that's typically what consumers see. In some cases, years and hundreds of publications go by before a consumer queries a feed for the available versions that meet their query criteria.

My best advice is to say what you do and do what you say, but that's a side-channel of information that SemVer doesn't cover. You should also adopt one of the packaging/publishing tool chains that supports SemVer. Nuget and NPM come to mind, but there are many others. They tend to encourage producers and consumers to use a more limited set of best practices, but with some effort, you can get out of the box. If you're developing your own tooling, get to know some of the dominant packaging tools anyway,

@jwdonahue commented on GitHub (Jul 14, 2018): > Question is, should the hypothetical version 1.1.0 have considered 1.1.0-dev.3's change when choosing the version number, or it did it correctly by only considering 1.0.0, but nothing in between? How a version considers another version? What are we talking about here? A tool of some kind perhaps? I kind of get the impression you are working on something that is supposed to decide what version of the package to generate next, but maybe you're interested in which version to consume next? When defining user scenarios, you have to be very clear about who the user is. Remember we have consumers and producers, and depending on who you ask, you can break those two classes up into a bunch of sub-categories. The producers test automation is a kind of consumer, so it will have different rules regarding which is the next version to update to, than an end user might have, and I could go on for too long. I think you're focused on how to go about generating the next version? SemVer gives you lots of latitude. You can go with the target-release scheme as you seem to be doing in H2 and H3, or you can go with the monotonic-version-triple, where every publication applies the full semantic versioning scheme to the version triple, just to name two of the infinite possiblities. The later, tends to lead to faster changing version triples over time and it can be confusing to derive a real history from the visible version strings. Consider this scenario: H5 [...] 1.0.0 1.1.0-a.dev // New feature 2.0.0-a.dev // Removed feature. 2.1.0-a.dev // Added another feature. 2.1.0-alpha // Early adopter release. 2.1.1-alpha // Bug fix. 2.1.1-beta // Wider audience. // 2.1.1 or 1.1.0 ? That's entirely up to the publisher! If you think about the potential consumers at each sequential publication, you are telling them exactly what to expect with regard to back-compat with the previous publication. The terminal release version in question, is not a breaking change relative to the previous release (1.0.0), so 1.1.0 would be perfectly legal as far as SemVer is concerned, as is 2.1.1. Now I ask you to think about that, and the many other possible histories, in terms of a main release branch and a developer branch, or many branches? What developer scenarios are you trying to support? Don't ask "what can I do with SemVer?", ask "does this scenario violate any SemVer clauses?", because the set of constraints is infinitely smaller than the set of can-do's. Also keep in mind that producers see the full history unfolding as they go, but consumers generally have a much more limited view of the historical set. For any possible producer scenario, think of the consequences of randomly removing half or more of the history, and that's typically what consumers see. In some cases, years and hundreds of publications go by before a consumer queries a feed for the available versions that meet their query criteria. My best advice is to say what you do and do what you say, but that's a side-channel of information that SemVer doesn't cover. You should also adopt one of the packaging/publishing tool chains that supports SemVer. Nuget and NPM come to mind, but there are many others. They tend to encourage producers and consumers to use a more limited set of best practices, but with some effort, you can get out of the box. If you're developing your own tooling, get to know some of the dominant packaging tools anyway,
Author
Owner

@jwdonahue commented on GitHub (Oct 11, 2018):

@jadaml, if you have no further questions, please close this issue at your earliest possible convenience.

@jwdonahue commented on GitHub (Oct 11, 2018): @jadaml, if you have no further questions, please close this issue at your earliest possible convenience.
Author
Owner

@jadaml commented on GitHub (Oct 14, 2018):

Yes, of course. Sorry.

@jadaml commented on GitHub (Oct 14, 2018): Yes, of course. Sorry.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/semver#323