Files
atelier/languages/go-tooling.md
T
Jon Chery 4e433158cd docs(P03): complete language-derived extension — v0.4
---ci---
project: atelier
phase: 3
milestone: v0.4
status: complete
phase_role: execution
phase_tag: v0.3.3
requirements:
  covered: [ATELIER-102, ATELIER-103, ATELIER-104, ATELIER-105]
  partial: []
---/ci---
2026-08-05 16:07:21 +00:00

4.8 KiB

Go Tooling — Derived Application

Applies Atelier's domain principles to Go tooling specifically. Derives from domains/ docs; introduces no new P-rules (D-063). See languages/go.md for the language first-principles stub.

go vet and golangci-lint (DevOps P2 Automation, C2 Clarity)

  • go vet is the stdlib baseline: it catches printf format mismatches, lock-copy-by-value, and unreachable code. Run on every build.
  • golangci-lint aggregates vet + dozens of linters: enable errcheck (no _ = err), govet, staticcheck, ineffassign, unused, gofmt, goimports. Each enabled linter has a one-line # reason: in .golangci.yml.
  • errcheck enforces errors/P2 (fail loudly): a discarded error is a silent failure. errcheck fails the build on _ = doX().
  • goimports over gofmt: goimports adds missing imports and removes unused ones, in addition to formatting. The format is not debated in review (Clarity C2).
# .golangci.yml
linters:
  enable:
    - errcheck    # reason: Errors P2 — no swallowed errors
    - govet
    - staticcheck
    - ineffassign
    - unused
    - gofmt
    - goimports
linters-settings:
  errcheck:
    check-blank: true   # fail on _ = fn()

go test -race (Concurrency P6 No Silent Races)

  • go test -race in CI, always: the race detector instruments memory accesses and fails on data races. It is the primary enforcement of concurrency/P6 (no silent races).
  • -race adds overhead; run it in a separate CI job: the race build is ~2x slower; keep the fast unit-test job and add a race job.
  • -race requires tests that actually exercise the concurrent path: a test that calls Get/Set serially finds no race. Write tests that spawn goroutines hitting the same map.
  • Applies concurrency/P6: a race detected is a bug fixed; a race undetected is a heisenbug in production. The detector is the safety net.
# CI race job
go test -race -count=1 ./...

Module Discipline (DevOps P1 Reproducibility)

  • go mod tidy on every change that touches imports: removes unused deps and adds missing ones. A go.mod with stale entries breaks reproducibility.
  • go.sum committed and verified: go mod verify checks the checksums of the module cache against go.sum. A drifted go.sum is a supply-chain signal.
  • Pinned major versions in go.mod: require github.com/x/y v1.2.3 pins the minor; a v1.2.4 patch may auto-update. For applications, consider a go.mod proxy that pins to exact commits.
  • go mod vendor for hermetic CI: vendoring vendor/ into the repo means CI builds without network. The trade-off is repo size; the win is reproducibility (DevOps P1).
# CI build gate
go mod tidy
go mod verify
go build ./...
go test -race ./...

Reproducible Builds (DevOps P1 Reproducibility, C3 Simplicity)

  • One Go toolchain version, pinned: goenv or asdf pins the Go version per repo; a .go-version file declares it. A CI job that uses "latest" Go drifts.
  • CGO_ENABLED=0 for static binaries: a static binary runs in a scratch container with no libc dependency. Set in CI for all release builds.
  • -trimpath and -ldflags='-s -w' for reproducible output: strips the build path from the binary and removes debug info. Two builds of the same commit produce byte-identical binaries.
# Reproducible release build
CGO_ENABLED=0 go build -trimpath -ldflags='-s -w' -o app ./cmd/app

Documentation in the Pipeline (Documentation P1 Documentation is Code, DevOps P9 Documentation in the Pipeline)

  • go doc from comments: package comments and exported-symbol comments are the API docs; go doc and pkg.go.dev render them. Missing comments on exported symbols fail revive/golint (Documentation P1).
  • // Example functions are run by go test: an ExampleUser function with // Output: is a tested artifact; a stale output fails the build.
  • README.md and docs/ are built by mkdocs or similar: the pipeline validates links and renders; a broken link fails CI (Documentation P1).
// GetUser fetches a user by id.
//
// Example:
//
//	u, err := GetUser(id)
//	if err != nil { ... }
func GetUser(id UserId) (*User, error) { /* ... */ }

func ExampleGetUser() {
    u, err := GetUser("abc")
    fmt.Println(u, err)
    // Output: <nil> not found
}

Cross-References

  • domains/devops/ci-cd.md — the pipeline gates that host vet/lint/test.
  • domains/devops/first-principles.md — DevOps P1 Reproducibility, P2 Automation.
  • domains/concurrency/first-principles.md — Concurrency P6 No Silent Races (-race).
  • domains/documentation/first-principles.md — Documentation P1 Documentation is Code.
  • languages/go-types.md — the type rules staticcheck enforces reference this doc.
  • languages/go-testing.md — the go test flags (-race, -count) detailed here.