Optimistic vs. Pessimistic Locking: When to Use Each and How to Wire Them End-to-End
Lost updates and oversells are two of the most common data-corruption bugs in multi-user web apps. Choosing the wrong lock strategy either burns throughput with unnecessary waits or silently drops writes that should have succeeded.
Two users editing the same order can silently overwrite each other's changes. Optimistic locking solves this by adding a version column that increments on every update; the write only succeeds if the version matches what was originally read. The version number must travel to the frontend and back on every write — it acts as a concurrency credential, not a secret. Pessimistic locking takes the opposite approach: it locks the row at read time so no one else can touch it until the transaction completes.
Inventory deductions, flash sales, and payment callbacks are poor fits for optimistic locking because conflict rates are high and failure is unacceptable. Atomic SQL (`UPDATE stock SET qty = qty - n WHERE qty >= n`) handles inventory without explicit locks. Row-level `SELECT ... FOR UPDATE` works for short-lived state transitions like shipment confirmation. Redis distributed locks serialize cross-service jobs such as settlement generation.
A practical checklist covers both sides: backends must return clear conflict errors instead of silent failures; frontends must treat the version as part of the payload and refresh on conflict rather than blindly retrying. Snowflake IDs stay as strings to avoid JavaScript precision loss, while version numbers remain integers since they never approach `Number.MAX_SAFE_INTEGER`.
Exposing the version number to the frontend is not a security concern — it is a stateless concurrency token, and the industry standard across JPA, MyBatis, and REST APIs.
Many teams overuse optimistic locking for inventory and underuse it for documents. The decision tree is straightforward: if the user holds state for minutes, use optimistic; if the operation is a sub-second API call that must succeed, use pessimistic or atomic.
The checklist format for frontend, backend, and product roles is a practical artifact that teams can adapt directly into their definition-of-done for any feature touching shared mutable state.
Combining strategies is normal: a single system might use optimistic locking on the order header, atomic SQL on inventory, and a Redis lock on settlement generation.