Design a key-value store: the system design interview walkthrough
The request to design a key-value store sounds like one line — get(key) returns a value, put(key, value) saves one — and that one line hides the whole interview. Do it for one process and you reach for an in-memory hash map; do it for terabytes spread across many machines, serving millions of operations per second, and surviving a node dying without losing data, and it becomes a partitioning-plus-replication problem with a dozen sharp edges. This is one of the most common key-value store system design prompts because it forces every distributed-systems muscle at once: sharding, replication, consistency, and failure recovery.
The round turns on one structural insight. A single-machine hash map is trivial; a distributed key-value store instead has to decide how it spreads keys across nodes, how many copies of each key it keeps, and what it gives up to stay available when a machine is gone. Get that one reframe — the system lives or dies on how it shards, how it replicates, and the consistency/availability trade it makes, not on the get/put API — and the rest of the system design interview is deciding how to place keys on a ring, how to tune the read and write quorums, and how replicas re-sync after a failure. This walkthrough follows the path a strong candidate draws on the whiteboard — requirements, estimate, API and data model, high-level design, then the deep dives — and at each step it names the follow-up the interviewer uses to tell a memorised answer from an engineer who understands the tradeoff.
How the key-value store round is actually run
A key-value store round runs about 60 minutes on a shared canvas — you draw the boxes and arrows while you talk, and the interviewer interrupts. It is a favourite because the one-line problem opens onto deep water fast: the moment you draw "client calls put, we store it," the question becomes which machine holds this key, and what happens to it when that machine dies, and every answer after that (even load, replication, consistency) compounds from how you partition. Get the ring clean and you have shown the whole loop in one problem.
The interviewer rarely lets "we store the key" stand. "Two clients write the same key at the same instant, and each write lands on a different replica — who wins, and how does the store even know there was a conflict?" That question — concurrent writes under replication — is the one a copied answer can never rehearse, and the one this round is really testing.
The rounds here are modelled on how the loop runs, not scraped from any candidate's recall. What matters for prep is the shape: a blank canvas, a clock, and a follow-up after every decision you make.
The five-step framework for any HLD round
Every high-level design answer fits the same spine. Say it out loud at the start so the interviewer knows you have a plan, then walk it:
- Clarify requirements — functional and non-functional, and what you are explicitly not building.
- Estimate — data volume, operations/sec, object size and replication factor, to justify every later choice.
- High-level design — the boxes and arrows: clients, a coordinator, a ring of storage nodes, replication.
- Deep dive — the two or three decisions that actually decide this system.
- Follow-ups — bottlenecks, failure modes, and what you would change at ten times the scale.
What the debrief measures
The score is not "right or wrong design." A good HLD debrief reads on four dimensions, and every move below maps to one: problem solving (did you reach a partitioned, replicated ring with a defensible consistency model), communication (did you say why consistent hashing beats hash % N in a sentence), technical execution (virtual nodes, N/R/W quorums, gossip, hinted handoff, Merkle trees), and time management (did you spend your 60 minutes on partitioning, replication, and failure recovery, not on the value encoding). Reading a walkthrough teaches the design; it does not teach you to deliver it on the clock while someone pushes back.
Step 1 — Requirements: what a key-value store must do
Spend the first few minutes here. Jumping to boxes before you have scoped the problem is the fastest way to design the wrong system.
| Type | Requirement |
|---|---|
| Functional |
get(key) returns the stored value; put(key, value) stores or overwrites it; data is durable across node failures; the store scales horizontally as data and traffic grow; values are opaque bytes the store does not interpret. |
| Functional (optional) |
delete(key), range scans, secondary indexes, TTL/expiry — call these out, then scope most of them for later. |
| Non-functional | Huge scale (terabytes to petabytes, millions of ops/sec); high availability (serve reads and writes even when nodes are down); low latency; horizontal scalability on commodity hardware; a tunable consistency model rather than one fixed guarantee. |
The sentence that earns points: "An in-process hash map is trivial — the hard part is storing far more than one machine can hold, serving millions of ops/sec, and staying available when a node dies, so the core of the design is how I partition keys, how I replicate them, and the consistency/availability trade I choose, not the get/put API." Say that and you have framed the entire design before drawing a box.
Step 2 — Capacity estimation (back of the envelope)
Estimation here is about proving one thing: the hard axis is spreading data across many machines and keeping replicated copies alive, not clever lookup logic.
| Quantity | Assumption | Result |
|---|---|---|
| Data volume | terabytes to petabytes | far more than one machine holds, so you must shard across nodes |
| Throughput | millions of ops/sec | keep hot data in memory, persist to SSD; an LSM-tree turns writes into fast sequential appends |
| Object size | small values, ~KB each | billions of keys; the index and key placement matter more than per-value size |
| Replication factor | keep ~3 copies of every key | storage and write cost multiply by ~3, and reads can be served from any copy |
Two conclusions fall out, and you should say both out loud. First, the system must partition — because the data dwarfs any single machine, the first real decision is how you split keys across nodes so load stays even and the cluster can grow. Second, because you keep roughly three copies of every key for durability and availability, replication is not optional — it drives both the storage cost and the consistency question of how those copies stay in agreement. You have now pointed the interview at the real deep dives — partitioning and replication — before anyone asked.
Step 3 — API and data model
The API is deliberately small: the whole contract is get and put, with delete as an optional extra.
| Operation | Shape |
|---|---|
get(key) |
returns the current value for the key (or not-found); the coordinator routes to the key's replicas and gathers enough responses to answer. |
put(key, value) |
stores or overwrites the value; the coordinator writes to the key's replicas and waits for enough acknowledgements to confirm. |
delete(key) |
optional; removes the key — typically written as a tombstone marker that replicates and is cleaned up later. |
The data model is thin. The externally visible unit is just a key mapped to an opaque value, but the store keeps internal metadata to place and reconcile it:
| Entity | Notes |
|---|---|
key → value |
value is opaque bytes the store never interprets; the key is hashed to decide placement. |
partition / vnode |
which arc of the hash ring a key belongs to, and therefore which nodes own it. |
version |
a version vector (vector clock) or timestamp stamped on each write, so conflicting copies can be detected and resolved. |
replica set |
the N nodes that each hold a copy of the key — the next N nodes clockwise on the ring. |
Step 4 — High-level design: the consistent-hashing ring
Now the boxes. A client sends a get or put to any node, which acts as the coordinator for that request; the coordinator hashes the key, finds its position on a consistent-hashing ring, and routes to the nodes that own it. Around that sit the pieces that make scaling and durability work:
-
Clients: issue
get(key)/put(key, value)to any node; there is no special entry point to overload. - Coordinator node: any node can coordinate a request — it hashes the key, locates the owning nodes on the ring, forwards the operation to the replicas, and collects their responses.
- Ring of storage nodes: each node owns a set of virtual nodes (arcs of the ring) and holds a local store on disk — typically an LSM-tree with SSTables — for the keys that hash into its arcs.
- Replication to the next N nodes: each key is copied to the N nodes that follow its position clockwise on the ring, so every key has N copies on distinct nodes.
- Gossip for membership: nodes exchange membership and health with a gossip protocol, so every node learns the ring layout and which peers are up without a central coordinator.
Say explicitly that there is no single master — any node can take any request and forward it by the ring, and the ring plus replication is the whole system. Naming that unprompted is a signal the interviewer is listening for.
Deep dive: partitioning with consistent hashing
This is the first decision that actually decides the round. Which machine holds a key, and why does that choice shape everything else?
The naive answer is node = hash(key) % N. It spreads keys evenly, but it breaks the moment N changes: add or remove one node and almost every key remaps to a different machine, triggering a cluster-wide reshuffle. Consistent hashing fixes this. Hash both the keys and the nodes onto the same ring; a key is owned by the first node found clockwise from its position, so each node owns an arc of the ring. Three properties fall straight out of that choice:
| Property | Why consistent hashing gives it |
|---|---|
| Elastic scaling | Adding or removing a node only remaps the keys on the adjacent arc — the keys between the new node and its predecessor — not the whole keyspace, so the cluster grows and shrinks cheaply. |
| Even load via virtual nodes | Each physical node is placed at many points on the ring (virtual nodes), so arcs are small and numerous; load and data spread evenly, and a powerful machine can simply own more vnodes. |
| Minimal key movement | When a node joins or leaves, only its neighbours' arcs change hands, so rebalancing moves a small, bounded fraction of keys instead of reshuffling everything. |
Virtual nodes are what make this practical. With one point per node the arcs are uneven and a single failure dumps all its load onto one neighbour; with many vnodes per node the keyspace is finely sliced, so load stays even and a departing node's keys spread across many survivors rather than one. That is also how you avoid hotspots — no single arc carries a disproportionate share.
"You add a node to a 100-node cluster — how many keys move, and why not just hash mod N?" Only the keys on the arc between the new node and its ring predecessor move — on the order of 1/100 of the data, not the whole set. With
hash % N, changing N from 100 to 101 changes the modulus for nearly every key, so almost everything remaps and the cluster spends itself reshuffling. Consistent hashing bounds the movement to one arc.
System Design
HLD · Storage & files · 60 min
Design a distributed key-value store out loud, with follow-ups
Deep dive: replication and tunable consistency
This is the decision most candidates under-think. Placing the key is easy; the follow-up is always "how many copies do you keep, how many do you wait for, and what happens when two writes to the same key race each other?"
Each key is replicated to N nodes — the next N nodes clockwise on the ring from the key's position. The store is leaderless (Dynamo-style): there is no primary replica, so any of the N can take a read or a write, and the coordinator decides when an operation is "done" using two quorum numbers. A write waits for W acknowledgements, and a read waits for R responses, out of the N replicas.
N, R and W are the tuning knobs:
| Preset | Meaning | Trade |
|---|---|---|
W = N, R = 1
|
write to all copies, read from one | reads are fast and cheap, but writes are slow and fail if any replica is down — read-heavy workloads. |
W = 1, R = N
|
write to one copy, read from all | writes are fast and highly available, but reads are slow — write-heavy workloads. |
R + W > N (e.g. N=3, R=2, W=2) |
read and write sets must overlap | any read sees at least one replica from the latest write, giving read-your-writes-style consistency while tolerating one node down — the common balanced default. |
R + W ≤ N |
no guaranteed overlap | fastest and most available, but a read can miss the latest write — eventual consistency only. |
Because the store is leaderless, two clients can write the same key concurrently on different replicas, and neither is "wrong." The store detects this with version vectors (vector clocks): each write carries version metadata, so the system can tell whether one write descends from the other (keep the newer) or whether they are concurrent (a genuine conflict). Conflicts are resolved by last-write-wins (simple, can drop data) or by returning both versions for the application to merge, and divergent replicas are repaired on the read path — read repair — where the coordinator notices a stale replica during a read and writes the latest value back to it.
The honest interview answer is: pick R + W > N for a balanced, available store, use version vectors to detect concurrent writes, and name last-write-wins vs application merge as the conflict-resolution choice rather than pretending conflicts cannot happen.
"Two clients write the same key at the same instant on different replicas — who wins?" Neither automatically: the writes are concurrent, and the version vectors show neither descends from the other, so the store flags a conflict. You then resolve it by last-write-wins (a timestamp picks one, accepting possible data loss) or by returning both siblings for the application to merge — and read repair pushes the resolved value back to the stale replicas.
System Design
HLD · Storage & files · 60 min
Defend replication and N/R/W quorums out loud
Deep dive: fault tolerance and membership
Quorums decide who answers; this decides how the cluster even knows which nodes are alive and how a replica catches up after it comes back. The follow-up is always the node-down case.
Membership and failure detection spread by gossip. There is no central coordinator tracking the cluster; instead each node periodically exchanges state with a few random peers in a gossip protocol, so membership changes and failure suspicions propagate across the whole cluster in rounds. Every node converges on the same view of the ring and which peers are up, with no single point of failure to track it.
| Mechanism | What it does |
|---|---|
| Gossip protocol | Nodes exchange membership and health with random peers each round; the ring layout and failure detection spread with no central coordinator and no single point of failure. |
| Hinted handoff | If a replica is down when a write arrives, a healthy node accepts the write on its behalf, stores a hint, and replays it to the original node once it recovers — so writes stay available during a temporary outage. |
| Merkle trees | To re-sync two replicas that have diverged, each builds a Merkle tree (a hash tree over its key ranges) and they compare roots down the branches, so they transfer only the keys that actually differ instead of copying everything. |
Hinted handoff keeps writes available through a brief failure: the coordinator still achieves its W by letting a healthy node hold a hinted copy, and when the down node returns the hints are handed back, so no write was rejected just because one replica blinked. Merkle trees handle the longer divergence — anti-entropy — cheaply: comparing root hashes and descending only into subtrees that disagree means a replica that was out for ten minutes copies just the handful of key ranges that changed, not its entire dataset.
"A replica is down for 10 minutes then rejoins — how does it catch up without copying everything?" Two mechanisms. Writes it missed during the outage were taken by a healthy node via hinted handoff and are replayed to it on return. For anything still divergent, the two replicas compare Merkle trees — matching root hashes mean whole ranges are identical and are skipped, so only the differing key ranges are transferred, not the full dataset.
The follow-ups that decide the key-value store round
Scan the probes from each deep dive and they rhyme. Master these five and you are ready for a version of the question that is not drawn exactly like this one.
-
"Why consistent hashing and not
hash % N?" → With mod N, changing the node count remaps almost every key; consistent hashing moves only the keys on the adjacent arc, and virtual nodes keep load even. (communication) - "Add a node to a 100-node cluster — how many keys move?" → Only the arc between the new node and its predecessor — roughly 1/100 of the keys — because ownership is by ring position, not a global modulus. (problem solving)
- "Two clients write the same key at once — who wins?" → The writes are concurrent; version vectors detect it, then last-write-wins or an application merge resolves it, and read repair fixes stale replicas. (technical execution)
-
"You want read-your-writes but also availability — how?" → Tune N/R/W so
R + W > N(e.g. N=3, R=2, W=2): the read and write sets overlap while one node can still be down. (technical execution) - "A node is down for 10 minutes then rejoins — how does it catch up?" → Hinted handoff replays the writes it missed, and Merkle-tree comparison re-syncs only the diverged key ranges rather than the whole dataset. (problem solving)
Each maps to a debrief dimension. Reading this list teaches you the answers; it does not teach you to deliver them while someone interrupts — that only comes from saying them out loud.
How to prepare: a one-week plan
You do not need fifty systems. You need this one cold and the framework automatic, so the next "design X" is just new nouns in the same five steps.
- Days 1–2: Learn the five-step framework until it is reflex. Do the key-value store end to end on paper — requirements, estimate, API, data model, the consistent-hashing ring boxes.
- Days 3–4: Drill the partitioning deep dive: consistent hashing, virtual nodes, why adding a node moves only one arc, and how vnodes avoid hotspots. Say each tradeoff in one sentence.
-
Days 5–6: Own replication and recovery — N/R/W quorums and
R + W > N, version vectors and read repair, then gossip, hinted handoff and Merkle-tree anti-entropy. Answer each of the five follow-ups out loud. - Day 7: A full timed 60-minute round, narrating the whole way, with someone (or something) interrupting. This is the rep that counts.
This post is the deep walkthrough behind one row of the broader Top 25 system design interview questions — once the key-value store is automatic, the same spine carries the rest of the list.
Frequently asked questions
How do I design a key-value store step by step?
Follow the five-step HLD framework. Clarify requirements (durable get/put, horizontal scale to terabytes, high availability, a tunable consistency model); estimate to show the hard axis is sharding across machines plus keeping replicated copies, not clever lookup logic; define the small API (get, put, optional delete) over a key-to-opaque-value model with internal partition, version and replica-set metadata; draw the consistent-hashing ring (clients to any coordinator node, keys hashed onto a ring of storage nodes with virtual nodes and local LSM-tree stores, each key replicated to the next N nodes, gossip for membership); then deep-dive the decisions that matter — partitioning, replication and tunable consistency, and fault tolerance. Finish on the follow-ups about key movement and concurrent writes.
Why use consistent hashing instead of hash mod N?
Because hash(key) % N ties every key's placement to the node count, so changing N — adding or removing a single machine — changes the modulus for almost every key and triggers a cluster-wide reshuffle. Consistent hashing places both keys and nodes on a ring and assigns each key to the next node clockwise, so a node joining or leaving only remaps the keys on the adjacent arc. Virtual nodes place each physical node at many points on the ring, which keeps load even and lets a departing node's keys spread across many survivors rather than one.
How do N, R and W quorums tune consistency in a key-value store?
N is how many replicas hold each key, W is how many acknowledgements a write waits for, and R is how many responses a read waits for. When R + W > N the read and write sets are guaranteed to overlap, so a read sees at least one replica carrying the latest write — read-your-writes-style consistency — while still tolerating a node being down. Lowering the numbers (R + W ≤ N) buys speed and availability at the cost of possibly reading stale data, which is eventual consistency; you tune the three to match a read-heavy or write-heavy workload.
How does a leaderless key-value store handle two concurrent writes to the same key?
Because any replica can take a write, two clients can update the same key at the same instant on different replicas, and neither is authoritative. The store stamps each write with a version vector (vector clock), which lets it tell whether one write descends from the other — in which case it keeps the newer — or whether they are genuinely concurrent, which is a conflict. Conflicts are resolved by last-write-wins (a timestamp picks one, accepting possible data loss) or by returning both versions for the application to merge, and read repair writes the resolved value back to any stale replica.
What are hinted handoff and Merkle trees used for?
Both keep replicas consistent through failure. Hinted handoff keeps writes available when a replica is down: a healthy node accepts the write on the absent node's behalf, stores a hint, and replays it once that node recovers, so no write is rejected during a brief outage. Merkle trees handle anti-entropy — re-syncing replicas that have diverged — by building a hash tree over each replica's key ranges and comparing roots down the branches, so only the key ranges that actually differ are transferred instead of copying the entire dataset.
How do I practise a system design interview out loud?
Reading a walkthrough is not the same as defending a design while someone interrupts. On Zynter, the HLD track runs live voice rounds modelled on how real system design loops run — you talk through a shared design on the canvas, and a voice interviewer pushes with the follow-ups above one at a time, then gives you a debrief on problem solving, communication, technical execution, and time management. The score is a practice signal, not a hiring decision.
Practise it on Zynter
You can recite every quorum rule. The round is won on partitioning and the consistency trade.
Pick the Distributed Key-Value Store round, draw the consistent-hashing ring on the canvas, and defend it out loud to a voice interviewer that keeps asking "add a node — how many keys move?" and "two clients write the same key at once — who wins?" — then read the debrief. Zynter.ai is on-demand mock interviews modelled on real company loops, with 150+ rounds across 20 companies including the HLD system design track (Sep 2026).
Practise the Key-Value Store round →
Browse all interview tracks




Top comments (0)