This is a feature request for the default behavior of the go mod init
command (doc, ref).
Background
This command creates a go.mod file for a new module, initialized with two directives: module
and go
. The module path, a part of the module
directive, is provided explicitly to go mod init
via an argument in many cases. The version used in the go
directive is not explicitly specified by the user, and so its value is currently inferred from the currently selected version of the Go toolchain. For example:
$ cd $(mktemp -d)
$ go mod init example.com # using go version go1.25rc2
go: creating new go.mod: module example.com
$ cat go.mod
module example.com
go 1.25rc2
The go command has no good way of knowing what value of the go
directive the user intends to use in the module being created eventually, so when picking an initial value for it, it uses a simple heuristic: "the latest version supported by this toolchain".
This behavior has a property of being simple, predictable, and importantly, it can work while completely offline (i.e., no internet connection required).
Feature Request
I suggest changing the initial default version to the following behavior:
- if the current toolchain version is a stable version of Go 1.N.M, default to
go 1.(N-1).0
- if the current toolchain version is a pre-release version of Go 1.N (Release Candidate M) or a development version of Go 1.N, default to
go 1.(N-2).0
Using version types as defined here. This behavior maintains the property of being able to work offline. While more complex and less predictable, it is still fairly simple.
Examples of the new behavior:
$ cd $(mktemp -d)
$ go mod init example.com # using go version go1.25rc2
go: creating new go.mod: module example.com
$ cat go.mod
module example.com
go 1.23.0
$ cd $(mktemp -d)
$ go mod init example.com # using go version go1.24.5
go: creating new go.mod: module example.com
$ cat go.mod
module example.com
go 1.23.0
(Edit: The go work init
command also chooses an initial value for its go directive, and so it likely makes sense to have it continue to follow what go mod init
does.)
Motivation
The motivation of adopting the new default value is to provide what should be a better initial value of the go
directive in most cases, especially for cases where the user will choose not to pick a different value. Per the Go release policy, we support the two most recent major releases of Go, so provided one of those is used during go mod init
, the new default value will never cut off one of the currently supported Go toolchains.
It's still a heuristic and is imperfect: if someone's using a supported Go toolchain like Go 1.23.11, its go mod init
would pick go 1.22.0
while it could also pick go 1.23.0
without cutting off a currently supported Go toolchain. It's not a goal of this feature request to add further complexity to avoid that. Ultimately the user will be able to do go get go@{desired version}
whenever the default needs to be changed. This also applies to modules that only provide commands, not packages to be imported by others, where the user may intentionally wish to require a newer major Go version. As for why ".0" and not a newer minor release, motivation is similar to Why not bump on each minor release? of a past proposal, and because it can be done without needing internet.
Alternatives
The go get command supports version query latest
and patch
. For example, go get go@latest
or go get go@patch
. If there was another query added, something that would resolve to "oldest supported major release", and if it were desirable for go mod init
to resolve it over the internet, then that query could be reused. I considered this but decided the simpler, query-free path should be a better place to start with. This is still something that can be done in the future if desired.
CC @golang/command-line.
Comment From: seankhliao
Given that language features, and compatibility settings are gated on the go directive, I think this is a worse default, as starting out with a latest toolchain, you wouldn't be able to use the latest features without manually updating the go version. Setting it as default would also mean the default experience is to have editor / vet warnings for using standard library packages and functions of the toolchain you have installed.
Wanting to support a previous release should be a conscious choice.
Comment From: dmitshur
Yes, that is intentional. The go directive sets both the semantics of the module and the minimum version required to use it. If someone has a need to rely on recently added features or semantics, they'd need to explicitly select the version.
I'm suggesting the inverse: if a user goes all the way from creating a module locally to publishing it for consumption by other users, and does not explicitly change the go directive from the value that go mod init
chose, I think it being initialized to 1.(N-1).0 should lead to a better outcome. It means any other module whose minimum version requirement is already a supported Go version will be able to require the newly published module without involving a go directive increase.
Comment From: gabyhelp
Related Issues
- proposal: cmd/go: refine the default language version based on context #36875 (closed)
- cmd/go: go mod init should be able to specify go/toolchain version #61299 (closed)
- proposal: cmd/go: rename go mod init to go init #26801 (closed)
- cmd/go: inconsistent behavior when 'go' directive is missing from 'go.mod' #44976 (closed)
- proposal: cmd/go: GOTOOLCHAIN=mod to use exact version of toolchain directive #69956 (closed)
- cmd/go: default to \& improve -mod=readonly #40728 (closed)
Related Code Changes
- cmd/go: check missing go line in go.mod when 'go get go@1.N.P'
- cmd/go: add 'go version' statement in go.mod
Related Documentation
Related Discussions
(Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.)
Comment From: mvdan
I agree with this proposal. Most modules I write don't need to require the very latest Go language version, and most of the modules I maintain tend to support two versions.
Plus, since I daily drive tip, I practically always have to fix up a module after go mod init
if I want it to work anywhere else.