Table of Contents
- Tooling & Modules
- Contents
- Modules
- go mod commands
- Build, run, install
- Vet & format
- Build tags
- Cross-compilation
- Common test flags
- pprof quickstart
- golangci-lint
- Makefile snippet
Tooling & Modules¶
Contents¶
- Modules
- go mod commands
- Build, run, install
- Vet & format
- Build tags
- Cross-compilation
- Common test flags
- pprof quickstart
- golangci-lint
- Makefile snippet
Modules¶
A module is a tree of packages with a go.mod at its root defining the module path and dependencies.
go.mod example:
module github.com/you/project
go 1.22
require (
github.com/stretchr/testify v1.9.0
golang.org/x/sync v0.7.0
)
require github.com/davecgh/go-spew v1.1.1 // indirect
replace github.com/foo/bar => ../bar // local override
exclude github.com/bad/dep v1.2.3
go.sum holds cryptographic checksums — commit it. GOFLAGS=-mod=readonly keeps CI honest.
go mod commands¶
go mod init <path> # initialize a new module
go mod tidy # add missing + remove unused deps; sync go.sum
go mod download # download modules to the cache
go mod verify # check cached modules match go.sum
go mod why <pkg> # explain why a dependency is needed
go mod graph # print the module dependency graph
go mod edit -require=x@v1.2.3 # programmatically edit go.mod
go mod vendor # write deps into ./vendor (then build -mod=vendor)
go get example.com/pkg@latest # add/upgrade a dependency
go get example.com/pkg@v1.2.3 # specific version
go get example.com/pkg@none # remove a dependency
go get -u ./... # upgrade all deps to latest minor/patch
go get -u=patch ./... # patch-level upgrades only
go list -m all # list all modules in the build
go list -m -versions example.com/pkg # available versions
go work init ./a ./b # multi-module workspace (go.work)
go work use ./c
go mod tidy is the one you run most: it makes go.mod/go.sum exactly reflect imports.
Build, run, install¶
go run . # compile + run current package
go run ./cmd/server # run a specific main package
go run main.go # run a file
go build ./... # compile everything (no output for non-main)
go build -o bin/app ./cmd/app # named output binary
go build -ldflags="-s -w" # strip debug info -> smaller binary
go build -ldflags="-X main.version=$(git describe)" # inject a variable
go build -race ./... # with race detector
go build -trimpath # remove local paths from binary (reproducible)
go install ./cmd/app # build + install to $GOBIN (or $GOPATH/bin)
go install example.com/cmd/tool@latest # install a remote tool
go clean -cache # clear build cache
go env GOPATH GOBIN GOMODCACHE # inspect environment
Vet & format¶
gofmt -w . # format files in place (canonical formatting)
gofmt -l . # list files that need formatting (CI gate)
go fmt ./... # gofmt over packages
goimports -w . # gofmt + manage import grouping/removal (separate tool)
go vet ./... # static checks: printf args, lock copies, etc.
go vet -vettool=$(which shadow) ./... # extra analyzers
gofmt is non-negotiable in Go; there is one true style. go vet catches real bugs (e.g. Printf verb mismatches, struct-tag typos, unreachable code).
Build tags¶
Conditional compilation via //go:build constraints (must be near the top, above package, followed by a blank line).
Common forms: //go:build linux, //go:build !windows, //go:build (darwin || linux) && cgo, //go:build ignore (excluded from normal builds).
Filename suffixes also constrain builds: file_linux.go, file_amd64.go, file_linux_arm64.go, file_test.go.
Other useful directives:
//go:embed templates/*.html // embed files into the binary (needs import "embed")
//go:generate stringer -type=Color // ran by `go generate ./...`
//go:noinline
Cross-compilation¶
Set GOOS and GOARCH. No C toolchain needed if CGO_ENABLED=0.
GOOS=linux GOARCH=amd64 go build -o app-linux ./cmd/app
GOOS=darwin GOARCH=arm64 go build -o app-mac-m1 ./cmd/app
GOOS=windows GOARCH=amd64 go build -o app.exe ./cmd/app
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build ./... # static, for scratch containers
go tool dist list # all supported GOOS/GOARCH pairs
go env GOOS GOARCH # current target
Common GOARCH: amd64, arm64, 386, arm. Common GOOS: linux, darwin, windows, freebsd, js (with GOARCH=wasm), wasip1.
Common test flags¶
go test ./... # all packages
go test -v ./pkg # verbose
go test -run 'TestFoo/sub' # regexp filter on test+subtest name
go test -count=1 # bypass test cache
go test -race # data race detector
go test -cover # coverage %
go test -coverprofile=c.out -covermode=atomic ./...
go test -bench=. -benchmem # benchmarks with allocs
go test -benchtime=3s -count=5 # tune benchmark runs
go test -fuzz=FuzzX -fuzztime=30s
go test -timeout=30s # per-package test timeout (default 10m)
go test -short # skip tests guarded by testing.Short()
go test -shuffle=on # randomize test order
go test -parallel=4 # max parallel tests
go test -tags=integration # build tag selection
go test -json ./... | gotestsum # machine-readable output
go test -failfast # stop after first failure
pprof quickstart¶
import _ "net/http/pprof" // registers handlers on the default mux
go func() { log.Println(http.ListenAndServe("localhost:6060", nil)) }()
# Collect & explore live profiles:
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30 # CPU
go tool pprof http://localhost:6060/debug/pprof/heap # memory
go tool pprof http://localhost:6060/debug/pprof/goroutine # goroutines
# From benchmarks:
go test -bench=. -cpuprofile=cpu.out -memprofile=mem.out
go tool pprof cpu.out
# Inside pprof: top, list <func>, web (needs graphviz), peek, traces
# Flame graph in browser:
go tool pprof -http=:8081 cpu.out
# Execution tracer:
go test -trace=trace.out
go tool trace trace.out
golangci-lint¶
Aggregates many linters into one fast run. (Third-party, the de facto standard.)
.golangci.yml:
linters:
enable:
- errcheck
- govet
- staticcheck
- revive
- gosimple
- ineffassign
- unused
- gofmt
- goimports
linters-settings:
govet:
enable: [shadow]
run:
timeout: 5m
issues:
exclude-rules:
- path: _test\.go
linters: [errcheck]
Makefile snippet¶
BINARY := app
PKG := ./...
VERSION := $(shell git describe --tags --always --dirty)
LDFLAGS := -s -w -X main.version=$(VERSION)
.PHONY: all build test lint fmt vet tidy clean run cover
all: lint test build
build:
CGO_ENABLED=0 go build -trimpath -ldflags "$(LDFLAGS)" -o bin/$(BINARY) ./cmd/$(BINARY)
run:
go run ./cmd/$(BINARY)
test:
go test -race -count=1 $(PKG)
cover:
go test -coverprofile=cover.out $(PKG) && go tool cover -html=cover.out
bench:
go test -bench=. -benchmem $(PKG)
lint:
golangci-lint run $(PKG)
fmt:
gofmt -w . && goimports -w .
vet:
go vet $(PKG)
tidy:
go mod tidy
clean:
rm -rf bin cover.out
go clean -cache
Tabs (not spaces) indent Makefile recipes.