Race conditions happen when the outcome of a workflow depends on timing: two (or more) concurrent requests hit shared state and the app fails to enforce correct ordering/atomicity. In web apps, that often means you can double-spend, apply discounts multiple times, bypass limits, or desync account state.
Core idea: Time-of-Check to Time-of-Use (TOCTOU). The app checks something, but the state changes before it uses that result.
Signal: anything involving balance, inventory, redemption, or state transitions is a candidate.
It’s usually shared state + concurrency + missing locks/transactions.
Most race bugs are broken state transitions. If the app has states like created → paid → shipped, a race can let you trigger transitions out of order or twice.
Attack plan: identify the state-changing request, then send it concurrently before the server commits the first transition.
Goal: two requests should not both succeed if the business rule says only one should.
SELECT ... FOR UPDATE where needed.