A race condition exists whenever two threads, processes, requests, or async tasks interact with shared state without enforcing an order between them. The bug only appears under specific timing — which is why it's the hardest class of bug to catch in review and the easiest to ship to production.

The classical example is the check-then-act pattern: if !exists(x) { create(x) }. Two threads both check, both see false, both create. The fix is to make the check-and-act atomic — a database INSERT ... ON CONFLICT DO NOTHING, a compareAndSwap on a hardware atomic, a mutex held across the pair, or a SELECT ... FOR UPDATE that prevents another transaction from observing the inconsistent intermediate state.

Most codebases don't have many true threads — they have async runtimes, request handlers, retries, webhooks, and queue workers. All of those are concurrent state machines. The same patterns that bite multithreaded code bite distributed systems harder, because the time window between check and act stretches from microseconds to seconds.

Review heuristic

Every check-then-act over shared state is a race waiting for production load. Look for read-then-write pairs that aren't inside a transaction, a lock, or an atomic CAS. If you can articulate the bug as "if two requests arrive at the same time, both will...", it's a race.

External reference

CWE-362: Concurrent Execution using Shared Resource — the canonical industry classification for this bug class. Useful when filing tickets, writing security policies, or arguing with a static analyzer.