Races
Concurrency versus parallelism, IO-bound and CPU-bound work, data races and race conditions (not the same thing), races in single-threaded async code, check-then-act and read-modify-write bugs, and race detectors (Go -race, ThreadSanitizer).
Concurrency and parallelism
Rob Pike's line is the cleanest summary: concurrency is about dealing with lots of things at once; parallelism is about doing lots of things at once. A well-structured concurrent program can be run in parallel; a parallel program without good concurrent structure is a source of bugs.
Races without threads, and race detectors
// the fix: make the check and the update one atomic operation in the database const r = await db.query( 'UPDATE wallets SET balance_kobo = balance_kobo - $1 WHERE id = $2 AND balance_kobo >= $1 RETURNING balance_kobo', [amount, walletId]); if (r.rowCount === 0) throw new InsufficientFunds();
A data race is two threads accessing the same memory concurrently with at least one write and no synchronisation: undefined behaviour in C, C++ and Rust (where safe Rust forbids it at compile time), a bug everywhere. A race condition is any outcome depending on timing, including the async example, which has no data race at all. Detectors: go test -race, ThreadSanitizer for C, C++ and Rust, and stress tests that run the same operation concurrently thousands of times.