What does the `go 1.22` line in a module's go.mod declare, and is it the Go version you must have installed?
answer
- one line, two separate jobs
- a floor, not the compiler you have
- minimum release plus language version
- newer compiler, still the old semantics
- go mod edit -go= raises it deliberately
basics
~20 sThe go line names the minimum Go release the module needs and the language version its files compile under. It is a floor, not the exact compiler you run: any newer Go release builds the module fine, and still applies the older language version.
solid answer
~40 sThe `go` directive in go.mod does two jobs. It is a minimum requirement: since Go 1.21 the go command treats a go line newer than the release doing the build as an error rather than compiling anyway. And it sets the language version for every file in the module, so language features introduced after that version are rejected even when a much newer release is compiling. It says nothing about which compiler you happen to have installed — Go 1.27 will happily build a `go 1.22` module and will keep giving it Go 1.22 language semantics. Raising it is a deliberate edit: `go mod edit -go=1.24`, or `go mod tidy -go=1.24` if you also want requirements reconciled in the same step.
code
mod · 5 linesmodule example.com/internal/telemetry
go 1.22
require example.com/other v1.4.0go deeper
Be ready to say the two things the line means in one breath: minimum Go release required, and the language version the module's files compile under. Knowing it is not the compiler you installed is the whole point.
Explain the mechanics: which kinds of change are gated on it (language features) versus which are not (standard-library symbols, which come from the installed release), and name the commands that change it.
Show the production angle. A go line that is too low hides new-API usage behind confusing undefined-symbol errors for consumers; one that is too high blocks builds. Know how to read every module's go line before you upgrade anything.
Own the framing that the directive is a compatibility floor imposed on everyone downstream. Argue where the floor should sit for the modules your organisation publishes, and who is consulted before it moves.
## The one line, and its two jobs Every module has a `go.mod` file, and near the top it carries a directive like: ``` module example.com/internal/telemetry go 1.22 ``` That single line does two different things, and most confusion about it comes from collapsing them into one. ### Job 1: a minimum required release The go line says "this module needs Go 1.22 or later". Since **Go 1.21** that is a hard requirement: if the release doing the build is older than the go line, the go command reports an error naming the required version instead of attempting the compile, unless it is able to run a newer Go release instead. Before Go 1.21 the line was advisory — the toolchain compiled anyway and only mentioned the module's go version as a note appended to whatever compile error you got, which produced very confusing bug reports ("undefined: something" plus a version hint at the bottom). It is a **floor**, not a pin. Nothing stops a newer release from building the module; the whole point is that a Go 1.27 install can build modules whose go lines say 1.16, 1.21 or 1.25. ### Job 2: the language version The go line also sets the **language version** used to compile every file in the module. Language changes in Go are gated on it, so a module with a low go line does not silently pick up new syntax and semantics when someone builds it with a newer compiler. Concretely: - `min`, `max` and `clear` builtins arrived in **Go 1.21**; - per-iteration loop variables and `for range` over an integer arrived in **Go 1.22**; - range-over-function iterators arrived in **Go 1.23**; - generic type aliases arrived in **Go 1.24**. A module whose go.mod says `go 1.20` gets none of those, no matter how new the installed compiler is. Reach for one and the compiler rejects it with a message that names the language version it was given and points you back at go.mod. This is what makes the go line a *feature gate*. It exists so that upgrading your compiler is a safe, boring operation: the language your existing code is compiled as does not move until you move it. ### What the go line is not - **Not the compiler version you installed.** Those are independent; the installed release just has to be at least as new. - **Not a version of your dependencies.** Each dependency records its own go line and is compiled at its own language version. - **Not necessarily a per-file setting.** It applies module-wide, although a `//go:build go1.x` constraint can raise the language version for a single file. ### A standard-library subtlety worth knowing The go line gates *language* features. Standard-library **symbols** come from whatever release compiles the code, so a `go 1.20` module that calls a function introduced in Go 1.24 will compile happily on a Go 1.24 install and fail with a plain "undefined" error for anyone on Go 1.22 — a much worse error than a version requirement. Since **Go 1.27** `go test` runs the `stdversion` vet check by default, which reports uses of standard-library symbols that are too new for the module's effective Go version, turning that trap into a clear diagnostic. When you start depending on new stdlib APIs, raise the go line so consumers get an honest version error instead. ### Changing it `go mod edit -go=1.24` rewrites just the directive. `go mod tidy -go=1.24` sets it and reconciles the module's requirements to what that version implies. Either way, raising it is a decision with other people attached: every consumer of your module inherits the floor.
- Does building a `go 1.20` module with Go 1.27 give it any Go 1.22 language features?No. The language version comes from the module's go line, so the code compiles under Go 1.20 rules: no per-iteration loop variables, no `for range` over an integer, no `min`/`max` builtins. The newer compiler only means the module is allowed to build at all; it does not upgrade the semantics.
- What changed about the go directive's status in Go 1.21?It became a hard minimum. From Go 1.21 on, a go line newer than the release doing the build is reported as a version error rather than being treated as advice. Before that the toolchain compiled anyway and only appended a note about the required version when compilation happened to fail.
- What is the difference between `go mod edit -go=1.24` and `go mod tidy -go=1.24`?`go mod edit -go=1.24` mechanically rewrites the directive and touches nothing else. `go mod tidy -go=1.24` sets the same directive and then reconciles the module's requirement list to what that language version implies, rewriting go.mod and go.sum as needed. Use edit for a surgical change, tidy when you want the file consistent afterwards.
It is like a document saying "requires Word 2016 or later, formatted for Word 2016": a newer editor opens it, but it is still laid out by the older rules until someone re-saves it.
saying these in an interview costs you the question
- Says the go line pins which compiler version is used
- Thinks a newer Go install automatically enables newer language features
- Believes the go line is only documentation with no effect
- Confuses the go directive with a dependency's version
- Assumes every dependency compiles at the main module's language version