There is one moment that defines a brokerage's reputation forever, and it's not your IPO listing or your ad on a billboard. It's the first time two concurrent withdrawal requests hit your backend at the same millisecond.
If one of them succeeds twice — even once, on one Tuesday afternoon — you're done.
The naive code
Most MVP brokerage codebases look like this:
```
balance = read_balance(user_id)
if amount <= balance:
write_balance(user_id, balance - amount)
enqueue_payout(user_id, amount)
```
This fails under any concurrency. Two requests race the read_balance call, both see the original balance, both subtract, both win.
The fix is boring
A single transactional pattern: SELECT ... FOR UPDATE. The row is locked for the duration of the transaction; the second concurrent request waits until the first commits, then sees the updated balance.
In Prosper's case:
- Every withdraw runs inside a single SQL transaction
- The user row is locked with
FOR UPDATEbefore the balance check - Insufficient-funds returns 422 before the payout enqueue happens
- Stripe webhooks credit balances inside the same locking pattern, keyed on the event id to dedupe retries
Why this matters
A broker that loses a customer's money once doesn't get a second chance. Every "flashy" feature on your roadmap depends on the boring plumbing under it working perfectly, every time, forever.
Balance integrity is the product. Everything else is gravy.