Format Go Code Safely with gofmt
You will finish with a small, repeatable workflow for checking and formatting Go source with the installed gofmt 1.22. The workflow starts with a diff, keeps a backup before an overwrite, and separates formatting from optional source rewrites.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about ten minutes for a first pass. You need a shell, a readable Go source tree and the gofmt command from the Debian golang-go package. No step needs elevated privileges. Do not use sudo to format code in a project you do not own: root can create root-owned files and hide an ordinary permission problem.
1. Confirm the installed formatter
Check the command and package version before relying on its exact behaviour. This is read-only:
$ command -v gofmt
/home/linuxbrew/.linuxbrew/bin/gofmt
$ gofmt -h | sed -n '1,12p'
usage: gofmt [flags] [path ...]
-cpuprofile string
write cpu profile to this file
-d display diffs instead of rewriting files
-e report all errors (not just the first 10 on different lines)
-l list files whose formatting differs
-r string
rewrite rule (e.g., 'a[b:len(a)] -> a[b:]')
-s simplify code
-w write result to (source) file instead of stdout
$ dpkg-query -W -f='${Package} ${Version}\n' golang-go:amd64
golang-go:amd64 2:1.22~2build1
The path printed by command -v is the binary you are actually invoking. The Debian package version identifies the installed distribution package, while the command itself does not provide a --version flag in this installation. Keep that distinction in mind when comparing output between machines.
Checkpoint
You have confirmed the executable and recorded the local package version.
2. Inspect a file without changing it
By default, gofmt writes the reformatted source to standard output. That makes this a safe first check, but redirecting it casually can still overwrite a file. Start with a diff instead. Create a disposable example if you do not already have a Go file to inspect:
$ tmpdir=$(mktemp -d)
$ printf '%s\n' 'package main' '' 'import "fmt"' '' 'func main(){fmt.Println("hello")}' > "$tmpdir/main.go"
$ gofmt -d "$tmpdir/main.go"
diff "$tmpdir/main.go" gofmt/main.go
--- gofmt/main.go
+++ gofmt/main.go
@@
-func main(){fmt.Println("hello")}
+func main() { fmt.Println("hello") }
The exact diff header includes a temporary path and gofmt's output uses tabs for indentation, which may display as spacing in a terminal. The important result is that the file is still unchanged. Remove the disposable directory after the experiment with rm -rf "$tmpdir"; it contains only the file you just created.
For a real project, replace the temporary path with a known file:
$ gofmt -d -- ./path/to/file.go
The -- marks the end of options. It is useful when a path is supplied by a script, although a path beginning with a hyphen is best rejected rather than guessed at.
3. Check a tree without rewriting it
Use -l in a pre-commit check or before reviewing a branch. It prints the names of files whose formatting differs and prints nothing when they are already formatted:
$ gofmt -l -- ./path/to/package
./path/to/package/main.go
./path/to/package/worker.go
$ gofmt -l -- ./path/to/package | tee /tmp/gofmt-files.txt
$ test ! -s /tmp/gofmt-files.txt && echo 'all Go files are formatted'
all Go files are formatted
A directory is searched recursively for .go files, but files whose names start with a period are ignored. The command does not understand your build graph, run tests or prove that the program is correct. It only reports formatting differences.
Checkpoint
Use -l when you need a yes-or-no check, and -d when you need to review the actual edits.
4. Apply ordinary formatting after reviewing the diff
Once the diff is expected, use -w to update the source file in place:
$ cp --preserve=all ./path/to/file.go ./path/to/file.go.bak
$ gofmt -w -- ./path/to/file.go
$ gofmt -d -- ./path/to/file.go
$ test ! -s <(gofmt -l -- ./path/to/file.go) && echo 'file is formatted'
file is formatted
The backup is an explicit recovery point. If the result is wrong for your workflow, restore it with cp --preserve=all ./path/to/file.go.bak ./path/to/file.go. Do not remove the backup until you have reviewed the complete change and, ideally, run the project's tests. Deleting it with rm is irreversible.
-w normally restores the original file if an error occurs while overwriting, but it is still wise to keep your own backup when a change matters. Formatting can alter whitespace throughout a file, so inspect the diff rather than assuming a successful exit means a small diff.
5. Format standard input and fragments
With no path, gofmt reads standard input and prints formatted text. This is useful in a pipeline and does not modify a source file:
$ printf '%s\n' 'func main(){println("hello")}' | gofmt
func main() {
println("hello")
}
Standard input can be a full Go program or a syntactically valid declaration list, statement list or expression. For example:
$ printf '%s\n' 'x:=1+2' | gofmt
x := 1 + 2
Leading indentation and leading or trailing spaces have special preservation rules for fragments. If a pipeline's output is going back into a larger file, verify the surrounding context rather than treating the fragment as a complete program.
6. Use simplification and rewrites deliberately
The -s option asks gofmt to simplify code after applying any rewrite rule. In the installed tool, that includes forms such as s[a:len(s)] becoming s[a:], and a loop that discards both range values becoming for range v. The manual warns that simplification can produce code incompatible with earlier Go versions, so review and test the resulting diff:
$ gofmt -s -d -- ./path/to/file.go
$ gofmt -s -w -- ./path/to/file.go
The -r option applies a rule in the form pattern -> replacement. Lowercase single-character identifiers in the pattern act as wildcards:
$ gofmt -r '(a) -> a' -l -- ./path/to/package
$ gofmt -r '(a) -> a' -w -- ./path/to/package
Do not combine an unreviewed rewrite with a broad tree and an automatic commit. First run it with -l or -d on a small, known set, then test the result. A rewrite is a source change, not merely presentation.
7. Diagnose failures without guessing
For malformed input, use -e when you need all available errors rather than the first ten reported on different lines:
$ gofmt -e -- ./path/to/file.go
$ printf 'exit status: %s\n' "$?"
exit status: 0
Replace the example path with the file that failed. A non-zero status means gofmt could not complete its requested operation. Check the path, read permission and source syntax first:
$ test -r ./path/to/file.go && echo readable
$ gofmt -d -- ./path/to/file.go
Do not fix an access problem by running as root unless the project owner and ownership change are understood. If a generated file is repeatedly reported, find the generator or build step that writes it; formatting the generated output may only hide the real source of the difference.
Done means
- You confirmed the installed gofmt binary and Debian package version.
- You used
-dor-lbefore changing source. - You reviewed the diff before using
-w. - You kept a recoverable backup for an in-place change.
- You treated
-sand-ras source transformations that need testing. - The final
gofmt -lcheck reports no files needing formatting.