Formatting, Vetting, and Documentation
Overview
Go has a unique culture: there’s one blessed way to format code, a built-in static analyzer for common mistakes, and a documentation system that extracts docs from comments. This chapter covers gofmt, go vet, and godoc standards.
Code Formatting with gofmt
Why Gofmt Exists
Unlike other languages where style guides vary (tabs vs. spaces, brace placement), Go has a single canonical format enforced by gofmt. This eliminates style debates and ensures all Go code looks the same.
“Gofmt’s style is no one’s favorite, yet gofmt is everyone’s favorite.” — Rob Pike
Basic Usage
# Format a file (print to stdout)
$ gofmt main.go
# Format and overwrite file
$ gofmt -w main.go
# Format all files recursively
$ gofmt -w .
# Using go fmt (recommended)
$ go fmt ./...Formatting Rules
gofmt enforces:
// BEFORE (your style)
func foo(x int,y string){
if x>0{
return
}
}
// AFTER (gofmt style)
func foo(x int, y string) {
if x > 0 {
return
}
}| Rule | Gofmt Standard |
|---|---|
| Indentation | Tabs |
| Spacing | Spaces around operators |
| Braces | Opening brace on same line |
| Line length | No limit (but keep reasonable) |
| Blank lines | Strategic for readability |
Code Simplification
Use -s to simplify code:
$ gofmt -s -w main.go// Before
s[a:len(s)]
for x, _ := range v {}
[]int{1, 2, 3}[0:2]
// After -s
s[a:]
for x := range v {}
[]int{1, 2, 3}[:2]Import Formatting with goimports
goimports does everything gofmt does, plus it manages imports:
# Install
$ go install golang.org/x/tools/cmd/goimports@latest
# Format and fix imports
$ goimports -w main.go// Before (missing import, unused import)
package main
import (
"unused"
)
func main() {
fmt.Println("Hello")
}
// After goimports
package main
import "fmt"
func main() {
fmt.Println("Hello")
}Static Analysis with go vet
What Vet Catches
go vet detects code that compiles but is probably wrong:
$ go vet ./...Common Issues Detected
Printf Format Errors
// go vet catches this
fmt.Printf("%d", "string") // wrong type
fmt.Printf("%s %s", name) // wrong number of argsUnreachable Code
func example() int {
return 42
fmt.Println("never runs") // go vet: unreachable code
}Suspicious Loop Variables
// go vet warns about this common bug
for i, v := range values {
go func() {
fmt.Println(i, v) // captures loop variable
}()
}Useless Assignments
x := 5
x = x // go vet: self-assignmentRunning Specific Checks
# List available analyzers
$ go tool vet help
# Run specific analyzer
$ go vet -composites=false ./...
$ go vet -printf=true ./...Enhanced Linting with staticcheck
staticcheck provides additional checks beyond go vet:
# Install
$ go install honnef.co/go/tools/cmd/staticcheck@latest
# Run
$ staticcheck ./...Checks include: - Unused code - Deprecated function usage - Simplification suggestions - Performance improvements - Common bugs
Documentation with godoc
Writing Documentation
Go extracts documentation from comments directly before declarations:
// Package math provides basic mathematical operations.
// It includes functions for arithmetic, trigonometry, and more.
package math
// Pi represents the mathematical constant π.
const Pi = 3.14159
// Add returns the sum of two integers.
// It handles overflow by wrapping around.
func Add(a, b int) int {
return a + b
}
// Calculator provides stateful mathematical operations.
type Calculator struct {
// Result holds the current calculation result.
Result float64
}
// Add adds n to the current result.
func (c *Calculator) Add(n float64) {
c.Result += n
}Documentation Conventions
| Rule | Example |
|---|---|
| Start with name | // Add returns the sum... |
| Complete sentences | End with period |
| First sentence is summary | Shown in package lists |
| Blank line for paragraphs | Separate blocks |
Code Examples in Docs
Create examples in *_test.go files:
// example_test.go
package math_test
import (
"fmt"
"myproject/math"
)
func ExampleAdd() {
result := math.Add(2, 3)
fmt.Println(result)
// Output: 5
}
func ExampleCalculator_Add() {
c := &math.Calculator{}
c.Add(10)
c.Add(5)
fmt.Println(c.Result)
// Output: 15
}These examples: - Appear in documentation - Are tested by go test - Show real usage
Viewing Documentation
# Command line
$ go doc fmt
$ go doc fmt.Println
$ go doc -all fmt
# Local web server
$ go install golang.org/x/tools/cmd/godoc@latest
$ godoc -http=:6060
# Visit http://localhost:6060Package Comments
For larger packages, use a doc.go file:
// doc.go
/*
Package server implements an HTTP server with middleware support.
# Getting Started
Create a new server and add routes:
srv := server.New()
srv.Get("/", handleHome)
srv.Listen(":8080")
# Middleware
Add middleware to process all requests:
srv.Use(server.Logger())
srv.Use(server.Recovery())
# Configuration
Configure the server using options:
srv := server.New(
server.WithTimeout(30 * time.Second),
server.WithMaxBodySize(1 << 20),
)
*/
package serverCombining Tools in Workflow
Pre-commit Hook
#!/bin/bash
# .git/hooks/pre-commit
# Format
gofmt -l -w .
# Vet
go vet ./...
if [ $? -ne 0 ]; then
echo "go vet failed"
exit 1
fi
# Staticcheck
staticcheck ./...
if [ $? -ne 0 ]; then
echo "staticcheck failed"
exit 1
fiMakefile Integration
.PHONY: check fmt vet lint
fmt:
gofmt -w .
vet:
go vet ./...
lint:
staticcheck ./...
check: fmt vet lint
@echo "All checks passed"CI Pipeline (GitHub Actions)
- name: Check formatting
run: |
if [ "$(gofmt -l . | wc -l)" -gt 0 ]; then
echo "Code is not formatted"
gofmt -d .
exit 1
fi
- name: Vet
run: go vet ./...
- name: Staticcheck
run: |
go install honnef.co/go/tools/cmd/staticcheck@latest
staticcheck ./...Summary
| Tool | Purpose | Usage |
|---|---|---|
gofmt |
Format code | gofmt -w . |
goimports |
Format + manage imports | goimports -w . |
go vet |
Catch common mistakes | go vet ./... |
staticcheck |
Enhanced linting | staticcheck ./... |
godoc |
Generate documentation | godoc -http=:6060 |