Race detector
Use the race detector routinely in tests; data races are correctness bugs, not minor warnings.
Canonical guidance
- run race detection in tests and representative concurrent workloads
- treat reported races as real bugs
- fix shared-memory ownership, synchronization, or lifecycle errors directly
Use when
- testing concurrent code
- debugging flaky integration tests
- reviewing code with shared state
Avoid
- dismissing races because code “usually works”
- adding sleeps to hide synchronization bugs
- assuming channels alone make all shared state safe
Preferred pattern
func TestCounter(t *testing.T) {
var mu sync.Mutex
var n int
var wg sync.WaitGroup
for i := 0; i < 4; i++ {
wg.Add(1)
go func() {
defer wg.Done()
mu.Lock()
n++
mu.Unlock()
}()
}
wg.Wait()
if n != 4 {
t.Fatalf("n = %d, want 4", n)
}
}
Anti-pattern
- merging concurrency-heavy code without race-detector coverage
Explanation: This anti-pattern is common under schedule pressure, but timing hacks make tests pass while the underlying synchronization bug remains.
Why
- data races can corrupt behavior silently and nondeterministically
Sources
- Data Race Detector - Go Team
- Introducing the Go Race Detector - Dmitry Vyukov and Andrew Gerrand