rgoussu@goussu: ~/library/go/toolchain
~/library/go/toolchain cat go-generate.md

go generate

# Explicit, committed code generation via //go:generate directives — Go's answer to annotation processing and reflection-heavy magic.

Conceptsaved 2026-08-09 #go#tooling#codegen

Overview

go generate scans source files for //go:generate <command> comments and runs them — nothing more. It is never invoked by go build; generation is an explicit developer step whose output is committed to version control. That deliberate dumbness is the point: no build-time magic, no hidden classpath scanning, just checked-in Go code anyone can read.

Key points

  • A directive is a comment: //go:generate stringer -type=Status placed near the declaration it serves; go generate ./... runs all of them in file order. The command gets $GOFILE, $GOPACKAGE, $GOLINE in its environment.
  • The usual suspects: stringer (String() for enums), mockgen/moq (interface mocks), sqlc (type-safe code from SQL), protoc/buf (protobuf/gRPC stubs), oapi-codegen (OpenAPI handlers).
  • Generation beats reflection: where Java reaches for annotation processing or runtime reflection (see javac annotation processing), Go generates plain code ahead of time — greppable, debuggable, no runtime cost, no framework classloader tricks. encoding/json is the notable stdlib exception that pays the reflection tax.
  • Commit the output: generated files (conventionally *_gen.go or a // Code generated … DO NOT EDIT. header) go into VCS, so go build and go install work from a bare clone with no generators installed. CI re-runs generate and fails on diff to catch drift.
  • Pin generator versions: since 1.24 a tool directive in go.mod tracks tool dependencies (go get -tool); before that, the tools.go blank-import pattern. Either way the generator's version is reproducible.
  • Not a build system: no dependency tracking, no caching, no ordering guarantees across packages — for anything complex, drive generators from make or the build pipeline instead.

Examples

//go:generate stringer -type=State -trimprefix=State
type State int
go generate ./... && git diff --exit-code   # CI drift check

Related