System Design Interview Questions: Scalability Fundamentals
Reviewed by Mark Dickie · Last updated
Scalability is a system's ability to handle increasing load by adding resources without redesigning its core architecture. For a system design interview, you should be able to distinguish vertical scaling (upgrading a single machine's CPU, memory, or storage) from horizontal scaling (adding more machines) and explain when each approach applies. You also need to describe how load balancers, caching, database sharding, and CDNs each contribute to a scalable request path, and reason about where a system breaks under load and what specific change addresses it.
What does a system design interview test on scalability?
Interviewers assess whether you can reason about how a system behaves as traffic grows. They look for your ability to identify the specific component that limits throughput and propose a targeted fix, rather than answering "add more servers" as a catch-all. A strong candidate names the bottleneck, picks a scaling strategy, and explains the trade-offs that come with it.
| Scaling approach | What it means | Typical limit | When to choose |
|---|---|---|---|
| Vertical (scale up) | Add more CPU, RAM, or disk to one machine | Hardware ceiling; single point of failure | Quick wins, low traffic, databases hard to shard |
| Horizontal (scale out) | Add more machines behind a load balancer | Coordination overhead, data consistency complexity | Stateless services, high-availability needs |
| Caching | Store computed results closer to the caller | Stale data, cache invalidation complexity | Repeated reads of the same data, expensive queries |
| Sharding | Partition data across multiple database nodes | Cross-shard queries, rebalancing pain | Write-heavy workloads exceeding single-node capacity |
How do you choose between horizontal and vertical scaling?
- Identify whether the bottleneck is CPU, memory, network, or disk I/O on a single machine.
- If one upgraded machine can handle the load, vertical scaling is the fastest and simplest fix.
- If you need availability beyond what one machine provides, or the load exceeds any single machine, scale horizontally.
- For stateless services (API servers, static content), horizontal scaling is straightforward: add machines behind a load balancer.
- For stateful components (databases, session stores), evaluate sharding or read replicas before committing to a partitioning scheme.
What are the most common scalability bottlenecks?
The usual suspects are the database (connection limits, slow queries), the network (bandwidth saturation), and the application layer (thread exhaustion, memory leaks). Caching with Redis or Memcached relieves database pressure by serving repeated reads from memory. Read replicas spread query load across multiple database instances. A CDN offloads static assets so your origin servers handle only dynamic requests. Connection pooling and asynchronous I/O prevent the application tier from becoming the bottleneck under concurrent load.
Key facts
- Tarmac has 33 System Design interview questions on this topic, 25 of them on this page, at difficulty 1–5 of 5.
- Tarmac tracked 4,937 job postings asking for System Design in August 2026.
- Roles asking for System Design advertise a median base salary of US$180,700, across 1,166 job postings as of August 2026.
- Tarmac last reviewed these System Design interview questions on 31 August 2026.
At a glance
| Questions | 25 shown · 33 in the bank |
|---|---|
| Difficulty | 1–5 of 5 |
| Formats | True / false, Ordering, Multiple choice, Fill in the blank, Multiple answer, Flashcard, Short answer, Design exercise |
What you'll review
- scalability
- horizontal vertical
- message queues
- rate limiting
Practice questions
Try one before you open the answer. Pick an option and press Check; it's marked on the spot.
System Design/sd-fundamentals/scalability
True or False: Vertical scaling (scaling up) has no practical upper limit — you can always keep adding more CPU and RAM to a single server to handle increasing load indefinitely.#
Options
Show answer
False. Vertical scaling has a hard practical upper limit. Hardware only scales so far — there is a maximum amount of CPU cores, RAM, and storage a single server can hold, and costs escalate steeply long before that ceiling is reached. This fundamental constraint is why large-scale systems favor horizontal scaling across many machines instead.
Vertical scaling is bounded by the physical and economic limits of hardware. At some point, no single machine can be made powerful enough, or the cost becomes prohibitive. This is one of the core reasons distributed, horizontally-scaled architectures are favored for large-scale systems. There is always a ceiling on how much a single machine can be upgraded.
System Design/sd-fundamentals/scalability
A social media platform is experiencing rapid user growth and needs to scale its architecture. Arrange the following scalability measures in the order they are most commonly adopted — from the simplest first step to the more advanced technique applied later:#
Put these in order
Show answer
The most common order is: Vertical scaling → Horizontal scaling with a load balancer → Adding a caching layer → Database sharding. Teams start with the simplest change (upgrading one server), then distribute load across multiple servers, then reduce database pressure with caching, and finally tackle the hardest problem — splitting the database — only when truly necessary.
The typical evolution of scalability in a growing system follows this path: (1) First, vertically scale the existing server since it requires no architectural change. (2) Once vertical limits are hit, introduce horizontal scaling with a load balancer to add more app servers. (3) Add a caching layer to relieve read pressure on the database. (4) Finally, shard the database once write volume and data size exceed what a single database node can handle — this is the most complex step and is deferred as long as possible.
System Design/sd-fundamentals/scalability
A web application is receiving more traffic than a single server can handle. Which of the following techniques distributes incoming requests across multiple servers so that no single server becomes a bottleneck?#
Options
Show answer
Load balancing is the technique that distributes incoming requests across multiple servers to prevent any single server from becoming a bottleneck. A load balancer sits in front of the server pool and routes each request to an available server (e.g., using round-robin or least-connections), enabling the system to scale horizontally as traffic grows.
Load balancing is the practice of distributing incoming network requests across a pool of servers (the 'server farm' or 'backend pool'). This prevents any one server from becoming overwhelmed and is the foundational technique for horizontal scalability. Database indexing speeds up queries on a single node, data compression reduces payload size, and SSL termination offloads encryption — none of those distribute request load across servers.
System Design/sd-fundamentals/scalability
Vertical scaling (scaling up) means adding more machines to a system, while horizontal scaling (scaling out) means increasing the CPU, RAM, or storage of an existing machine.#
Options
Show answer
This statement is false — the definitions are swapped. Vertical scaling (scaling up) means increasing the resources (CPU, RAM, storage) of an existing machine. Horizontal scaling (scaling out) means adding more machines to the system and spreading the load across them. Confusing the two is a very common mistake in system design discussions.
The definitions are reversed. Vertical scaling ('scaling up') means upgrading the resources of an existing machine — more CPU cores, more RAM, faster storage — on the same single node. Horizontal scaling ('scaling out') means adding more machines (nodes) to the system and distributing the workload across them. Because the statement swaps the two definitions, it is false.
System Design/sd-fundamentals/scalability
To reduce repeated expensive database queries and improve read scalability, engineers store the results of those queries temporarily in a fast in-memory store. This technique is called _____, and a widely used open-source tool for this purpose that supports rich data structures (strings, lists, sets, hashes, and more) beyond simple key-value pairs is _____.#
Show answer
To reduce repeated expensive database queries and improve read scalability, engineers store the results of those queries temporarily in a fast in-memory store. This technique is called caching, and a widely used open-source tool for this purpose that supports rich data structures (strings, lists, sets, hashes, and more) beyond simple key-value pairs is Redis.
Caching is the technique of storing the results of expensive or frequently repeated operations (such as database queries) in a fast, temporary storage layer so that subsequent requests can be served without hitting the database again. The second blank is pinned to Redis specifically because the prompt qualifies it as supporting rich data structures (strings, lists, sets, hashes, and more) beyond simple key-value pairs — a distinguishing feature of Redis that does not apply to Memcached, which supports only plain string key-value storage. Redis is one of the most widely adopted open-source in-memory data stores used for caching in distributed systems.
System Design/sd-fundamentals/scalability
A startup's web application runs on a single server. As traffic grows, the team decides to add more servers and distribute incoming requests across them using a load balancer. Which scalability strategy does this best describe?#
Options
Show answer
Horizontal scaling (scaling out) is the correct answer. This strategy involves adding more servers/instances and using a load balancer to distribute traffic among them, rather than upgrading a single machine's resources (which would be vertical scaling). It allows a system to handle growing traffic by simply adding commodity machines.
Horizontal scaling (scaling out) means adding more machines/instances to share the load, as opposed to vertical scaling (scaling up), which means upgrading the existing machine's CPU, RAM, or storage. Using a load balancer to distribute traffic across multiple servers is the canonical example of horizontal scaling. Database sharding and cache invalidation are separate, more specific techniques.
System Design/sd-fundamentals/scalability
Vertical scaling (scaling up) is generally considered easier to implement than horizontal scaling (scaling out), but it has a hard upper limit because a single machine's hardware capacity is finite.#
Options
Show answer
This statement is true. Vertical scaling is simpler to implement because it involves only upgrading a single machine's hardware with no application-level changes. However, it hits a hard ceiling determined by the maximum specs available for a single server. Horizontal scaling bypasses this limit by adding more machines, at the cost of increased architectural complexity.
Vertical scaling requires no changes to application architecture — you simply upgrade the server's CPU, RAM, or storage — making it straightforward to implement. However, hardware has a physical ceiling: there is a maximum amount of RAM or number of CPU cores you can add to one machine. Horizontal scaling removes this ceiling by adding more machines, but it introduces complexity such as load balancing, distributed state management, and network partitioning concerns.
System Design/sd-fundamentals/scalability
A rapidly growing e-commerce site is experiencing slow response times. Arrange the following scalability improvements in the order they are most commonly applied, from the simplest/first step to the most complex/later step:#
Put these in order
Show answer
The most common progression is: (1) Vertically scale the single server — zero architectural changes needed; (2) Add a caching layer like Redis to reduce database read load; (3) Horizontally scale the application tier behind a load balancer for concurrency; (4) Shard the database across multiple servers for write scalability. Each step increases in complexity and is usually only attempted after simpler options are exhausted.
Teams typically start with the easiest change: vertically scaling the existing server requires zero code changes. Next, adding a caching layer (like Redis) dramatically reduces database read pressure with minimal architectural change. After that, horizontal scaling of the application tier is introduced behind a load balancer to handle more concurrent users. Database sharding is the most complex and disruptive step, requiring data partitioning strategy and application-level routing, so it is tackled last.
System Design/sd-fundamentals/horizontal-vertical
Your API runs on a fleet behind a round-robin load balancer, but users intermittently get logged out as requests land on different nodes. The team stores session state in each node's local memory. What is the single most important change to enable safe horizontal scaling?#
Options
Show answer
Move session state out of each node's local memory into a shared store such as Redis, so any node can serve any request. The nodes are stateful today — each holds session data only it can see — which is why round-robin routing logs users out. Externalising state makes nodes interchangeable, the precondition for safe horizontal scaling, rolling deploys, and failover.
The root problem is that the app nodes are stateful: each holds session data only it can see. Externalising state to a shared store makes the nodes interchangeable, which is the precondition for horizontal scaling and for free rolling deploys and failover. Sticky sessions are a workaround, not a fix — they pin users to a node, so a deploy or crash still drops their session and they defeat even load distribution. Vertical scaling (more RAM) raises the ceiling but keeps the state trapped on one box, so the cross-node inconsistency remains. A CDN caches public, cacheable responses; per-user session data is neither, so it does nothing here.
System Design/sd-fundamentals/scalability
A service handling 8.64 million requests evenly across a 24-hour day averages _____ requests per second, and back-of-the-envelope capacity sizing usually plans for the _____ load rather than the average to survive traffic spikes.#
Show answer
A service handling 8.64 million requests evenly across a 24-hour day averages 100 requests per second, and back-of-the-envelope capacity sizing usually plans for the peak load rather than the average to survive traffic spikes.
8,640,000 / 86,400 seconds = 100 requests per second average. Capacity must be provisioned for peak, not average, because real traffic is bursty — a common heuristic is to assume peak is several times the mean (e.g. 2-10x) and size headroom accordingly. Estimating QPS, then storage and bandwidth from request size, is the standard opening of a capacity-estimation exercise.
System Design/sd-fundamentals/scalability
A stateless web service is receiving increasing read traffic and its single server is approaching CPU saturation. Which scalability strategy most directly addresses this by allowing the service to handle more concurrent requests without changing the server's hardware specs?#
Options
Show answer
Horizontal scaling (adding more identical server instances behind a load balancer) is the correct approach. Because the service is stateless, any instance can handle any request, so distributing traffic across multiple servers directly increases concurrent-request capacity without touching the existing hardware. Vertical scaling also helps but requires downtime and has a hard ceiling.
Horizontal scaling (scaling out) adds more machines/instances to distribute load, making it well-suited for stateless services. Vertical scaling (scaling up) increases the resources of a single machine. A load balancer distributes traffic but does not itself replicate application logic. A CDN caches static content closer to users. Only horizontal scaling directly handles increased read throughput on stateless web servers by adding replicas behind a load balancer.
System Design/sd-fundamentals/scalability
Which of the following techniques directly improve the read scalability of a relational database? Select all that apply.#
Options
Pick every one that applies.
Show answer
The techniques that directly improve read scalability of a relational database are: read replicas (routing SELECTs to replica nodes adds horizontal read capacity), read-through caching (serving repeated reads from memory bypasses the database entirely), and sharding (partitioning data so each shard handles a smaller dataset and fewer concurrent queries). Increasing connection pool threads alone and rewriting stored procedures do not directly increase the database's read throughput.
Read replicas (a) directly improve read scalability by creating additional database nodes that serve SELECT queries, relieving pressure on the primary and allowing the system to handle more concurrent reads horizontally. Read-through caching (b) (e.g., Redis) directly improves read scalability by intercepting repeated read requests and returning results from memory, drastically reducing the number of queries that ever reach the database. Sharding (e) directly improves read scalability by partitioning data across nodes so each node owns and serves a subset of the data — queries for a given user hit only one shard, reducing per-node dataset size and concurrent read contention. All three are well-accepted, direct read-scalability techniques. Increasing connection pool threads (c) is tempting, but the question specifies doing so without changing the database server's own max_connections or hardware: the database itself still becomes the bottleneck once its connection limit is saturated, so this manages client-side overhead rather than increasing the database's fundamental read throughput. Rewriting stored procedures in the application layer (d) is an architectural refactoring that does not inherently increase the database's capacity to serve more reads.
System Design/sd-fundamentals/scalability
Arrange the following scalability milestones in the order a typical web application team would adopt them as traffic grows from hundreds to millions of daily active users (earliest first), following the canonical progression described in widely cited system design literature (e.g., Alex Xu's System Design Interview).#
Put these in order
Show answer
The canonical order is: (1) single-server monolith, (2) move the database to a dedicated server, (3) add a load balancer with multiple stateless app servers, (4) deploy a caching layer, (5) shard the database. Separating the database onto its own server comes before horizontal app-server scaling because resource contention on a shared box must be resolved first, and a standalone DB server is a prerequisite for multiple app instances to connect to it correctly.
According to canonical system design literature (e.g., Alex Xu's System Design Interview), the standard incremental scaling progression is: (1) Start with a single-server monolith to ship and validate quickly. (2) Move the database to a dedicated server — this is done early to eliminate resource contention between the app and the DB on the same machine, and it is a prerequisite for meaningful horizontal app-server scaling. (3) Add a load balancer with multiple stateless app servers now that the DB is independently reachable by all instances. (4) Introduce a caching layer (e.g., Redis/Memcached) to absorb hot reads and reduce DB load. (5) Shard the database to distribute write load and data volume at massive scale. This C → E → B → A → D order is the consensus ordering in the most widely cited system design references.
System Design/sd-patterns/message-queues
Your checkout flow synchronously calls an email service, an analytics pipeline, and a fraud-scoring job, and a slow dependency now blocks orders. You introduce a message queue so checkout publishes events and workers consume them. Which benefits does this asynchronous decoupling genuinely provide?#
Options
Pick every one that applies.
Show answer
The real benefits are that checkout no longer blocks on slow consumers (it returns once the event is enqueued), the queue absorbs traffic spikes and buffers work so consumers drain at their own pace, and a temporarily down consumer can recover and process the backlog without losing events. It does not guarantee lower per-request end-to-end latency, and it does not remove the need to handle duplicate deliveries.
A queue decouples producers from consumers: checkout returns as soon as the event is enqueued (a), the queue acts as a buffer that smooths spikes so consumers process at a sustainable rate (b), and durable queues retain messages so a consumer that was down can drain the backlog on recovery (c). The two wrong options reflect common misconceptions. Async processing does not lower per-request end-to-end latency (d) — the downstream work still happens, just later; you trade latency-to-completion for responsiveness and resilience. And most queues offer at-least-once delivery, so consumers must be idempotent to tolerate duplicates (e); the queue does not remove that obligation.
System Design/sd-fundamentals/scalability
You want to scale a web service horizontally (add more instances) to handle growing traffic. Which design decisions enable clean horizontal scale-out?#
Options
Pick every one that applies.
Show answer
Clean horizontal scale-out needs that any instance can handle any request. Keep application instances stateless by storing session state in a shared store like Redis, use a health-checking load balancer so new instances receive traffic once they pass readiness checks, and implement graceful shutdown so scale-in lets in-flight requests finish. Sticky sessions and local-filesystem uploads both pin work to one node, defeating that goal.
Horizontal scale-out requires that any instance can handle any request. Stateless instances (a) make this trivially true — session data lives in a shared external store, so routing a request to a different instance is transparent. A health-checking load balancer (b) ensures the new capacity is used immediately on startup and that unhealthy nodes aren't given traffic. Graceful shutdown (c) means scale-in operations don't cause request errors — a critical production concern. Sticky sessions (d) are the opposite of scale-friendly: they reintroduce instance affinity, so the failure or scale-in of one node loses that user's session. Local filesystem storage for uploads (e) has the same problem: files written to one instance are invisible to all others, breaking the assumption that any instance can serve any request.
System Design/sd-fundamentals/scalability
Why must application servers be stateless to scale horizontally, and where does the state go?#
Show answer
A stateless server holds no session or user data in local memory between requests, so any instance can serve any request — the load balancer can route freely and you can add/remove instances without sticky sessions or data loss. State migrates to shared external stores: sessions to Redis or a distributed cache, user data to a relational or document database, uploaded files to object storage (S3). The server becomes a thin compute layer; scaling it is just adding more identical processes. Sticky sessions are the anti-pattern: they couple a user to one instance, break on instance failure, and prevent even distribution.
Statelessness is a prerequisite for horizontal scale. The 12-Factor App principle 'processes are stateless and share-nothing' captures this precisely. In practice, teams often accidentally bake state in (in-memory caches, local disk uploads) and discover the problem only when load balancing or autoscaling is introduced.
System Design/sd-patterns/rate-limiting
You are implementing rate limiting at the API gateway layer to protect backend services. Which statements are accurate about common rate-limiting algorithms and their trade-offs?#
Options
Pick every one that applies.
Show answer
Four statements are accurate: a token bucket allows short-term bursts up to the bucket capacity while enforcing an average refill rate, a sliding window log tracks per-request timestamps and prevents the burst-at-boundary problem, a fixed window counter can allow up to 2× the limit at a window boundary, and distributed rate limiting requires a shared counter (e.g. in Redis) to enforce limits accurately. Token buckets do not guarantee zero burstiness — that is the leaky bucket.
Token buckets (a) are the canonical burst-tolerant algorithm: tokens accumulate at the refill rate up to bucket capacity, so a burst of capacity tokens is permitted before the rate kicks in. The sliding window log (b) solves the boundary problem by tracking exact timestamps, so a burst at the end of one period plus the start of the next can't exceed the limit. The fixed window boundary exploit (c) is a real and well-documented flaw: if the limit is 100/minute and 100 requests arrive in the last second of window 1 and 100 in the first second of window 2, you've accepted 200 in two seconds. Distributed coordination (e) is essential in multi-node gateways — without a shared counter each node enforces the limit independently, effectively multiplying it by the number of nodes. Option (d) is wrong: token bucket explicitly allows bursts; the algorithm that spaces requests exactly 1/rate apart is the leaky bucket (or its fixed-rate output variant), not the token bucket.
System Design/sd-fundamentals/scalability
Stateful services (e.g. those holding in-memory session state) scale horizontally just as easily as stateless services.#
Options
Show answer
False. Stateless services are trivially horizontally scalable because any node can handle any request. Stateful services carry session data tied to a specific node, so scaling them out requires either sticky sessions — which reintroduce node affinity and uneven load — or externalising the state to a shared store like Redis. That added complexity is why statelessness is the first design goal for horizontal scaling.
Stateless services are trivially horizontally scalable: any node can handle any request because no local state distinguishes one node from another. Stateful services carry session data (auth tokens, cart contents, in-progress computations) that is tied to a specific node. Scaling them horizontally requires either sticky sessions (which re-introduce the node affinity problem and make load balancing uneven), or externalising the state to a shared store like Redis so that any node can reconstruct it. The added complexity — and the external store becoming a bottleneck — is why statelessness is the first design goal for horizontally scalable systems.
System Design/sd-fundamentals/scalability
Your read-heavy service is bottlenecked on the database. Compare adding read replicas versus adding a cache as ways to scale reads, and name the correctness gotcha each one introduces.#
Show answer
Read replicas scale reads by copying the primary's data to additional nodes that serve read queries, so you spread read load across machines while writes still go to the primary; they shine when reads are diverse (many distinct queries) and you need durable, queryable copies. A cache scales reads by keeping hot results in fast memory in front of the database, so it shines when a small set of keys is read very often and absorbs the repeated lookups entirely. The correctness gotcha for replicas is replication lag: a replica can be behind the primary, so a client may read its own just-written value as stale (read-after-write inconsistency) unless you route those reads to the primary. The gotcha for caching is staleness/invalidation: the cache can hold data that's already changed in the database, so you need a TTL or explicit invalidation, and getting invalidation right is famously hard.
Both offload the primary but differently. Read replicas add queryable copies and scale diverse read traffic across nodes (writes stay on the primary); their hazard is replication lag — a replica trails the primary, so a freshly written value can read back stale (read-after-write inconsistency), fixed by routing critical reads to the primary or using read-your-writes routing. A cache fronts the DB with in-memory hot results and is unbeatable for repeated reads of a small key set; its hazard is staleness / invalidation — cached data drifts from the source of truth, so you need TTLs or explicit invalidation, the genuinely hard part. They're complementary: cache the hot path, replicate the long tail.
System Design/sd-fundamentals/scalability
A service is degrading under a sustained traffic surge. Order the scaling response from cheapest/fastest to most invasive.#
Put these in order
Show answer
Scale from cheapest and fastest to most invasive, measuring before acting:
- Confirm the bottleneck via metrics/dashboards (CPU, latency, queue depth).
- Add a cache or raise cache TTLs to shed repeat reads off the backend.
- Horizontally scale out the stateless app tier behind the load balancer.
- Scale the data tier — add read replicas, then shard the database.
Always measure before acting so you scale the actual bottleneck, not a guess. Caching is the cheapest lever because it removes work entirely; adding stateless app instances is straightforward when the tier holds no session state; resharding the data tier is last because it is the most disruptive and hardest to reverse. The ordering reflects rising blast radius and engineering cost.
System Design/sd-fundamentals/scalability
A team is horizontally scaling a stateless web service behind a round-robin load balancer. They notice that certain user sessions break when requests land on different instances, so they enable sticky sessions (session affinity) at the load balancer.#
Options
Show answer
Sticky sessions re-introduce per-instance state, unevenly distributing load and creating a soft single point of failure per user — defeating horizontal scalability. The canonical fix is to externalise session state to a shared distributed cache (e.g., Redis or Memcached) so the service remains truly stateless and any instance can handle any request without affinity.
In a horizontally scaled stateless service behind a load balancer, session affinity (sticky sessions) pins a user's requests to a single instance. This re-introduces a single point of failure per user and prevents full horizontal elasticity. The correct alternative is to externalise session state into a shared distributed store (e.g., Redis) so any instance can serve any request. Rate-limiting at the LB, distributing static assets via CDN, and database read replicas are all orthogonal scalability improvements that do not conflict with statelessness.
System Design/sd-fundamentals/scalability
You are designing a globally distributed database for a social-media 'likes' counter that must keep serving reads and writes even during a network partition between data centres. You choose an AP (Available + Partition-tolerant) system per the CAP theorem.#
Options
Pick every one that applies.
Show answer
In an AP system, both sides of a network partition keep accepting writes, so the 'likes' counts can diverge until the partition heals and the replicas reconcile. CRDTs such as a G-Counter are a good fit for merging those diverged counts, since increment-based counters combine without conflict. The application layer must tolerate eventual consistency, so reads can temporarily return stale counts until replicas catch up.
This question probes deep knowledge of the CAP theorem's practical implications for scalable distributed databases. A network partition means nodes cannot communicate, so a CP system (e.g., HBase, Zookeeper) will refuse writes or reads to avoid returning stale data, sacrificing Availability. An AP system (e.g., Cassandra, DynamoDB in eventual-consistency mode) will continue serving reads and accepting writes on both sides of the partition, accepting that data may diverge and require later reconciliation. CA systems cannot exist in a distributed setting when partitions are possible — the theorem asserts you must choose between C and A when P occurs. The reconciliation mechanism for AP systems is typically vector clocks, last-write-wins, or CRDTs.
System Design/sd-fundamentals/scalability
A write-heavy event-logging system uses range-based sharding on a monotonically increasing event_id to distribute data across 16 database shards. After launch, operators observe that almost all writes land on a single shard while the others sit idle.#
Show answer
-
This is the hot shard (or write-hotspot) anti-pattern. Because
event_idis monotonically increasing, new events always map to the uppermost range, which belongs to the last (highest-range) shard. All other shards only receive reads for historical data, never new writes. The range boundaries do not self-adjust, so the system is effectively single-shard for writes. -
Consistent hashing maps both shard nodes and keys onto a circular hash ring. Each key is owned by the first node clockwise from the key's position on the ring. When a node is added or removed, only the keys between the new node and its predecessor (approximately K/n keys, where K is total keys and n is node count) need to be remapped and migrated. All other keys remain on their current nodes. This minimal disruption property — O(K/n) remapping vs O(K) for naive modular hashing — makes it operationally far superior for elastic, dynamically scaled shard clusters.
This question tests nuanced understanding of database sharding strategies and their scalability trade-offs. Range-based sharding on a monotonically increasing key (e.g., timestamp or auto-increment ID) concentrates all writes on the last shard ('hot shard' or 'write hotspot'), making it effectively unscalable for write-heavy workloads. Consistent hashing distributes keys uniformly across nodes and, crucially, minimises key remapping when nodes are added or removed — only ~K/n keys need to move (K = total keys, n = node count). Directory-based sharding with a lookup service adds a network hop and a single point of failure unless the directory itself is highly available. Composite/hierarchical sharding (e.g., hash on user ID, then range on timestamp within shard) is common in practice but adds operational complexity.
System Design/sd-fundamentals/scalability
Why is making the application tier stateless the precondition for scaling it horizontally, and what specifically breaks if you keep per-user session state in each server's local memory behind a load balancer?#
Show answer
Horizontal scaling assumes any instance can handle any request, so the load balancer can spread traffic freely and you can add or kill instances at will. That only holds if instances are stateless — they keep no request-specific data locally, so no single instance is special. If you store per-user session state in a server's local memory, requests for that user must keep landing on that exact server (session affinity / sticky sessions), which fights the load balancer and unbalances load; worse, when that instance dies or is scaled down its sessions are lost and the user is logged out, and autoscaling can't freely move traffic. The fix is to externalize the state — push sessions into a shared store like Redis or a database, or use stateless tokens (signed JWTs) — so every instance is interchangeable and any of them can serve the next request.
Horizontal scaling works because any instance can serve any request, letting the load balancer spread traffic and autoscaling add/remove nodes freely — and that interchangeability requires the tier to be stateless. Local per-user session memory breaks it: you're forced into sticky sessions / session affinity, which unbalances load and defeats free scheduling, and when an instance dies or scales down its in-memory sessions vanish (users logged out, work lost). The fix is to externalize state — a shared session store (Redis/DB) or self-contained signed tokens (JWT) — so no instance holds anything another can't reconstruct. 'Make it stateless, push state to a shared store' is the canonical scaling move for the app tier.
System Design/sd-fundamentals/scalability
A fast-growing marketplace's product catalog read service has hit a wall: a single primary database serving every product-page and search-result read is saturating, page loads are slowing, and a sale event nearly took it down. Design how to scale this read-heavy service to handle an order-of-magnitude more traffic without melting the database.#
Show answer
Requirements & freshness. The workload is wildly read-heavy (~6,000:1 at peak) and global, with a p99 < 150 ms budget everywhere. The unlock is freshness tolerance: most catalog fields (title, description, images) can be seconds stale, while price/availability must propagate fast and be correct at checkout. That split is what lets me cache and replicate aggressively for the bulk of reads while treating the few critical fields specially.
Caching. A multi-tier cache fronts the read path: a small in-process cache on each app server, a shared distributed cache (Redis) behind it, and a CDN/edge cache for whole responses where possible. Reads are cache-aside: check cache → on miss, read the store, populate cache with a TTL. Writes update the store and invalidate/refresh the affected keys. With sensible TTLs this absorbs the overwhelming majority of the 30M reads/sec so the database barely sees them.
Replication. The single primary can't serve reads at this scale, so I add read replicas: all writes go to the primary, reads fan out across replicas (and mostly never reach them thanks to cache). This removes the read load from the primary, which now only handles the ~5K writes/sec. The catch is replication lag — a read replica may briefly trail the primary.
Consistency. I run eventual consistency for general catalog reads, with staleness bounded by cache TTL and replica lag — fine for browsing. For correctness-critical reads (the price/availability shown at checkout), I read from the primary (or a freshly-revalidated cache entry) so a customer never buys at a stale price. This is a deliberate CAP/freshness trade-off per field, not one global setting.
Hot keys & thundering herd. A flash-sale item that's 40% of reads would crush whichever single node holds its cache entry, so I spread the hot key: replicate it across multiple cache nodes / serve it from many edge locations / pin it in every app server's local cache — turning one hotspot into many. For the stampede when a hot entry expires and thousands miss at once, I use request coalescing (single-flight) so only one request recomputes while others wait, plus staggered/jittered TTLs and serve-stale-while-revalidate so an expiry never lets a herd through to the database.
Multi-region & bottleneck. Globally, I put edge caches and regional read replicas on each continent so reads are served near the user within the 150 ms budget; the primary stays in one region (writes are rare). The dominant bottleneck is the single write primary plus cache misses on hot keys — scaled by adding replicas and edge capacity, the hot-key and herd mitigations above, and, only if writes themselves outgrow one primary, sharding the catalog by product id. A read-your-writes need (seller edits then reloads) is met by routing that session's reads to the primary or a sticky/fresh replica.
Scaling a read-heavy service is the canonical caching + replication problem, and the lever that makes it tractable is a precise freshness stance: because most catalog fields tolerate a few seconds of staleness, you can absorb the overwhelming bulk of reads in a multi-tier cache (in-process → Redis → CDN edge) and fan the rare misses across read replicas, leaving the single write primary to handle only the sparse writes. The consistency answer is deliberately per-field — eventual consistency for browsing, but the price/availability shown at checkout is read fresh (primary or revalidated) so no one buys at a stale price (a real CAP trade-off, not a global toggle). The two failure modes that distinguish a strong answer are the hot key (a flash-sale item on one cache node — spread it across nodes/edge/local caches) and the thundering herd / cache stampede on expiry (request coalescing / single-flight + jittered TTLs + serve-stale-while-revalidate). Globally, edge caches and regional replicas meet the latency budget, and read-your-writes is handled by routing a writer's own reads to the primary.
Related interview questions
Job market
See system-design salaries and hiring demand from live job postings.
The other 8 questions
This page shows 25 and marks what you pick. That's as far as a page can go. A free account opens the other 8 and keeps every answer. What you miss comes back until it's right: after a day, then at longer gaps.
Free · the whole bank · 100 marked answers per 30 days · written feedback on the paid plan