What does a //go:generate comment in a Go source file do, and when does it run?
answer
- a comment, not a build step
- who actually executes it
- no shell, no pipes, no globs
- the artefact the build depends on is committed
- a space after the slashes kills it
basics
~20 sA //go:generate line is just a comment; no build step reads it. Only the go generate command scans for these lines and runs each named command in the package directory. go build and go test never run them, so the generated output must be committed.
solid answer
~40 s`//go:generate <command> <args>` is an ordinary comment to the compiler. It has no effect on `go build`, `go test`, `go vet` or `go install`. It runs only when a human (or a build step someone wrote) invokes `go generate ./...`, which scans the named packages' source files, finds lines that begin exactly with `//go:generate` at the start of a line, and executes each command in the directory of the file that contains it, in file order. The command must already exist on `PATH`; `go generate` does not build it for you and does not run it through a shell. Because the compiler never triggers it, the *output* — say the `status_string.go` that stringer writes — is what the build actually depends on, so it is committed to the repository like any other source file.
code
go · 9 lines//go:generate stringer -type=Status
type Status int
const (
Pending Status = iota
Active
Closed
)go deeper
Be ready to say plainly that it is a comment, that only the go generate command runs it, and that the generated file is committed. Knowing the exact spelling with no space after the slashes is worth a mark on its own.
Explain the execution model: per package, per file, in source order, with the working directory set to the file's directory, executed directly rather than through a shell, with $GOFILE and friends substituted by the go command.
Show how you debug a directive that is not firing — -n, -x, -v, build constraints, PATH — and argue why the build deliberately never runs generators, and therefore why generated output belongs in version control.
Be ready to defend generation as a repository convention: what a team gains from checked-in, reviewable output versus a build that regenerates, and where the cost lands when generated code and its source drift apart.
## What the directive is `go generate` is Go's deliberately minimal answer to code generation. There is no build DSL, no task graph, no plugin system: you put a comment in a Go file naming a command line, and a separate `go` subcommand runs it. ```go //go:generate stringer -type=Status ``` The syntax rules are strict and unforgiving: - The line must begin at the **start of a line** in a Go source file, with no leading whitespace before `//`. - There must be **no space** between `//` and `go:generate`. `// go:generate stringer -type=Status` is a plain comment and is silently ignored — this is the single most common mistake, and nothing warns you about it. - Everything after the directive is the command and its arguments, split on spaces (quoting with `"` and backquotes is honoured for a single argument). ## When it runs — and when it does not It runs when, and only when, someone runs `go generate` on the package: ``` go generate ./... ``` `go build`, `go install`, `go test` and `go vet` do **not** run generators. This is a design decision, not an omission: building a package must never execute arbitrary programs from someone else's source tree. It follows directly that the generated files are part of the repository. Anyone who fetches your module and builds it gets the committed `*_string.go`; they never run your generator, and may not even have it installed. `go generate` processes the packages you name, file by file in the order the go command lists them, and directives within a file in the order they appear. Each command runs with its working directory set to the directory of the file containing the directive, so relative paths in generator arguments are relative to the package, not to where you typed the command. ## No shell The command is executed directly, not through `sh` or a shell of any kind. Pipes, redirection, `&&`, `*` globbing and shell variable expansion do **not** work. If you need any of that, put it in a script or a small Go program and invoke that. What *is* substituted is a small fixed set of variables that `go generate` expands itself before executing: - `$GOFILE` — the base name of the file containing the directive - `$GOLINE` — the line number of the directive - `$GOPACKAGE` — the name of the package - `$GOOS`, `$GOARCH`, `$GOROOT` — the usual build environment values - `$DOLLAR` — a literal `$`, for when you need one There is also a `//go:generate -command <alias> <name> <args...>` form that defines a shorthand for the rest of that file's directives. ## Useful flags - `go generate -n ./...` prints the commands without running them — the first thing to reach for when a directive is not doing what you expect. - `go generate -x ./...` prints each command as it runs. - `go generate -v ./...` lists the packages and files as they are examined. - `go generate -run <regexp> ./...` runs only directives whose original source text matches the regular expression, which is how you re-run one generator across a large repository. If a directive does not fire at all, `-n` plus `-v` usually reveals the cause immediately: a space after `//`, a file excluded by a build constraint, or a package pattern that never matched. ## What it is typically used for The canonical example is `stringer`, which reads a set of `iota`-style constants and writes a `String() string` method for the type, so that printing a `Status` gives `Active` instead of `1`. Other common uses are turning a schema or an interface description into Go types, or producing a lookup table that would be tedious and error-prone to type by hand. The common thread is that the *input* is something already in your repository and the *output* is deterministic Go code. That is what makes committing the output sane: the generated file is reproducible from the source, and a reviewer can read the diff. ## The mental model Think of `//go:generate` as a note to a human that has been made machine-readable. It records, next to the code it produces, exactly which command produced it. The go command will run that note for you on request; it will never run it behind your back.
- Does go generate run the directive through a shell?No. The command is executed directly with its arguments split on spaces, so pipes, redirection, `&&` and shell globbing do not work. The only expansion is the go command's own substitution of `$GOFILE`, `$GOLINE`, `$GOPACKAGE`, `$GOOS`, `$GOARCH`, `$GOROOT` and `$DOLLAR`. If you need shell behaviour, invoke a script or a small Go program from the directive instead.
- How do you re-run just one generator across a large repository?`go generate -run <regexp> ./...` — the regular expression is matched against the directive's original source text, so `-run stringer` fires only the stringer lines. Pair it with `-n` to print the commands without executing them and `-x` to trace them as they run.
- A directive is not firing at all. What do you check first?Whether the line starts at column one with `//go:generate` and no space after the slashes — `// go:generate` is an ordinary comment and is ignored silently. Then check that the file is not excluded by a build constraint, that your package pattern actually matches, and that the generator binary is on `PATH`. `go generate -n -v ./...` shows all three.
It is a sticky note on a drawer saying which machine produced what is inside. The note does not run the machine; someone has to walk over and press the button.
saying these in an interview costs you the question
- Believes go build or go test runs //go:generate automatically
- Writes // go:generate with a space, then wonders why nothing happens
- Expects shell pipes, redirection or glob expansion in the directive
- Gitignores generated files, so nobody else can build the package
- Thinks go generate downloads or builds the generator for you