Contention
A permission model can't stop two people booking the same 3pm slot. Preconditions can — and each is just an argument on a call you already make.
A 409 conflict is the mechanism working, not an
error to retry away. Catch it and tell the user the slot, stock, or edit was
taken.
Uniqueness — keyed creates
Derive the key from the thing that must be unique. The second writer gets a 409; your
own retry returns your record back (existing: true); deleting the record
frees the key.
await bookings.create({ who: email }, { key: "slot:2026-07-15T15:00" })
Limited stock — N units anyone can buy
Claim units with keyed creates — on conflict try the next unit; all taken = sold out. Race-proof under every safety rule, because writes on other users' records are never allowed.
await orders.create({ item: 42 }, { key: "unit:item42:1" }) // then :2 … :N
Counters — the server does the math
Atomic ops run against current state on the server. Breaching the floor or ceiling → 409, record untouched.
await products.update(id, { stock: { decrement: 1, floor: 0 } })
An object in a patch is an atomic op ONLY when its keys are exactly
increment|decrement (+ optional
floor|ceiling), all numbers — anything else is stored as plain
data.
Shared editing — versions
Pass back the version you read. A stale save gets a 409 instead of silently clobbering someone's edit — re-read, reapply, retry.
await pages.update(id, { body }, { ifVersion: page.version })
Compound: one-per-user AND limited
A booking that must be unique and capped per user wants two keyed creates — claim the resource first, then the per-user cap; if the second 409s, delete the first (deleting frees its key):
const slot = await bookings.create({ when }, { key: "slot:" + when })
try {
await quotas.create({ ref: slot.id }, { key: "user-day:" + user.userId + ":" + day })
} catch (e) {
if (e.code === "conflict") { await bookings.delete(slot.id) } // give the slot back
throw e
}
Never find-then-create, never compute counters
client-side. Both race — and both fail only when real users collide, which is exactly
when it matters. Keys are 1–120 chars of letters, numbers, and
: _ . @ / -.