Patterns & Anti-Patterns
The pattern catalogue with problem, structure, trade-offs and failure modes for each — and the anti-patterns that show up in real code review: one global lock, unbounded spawning, locks held across I/O, swallowed task exceptions, and lock-free code written without a reason.
Thirteen shapes that recur in every concurrent system, each with the problem it solves, the structure it imposes, what it costs and how it fails. Recognising the shape is most of the work — the implementation is almost always already in your standard library.
Q · What shape does this coordination problem already have a known answer for?
Thirteen things that look reasonable in review and fail in production. Each one is tempting for a specific reason — it is simple, it is fast to write, it makes the test pass — and each one produces a specific, nameable failure. Knowing the temptation is what makes the pattern recognisable in your own code.
Q · Which concurrency constructs look correct in review and fail only under production load?
Busy waiting is usually the wrong answer, and sometimes it is decisively the right one. When the expected wait is genuinely shorter than a context switch, spinning wins — and when it is not, spinning burns a core to make everyone slower. Context decides, and this is one of the few places in the domain where the correct answer depends on the hardware.
Q · When is burning CPU in a loop cheaper than letting the scheduler put the thread to sleep?
A type or operation is thread-safe when its documented guarantees permit correct concurrent use under stated conditions. Not "it has locks" — locks are one implementation. And the trap that catches everyone: a thread-safe method does not make a sequence of thread-safe calls atomic.
Q · What does the phrase "this class is thread-safe" actually promise me?
Can this function be safely entered again before a previous call has finished? That is a different question from whether two threads can call it at once, and the two properties are independent — a function can be either, both, or neither. Confusing them produces self-deadlocks and corrupted state that look like race conditions and are not.
Q · What happens if this function is called again — by a callback, a signal, or itself — before the first call returns?
Promise.all over ten thousand items, or a thread per request with no ceiling. The concurrency limit becomes whatever the input happens to contain, which is not a limit. Sockets, memory, file descriptors, downstream capacity and rate limits all have real ceilings, and you will find whichever one is lowest.
Q · What number limits how many of these run at once — and is that number one I chose?