gofmt & goimports Enforcement
gofmt and goimports are the formatting layer every Go repo should automate.
Search across all documentation pages
gofmt and goimports are the formatting layer every Go repo should automate.
Teams enforce them in editors, pre-commit hooks, and CI so reviews focus on behavior instead of whitespace.
Quick-reference recipe card - copy-paste ready.
# Format all packages in place
gofmt -w .
goimports -local example.com/mycorp -w .
# CI: fail if any file would change
test -z "$(gofmt -l .)"
test -z "$(goimports -local example.com/mycorp -l .)"
# Optional simplifications
gofmt -s -w .When to reach for this:
Repository layout:
example.com/widget/
go.mod # module example.com/mycorp/widget
cmd/api/main.go
internal/store/store.gocmd/api/main.go before enforcement:
package main
import (
"fmt"
"example.com/mycorp/widget/internal/store"
"net/http"
)
func main() {
store := store.New()
http.HandleFunc("/ok", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, store.Ping())
})
http.ListenAndServe(":8080", nil)
}Enforce:
goimports -local example.com/mycorp -w ./cmd ./internal
gofmt -s -w ./cmd ./internalAfter (import grouping shows -local effect):
package main
import (
"fmt"
"net/http"
"example.com/mycorp/widget/internal/store"
)
func main() {
store := store.New()
http.HandleFunc("/ok", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, store.Ping())
})
http.ListenAndServe(":8080", nil)
}GitHub Actions excerpt:
- name: Format check
run: |
test -z "$(gofmt -l .)"
test -z "$(goimports -local example.com/mycorp -l .)"What this demonstrates:
-local keeps internal modules in a separate import group..editorconfig overrides for tab width.formatting.gofumpt or goimports settings are enabled.| Group | Example paths | Separator |
|---|---|---|
| Standard library | fmt, net/http | blank line below |
| Third-party | github.com/go-chi/chi/v5 | blank line below |
Local (-local prefix) | example.com/mycorp/widget/internal/store | end of block |
| Mode | Command | Best for |
|---|---|---|
| Check only | gofmt -l . | CI gates |
| Fix locally | gofmt -w . | pre-commit |
| Fix in CI bot | auto-commit job | high-churn OSS |
| Editor on save | gopls formatting | daily editing |
# Verify tool versions match CI
gofmt -V # bundled with go version
go version -m $(which goimports) | head -1Pin golang.org/x/tools pseudo-version in tools.go for reproducible go install of goimports across laptops and agents.
| Flag | Tool | Effect |
|---|---|---|
-w | both | Write formatted file |
-l | both | List files that would change |
-s | gofmt | Apply simplifications |
-local | goimports | Prefixes for local import group |
-local in CI - Internal imports mingle with third-party paths. Fix: Pass the same -local prefixes in docs, hooks, and workflows.*.pb.go from gofmt -l paths or run format as part of codegen.tools.go and go install in CI.make fmt target).go fmt as a check - go fmt always rewrites files; it is not a list-only CI pattern. Fix: Use gofmt -l or dedicated lint job.| Alternative | Use When | Don't Use When |
|---|---|---|
| gofumpt | Stricter spacing rules than gofmt | OSS libs where contributors expect stock gofmt |
golangci-lint gofmt linter | Already running golangci-lint for everything | Tiny repos wanting zero YAML |
| Manual review only | Never for production Go | - |
cargo fmt-style config files | - | Go ecosystem does not support this model |
Pick one primary check.
Most teams run goimports (which includes gofmt) locally and verify with goimports -l in CI.
Simplifications like s[a:len(s)] to s[a:].
Optional but common in hooks; document if your CI requires -s.
Use git diff --name-only --cached '*.go' | xargs goimports -w to limit scope and keep hooks fast.
Configurable via editor settings.
Align gopls with CI so on-save matches pipeline expectations.
Usually no - vendor is third-party source copied verbatim.
Exclude vendor paths from format checks.
Out of scope for gofmt.
Use Prettier or language-specific formatters for YAML, Markdown, and protobuf .proto sources.
gofmt preserves build tags at the top of the file.
Run formatters on tagged files with the same tags CI uses when analyzing.
Repeat the flag: goimports -local example.com/a -local example.com/b -w ..
Provide make fmt and optional bot auto-fix.
Consistency lowers long-term friction more than one-time format commits.
Generated output should be formatted by the generator or a post-step.
Do not hand-format generated files that regen overwrites.
Stack versions: This page was written for Go 1.26.x (Green Tea GC default, go fix modernizers - verify patch at build), chi (latest - verify at build), gin (latest - verify at build), echo (latest - verify at build), google.golang.org/grpc (latest - verify at build), sigs.k8s.io/controller-runtime (latest - verify at build), kubebuilder (latest - verify at build), tinygo (latest - verify board targets at build), wazero (latest - verify at build), and golangci-lint (latest - verify linter set at build).
Reviewed by Chris St. John·Last updated Jul 16, 2026