Consistency Models
Strong, eventual, causal, read-your-writes. Consistency isn't binary, it's a spectrum, and choosing the right model shapes your entire data architecture.
The Concept Explained
A consistency model is a contract between a storage system and the code that uses it. It states which behaviours a reader may observe, and by omission, which anomalies the application must be prepared to handle.
That framing is more useful than "how fresh is the data," because it makes clear that a weaker model does not simply mean worse. It means the burden of handling certain anomalies has moved from the database into your application. Someone is always paying for consistency. The model determines who.
The reason there is a spectrum at all rather than a single setting is that guarantees cost coordination, and coordination costs latency and availability. Every rung you climb requires replicas to talk to each other more before they can answer, so the strongest models are the slowest and the least available under failure. The engineering question is never "how much consistency can I get" but "what is the weakest model my application can correctly be built on."
Weaker consistency does not remove the problem, it relocates it. Choosing eventual consistency means your application code now owns conflict resolution, ordering, and the user experience of stale reads. That is often the right trade, but it should be a decision rather than a side effect of a default setting.
How It Works
The models form a rough ladder. Each rung permits everything the rungs below permit, plus forbids one more class of anomaly.
The Consistency Spectrum, Strongest to Weakest
Every read returns the most recent completed write, and operations appear to take effect at a single instant between their invocation and response. The system behaves exactly like one copy of the data. Requires coordination on every operation, so it costs a round trip and becomes unavailable on the minority side of a partition.
All processes observe operations in the same total order, and each process own operations appear in program order. Unlike linearizability there is no real-time requirement, so a read can return a value that is stale in wall-clock terms as long as everyone agrees on the ordering.
Operations that are causally related are seen in the same order by everyone. Concurrent operations, which have no causal link, may be seen in different orders by different replicas. This is the strongest model that remains available during a partition, which makes it the practical ceiling for always-available systems.
Guarantees scoped to one client session rather than globally: read-your-writes, monotonic reads, monotonic writes, and writes-follow-reads. Cheap to implement with sticky routing or version tokens, and they eliminate the anomalies users actually notice.
If writes stop, all replicas converge on the same value. That is the entire guarantee. Nothing is promised about how long convergence takes, what order updates are applied in, or what a reader sees along the way. Cheapest and most available.
Hover to expand each layer
Linearizability Is Not the Same as Serializability
These are confused constantly and they sit on different axes.
Linearizability is about single operations on single objects, and it adds a real-time requirement: if a write completes before a read begins, that read must see the write. It is a recency guarantee.
Serializability is about transactions over multiple objects, and it says the outcome equals some serial execution of those transactions. It says nothing about which serial order, so a serializable system may legally execute your transaction as though it happened earlier than it really did.
A system with both is strictly serializable, which is what people usually mean when they say "strong consistency" about a transactional database.
The Session Guarantees Are the Practical Sweet Spot
Full linearizability is expensive and often unnecessary. What users actually notice are a small number of specific anomalies, and each has a targeted, cheap fix.
Read-your-writes means a client always sees its own updates. Without it, a user edits their profile, the page reloads from a lagging replica, and their change appears to have vanished. This is the single most common consistency complaint in real products.
Monotonic reads means a client never sees time move backwards. Without it, a user refreshes and sees a comment they just saw disappear, because the second read hit a replica further behind. Sticky routing to one replica per session gives you this almost for free.
Monotonic writes means a client's writes are applied in the order it issued them. Without it, two rapid edits can land out of order and the older one wins.
Writes-follow-reads means that if you read a value and then write based on it, everyone who sees your write also sees the value you read. This is what stops a reply appearing before the comment it replies to.
Causal consistency is the strongest model a system can provide while remaining available during a partition. Anything stronger, including sequential consistency and linearizability, requires coordination that a partition can block. If a requirement combines "always available" with "strongly consistent," the requirement is impossible and the conversation needs to move to which one actually matters.
System Design Implications
Take the three cases from the interview question, because they land on three different rungs and the reasoning is the point.
A bank balance needs the strong end. Two concurrent withdrawals must not both see the pre-withdrawal balance and both succeed. This is a read-modify-write on a shared value where being wrong creates money, so it needs linearizability on that record, or a transaction with serializable isolation. The right answer is to refuse the operation when coordination is unavailable. Note the nuance worth raising: real banking systems are often eventually consistent at the ledger level and handle conflicts through reconciliation and overdraft rules, precisely because availability matters commercially. The interview answer is strong consistency, and mentioning the real-world nuance afterwards is a differentiator.
A social media feed needs the weak end. A post appearing a few seconds late for some viewers costs nothing. Feeds are enormous fan-out, read-dominated workloads where coordination on every read would be ruinously expensive. Eventual consistency is correct, with one qualification: add causal consistency where ordering is visible to users, so a reply never appears before its parent comment and your own post appears in your own feed immediately.
A shopping cart sits in the middle. Availability matters, because refusing to add an item loses revenue directly. Perfect consistency does not, because a briefly stale cart is a minor annoyance. Carts are the classic case for an add-wins merge: if a user adds items on their phone and their laptop during a partition, the union of both is a better answer than picking one side. Dynamo's shopping cart is the canonical example, and it is worth naming because it also exposes the failure mode, where a removed item reappears after a merge. The fix is to model removals as explicit tombstones rather than as absence, which is exactly what a well-designed CRDT set does.
Then note where the cart actually needs strength. Adding to a cart is weakly consistent. Checkout, where inventory is decremented, must be strongly consistent, because overselling is a real cost. One user journey, two different models, and identifying that boundary is the strongest possible answer.
Ask "what does the user see when this is stale, and what does it cost?" for each piece of data. A stale like count costs nothing. A stale cart is an annoyance. A stale balance is money. A stale permission check is a security incident. That question sorts data onto the ladder faster than any theoretical argument.
Tradeoffs and Decision Framework
| Model | Guarantee | Typical cost | Fits |
|---|---|---|---|
| Linearizable | Reads see the latest completed write | Round trip per operation, unavailable in minority | Balances, inventory, locks, permissions |
| Serializable transactions | Outcome equals some serial order | Contention, aborts and retries | Multi-object business invariants |
| Causal | Related operations ordered for everyone | Dependency metadata, more storage | Comments, messaging, collaborative state |
| Read-your-writes | You always see your own updates | Sticky routing or a version token | Profile edits, settings, any user-authored data |
| Monotonic reads | Time never goes backwards for a client | Session affinity to one replica | Feeds, timelines, anything paginated |
| Eventual | Replicas converge if writes stop | Application owns conflict resolution | Counters, analytics, recommendations, caches |
The framework runs in three steps. Classify each piece of data by what a stale or conflicting read costs. Choose the weakest model whose anomalies you can genuinely tolerate, since every rung up costs latency and availability permanently. Then, for anything below causal, decide explicitly how conflicts resolve: last write wins is simple and silently loses data, application merge is correct and effortful, and CRDTs are automatic within the operations they support.
Above all, do not set one model for an entire system. The interesting boundary is almost always inside a single user journey.
Common Mistakes
Treating consistency as a binary. "Strong or eventual" collapses a spectrum where the useful answers are usually in the middle.
Confusing linearizability with serializability. One is about recency of single operations, the other about the outcome of multi-object transactions. Strong consistency in a transactional database usually means both.
Choosing eventual consistency without designing conflict resolution. Eventual consistency without a merge strategy is not a design, it is a bug that surfaces later as data loss.
Defaulting to last write wins. It is the default in many systems and it silently discards one of two concurrent updates, with the winner decided by clocks you should not trust.
Applying one model to the whole system. Payments and view counters have opposite requirements, and one global setting gets one of them badly wrong.
Ignoring the session guarantees. Read-your-writes and monotonic reads eliminate the anomalies users actually complain about, at a fraction of the cost of full linearizability.
Promising strong consistency and high availability during partitions. That combination is impossible, and asking for it in a design review means the requirements have not been thought through.
Walk me through the consistency spectrum from strong to eventual. For a social media feed, a bank balance, and a shopping cart, which consistency model fits each and why?