Production case study
Proving a race condition with Playwright
Two APIs, page submit and last-visited page, were racing each other. I proved it with a Playwright concurrency script, then fixed it.
- Role
- Developer, bbSlate
- When
- 2025–2026
- Where
- bbSlate core APIs
- Stack
- ASP.NET Core
- Playwright
- SQL Server
2 APIsracing, reproduced on demand
Context
In bbSlate, students work through pages. Two APIs run close together: one submits the page, and one records the last page the student visited, so they can pick up where they left off.
Problem
Sometimes the saved state was wrong, and it was not repeatable by hand. Clicking through the flow worked almost every time. A bug that only shows up under timing is easy to wave away as "can't reproduce".
Investigation
Both calls touch the same student's state, close together in time. If they arrive in an unlucky order, or overlap, one can overwrite the other. That was a theory; it needed proof.
I wrote a Playwright script that fires the two API calls concurrently, many times, and checks the stored state after each round. A person clicking can't control timing. A script can.
Root cause
The script reproduced the wrong state reliably. The two APIs were racing on the same data, and the result depended on which one finished last.
Fix
With the race reproducible on demand, I fixed it and used the same script as the check: it failed before the fix and passed after. In the same area I also fixed grader submission failures, an HTTP 200 returned for expired tokens, and missing token-expiry logs.
Result
An intermittent "can't reproduce" became a proven, fixed bug with a test that keeps it fixed.
What I learned
Turn "it happens sometimes" into a test that fails every time. When a bug depends on timing, make the timing the input of the test.