AI Coding Agents
Testing
How to test concurrent code for races and deadlocks
September 27, 2026 - 4 minute read
AI Coding Agents
Testing
September 27, 2026 - 4 minute read
To test concurrent code, run focused tests under a race detector such as Go's -race flag or ThreadSanitizer, force the risky execution order with barriers or a controllable executor, assert the business invariant, and add bounded stress and shutdown tests. Save the seed and command so any failure can be repeated.
Concurrency testing gives reviewers evidence about behavior that ordinary unit tests often miss. A coding agent can make a local synchronization change that compiles, passes once, and still introduces a data race, deadlock, lost update, or stale read under a different schedule.
Dynamic detectors are a strong first gate. Go's official race detector instruments memory access when tests run with -race. Clang's ThreadSanitizer documentation describes a similar detector for C and C++. Both observe executed paths, so a clean report covers the tested workload rather than every possible execution order.
State the property that must remain true across workers. A counter must equal committed updates. A cache must never expose a partially initialized value. A job may have one active owner. A shutdown path must stop accepting work before shared resources close.
Map every shared value to its synchronization mechanism and ownership. Locks, channels, atomics, database constraints, and leases protect different boundaries. Ask the coding agent to explain which boundary changed and why the existing mechanism still covers every access.
Keep timing out of correctness assertions where possible. Sleeping for a fixed interval makes a test depend on machine speed. Barriers, latches, fake clocks, and controllable executors can place operations at the contested point and make the failure reproducible.
Start with focused tests under the language's race detector or sanitizer. Exercise repeated access, cancellation, error returns, and shutdown. Run enough iterations to explore schedules, but save the random seed and command so a failure can be repeated.
Add a controlled test for the reported or plausible execution order. Pause one worker after it reads state, let another update that state, then resume the first. Assert the business invariant, not the internal order, unless ordering is itself the contract.
A controlled test makes the race happen on purpose. The flow below is the shape of a lost-update test.
Stress tests can reveal starvation, lock contention, and resource leaks that short tests miss. Record worker count, duration, CPU allocation, and detector settings. Treat a timeout as evidence to investigate rather than rerunning until green.
Race detectors do not establish freedom from deadlock. Add bounded completion tests for lock ordering, cancellation, and shutdown paths. When the language exposes blocked-thread or goroutine diagnostics, retain that output with a failure so the next run starts from evidence.
Each layer covers a different failure and has its own blind spot.
| Layer | What it finds | Limit |
|---|---|---|
| Race detector or sanitizer | Data races on executed paths | Only paths the test ran |
| Controlled execution order | Lost updates and stale reads at a known point | Needs a hypothesis about the order |
| Stress run | Starvation, contention, and resource leaks | Nondeterministic, so record the settings |
| Bounded completion test | Deadlocks in lock ordering, cancellation, and shutdown | Covers the chosen paths only |
The pull request should identify shared state, the synchronization decision, detector commands, controlled execution orders, and stress results. Include a regression test that fails without the fix when practical. Do not weaken assertions or add broad retries to hide nondeterminism.
Some failures remain environment-sensitive. Stop and report the limitation when a required architecture, kernel facility, sanitizer runtime, or production workload cannot be reproduced. A documented boundary is more useful than a confident result from an unrepresentative test.
Factory is a platform for running a software factory, where agents called Droids take work from a signal to a reviewed pull request. For concurrent code, Factory adds a reviewer that looks for race patterns, and keeps the exact verification commands in front of the Droid.
Code review on each pull request. Factory's automated code review lists race conditions, async and await mistakes, and resource leaks among the bugs it looks for. It runs on pull request events, skips drafts, and posts inline comments. A review-guidelines skill can add rules for the repository, such as requiring a named synchronization mechanism for every new shared value.
Exact commands in AGENTS.md. AGENTS.md gives Droid the project briefing it carries into every session, including how to run and verify tests. Putting the detector command, iteration count, and seed handling there keeps a Droid from choosing an easier substitute.
Clear task specs. Factory's delegation guides recommend putting the repository, desired outcome, acceptance criteria, and verification steps in the issue before assigning it to Droid. For a race fix, the acceptance criteria are the invariant and the controlled test that fails without the fix.
Concurrency testing checks behavior under different execution orders, including data races, deadlocks, lost updates, and stale reads that ordinary unit tests miss.
No. Detectors such as Go's race detector and ThreadSanitizer only observe the paths the test executed. They also do not establish freedom from deadlock, which needs bounded completion tests.
Use barriers, latches, fake clocks, or a controllable executor to pause one worker after it reads state, let another update that state, then resume the first and assert the invariant.
A fixed sleep ties the test to machine speed, so it can pass or fail for reasons unrelated to the code. Explicit synchronization points make the failure reproducible.
Factory automated code review lists race conditions among the bugs it checks for on pull requests, and a review-guidelines skill can add repository rules. AGENTS.md keeps the exact detector commands in front of Droid.
Discuss your team’s software development, privacy, or deployment requirements.
Start building