Debugging & Defect Scenarios Best Practices
Prevention checklists and RCA templates for Go incidents.
Search across all documentation pages
Prevention checklists and RCA templates for Go incidents.
These rules turn recurring sharp edges into review habits, CI gates, and post-incident notes that stop the same defect class from shipping twice.
go test -race, govet, and golangci-lint on packages that share mutable state.GOTRACEBACK=all and pprof endpoints accelerate root cause.-race and realistic parallelism. Flaky local passes hide data races that production load exposes.(nil, nil) for "not found" on pointer types. Return ErrNotFound or a typed sentinel callers handle with errors.Is.err == nil does not guarantee non-nil *T.error interfaces. Return untyped nil error or a concrete error value, not (*MyError)(nil) unless callers expect it.null unmarshals to nil without error.nilaway, staticcheck) on service boundaries. Catch return paths analyzers flag before runtime panic.s[low:high:high] or append([]T(nil), s...) when consumers may append.sync.Map, or shard locks for caches.bytes.Buffer.Bytes() views as ephemeral. Copy if retaining beyond the next buffer mutation.-race when slices or structs are mutated from handlers and background refresh loops.r.Context() through HTTP handlers to all blocking I/O. Client disconnect and Shutdown must stop work.cancel() from WithCancel/WithTimeout. defer cancel() immediately after creation.select on ctx.Done() for goroutines blocked on channels. Prevent leaks when producers never send.v := v or parameter pass remains defensive after Go 1.22.defer inside loops without a per-iteration function. Defers stack until function exit and leak file descriptors.sync.Once for lazy singletons, not unsynchronized double-checked init. Race detector validates Once paths cheaply.ParseInLocation. Zone-less layouts use UTC, not the developer laptop zone.In(loc) for display. Compare with Equal, not formatted strings.time/tzdata or install tzdata in minimal containers. LoadLocation fails silently in CI if never called.http.Server.Shutdown timeout. Cancel root context when signal arrives.Context propagation, no shared mutable maps without locks, explicit error returns instead of (nil, nil), and -race in CI on handler packages.
Use A for incidents, B-D for concurrent or parser changes, E for API and deploy changes.
Full pass monthly on hot packages.
Each letter maps to articles in this section.
Use the list for prevention; use walkthroughs when teaching reproduction steps.
Symptom family, timeline, goroutine/panic evidence, root cause frame, fix PR, and guard (test, lint, or monitor) proving recurrence is detected.
Single-goroutine logic bugs and small repro programs.
Move to Delve and pprof when concurrency, leaks, or interface nil semantics appear.
Core nil, slice, and race rules apply.
Some stdlib (tzdata, parts of reflection) differ - verify board targets per manifest pins.
Quarterly on staging: load, stop traffic, confirm goroutine count returns to baseline within minutes.
No - linters catch patterns reviewers miss.
Review catches wrong semantics linters cannot model.
Run go test -race -count=50 on the flaky package.
Most flakiness in Go services is races or time assumptions.
Yes for packages that mutate shared state or spawn goroutines in handlers.
Pure packages may be excluded explicitly in CI config.
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).
Reviewed by Chris St. John·Last updated Jul 19, 2026