Go's design — explicit error returns, no exceptions, simple goroutines, structural interfaces — eliminates whole classes of bugs that other languages have. The remaining bug surface clusters around resource lifetimes, shared-state concurrency, and the language's choices around nil and zero values.
The recurring shapes: a missing `defer file.Close()` after `os.Open`, a goroutine that captures a loop variable by reference (a behavior the language fixed in 1.22 but legacy code still has), unguarded shared maps that should have been a `sync.Map` or behind a mutex, and silently-ignored errors where `_ = err` lives.
Go's race detector (`go test -race`) is a superpower for finding the second category. The static-analysis ecosystem (`go vet`, `staticcheck`, `errcheck`) catches a surprising amount of the rest.
Common pitfalls
Missing `defer` for cleanup
Every `os.Open`, `Begin`, `Lock`, `LockShared` needs a paired close/release that fires on every return path.
Loop-variable capture in goroutines
Pre-1.22, `for _, x := range xs { go work(x) }` was usually fine but `go func() { ... x ... }()` shared the same `x`.
Ignored errors
`f(...)` (no return value capture) and `_ = f()` are both error-swallowing. `errcheck` flags them.