Stage 1 · Code
Testing & Debugging in Go
Race Detector
Find and fix data races before they cause production incidents using Go's built-in race detector.
What is a Data Race?
A data race occurs when two goroutines access the same memory location concurrently and at least one access is a write. The result is undefined behaviour — your program may produce wrong answers, crash, or appear to work correctly some of the time.
Using the Race Detector
# During development and in CI
go test -race ./...
# For a running binary (useful for integration tests)
go run -race main.go
# Build a race-instrumented binary for staging
go build -race -o server-race ./cmd/serverThe race detector output identifies the exact goroutines and lines involved. It looks like this:
==================
WARNING: DATA RACE
Write at 0x00c000014098 by goroutine 7:
main.main.func1()
/tmp/main.go:15 +0x44
Previous read at 0x00c000014098 by goroutine 6:
main.main.func1()
/tmp/main.go:15 +0x38
Goroutine 7 (running) created at:
main.main()
/tmp/main.go:13 +0x7e
==================The detector tells you the memory address, the goroutine IDs, the file and line of the racing accesses, and where each goroutine was created.
Fixing Races with Mutex
Fixing Races with Atomic
For simple numeric counters, sync/atomic is faster than a mutex because it uses CPU-level atomic instructions with no lock contention.
| Approach | Best for | Overhead |
|---|---|---|
sync.Mutex | Complex state, struct fields, conditional logic | Small but non-zero |
sync.RWMutex | Many readers, few writers | Slightly more than Mutex |
sync/atomic | Single numeric counters and flags | Very low — CPU instruction |
| Channels | Communicating values between goroutines | Higher, but semantically clearest |
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.