Why this matters
The bug. Lock ordering must be a total order *across all goroutines*. If two threads acquire the same locks in different orders, you have a deadlock waiting for the right interleaving.
The fix. Pick a canonical order — e.g. by account id — and always acquire in that order. Now transfer(X, Y) and transfer(Y, X) both lock the lower-id account first.
In practice. Many production codebases use a single mutex for the whole table or a striped lock keyed by id hash. Two-mutex transfer is a classic CS-class example because deadlocks in production are nearly impossible to reproduce.
Review heuristic
Any function that acquires more than one lock, holds onto the first while waiting for the second, and isn't ordered consistently with every other function that touches those locks — that's a deadlock waiting for the right two requests to arrive together.
External reference: CWE-833: Deadlock.
↳ Dining philosophers; CWE-833.