gofmt - Automatic Source Code Formatting
An in-depth guide to gofmt, the Go source formatter that enforces a consistent style across all Go code and eliminates formatting debates.
What gofmt Is
gofmt is a tool that reads Go source code and prints it back out in the standard Go formatting style. It is part of the core Go distribution and serves as the canonical reference for how Go code should look. When you run gofmt on a .go file, it parses the entire source, builds an abstract syntax tree, and then writes a perfectly formatted version of that tree to the output — fixing indentation, spacing, comment alignment, and line breaks without changing the program’s behavior.
The tool is also accessible through the go fmt subcommand, which applies gofmt at the package level. The two are interchangeable for most practical purposes, but go fmt is the recommended way to format an entire package or project because it knows how to find all the .go files without manual globbing.
gofmt vs go fmt:
go fmt runs gofmt -l -w on the packages you specify. Using go fmt ./... formats all packages in a module. The standalone gofmt command works on individual files or wildcard patterns and offers flags like -d (show diff) that go fmt does not expose.
Why gofmt Exists
Go was designed at Google to serve large teams building massive codebases. In that environment, code review time spent arguing about tabs versus spaces, bracket placement, or vertical alignment is a real productivity cost. The language designers made a deliberate choice: there is exactly one correct way to format Go code, and a tool enforces it automatically. This eliminates entire categories of style debate and makes every codebase look familiar to every Go developer.
Rob Pike, one of Go’s creators, put it this way: "Gofmt's style is no one's favorite, yet gofmt is everyone's favorite." The point is not that the style is aesthetically superior; it’s that the style is uniform. A developer switching between projects never has to mentally adjust to a different formatting convention. A code reviewer never has to flag a formatting nitpick.
The machine-enforceable style also enables other tooling. Because every Go program can be parsed into a predictable AST and rendered back in a known format, tools like gorename (for renaming identifiers) and go fix (for mechanical API migrations) can operate reliably on source code without being tripped up by formatting variations.
How gofmt Works
gofmt uses Go’s own parser (go/parser) to read source files and produce an AST. It then passes that AST to a printer (go/printer) configured with the official style rules. The printer determines indentation depth, where line breaks fall, how to align comments, and how to wrap long lines. Because the printer works from the AST, it discards all original formatting — extra spaces, inconsistent indentation, non-standard brace placement — and replaces it with a canonical rendering.
This means gofmt does not edit your file in place by default. It sends the reformatted code to standard output so you can inspect the result. The -w flag tells it to write the changes back to the file, replacing the original.
The parser also resolves imports, which allows gofmt to sort import blocks into two groups (standard library and external) and sort each group alphabetically.
Formatting Rules Enforced by gofmt
The style gofmt applies is non-negotiable and fully described by the source code of the go/printer package. The most visible rules are:
- Tabs for indentation. Go source files use tabs, never spaces.
gofmtemits tabs for every logical indentation level. - No trailing whitespace. Blank lines at the end of the file are stripped.
- Braces on the same line. Opening braces for functions, control structures, and type declarations are placed at the end of the line, not on a new line.
- Consistent spacing around operators and punctuation. Commas are followed by a space. Binary operators have spaces on both sides. Unary operators hug their operand.
- Aligned comments. Doc comments and field-level comments are vertically aligned to the right of the code they document, at the same indentation level.
- Sorted imports. Import declarations are grouped: standard library packages come first, then third-party packages, separated by a blank line.
An example of comment alignment before and after gofmt:
// Before gofmt
type Config struct {
Host string // Hostname of the server
Port int // Port number
}
// After gofmt
type Config struct {
Host string // Hostname of the server
Port int // Port number
}
The tool does not enforce maximum line length. Go has no official limit, and gofmt will not break a line that fits within 80 or 120 columns. The community generally keeps lines reasonably short, but it’s a human judgment, not a mechanical one.
Using gofmt - Commands and Flags
The core workflow is simple: you point gofmt at Go source files, and it outputs formatted code. The command-line flags give you control over what happens to that output.
Format a Single File and View the Result
gofmt hello.go
This prints the formatted version of hello.go to the terminal without modifying the file.
Write Changes In-Place
gofmt -w hello.go
The -w flag overwrites the file with the formatted version. Use with care: there is no undo built into gofmt. Before adopting -w in a script or on a large codebase, run it with -d first to preview what will change.
Irreversible changes with -w:
gofmt -w modifies files in place with no backup. On a large codebase, always run gofmt -d first to see the diff, or commit your work before running the command so you can revert if something goes wrong.
Preview Changes as a Diff
gofmt -d hello.go
-d shows a unified diff of the changes that -w would make. This is the safest way to see what gofmt wants to change before allowing it to touch the files.
List Files That Need Formatting
gofmt -l ./...
-l lists the file paths that are not already formatted. Combined with -w, it will only print the names of files it changed. This is useful in CI checks: if gofmt -l produces any output, the build should fail.
Simplify Code with -s
gofmt -s -w main.go
The -s flag applies additional source transformations that simplify code without changing its meaning. It rewrites constructs that are syntactically valid but unnecessarily verbose. The most common simplifications are:
- Removing unnecessary parentheses:
a + (b * c)becomesa + b*c. - Simplifying range clauses:
for _ = range sbecomesfor range s. - Reducing slice expressions:
s[a:len(s)]becomess[a:].
Here is an example that shows all three simplifications in a small piece of code:
// Before gofmt -s
package main
func main() {
s := []int{1, 2, 3}
for _ = range s {
println(s[0:(len(s))])
}
}
// After gofmt -s
package main
func main() {
s := []int{1, 2, 3}
for range s {
println(s[0:])
}
}
Write simpler code with -s:
Using -s regularly ensures that new code doesn't accumulate unnecessary syntactic noise. Most editors that integrate gofmt have a checkbox to enable the simplify flag on save.
Mechanical Refactoring with Rewrite Rules
gofmt supports a -r flag for pattern-based rewriting of the AST. You specify a pattern and a replacement, and gofmt applies the transformation wherever the pattern matches.
gofmt -r 'foo -> Foo' -w main.go
This command renames the identifier foo to Foo — but only where foo is used as a variable, not inside strings or comments. The rewrite engine operates at the AST level, so it won’t accidentally replace the word "foo" in a log message.
A more realistic use case: replacing calls to the old strings.Replace with strings.ReplaceAll after upgrading to Go 1.12.
// Before rewrite
s := strings.Replace(input, " ", "", -1)
// Run: gofmt -r 'strings.Replace(a, b, c, -1) -> strings.ReplaceAll(a, b, c)' -w .
// After rewrite
s := strings.ReplaceAll(input, " ", "")
Rewrite rules use single lowercase letters as wildcards that match arbitrary expressions. The letters in the pattern and replacement must correspond exactly.
Rewrite rules are AST-based, not textual:
If your pattern does not match the AST shape of the code — for example, if you use -1 instead of the integer literal -1 — the rewrite will silently do nothing. Always combine -r with -d first to verify that the changes are what you expect. A find-and-replace that looks correct textually may produce no results because the AST structure differs from what the pattern requires.
gofmt and Editor Integration
Nearly every Go editor and IDE supports running gofmt on save. This integration ensures that your code is always properly formatted before it reaches version control. Setting up format-on-save removes the need to remember to run gofmt manually and guarantees that the repository never contains unformatted code.
- VS Code with the Go extension: Format on save is enabled by default. The extension uses
gofmtorgoimportsas the formatter. - Vim/Neovim with vim-go: The
:GoFmtcommand runsgofmton the current buffer. Many users map it to theBufWritePreautocommand. - GoLand/IntelliJ: The built-in formatter uses the same algorithm as
gofmt, with an option to rungo fmton save. - Sublime Text: Plugins like GoTools invoke
gofmton save, with the ability to usegoimportsinstead.
The key is that the formatting is never subjective. Two developers using different editors will produce byte-for-byte identical files after gofmt runs. This is the guarantee that makes Go’s tooling philosophy possible.
Common Misconceptions
- "gofmt is a linter." It is not.
gofmtchanges code, but it does not report on possible bugs or suspicious constructs. That is the job ofgo vet. - "You can configure gofmt’s style." No. There is no configuration file. The formatting rules are baked into the
go/printerpackage. If you don’t like a specific rule, you either accept it or write Go in a different language. - "gofmt only fixes indentation." It also sorts imports, aligns comments, removes trailing whitespace, standardizes spacing, and, with
-s, simplifies expressions. It touches every byte of the source. - "Running gofmt once is enough." Only if every developer has format-on-save enabled. Code that is formatted today will become unformatted tomorrow if someone edits it without
gofmtrunning. The safe practice is to enforce formatting in CI, so unformatted code never merges. - "gofmt will break my comments." It preserves comments, but it may move them to align with the standard vertical alignment. If you placed a comment in an unusual position,
gofmtmay shift it. That is by design.
gofmt vs. goimports
goimports is a superset of gofmt that also manages import lines. It formats code exactly as gofmt would, but additionally adds missing imports and removes unused imports. If you tend to forget to import a package or leave an unused one in the file, goimports solves that without requiring an explicit go get or manual edit.
The command is not part of the standard Go distribution. You install it with:
go install golang.org/x/tools/cmd/goimports@latest
Once installed, you can use goimports anywhere you would use gofmt. Most editors let you configure it as the default formatter. For a project that relies on go fmt, you can get the same behavior by running goimports -w followed by go mod tidy to clean up the module file, but many developers simply make goimports their format-on-save command.
Because goimports knows the full import path of every standard library and third-party package you reference, it can add the correct import statement automatically. It also groups imports the same way gofmt does.
Real-World Usage and Best Practices
In practice, gofmt fits into a workflow like this:
- Every developer enables format-on-save in their editor, using either
gofmtorgoimportswith the-sflag. - Before committing, they run
go fmt ./...to be certain the entire package is formatted. - The project’s CI pipeline runs
test -z $(gofmt -l .)(or a variant) to block any merge that contains unformatted code. If the command produces output, the build fails, and the developer knows exactly which files to fix. - When a new Go version ships with additional simplification rules or subtle formatting improvements, the team re-runs
gofmt -s -w ./...and commits the result as a separate, mechanical change.
This workflow turns formatting into a non-event. There is never a PR comment about a missing space, and code reviews stay focused on logic rather than style.
A passing gofmt check is a sign of a healthy project:
If you can run gofmt -l . and get no output, you know that every .go file in the directory is in the canonical format. This is a quick, objective signal that the codebase respects Go conventions.
Summary
gofmt solves a problem that most language communities accept as inevitable: inconsistent formatting across teams and projects. By making the official style machine-enforceable and non-configurable, Go removes an entire class of friction. The tool’s role extends beyond aesthetics — it is the foundation that makes other source-level transformations safe and reliable, because every Go program fed into a transformation tool is guaranteed to be in a parseable, predictable form.