Code Quality Basics
10 examples to get you started with Go code quality - 7 basic and 3 intermediate.
Prerequisites
- Install Go 1.26.x or later from go.dev/dl.
- Create a module:
mkdir quality && cd quality && go mod init example.com/quality. - Install helpers:
go install golang.org/x/tools/cmd/goimports@latestandgo install github.com/golangci/golangci-lint/cmd/golangci-lint@latest.
Basic Examples
1. gofmt in place
Rewrite a file to canonical Go formatting.
gofmt -w main.gopackage main
import "fmt"
func main() {fmt.Println("hello")}After gofmt -w, spacing and indentation match community style.
gofmtis deterministic: same input always yields same output.- CI often runs
test -z "$(gofmt -l .)"to fail on drift. - There is no project-level format config to maintain.
Related: gofmt & goimports Enforcement - team formatting policy
2. goimports for imports
Format and fix import blocks in one step.
goimports -w .package main
import "fmt"
func main() {
fmt.Println(time.Now()) // missing import before goimports
}- goimports adds missing stdlib imports and removes unused ones.
- Use
-local example.com/qualityto group your module paths separately. - Editors often run goimports on save via gopls.
Related: gofmt & goimports Enforcement -
-localgrouping
3. go vet on all packages
Run built-in analyzers across the module.
go vet ./...package main
import "fmt"
func main() {
fmt.Printf("%d", "not-a-number") // vet: Printf format mismatch
}- Vet reports issues the compiler accepts but are likely bugs.
- Run in CI on every PR; zero findings should be the default.
- New Go releases may add vet checks - read release notes.
Related: go vet & Common Analyzer Diagnostics - common messages
4. Minimal test gate
Quality includes behavior, not just static checks.
go test ./...package add
func Sum(a, b int) int { return a + b }package add_test
import "testing"
func TestSum(t *testing.T) {
if got := add.Sum(2, 3); got != 5 {
t.Fatalf("got %d", got)
}
}- Tests live in
*_test.gofiles in the same package (or_testexternal package). go testcompiles tests with the race detector when you pass-race.- Failing tests should block merge before linter debates matter.
Related: CI Quality Gates: Lint, Test, Race, Vuln - full pipeline
5. List unformatted files
Check formatting without writing files (CI-friendly).
gofmt -l .- Empty output means every
.gofile is formatted. - Non-empty output lists paths that need
gofmt -w. - Pair with
git diff --exit-codeafter format in pre-commit hooks.
Related: Pre-commit Hooks & Review Checklists - local automation
6. golangci-lint first run
Run the standard linter bundle.
golangci-lint run ./...- First run downloads analyzer binaries; pin version in CI for reproducibility.
- Start with default linters; tune
.golangci.ymlafter baseline is green. - Fix vet-class issues before chasing style nits.
Related: golangci-lint Configuration - config file patterns
7. Makefile check target
One command for contributors.
.PHONY: check
check:
gofmt -l . | tee /tmp/gofmt.out && test ! -s /tmp/gofmt.out
go vet ./...
go test ./...make check- Document
make checkin README and CONTRIBUTING. - Expand with
golangci-lint runwhen the team is ready. - Keep targets fast enough for pre-push hooks (< 2 minutes on laptops).
Related: Linting & Code Quality Best Practices - adoption pacing
Intermediate Examples
8. Race detector in tests
Catch data races that vet misses.
go test -race ./...package counter
import "sync"
type Counter struct {
mu sync.Mutex
n int
}
func (c *Counter) Inc() {
c.mu.Lock()
c.n++
c.mu.Unlock()
}-raceslows tests and needs more memory; run in CI, not every save.- Race failures include goroutine stacks - fix by adding synchronization.
- HTTP handlers sharing mutable state without locks are a common trigger.
Related: CI Quality Gates: Lint, Test, Race, Vuln - when to require race
9. go mod tidy check
Keep module metadata consistent.
go mod tidy
git diff --exit-code go.mod go.sum- Fails CI when dependencies drift without committed
go.sumupdates. - Run after dependency bumps and before tagging releases.
- Pair with
govulncheckfor security scanning on the tidy graph.
Related: govulncheck for Dependency Vulnerabilities - CVE scanning
10. Simple pre-commit script
Block commits that fail fast checks.
#!/usr/bin/env bash
set -euo pipefail
gofmt -l . | grep . && { echo "run gofmt -w"; exit 1; } || true
go vet ./...
go test ./...chmod +x scripts/pre-commit.sh
# wire with: ln -s ../../scripts/pre-commit.sh .git/hooks/pre-commit- Hooks catch issues before CI queue time.
- Keep hooks fast; defer golangci-lint to CI if it exceeds ~60s locally.
- Document bypass policy (
--no-verify) for emergencies only.
Related: Pre-commit Hooks & Review Checklists - hook templates
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).