How does Go ship behavior changes without breaking old code? Explain GODEBUG, the godebug directive and //go:debug. What does this print?
// go.mod: go 1.20 (built with a Go 1.24 toolchain)
package main
import "fmt"
func main() {
defer func() { fmt.Printf("%T\n", recover()) }()
panic(nil)
}
Answer: with go 1.20 it prints <nil>, because recover() returns nil and so looks like "no panic". Change the go line to go 1.21 or later and it prints *runtime.PanicNilError.
Since Go 1.21, behavior changes that could break programs are gated behind named GODEBUG settings (such as panicnil, asynctimerchan, httpmuxgo121, x509keypairleaf). The default for each setting comes from the main module's go line only. The go lines of dependencies do not matter. So upgrading the toolchain keeps the old behavior, and raising your go line opts you in.
// go.mod (Go 1.23+): keep old behavior while moving the go line forward
go 1.24.0
godebug (
panicnil=1
asynctimerchan=1
)
// or in a main package source file, before the package clause:
//go:debug panicnil=1
package main
// or at run time (highest priority):
// GODEBUG=panicnil=1,http2client=0 ./app
go list -f '{{.DefaultGODEBUG}}' ./cmd/app # what got baked in
Gotchas: //go:debug is honored only in the main package (and in test files for tests). Settings are kept for at least two years (four releases) and then may be removed. The runtime exposes /godebug/non-default-behavior/<name>:events metrics, so you can measure whether the old behavior is still being used before you drop the override. Interviewers want "defaults follow the main module's go line, which is why toolchain upgrades are safe".
More on Modules, Packages & Tooling
- Q437A vulnerable dependency shows up in your build. How do you find out why it's there and upgrade it safely?
- Q438What is Profile-Guided Optimization (PGO) in Go and how do you use it in production?
- Q440What are module graph pruning and lazy module loading (Go 1.17), and why did go.mod files get so much longer?
- Q441Explain the build cache, the module cache and test result caching. When does go test print "(cached)" and how do you defeat it?
- Q442What is //go:linkname, and what changed about it in Go 1.23?