What does gofmt do to a Go source file, and why does it have no style options?
answer
- one layout for every repository
- reprinted from the parsed syntax tree
- tabs indent, blanks align
- sorts imports in a block, adds none
- no flag for width or brace style
basics
~20 sgofmt reprints a Go file in one canonical layout: tabs for indentation, blanks for alignment, fixed spacing and brace placement, and import specs sorted inside each existing block. It exposes no style settings, so formatting stops being a review topic.
solid answer
~50 s`gofmt` parses the file and prints it back out using the single layout the Go printer knows: tabs to indent, blanks only to align things like adjacent trailing comments and struct field columns, canonical spacing around operators and braces, and import specs sorted within each contiguous import block. It deliberately takes no configuration — no tab width, no brace style, no line limit — so no repository has a house format to argue about and every diff is pure content. What it will not do is change the program: plain `gofmt` never adds or removes an import, never reorders declarations, and never rewrites an expression. Because it works from the parsed file, a file with a syntax error is left untouched. The `-s` flag is the one exception — it applies a small set of simplifications that do alter the code, which is exactly why it is opt-in.
code
go · 18 linestype Point struct{ X, Y int }
// gofmt -s: []Point{{1, 2}, {3, 4}}
var corners = []Point{Point{1, 2}, Point{3, 4}}
// gofmt -s: return s[1:]
func tail(s []byte) []byte {
return s[1:len(s)]
}
// gofmt -s: for range v
func count(v []int) int {
n := 0
for _ = range v {
n++
}
return n
}go deeper
Be ready to say what running gofmt on a file actually changes — whitespace, alignment, and the order of imports inside a block — and to state plainly that it has no options to argue about.
Explain that gofmt reprints from the parsed syntax tree rather than patching text, which is why it refuses a file that does not parse and why -s, which changes the tree, is a separate opt-in flag.
Show how you make formatting a non-event across a repository with many occasional contributors: format on save, one agreed toolchain version, and a review culture where nobody comments on layout.
Own the argument for zero configurability: a formatter with knobs re-creates the style debate once per repository. Be able to name what the team gives up — line length, hand-tuned tables — and why that trade pays at scale.
## What gofmt actually is `gofmt` is a program shipped with the Go toolchain that reads Go source, parses it into a syntax tree, and prints that tree back out in Go's one canonical layout. It is not a text-munging "style fixer" that patches lines with regular expressions; it is a parser plus a printer. Two consequences follow immediately, and both come up in interviews: - If the file does not parse, gofmt reports the syntax error and writes nothing. It never half-formats a broken file. - Anything that is not represented in the syntax tree — blank line runs, comment attachment, spacing — is regenerated, not preserved verbatim. Your own spacing choices simply do not survive. ## The layout it produces The rules worth being able to state out loud: - **Indentation is tabs.** Alignment — lining up trailing comments, or the types in a run of struct fields — uses blanks. The split matters: because indentation is tabs, every reader can set their own tab width and the code still lines up, since the alignment columns are made of spaces. - **Spacing is fixed.** One space around binary operators of the lowest precedence in an expression, none where Go's printer says none, the opening brace on the same line, `else` on the closing brace's line. - **Imports are sorted inside each block.** gofmt sorts the specs within each contiguous group, but it never adds an import you forgot or removes one you stopped using, and it never merges or splits your groups. Adding and removing imports is a different tool's job. - **Declarations are never reordered.** Functions stay where you put them. ## Why there are no knobs This is the design decision the question is really about. Every configurable formatter re-creates the style argument once per repository: tabs or spaces, 80 or 120 columns, brace on which line. gofmt has no configuration file and no style flags, so there is exactly one correct formatting of any Go program, everywhere, forever. In a codebase with dozens of occasional contributors this is worth more than any individual preference: nobody reviews layout, nobody rewrites a file into their own style, and diffs contain only the change you made. The cost is real — you cannot ask for a line limit, and you cannot keep a hand-tuned table — and the Go community accepted it deliberately. The usual formulation is that gofmt's style is nobody's favourite, and that is why everybody uses it. ## Running it - `gofmt -d file.go` prints a unified diff of what it would change. - `gofmt -w file.go` rewrites the file in place; most editors run this on save. - `go fmt ./...` is a thin wrapper that runs gofmt over the named packages. ## The `-s` flag `gofmt -s` adds simplifications on top of formatting. Unlike layout, these change the syntax tree, which is why they are opt-in: - A composite literal's element type is elided: `[]Point{Point{1, 2}}` becomes `[]Point{{1, 2}}`. - A slice expression ending at the length is trimmed: `s[a:len(s)]` becomes `s[a:]`. - A redundant blank in a range clause is dropped: `for x, _ = range v` becomes `for x = range v`, and `for _ = range v` becomes `for range v`. Each rewrite produces an equivalent program, so `-s` is safe, but it is a code change rather than a whitespace change and shows up as one in review. ## What it does not guarantee gofmt is stable, not frozen. The printer has changed between Go releases, so a file formatted by one toolchain version can differ from the same file formatted by another. That is the practical reason a team formats with one agreed toolchain version rather than whatever each laptop has. Within a single version, though, gofmt is a pure function of the source: same input, same output, regardless of editor, operating system or environment.
- What does `gofmt -s` change that plain gofmt leaves alone?`-s` applies simplifications that alter the code rather than the whitespace: a composite literal's element type is elided, so `[]Point{Point{1, 2}}` becomes `[]Point{{1, 2}}`; `s[a:len(s)]` becomes `s[a:]`; and `for x, _ = range v` becomes `for x = range v`. Each is an equivalent program, but because they are rewrites and not layout, they are opt-in rather than what plain gofmt does.
- If two people run gofmt on the same file in different editors, can the output differ?Not within one Go version. gofmt takes no configuration and reads nothing from the editor or environment, so its output is a pure function of the source. Across Go releases the printer itself has changed, so a file can be reformatted when the toolchain is upgraded — which is why a team should agree on one toolchain version rather than letting each machine pick.
- Can gofmt ever change what the program does?No. It reprints the same parsed syntax tree, so the compiled behaviour is identical, and `-s`'s rewrites are equivalences. The edge case worth knowing is a file that does not parse: gofmt reports the syntax error and leaves the file completely untouched rather than guessing at your intent.
It is less a style guide than a printing press: you hand in your text and it comes back set in the one typeface the language owns.
saying these in an interview costs you the question
- Thinks gofmt has a config file for tab width
- Says gofmt adds the imports you forgot
- Claims gofmt indents with spaces
- Believes gofmt -s is applied by default
- Assumes gofmt reorders declarations alphabetically
- Says gofmt partially formats a file with a syntax error