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

proposal: Go 2: start using semantic versions for Go releases #32450

Open
dmitshur opened this issue Jun 5, 2019 · 17 comments

Comments

Projects
None yet
7 participants
@dmitshur
Copy link
Member

commented Jun 5, 2019

At this time, the Go distribution uses versions that look like this:

go1
go1.12
go1.12.5
go1.11beta1
go1.11rc2

Those version strings are used both in the output of go version command, and as tag names in the source code repository.

Semantically, each version encodes a major, minor, and patch component, as well as optional pre-release metadata. However, the exact version string differs from semantic versions as defined by Semantic Versioning 2.0.0 specification in the following ways:

Differences
  1. If a trailing version component is zero, it's omitted rather than preserved.
  2. The second version component is referred to as "major version" rather than "minor version", and the third version component is referred to as "minor version" rather than "patch version". (For example, see https://github.com/golang/go/wiki/MinorReleases which by semver standard should be talking about patch versions, not minor versions.)
  3. Pre-release versions are denoted by appending a label immediately following a minor version, rather than by appending a hyphen and a series of dot separated identifiers immediately following the patch version.
  4. The prefix is "go" rather than "v".

Go's current version strings are nice-looking and familiar, and this scheme was created before the existence and proliferation of https://semver.org. However, there are some disadvantages compared to following semver:

  1. When someone talks about a "minor release" of Go, it's sometimes hard to know if they mean "x.Y.z" minor, or "x.y.Z" minor.

    For people who are familiar with semver, it's difficult to refer versions such as 1.12.6 as "minor releases" because semver refers to the increased version component as "patch", and it's hard to know if the target audience will understand what you actually meant.

    Following semver conventions, being as widespread as it is today, makes it possible to refer to all 3 version components without ambiguity or confusion.

  2. Omitting the trailing version component makes it difficult to have clear conversation about those versions of Go releases.

    For example, when someone says "I was able to reproduce the problem with go1.12", it's hard to tell if they mean it happens only the exact version go1.12[.0] only, or if they mean any patch version, including go1.12, go.1.12.1, go1.12.2, go1.12.3, etc.

    Similarly, it's hard to refer to the initial release version with a trailing zero version component. It can be temping to explicitly say "try it with go1.12.0" in order to be clear you mean that exact version and not any other.

  3. Modules have adopted semver, and many people are familiar with semver. Right now, the Go project uses two different version string schemes. By adopting semver for Go release versions, we can reduce it from two schemes to just one scheme. It's simpler, more consistent and fewer things for users to learn and keep in their head.

Performing any change to the existing version scheme has transitional costs. However, I think the medium- and long-term benefits will outweigh the costs by a wide enough margin to consider this.

This proposal is to consider changing the Go release version format to match that of Go module versions, which is semver-compliant. Specifically, the trailing zero version components should always be included, the "go" prefix changed to "v", and beta/RC pre-release suffixes represented in a way that satisfies semver.

The proposed version strings can look like this:

v2.0.0
v2.12.0
v2.12.5
v2.11.0-beta.1
v2.11.0-rc.2

@dmitshur dmitshur added this to the Proposal milestone Jun 5, 2019

@ianlancetaylor

This comment has been minimized.

Copy link
Contributor

commented Jun 5, 2019

I don't see anything "Go 2" about this proposal. It's not a language or library change, and could presumably take effect immediately. Any reason to keep the Go2 label here?

@aofei

This comment has been minimized.

Copy link

commented Jun 5, 2019

I love this proposal. I happened to have a problem with the runtime.Version today. So the naming of the Go versions caught my attention too. A huge thanks to you @dmitshur. 😁

However, I think we also need to pay attention to the Go versions without tags. According to the comment of the runtime.Version:

Version returns the Go tree's version string. It is either the commit hash and date at the time of the build or, when possible, a release tag like "go1.3".

The "commit hash" will disrupt the new naming rules you proposed. So, I think we can use some credits of Go modules. We can rename the original "commit hash" to "v0.0.0-20060102150405-0123456789ab". Then we can do something like semver.Compare(runtime.Version(), "v1.13.0").

@dmitshur

This comment has been minimized.

Copy link
Member Author

commented Jun 5, 2019

could presumably take effect immediately

Do you mean retroactively changing all previous releases to start using a new version scheme, or to start using it for new releases only?

I proposed this for Go 2 in the sense we can consider starting to do this as of Go v2.0.0 release. I didn't think it was possible to retroactively change previous releases, and mixing-and-matching different versioning schemes within v1.x.x releases didn't seem great either.

However, we can consider those options too. In that case, the Go 2 label isn't necessary.

Edit: As @aofei mentioned, there are additional considerations such as output from runtime.Version, build constraints, etc.

@ianlancetaylor

This comment has been minimized.

Copy link
Contributor

commented Jun 5, 2019

As discussed at https://github.com/golang/proposal/blob/master/design/28221-go2-transitions.md, it is, I think, unlikely that there will ever be a Go v2.0.0 release. But if you want to wait for that in case it happens, that is OK with me, in which case we should put this proposal on hold for now.

@dmitshur

This comment has been minimized.

Copy link
Member Author

commented Jun 5, 2019

I think keeping it targeted at Go 2 and putting on hold for now is okay.

I wanted to write up this initial proposal to have a proposal to reference and gather initial feedback. For now, it can continue to target a hypothetical v2.0.0 release. I can take the time to flesh out the details and see what would it take to implement this concretely in an earlier v1.x.x release, and then we can re-consider it for that.

@aofei

This comment has been minimized.

Copy link

commented Jun 5, 2019

Wait until v2.0.0 to adopt this proposal? lol 😂

I agree with @ianlancetaylor, there should be no "Go2". If this proposal is to be adopted, then it should take effect immediately. In fact, I think we should have done this long ago. I mean this proposal should be proposed along with the Go modules proposal. Unfortunately, it does not. Why is Go urging everyone to strictly follow the semver, but Go itself doesn't do it? This should not happen.

@ianlancetaylor

This comment has been minimized.

Copy link
Contributor

commented Jun 5, 2019

See #27255.

@dmitshur

This comment has been minimized.

Copy link
Member Author

commented Jun 5, 2019

Thanks for pointing out #27255 to me. I hadn't found it when I was doing a search to see if this was proposed in the past. It covers an important subset of this proposal, so I plan to read through it and take the information there into account.

@robpike

This comment has been minimized.

Copy link
Contributor

commented Jun 5, 2019

It would be a service to the community at large to have Go's module system use the strict semver model (https://semver.org/), which is three numeric fields separated by periods, followed by optional prerelease and build tags. There should always be 3 numbers and the 'v' prefix is spurious.

The argument for rejection in #27255 was, in essence, it's only us so it doesn't matter. But Go is part of a larger world and there are tools that work across languages. Also, large systems often contain many languages and consistency across their handling eases development. It would be better to lead by following the proposed standard than to stick to the unique form it has now.

@adg

This comment has been minimized.

Copy link
Contributor

commented Jun 5, 2019

We have a lovely consistent set of versions dating all the way back to Go 1. If we change the way Go version strings are written then anything that consumes such strings needs to consume both the old format and the new one. Or am I missing something?

@dmitshur

This comment has been minimized.

Copy link
Member Author

commented Jun 6, 2019

@adg I didn't include a section talking about the costs of implementing this proposal; perhaps I should have. You're absolutely right, that is one of the disadvantages of changing the version format.

I can think of some ways of dealing with it:

  1. At some point long long in the future, v1.x.x versions may become very old and less actively looked at by most users. This is similar to how today, versions before the initial go1 release are rarely looked at in practice. It's still important to keep them for historical reasons, but I doubt tags like release.r57.1 or weekly.2012-01-27 see much use, so it's more okay that they use a different format.

  2. If we limit the scope of this proposal to tags, then perhaps we can retroactively apply semver tags to all previous go1.x.x releases. That would be backwards compatible because all old tags would stay around. However, new users and tools can choose to start using semver tags and would only need to parse/handle those to have support for all versions of Go from v1.0.0 until the most recent version.

@aofei

This comment has been minimized.

Copy link

commented Jun 6, 2019

I found these for you @robpike, and I think their discussions may be helpful in determining whether there should be a "v" prefix: semver/semver#204 semver/semver#235 especially semver/semver#424 (comment)

And BTW, semver itself is using the "v" prefix. See https://github.com/semver/semver/releases.

@aofei

This comment has been minimized.

Copy link

commented Jun 6, 2019

After all, I think it's okay to use v1.0.0, go1.0.0 or even Go v1.0.0 (definitely not), just having their main parts strictly follow the semver is enough. Seems that the v1.0.0 style is more friendly and acceptable, since the Go modules has already rolled it out (https://github.com/golang/go/wiki/Modules#semantic-import-versioning).

What @rsc mentioned in #27255 (comment) seems that he sees this kind of changes as incompatible. I don't know, maybe. For me, I can only find one incompatible change at the moment, which is the output style of the runtime.Version. But I don't think it is enough to be an obstacle. If someone is really dependent on the output of the existing runtime.Version, then I think we can add a new func, such as runtime.Semver, perhaps.

Edit: In my opinion, implementing this new Go version naming convention from v1.13.0 will be very flattering, since v1.13.0 will bring huge enhancements to the Go modules.

@ng-0

This comment has been minimized.

Copy link
Contributor

commented Jun 6, 2019

We have a lovely consistent set of versions dating all the way back to Go 1. If we change the way Go version strings are written then anything that consumes such strings needs to consume both the old format and the new one. Or am I missing something?

Whoever (edit: to clarify: distributors, packagers, etc) has an update mechanism for Go in place today, will be able to make a small change by saying "this range follows what we previously defined for Go" and "everything else follows semantic versioning".

@adg

This comment has been minimized.

Copy link
Contributor

commented Jun 6, 2019

@ng-0 yes, precisely my point.

@thepudds

This comment has been minimized.

Copy link

commented Jun 6, 2019

There would be some advantages to having the Go tool itself following semver, but I would be OK if that doesn't happen, including because there are a different set of goals, constraints and possible mistakes that are in play for versioning an API or program (which is arguably what semver is most targeting), vs. versioning a programming language.

As I understand it, there is an option and likely plan to introduce breaking changes without incrementing the 1 for Go, but to do so carefully, and without redefining the meaning of any current programs, and while still allowing for example the removal of capabilities. For example, from the "Go 2 transition" proposal

I think the only feasible safe approach is to not permit language redefinitions.

and

For example, for issue #20733, the range issue, we could change range loops so that taking the address of a range parameter, or referring to it from a function literal, is forbidden. This would not be a redefinition; it would be a removal.

Perhaps one could argue that the go version directive in go.mod is a way of preserving backwards compatibility and hence perhaps one could argue that even if Go was to be under semver then those types of changes could happen without incrementing the major version of 1 for Go? However, the future semantics of the go version directive in go.mod seems to be a matter of some discussion or at least not widely understood yet (#30791), so I should probably not rely too much on my own understanding of how that might work.

That said, a narrower version of this proposal would be to always have the last dot for any future initial major release, such as go1.14.0 rather than go1.14.

Of all the flavors of version strings @dmitshur listed in his opening comment, I think versions like go1.12 and go1.13 are the most unnecessarily confusing and ambiguous. (I am ignoring go1 for now as a historical artifact).

@bcmills wrote in #29984 (comment):

Personally, I think it's pretty confusing to have the first patch version in the 1.11 line be named go1.11 rather than go1.11.0, especially given that go get some/module@1.11 in module mode does mean “the latest patch release of some/module with minor version 1.11”. The lack of minor-version on the first release buries a major clue to the user that there might be more recent versions available.

I would like to see us release Go 1.12 as go1.12.0 instead of go1.12.

@mvdan wrote:

I too have wished for 1.11 to be 1.11.0 for a while. Too many users think they're running the latest 1.11 because they're on 1.11. "What do you mean the latest 1.11? I am on 1.11"

@dmitshur, I am guessing you would view that as a completely separate proposal rather than a variation of your current proposal, but wanted to at least mention it, including it could be viewed as a smaller but useful step towards your proposal.

@dmitshur

This comment has been minimized.

Copy link
Member Author

commented Jun 7, 2019

@thepudds Thanks for surfacing that information, it's helpful to see that more people have voiced a desire for the ".0" suffixes to not be omitted.

I am guessing you would view that as a completely separate proposal rather than a variation of your current proposal

This proposal does not have a very specific implementation plan attached to it yet, and so I view a proposal to include trailing zero version components as closely related to this proposal.

To repeat and summarize my proposal at a high level, it is "use semver for Go release versions". The three benefits I'm looking to achieve are:

  1. Eliminate confusion/ambiguity about meaning of "go1.12" by always including zero version components, i.e., use "go1.12.0".
  2. Eliminate confusion/ambiguity about meaning of "minor version" by using semver notation to refer to version components (i.e., in goX.Y.Z, X should be called "major", Y "minor", and Z "patch").
  3. Eliminate use of two different version schemes (semver for module versions, Go's own notation for Go releases).

To me, both 1 and 2 are very important goals. 3 is nice but much less important.

As a result, a proposal/solution that resolves 1 would be a great win to me.

However, I suspect the cost of making any change, regardless of how small or trivial, to the version scheme used by Go releases is quite high. As a result, if we're going to change it from just go "go1.12" -> "go1.12.0", then I suspect the additional cost of changing prefix from "go" to "v" and doing the minor "beta1" -> "-beta.1" change to make it fully semver compliant would be relatively small that it'd be better to do it all at once rather than not.

This is why I framed this proposal about moving completely to semver, rather than a smaller "go1.12" -> "go1.12.0" change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.