Tooling
Overview
Go ships with a strong suite of built-in tools. This chapter will go over the most common tools and how to use them in your daily development workflow. In addition to the tools that ship with Go, there are a set of very important linters and vetters that can catch runtime bugs as well as significantly improve code quality and performance. This chapter shows what those tools are and how to use them.
What To Use And When
- Lot of built in and third party tools
- Some are so amazing they should be automated in your development process or even your IDE
- Some are rarely used, but when needed are lifesavers
The Go Command
$ go --help
bug start a bug report
build compile packages and dependencies
clean remove object files and cached files
doc show documentation for package or symbol
env print Go environment information
fix update packages to use new APIs
fmt gofmt (reformat) package sources
generate generate Go files by processing source
get add dependencies to current module and install them
install compile and install packages and dependencies
list list packages or modules
mod module maintenance
run compile and run Go program
test test packages
tool run specified go tool
version print Go version
vet report likely mistakes in packages
Go Fmt
$ go help fmt
usage: go fmt [-n] [-x] [packages]
Fmt runs the command 'gofmt -l -w' on the packages named
by the import paths. It prints the names of the files that are modified.
For more about gofmt, see 'go doc cmd/gofmt'.
For more about specifying packages, see 'go help packages'.
The -n flag prints commands that would be executed.
The -x flag prints commands as they are executed.
To run gofmt with specific options, run gofmt itself.
- Built in to most editors
- Useful for git hooks like a
pre-commit(example provided at end of unit)
Gofmt Rewrite Patterns
gofmt supports powerful rewrite rules with the -r flag to automatically refactor code:
# Replace interface{} with any (Go 1.18+)
$ gofmt -w -r 'interface{} -> any' .
# Simplify slice expressions
$ gofmt -w -r 'α[β:len(α)] -> α[β:]' .
# Replace context.TODO() with context.Background()
$ gofmt -w -r 'context.TODO() -> context.Background()' .
# Convert errors.New(fmt.Sprintf(...)) to fmt.Errorf(...)
$ gofmt -w -r 'errors.New(fmt.Sprintf(a, b)) -> fmt.Errorf(a, b)' .
Pattern syntax:
- Use Greek letters (α, β, γ) or single letters as wildcards
->separates pattern from replacement- Patterns match AST nodes, not text
Example transformation:
// Before
var x interface{} = 42
// After running: gofmt -w -r 'interface{} -> any' .
var x any = 42
Go Vet
$ go help vet
usage: go vet [-n] [-x] [-vettool prog] [build flags] [vet flags] [packages]
Vet runs the Go vet command on the packages named by the import paths.
For more about vet and its flags, see 'go doc cmd/vet'.
For more about specifying packages, see 'go help packages'.
For a list of checkers and their flags, see 'go tool vet help'.
For details of a specific checker such as 'printf', see 'go tool vet help printf'.
The -n flag prints commands that would be executed.
The -x flag prints commands as they are executed.
The -vettool=prog flag selects a different analysis tool with alternative
or additional checks.
For example, the 'shadow' analyzer can be built and run using these commands:
go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow
go vet -vettool=$(which shadow)
The build flags supported by go vet are those that control package resolution
and execution, such as -n, -x, -v, -tags, and -toolexec.
For more about these flags, see 'go help build'.
See also: go fmt, go fix.
- All code should pass
go vet(even tests) go vetwill find runtime errors such as incorrect struct field tags, improper format strings, copying locks, etc.- run
go doc cmd/vetfor detailed information to see whatgo vetcan detect
Doc
Look up docs on code definitions.
$
$ go doc --help
Usage of [go] doc:
go doc
go doc <pkg>
go doc <sym>[.<method>]
go doc [<pkg>].<sym>[.<method>]
go doc <pkg> <sym>[.<method>]
For more information run
go help doc
Flags:
-all
show all documentation for package
-c symbol matching honors case (paths not affected)
-cmd
show symbols with package docs even if package is a command
-src
show source code for symbol
-u show unexported symbols as well as exported
For more help, you can also use:
go help doc
Go Doc (Package)
To list the public methods in the json package (output abbreviated for slide):
$ go doc json
package json // import "encoding/json"
Package json implements encoding and decoding of JSON as defined in RFC 7159.
The mapping between JSON and Go values is described in the documentation for the
Marshal and Unmarshal functions.
See "JSON and Go" for an introduction to this package:
https://golang.org/doc/articles/json_and_go.html
func Compact(dst *bytes.Buffer, src []byte) error
func HTMLEscape(dst *bytes.Buffer, src []byte)
func Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error
func Marshal(v any) ([]byte, error)
func MarshalIndent(v any, prefix, indent string) ([]byte, error)
func Unmarshal(data []byte, v any) error
func Valid(data []byte) bool
type Decoder struct{ ... }
func NewDecoder(r io.Reader) *Decoder
type Delim rune
type Encoder struct{ ... }
func NewEncoder(w io.Writer) *Encoder
type InvalidUTF8Error struct{ ... }
type InvalidUnmarshalError struct{ ... }
type Marshaler interface{ ... }
type MarshalerError struct{ ... }
type Number string
type RawMessage []byte
type SyntaxError struct{ ... }
type Token any
type UnmarshalFieldError struct{ ... }
type UnmarshalTypeError struct{ ... }
type Unmarshaler interface{ ... }
type UnsupportedTypeError struct{ ... }
type UnsupportedValueError struct{ ... }
Go Doc (Symbol)
$ go doc json.Decoder
package json // import "encoding/json"
type Decoder struct {
// Has unexported fields.
}
A Decoder reads and decodes JSON values from an input stream.
func NewDecoder(r io.Reader) *Decoder
func (dec *Decoder) Buffered() io.Reader
func (dec *Decoder) Decode(v any) error
func (dec *Decoder) DisallowUnknownFields()
func (dec *Decoder) InputOffset() int64
func (dec *Decoder) More() bool
func (dec *Decoder) Token() (Token, error)
func (dec *Decoder) UseNumber()
Go Doc (Method)
$ go doc json.Decoder.Decode
func (dec *Decoder) Decode(v interface{}) error
Decode reads the next JSON-encoded value from its input and stores it in the
value pointed to by v.
See the documentation for Unmarshal for details about the conversion of JSON
into a Go value.
Pkgsite - Local Package Documentation
pkgsite is the modern way to browse Go documentation locally. It provides the same experience as pkg.go.dev but for your local packages.
To install pkgsite:
go install golang.org/x/pkgsite/cmd/pkgsite@latest
To run Go documentation locally:
pkgsite -http=:8080
- Browse all your local packages
- Very useful for closed source projects
- Useful to see API changes you’ve made locally
- Shows module information and dependencies
- Better performance than the old godoc tool
Example:
Navigate to http://localhost:8080 and search for your package name
Go Env
- Print your go environment.
- Helpful for debugging path issues
$ go env
GO111MODULE="on"
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/user/.cache/go-build"
GOENV="/home/user/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/home/user/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/user/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GOVCS=""
GOVERSION="go1.25.0"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/home/user/projects/myapp/go.mod"
CGO_CFLAGS="-O2 -g"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-O2 -g"
CGO_FFLAGS="-O2 -g"
CGO_LDFLAGS="-O2 -g"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build1234567890=/tmp/go-build -gno-record-gcc-switches"
Go Fix
Fix finds Go programs that use old APIs and rewrites them to use newer ones. After you update to a new Go release, fix helps make the necessary changes to your programs.
Usage:
go tool fix [-r name,...] [path ...]
- Use
--diffto print the changes it will make instead of actually making them go tool fix --helpwill list all available rewrites
Linters
Golangci-lint
The golangci-lint is a linter aggregator - your one-stop shop for linting Go code. It runs multiple linters in parallel, reuses Go build cache, and provides fast, comprehensive analysis.
Why golangci-lint?
- Runs linters in parallel for speed
- Reuses Go build cache
- Includes 50+ linters (errcheck, staticcheck, revive, gosec, etc.)
- Configurable - enable only what you need
- Great CI/CD integration
- Modern, actively maintained
Installing Golangci-lint
Recommended installation:
# Binary installation (recommended)
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin
# Or using go install (requires Go 1.19+)
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
# Verify installation
golangci-lint --version
Note: It is NOT recommended to use go get for installation.
Full installation instructions: https://golangci-lint.run/welcome/install/
Running Golangci-lint
Basic usage:
# Run with default configuration
$ golangci-lint run
# Run on specific directories
$ golangci-lint run dir1 dir2/... dir3/file1.go
# Run with all linters enabled
$ golangci-lint run --enable-all
# Run only specific linters
$ golangci-lint run --disable-all -E errcheck -E staticcheck -E revive
Configuration File
Create .golangci.yml in your project root for modern configuration:
# Modern golangci-lint configuration
run:
timeout: 5m
go: '1.25'
linters:
enable:
- errcheck # Check for unchecked errors
- gosimple # Simplify code
- govet # Vet examines Go source code
- ineffassign # Detect ineffectual assignments
- staticcheck # Advanced static analysis
- unused # Check for unused code
- revive # Fast, configurable linter (replaces golint)
- gofmt # Check formatting
- goimports # Check imports
- gosec # Security issues
- misspell # Spelling errors
- unconvert # Unnecessary type conversions
- unparam # Unused function parameters
linters-settings:
revive:
rules:
- name: blank-imports
- name: context-as-argument
- name: error-return
- name: error-strings
- name: error-naming
- name: exported
- name: if-return
- name: var-naming
- name: package-comments
- name: receiver-naming
errcheck:
check-type-assertions: true
check-blank: true
govet:
enable-all: true
issues:
exclude-use-default: false
max-issues-per-linter: 0
max-same-issues: 0
Enabling & Disabling Linters
Pass -E/--enable to enable linter and -D/--disable to disable:
# Disable all, enable specific linters
$ golangci-lint run --disable-all -E errcheck -E staticcheck
# Enable additional linters beyond defaults
$ golangci-lint run -E gosec -E revive
You can also use presets:
# Run bugs preset (errcheck, govet, staticcheck, etc.)
$ golangci-lint run --presets bugs
# Available presets: bugs, comment, complexity, error, format,
# import, metalinter, module, performance,
# sql, style, test, unused
Default Linters (golangci-lint V1.55+)
Enabled by default:
- errcheck - Unchecked errors
- gosimple - Simplify code
- govet - Suspicious constructs
- ineffassign - Ineffectual assignments
- staticcheck - Advanced static analysis
- unused - Unused code
Important Linters To Enable
These linters are not enabled by default but highly recommended:
Security:
- gosec - Security problems
Style & Quality:
- revive - Drop-in replacement for deprecated golint
- stylecheck - Style issues
Formatting:
Performance:
- prealloc - Slice preallocation
Revive - The Modern Linter
revive is included in golangci-lint and is the modern replacement for the deprecated golint.
Why revive?
- Fast and configurable
- Drop-in replacement for golint
- Extensible rule system
- Active maintenance
- Great defaults
Enabling revive in golangci-lint
# .golangci.yml
linters:
enable:
- revive
linters-settings:
revive:
rules:
- name: blank-imports
- name: context-as-argument
- name: context-keys-type
- name: dot-imports
- name: error-return
- name: error-strings
- name: error-naming
- name: exported
- name: if-return
- name: var-naming
Or via command line:
$ golangci-lint run -E revive
For standalone revive usage (less common):
$ go install github.com/mgechev/revive@latest
$ revive -config revive.toml ./...
Note: Most projects use revive via golangci-lint rather than standalone.
Errcheck
errcheck is a program for checking for unchecked errors in go programs.
Install
$ go install github.com/kisielk/errcheck@latest
errcheck requires Go 1.12 or newer and depends on the package go/loader from the golang.org/x/tools repository.
For basic usage, just give the package path of interest as the first argument:
$ errcheck github.com/kisielk/errcheck/testdata
To check all packages beneath the current directory:
$ errcheck ./...
Staticcheck
staticcheck is go vet on steroids, applying a ton of static analysis checks you might be used to from tools like ReSharper for C#.
Installation
$ go install honnef.co/go/tools/cmd/staticcheck@latest
Purpose
The main purpose of staticcheck is editor integration, or workflow integration in general. For example, by running staticcheck when saving a file, one can quickly catch simple bugs without having to run the whole test suite or the program itself.
The tool shouldn’t report any errors unless there are legitimate bugs - or very dubious constructs - in the code.
Running
All of the following examples are valid invocations:
staticcheck github.com/example/foo
staticcheck github.com/example/foo github.com/example/bar
staticcheck github.com/example/...
In addition, a single package can be specified as a list of files:
staticcheck file1.go file2.go file3.go
Note that all files of the package need to be specified, similar to how go build works.
Gosimple
gosimple is a linter for Go source code that specializes on simplifying code.
Gosimple requires Go 1.6 or later.
Installation
go install honnef.co/go/tools/cmd/gosimple@latest
Usage
Invoke gosimple with one or more filenames, a directory, or a package named by
its import path. Gosimple uses the same import path syntax as the go command
and therefore also supports relative import paths like ./... Additionally the
... wildcard can be used as suffix on relative and absolute file paths to
recurse into them.
The output of this tool is a list of suggestions in Vim quickfix format, which is accepted by lots of different editors.
Structcheck
structcheck finds unused struct fields.
Installation
go install gitlab.com/opennota/check/cmd/structcheck@latest
$ structcheck --help
Usage of structcheck:
-a Count assignments only
-e Report exported fields
-t Load test files too
```
```bash
$ structcheck fmt
fmt: /usr/lib/go/src/fmt/format.go:47:2: fmt.fmtFlags.zero
fmt: /usr/lib/go/src/fmt/format.go:41:2: fmt.fmtFlags.minus
fmt: /usr/lib/go/src/fmt/format.go:42:2: fmt.fmtFlags.plus
fmt: /usr/lib/go/src/fmt/format.go:43:2: fmt.fmtFlags.sharp
fmt: /usr/lib/go/src/fmt/format.go:44:2: fmt.fmtFlags.space
fmt: /usr/lib/go/src/fmt/format.go:52:2: fmt.fmtFlags.plusV
fmt: /usr/lib/go/src/fmt/format.go:53:2: fmt.fmtFlags.sharpV
fmt: /usr/lib/go/src/fmt/format.go:39:2: fmt.fmtFlags.widPresent
fmt: /usr/lib/go/src/fmt/format.go:40:2: fmt.fmtFlags.precPresent
fmt: /usr/lib/go/src/fmt/format.go:45:2: fmt.fmtFlags.unicode
fmt: /usr/lib/go/src/fmt/format.go:46:2: fmt.fmtFlags.uniQuote
Varcheck
varcheck finds unused global variables and constants.
$ varcheck --help
Usage of varcheck:
-e=false: Report exported variables and constants
$ varcheck image/jpeg
image/jpeg: /usr/lib/go/src/image/jpeg/reader.go:74:2: adobeTransformYCbCr
image/jpeg: /usr/lib/go/src/image/jpeg/reader.go:75:2: adobeTransformYCbCrK
image/jpeg: /usr/lib/go/src/image/jpeg/writer.go:54:2: quantIndexLuminance
image/jpeg: /usr/lib/go/src/image/jpeg/writer.go:55:2: quantIndexChrominance
image/jpeg: /usr/lib/go/src/image/jpeg/writer.go:91:2: huffIndexLuminanceDC
image/jpeg: /usr/lib/go/src/image/jpeg/writer.go:92:2: huffIndexLuminanceAC
image/jpeg: /usr/lib/go/src/image/jpeg/writer.go:93:2: huffIndexChrominanceDC
image/jpeg: /usr/lib/go/src/image/jpeg/writer.go:94:2: huffIndexChrominanceAC
Ineffassign
ineffassign detects when assignments to existing variables are not used.
Example usage:
$ ineffassign ./
client/examples/example.go:168:5: ineffectual assignment to err
client/examples/example.go:184:2: ineffectual assignment to spaces
handler_test.go:616:5: ineffectual assignment to err
influxql/parser.go:777:8: ineffectual assignment to pos
influxql/parser.go:777:13: ineffectual assignment to lit
reference: influxql/parser.go:777:8: ineffectual assignment to pos
// Next token should be ASC, DESC, or IDENT | STRING.
tok, pos, lit := p.scanIgnoreWhitespace()
if tok == IDENT || tok == STRING {
field.Name = lit
// Check for optional ASC or DESC token.
tok, pos, lit = p.scanIgnoreWhitespace() // this line pos is assigned but not used
if tok != ASC && tok != DESC {
p.unscan()
return field, nil
}
}
Dead Code
deadcode is a very simple utility which detects unused declarations in a Go package.
Usage
$ deadcode [-test] [packages]
-test Include test files
packages A list of packages using the same conventions as the go tool
Limitations
- Self-referential unused code is not currently reported
- A single package can be tested at a time
- Unused methods are not reported
Linters Disabled By Default
The following linters are disabled by default (-E/--enable). They can sometimes report false positive, or opinions and result in CI failing a build.
They are very useful for local development, but not as commonly enabled in a CI pipeline.
- bodyclose - checks whether HTTP response body is closed successfully
golint- DEPRECATED: Usereviveorstaticcheckinstead- stylecheck - Stylecheck is a replacement for golint (included in staticcheck)
- gosec - Inspects source code for security problems
interfacer- ARCHIVED: No longer maintained, removed from golangci-lint- unconvert - Remove unnecessary type conversions
- dupl - Tool for code clone detection
- goconst - Finds repeated strings that could be replaced by a constant
- gocyclo - Computes and checks the cyclomatic complexity of functions
- gofmt - Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification
- goimports - Goimports does everything that gofmt does. Additionally it checks unused imports
maligned- DEPRECATED: Usefieldalignment(part of govet) instead- depguard - Go linter that checks if package imports are in a list of acceptable packages
- misspell - Finds commonly misspelled English words in comments
- lll - Reports long lines
- unparam - Reports unused function parameters
- dogsled - Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())
- nakedret - Finds naked returns in functions greater than a specified function length
- prealloc - Finds slice declarations that could potentially be preallocated
scopelint- REMOVED: Replaced byexportlooprefin golangci-lint- gocritic - The most opinionated Go source code linter
- gochecknoinits - Checks that no init functions are present in Go code
- gochecknoglobals - Checks that no globals are present in Go code
- godox - Tool for detection of FIXME, TODO and other comment keywords
- funlen - Tool for detection of long functions
- whitespace - Tool for detection of leading and trailing whitespace
Golint (DEPRECATED - Historical Reference Only)
⚠️ WARNING: golint is officially deprecated and no longer maintained.
Use these modern alternatives instead:
- revive - Fast, configurable, extensible drop-in replacement
- staticcheck - Advanced static analysis with style checks included
- golangci-lint with
reviveenabled - Aggregated linting solution
Historical Context (for reference only)
Golint was a style linter that pointed out style mistakes based on Effective Go. It required human judgment to decide which suggestions to apply.
For new projects: Do not install or use golint. Configure golangci-lint with revive instead.
For legacy projects: Migrate to revive or staticcheck as soon as possible.
Betteralign - Struct Field Alignment Optimization
betteralign finds inefficiently packed structs and can automatically fix them. It’s the modern replacement for the deprecated aligncheck tool.
Installation
$ go install golang.org/x/tools/go/analysis/passes/fieldalignment/cmd/fieldalignment@latest
Usage
Check for alignment issues:
$ fieldalignment ./...
Automatically fix alignment issues:
$ fieldalignment -fix ./...
Example
Before optimization:
type Example struct {
flag bool // 1 byte + 7 bytes padding
count int64 // 8 bytes
active bool // 1 byte + 7 bytes padding
value int64 // 8 bytes
}
// Total: 32 bytes
After running fieldalignment -fix:
type Example struct {
count int64 // 8 bytes
value int64 // 8 bytes
flag bool // 1 byte
active bool // 1 byte + 6 bytes padding
}
// Total: 24 bytes (25% reduction!)
For the visualization of struct packing see http://golang-sizeof.tips/
Note: While betteralign can reduce memory usage, reordering fields may affect:
- Readability (logical grouping of fields)
- API compatibility (if struct is part of public API)
- Serialization formats (if field order matters)
Dupl
dupl is a tool written in Go for finding code clones. So far it can find clones only in the Go source files.
Due to the used method dupl can report so called “false positives” on the output. These are the ones we do not consider clones (whether they are too small, or the values of the matched tokens are completely different).
Installation
$ go install github.com/mibk/dupl@latest
Example
The reduced output of this command with the following parameters for the Docker source code looks like this.
$ dupl -t 200 -html > docker.html
Interfacer
interfacer is a linter that suggests interface types. In other words, it warns about the usage of types that are more specific than necessary.
$ go install github.com/mvdan/interfacer/cmd/interfacer@latest
Usage
func ProcessInput(f *os.File) error {
b, err := ioutil.ReadAll(f)
if err != nil {
return err
}
return processBytes(b)
}
$ interfacer ./...
foo.go:10:19: f can be io.Reader
Goconst
goconst finds repeated strings that could be replaced by a constant.
Installation
$ go install github.com/jgautheron/goconst/cmd/goconst@latest
Examples:
$ goconst ./...
$ goconst -ignore "yacc|\.pb\." $GOPATH/src/github.com/cockroachdb/cockroach/...
$ goconst -min-occurrences 3 -output json $GOPATH/src/github.com/cockroachdb/cockroach
$ goconst -numbers -min 60 -max 512 .
Gosec - Golang Security Checker
gosec inspects source code for security problems by scanning the Go AST.
Installation
$ go install github.com/securego/gosec/v2/cmd/gosec@latest
Git Hook - Pre-commit
<project-root>/.git/hooks/pre-commit
#!/usr/bin/env bash
fmtcount=`git ls-files | grep '.go$' | xargs gofmt -l 2>&1 | wc -l`
if [ $fmtcount -gt 0 ]; then
echo "Some files aren't formatted, please run 'go fmt ./...' to format your source code before committing"
exit 1
fi
vetcount=`go tool vet ./ 2>&1 | wc -l`
if [ $vetcount -gt 0 ]; then
echo "Some files aren't passing vet heuristics, please run 'go vet ./...' to see the errors it flags and correct your source code before committing"
exit 1
fi
# Ensure FIXME lines are removed before commit.
fixme_lines=$(git diff --cached | grep ^+ | grep -v pre-commit | grep FIXME | sed 's_^+\s*__g')
if [ "$fixme_lines" != "" ]; then
echo "Please remove the following lines:"
echo -e "$fixme_lines"
exit 1
fi
- Catches
go fmt,go vet, andFIXMEcomments. Custom tailor to your use.
Exercise - 10 Minutes
Using golangci-lint, run it against one of your local repos, or find one online to run against.
Installation: https://golangci-lint.run/welcome/install/
A sample repo to run against could be Pop
Mac/Linux
# Clone a sample repository
git clone https://github.com/gobuffalo/pop
cd pop
# Run golangci-lint with default linters
golangci-lint run
# Try with revive enabled
golangci-lint run -E revive
# Try with multiple linters
golangci-lint run -E revive -E gosec -E misspell
Windows
# Clone a sample repository
git clone https://github.com/gobuffalo/pop
cd pop
# Run golangci-lint with default linters
golangci-lint run
# Try with revive enabled
golangci-lint run -E revive
# Try with multiple linters
golangci-lint run -E revive -E gosec -E misspell