Two clients opened my booking platform, picked the same 2:00 PM slot at the same salon, and both hit "Confirm" within a couple hundred milliseconds of each other.
Both requests should have failed for one of them. Both succeeded.
Why this is scary and not just a bug: this is exactly the kind of failure a business owner doesn't notice until a customer shows up to a chair someone else is already sitting in. No error, no crash, no log line screaming at you — just a quietly overbooked afternoon.
What I tried first (and why it didn't work)
My first fix was the obvious one: check for a conflict, then insert the booking if there isn't one.
That works in every manual test I ever ran. It falls apart under real concurrency, because the check and the insert are two separate steps. Two requests can both check, both see nothing, and both proceed. Classic race condition — the fix looked right and was wrong.
The actual fix
I moved the conflict check into the database itself, as a trigger that runs in the same transaction as the write. Instead of "check, then maybe insert," it's "the database refuses the insert if a conflict exists — atomically, no gap for a second request to sneak through."
One extra wrinkle I didn't expect: bookings can have no assigned staff member yet (NULL), and in SQL, NULL = NULL isn't true — it's neither true nor false. A plain uniqueness check silently stops protecting anything the moment a field is optional. Postgres has a specific NULL-safe comparison operator for exactly this (IS NOT DISTINCT FROM), and that one detail was the actual fix, not the trigger itself.
What I took away from this
I'm not a formally trained backend engineer — I built this product mostly by directing Claude Code and thinking through the logic myself. The lesson that stuck with me: the bugs that scare me most now are the ones with no error message. A crash tells you something's wrong. A silent double-booking doesn't — you only find out when a real customer is standing in the wrong place at the wrong time.
Since then, my personal rule is: anything touching money, availability, or a shared resource gets a five-minute "what happens if this runs twice at once" pass before it ships. Not a formal audit — just one deliberate question I used to skip.
Pronto is the open-source booking/CRM/POS platform this happened in, if anyone wants to see the actual trigger: github.com/SGrappelli/pronto
Curious how other solo/non-technical founders building with AI coding tools catch this class of bug before it reaches production — do you have a checklist, or did you learn it the same way I did?
The NULL = NULL detail is what makes this post genuinely useful, because it's the trap inside the trap. Most people who know about race conditions would reach for the trigger and still ship the bug, because IS NOT DISTINCT FROM is exactly the kind of thing you don't know you need until it's silently not protecting you. Good catch.
On your actual question, catching this class of bug as a non-traditional founder building with AI tools: AI coding tools are structurally bad at this specific category, and knowing why is most of the defense. Claude and every tool write code that passes the test you can imagine. Race conditions, by definition, are the failures you can't easily imagine, because they only appear under concurrency you never reproduce in a manual test. The AI won't volunteer "but what if two of these run in the same 100ms," because you didn't ask, and it optimizes for the happy path you described. The gap isn't the AI's coding, it's that nobody prompted the adversarial case.
So the checklist that helps is a prompting discipline, not a code checklist. Before shipping anything touching money, availability, or a shared resource (your rule, the right rule), literally ask the AI the adversarial question: "what happens if two requests run this simultaneously? what if it runs twice? what if a field is NULL? what if it fails halfway through?" You're using the AI to find its own blind spot, but only if you name the category out loud. It knows about race conditions and NULL comparison, it just won't surface them unprompted.
The deeper pattern: your edge isn't out-coding a trained engineer, it's knowing which questions to force. The engineer has these failure modes in their gut. You make them explicit, which honestly makes you more deliberate than a lot of trained devs who assume they'd never hit it.
What's on the money/availability/shared-resource list for Pronto beyond bookings? Payments and inventory usually have the same "runs twice" exposure.
The IS NOT DISTINCT FROM detail is the real twist here. Most people would catch the race condition eventually, but the NULL comparison silently breaks after you fix it - that's the kind of thing that surfaces weeks later in production when someone first creates a booking without assigning staff. Great catch documenting that second layer. The "one deliberate question before shipping" rule for shared resources is solid - I'd extend it to anything where the cost of the bug compounds (bookings, payments, inventory reservations).
the silent ones are always the worst: no crash, no log, just a furious customer two days later. classic read-then-write race; the fix almost always has to live in the database (unique constraint or a row lock on the slot), because app-level checks lose that millisecond gap every time. did a DB constraint end up being your one-liner, or did you solve it in code?
the constraint is only half of it though. once it's there the race stops being a double booking and turns into a failed insert, so the other half is catching that unique violation and showing "that slot just went" with fresh availability, instead of letting a 500 out to the person who lost by 40ms.
same shape shows up in billing btw. stripe can deliver the same webhook twice, so a unique index on the event id is the identical fix on a different table.
This is a good example of why production bugs are often business problems, not just engineering problems.
The interesting part isn't only fixing the race condition — it's building the habit of asking what happens when two real users do the same thing at the same time.