Interview Prep Zoneby CuriouserLabs

Question 5 of 5 · architect level

Two replicas accepted conflicting writes to the same key. What are your options, and why is last-write-wins so dangerous?

🎤 Say this first

Four families. Last-write-wins: attach a timestamp, keep the highest — trivial, total, and it silently discards the loser, which is data loss dressed as a policy (worse when clocks are skewed, so LWW demands at least a hybrid logical clock). Keep both (siblings): return every concurrent version to the application and let it merge — Riak's model; honest, but it pushes real work onto every caller and requires you to handle siblings forever. CRDTs: pick data types whose merge is mathematically guaranteed to converge (G-Counter, OR-Set, LWW-Register, RGA for text) — the merge is automatic and provably correct, at the cost of metadata and constrained operations. Application-specific merge: domain rules like 'union the shopping cart' (Dynamo's canonical example — resurrected deleted items are the known artifact). The right question is never 'which algorithm' but 'what does the business want to happen when two people edit at once?'

The full picture

StrategyConverges?Loses data?Cost
Last-write-winsyesyes, silentlytrivial; needs a trustworthy clock
Siblings / multi-valueyes, deferrednoevery reader must merge
CRDTyes, provablyno (by construction)metadata growth; limited operations
App-specific mergeif the rule is associative/commutativedependsdomain design work
Reject & escalaten/anoneeds coordination — you left AP
  • Detect concurrency before resolving it: version vectors (per-replica counters) tell you whether two versions are causally ordered (one supersedes) or genuinely concurrent (a real conflict). Without them you can't distinguish 'stale' from 'conflicting', and LWW will happily overwrite a newer value with an older one that has a skewed timestamp.
  • Why LWW is more dangerous than it looks: it doesn't just lose the concurrent write — it loses it without an error, a log line, or a metric. There's no way to audit what was dropped after the fact. If you adopt LWW, at minimum log conflict events so you can measure whether the assumption 'concurrent edits are rare' is actually true.
  • CRDTs shine for specific shapes and are awkward outside them: counters, sets, flags, maps of those, and sequences for collaborative text (Yjs/Automerge power the Figma/Notion class of product). Tombstones and version metadata grow, so garbage collection is the operational reality nobody mentions in the papers.
  • Field-level granularity dramatically reduces conflicts: two users editing different fields of the same profile is only a conflict if your unit of replication is the whole document. Splitting the record — or using a map CRDT with per-field registers — turns most 'conflicts' into non-events. This is often the highest-value, lowest-effort fix.
  • Or avoid conflicts entirely by routing: pin each entity to a home region/partition so concurrent writes to the same key hit the same leader. Conflict resolution is a tax you pay for multi-leader; sharded single-writer ownership is how most large systems opt out of it.

🔄 Likely follow-up questions

  • How do version vectors distinguish a stale write from a concurrent one?
  • Why does an OR-Set need tombstones, and when can you garbage-collect them?
  • Design conflict handling for a collaborative to-do list — what merges cleanly and what doesn't?