System Design interview questions

Reviewed by Mark Dickie · Last updated

System design is the practice of defining the architecture, components, data flow, and operational characteristics of a software system so that it meets functional and non-functional requirements at scale. For interviews, the core skill being tested is your ability to reason through tradeoffs: picking the right database model, deciding where to cache, choosing between synchronous and asynchronous communication, and sizing capacity from realistic traffic estimates. You are expected to talk through bottlenecks, single points of failure, and what breaks first when load increases by 10×.

The table below maps the topic areas interviewers probe most often:

Topic AreaWhat You Need to ExplainCommon Anchor Question
Capacity estimationQPS, storage, bandwidth from user counts"Estimate storage for 5 years of photos"
Database selectionRelational vs. NoSQL, sharding, replication"Why Cassandra over PostgreSQL here?"
Caching strategiesWrite-through vs. write-back, cache invalidation, eviction policies"How do you keep the cache consistent?"
Load balancingL4 vs. L7, consistent hashing, session affinity"How do you distribute writes evenly?"
Message queuesAt-least-once vs. exactly-once, backpressure"What happens if the consumer crashes mid-processing?"
Consistency modelsStrong vs. eventual, CAP theorem, quorum reads/writes"Can you serve stale reads during a partition?"
MonitoringMetrics, logging, tracing, alerting thresholds"What would you alert on?"

What does a system design interview test?

The interview tests structured thinking more than memorisation. A typical 45-minute session follows this arc:

  1. Clarify requirements — functional (what the system does) and non-functional (latency, throughput, availability, consistency). Ask follow-up questions; interviewers want to see you narrow scope deliberately.
  2. Estimate scale — derive QPS, storage, and bandwidth from a user base. Round numbers are fine; the reasoning matters more than precision.
  3. Draw the high-level design — identify the main components (clients, load balancers, application servers, databases, caches, queues) and the connections between them.
  4. Go deep on bottlenecks — pick the weakest link and explain how you would fix it: shard the database, add a read replica, introduce a CDN, switch to an append-only log.
  5. Wrap up with failure modes — discuss what happens when a data centre goes offline, when a queue backs up, when a hot key skews your shard distribution.

How should you approach a system design question you have not seen before?

Start by restating the problem in your own words and listing the three or four requirements that will shape every downstream decision. Sketch a box-and-arrow diagram early, even if it is wrong, because it gives the interviewer something to react to. Then iterate: pick one component, explain the obvious approach, state its limitation, and offer the alternative. This pattern — propose, critique, adjust — is what separates strong candidates from those who freeze or jump straight to a final architecture without showing the reasoning.

What are the most common mistakes in system design interviews?

Jumping to a solution before clarifying requirements is the top mistake. If you start drawing Kafka and Cassandra before the interviewer has told you the read-to-write ratio, you are guessing. The second common mistake is naming technologies without explaining why — saying "we will use Redis" without connecting it to a specific latency or consistency requirement tells the interviewer you can list tools but cannot justify choices. Third is ignoring failure: a design that only works when everything is healthy is incomplete. Interviewers want to hear you talk about replication lag, split-brain scenarios, and degraded-mode behaviour.

Key facts

  • Tarmac has 171 System Design interview questions on this topic, 10 of them on this page, at difficulty 1–5 of 5.
  • Tarmac last reviewed these System Design interview questions on 18 August 2026.

At a glance

Questions10 shown · 171 in the bank
Difficulty1–5 of 5
FormatsFill in the blank, Multiple choice, True / false, Ordering, Flashcard, Multiple answer, Find the bug, Design exercise, Short answer

What you'll review

  1. sd architecture
  2. microservices
  3. caching strategies
  4. dead letter queue
  5. id generation
  6. scalability

Practice questions

System Design/sd-architecture

Complete the two key concepts: A _____ balances incoming network traffic across multiple servers to improve availability and throughput. When user session data must remain on the same server for the duration of a session, this is called _____ persistence.#

Show answer

Complete the two key concepts: A load balancer balances incoming network traffic across multiple servers to improve availability and throughput. When user session data must remain on the same server for the duration of a session, this is called sticky persistence.

Why:

A load balancer sits in front of a pool of servers and distributes client requests using algorithms such as round-robin or least-connections. When an application stores session state locally on a specific server, a 'sticky session' (also called session affinity) configuration tells the load balancer to always route a given client to the same backend server so that session data remains accessible. This avoids the need to share session state across servers but can create uneven load distribution.

System Design/sd-architecture/microservices

In a microservices architecture, Service A needs to call Service B to complete a user request. Service B is temporarily unavailable. Which pattern is most appropriate to prevent Service A from exhausting its own resources (e.g., thread pools) while Service B is down?#

Options

Show answer

The Circuit Breaker pattern is the most appropriate choice. It tracks failure rates to Service B and, once a threshold is exceeded, 'opens' the circuit so subsequent calls fail fast instead of blocking. This prevents Service A's thread pools from being exhausted waiting for a slow or unreachable dependency, allowing the system to degrade gracefully rather than cascade-fail.

Why:

The Circuit Breaker pattern monitors call failures to a downstream service and, after a threshold is crossed, 'opens' the circuit so that further calls fail immediately rather than waiting for a timeout. This prevents thread pools and other resources in the calling service from being exhausted while the dependency is down. Retries alone worsen the situation by consuming more resources. The Saga and Sidecar patterns address different concerns (distributed transactions and cross-cutting concerns, respectively).

System Design/sd-architecture/microservices

In a microservices architecture, each microservice should share a single centralized database with all other microservices so that data consistency is easier to enforce.#

Options

Show answer

This is false. A core microservices principle is that each service should own its own dedicated database ('Database per Service' pattern). Sharing a centralized database tightly couples services together — schema changes in one can break others, it creates a single point of failure, and it defeats independent deployability. Cross-service data consistency is handled through eventual consistency and patterns like Sagas.

Why:

One of the core principles of microservices architecture is that each service owns its own data store (the 'Database per Service' pattern). Sharing a single database creates tight coupling between services — a schema change in one service can break others, and the database becomes a single point of failure and a scaling bottleneck. Data consistency across services is instead handled through eventual consistency, events, or the Saga pattern.

System Design/sd-architecture/microservices

A client request arrives at a microservices system that uses client-side service discovery (e.g., Netflix Eureka + Ribbon / Spring Cloud). In this pattern the service consumer itself queries the registry and picks an instance. Place the following components in the order the request passes through, from the external entry point to business logic execution:#

Put these in order

Show answer

The correct order is: Load Balancer / Ingress → API Gateway → Service Registry lookup → Target Microservice. In client-side service discovery (e.g., Netflix Eureka + Ribbon), the API Gateway itself queries the Service Registry to find a healthy instance before forwarding the request, making the registry lookup a distinct step that occurs after the gateway and before the microservice.

Why:

In a client-side service discovery architecture (exemplified by Netflix Eureka + Ribbon or Spring Cloud LoadBalancer), the component that wants to call a downstream service is responsible for querying the Service Registry directly before forwarding the request. The canonical flow is: (1) The external Load Balancer / Ingress is the first hop — it terminates TLS and spreads traffic across gateway instances. (2) The API Gateway applies cross-cutting concerns such as authentication, rate-limiting, and routing logic. (3) The Gateway, acting as the service consumer, performs a Service Registry lookup (e.g., queries Eureka) to discover healthy instances of the target service and selects one (usually via a client-side load-balancing algorithm like round-robin). (4) The request is forwarded directly to the chosen Target Microservice instance, which executes the business logic. This four-step ordering is unambiguous for client-side discovery because the registry lookup is an explicit, runtime step performed by the gateway/consumer before it can forward the call — unlike server-side discovery (ALB + ECS) where the registry resolution is handled internally by the load balancer and is not a discrete step visible to the gateway.

System Design/sd-fundamentals/caching-strategies

Describe the cache-aside (lazy-loading) caching strategy and its main failure mode.#

Show answer

In cache-aside the application code owns the cache: on a read it checks the cache first, and on a miss it loads from the database, populates the cache, and returns. Writes go to the database and then invalidate (or update) the cache entry. Its main failure mode is the thundering herd / cache stampede — when a hot key expires, many concurrent requests all miss and hammer the database at once. Mitigate with request coalescing (single-flight), a short lock per key, or probabilistic early refresh.

Why:

Cache-aside is the most common strategy because the app stays in control and the cache only ever holds data that was actually requested. The trade-offs are an extra round trip on misses and the need to manage invalidation carefully, since stale entries linger until they expire or are explicitly evicted.

System Design/sd-architecture

You are designing a globally distributed, multi-region active-active database (e.g., a shopping-cart service). Each region accepts both reads and writes and replicates asynchronously to the others. A user updates their cart from two different regions nearly simultaneously before either update has replicated.#

Options

Show answer

Vector clocks (or version vectors) with application-level or CRDT-based merging are the best fit. They capture causal relationships between writes across regions, allowing the system to detect true conflicts (concurrent writes with no causal ordering) and apply deterministic merge logic — without requiring cross-region coordination on the write path. LWW risks silent data loss due to clock skew, 2PC sacrifices availability, and read repair only acts on the read path.

Why:

In a multi-region active-active architecture, the core challenge with eventual consistency is handling concurrent writes to the same record from different regions. Vector clocks (or similar mechanisms like CRDTs) track causality across nodes so that conflicts can be detected and resolved rather than silently overwriting data. Last-Write-Wins (LWW) based solely on wall-clock time is unsafe because clocks can skew, making it possible to overwrite a newer write with an older one. Two-phase commit provides strong consistency but introduces cross-region coordination latency and is an availability risk — the opposite of what active-active targets. Read repair is a read-path technique and does not prevent write conflicts. Vector clocks remain the standard causality-tracking answer for active-active replication conflict detection.

System Design/sd-architecture

A senior engineer is reviewing a system design document for a high-traffic e-commerce platform. Which of the following architectural statements are correct? Select all that apply.#

Options

Pick every one that applies.

Show answer

The correct statements are (b), (c), and (d). A message queue like Kafka decouples producers and consumers and smooths traffic spikes. Stateful services require externalised or sharded state before horizontal scaling; just adding instances risks split-brain. Read replicas offload read traffic from the primary. Option (a) is wrong because CDNs cache HTTP responses at the edge — they cannot introspect or cache arbitrary database query results.

Why:

This question probes several common but costly misconceptions in large-scale system design. (1) Adding a CDN for database query caching is FALSE — CDNs cache static or edge-cacheable HTTP responses, not raw database queries; that role belongs to an in-process or distributed cache like Redis. (2) A message queue (e.g., Kafka, SQS) decouples producers from consumers and naturally absorbs traffic spikes by acting as a buffer, so bursts don't overwhelm downstream services — TRUE. (3) Horizontal scaling of stateful services is NOT simply a matter of adding instances; state must be externalised or partitioned (sharding, consistent hashing) to avoid split-brain and inconsistency — FALSE. (4) Database read replicas reduce load on the primary by offloading read traffic — TRUE. All four require careful evaluation, making this a multi-select question.

System Design/sd-patterns/dead-letter-queue

This worker pulls messages off a queue and processes them. A single malformed message has brought the whole consumer to a halt — no other message gets processed. What is the bug?#

// queue.receive() blocks until a message is available
while (true) {
  const msg = queue.receive();
  try {
    process(msg);          // throws on a malformed message
    queue.ack(msg);
  } catch (err) {
    queue.requeue(msg);    // put it back so we don't lose it
  }
}

Options

Show answer

A message that always throws (a 'poison pill') is requeued endlessly with no attempt limit, so it loops forever at the head of the queue and starves every other message; it needs a redelivery counter that routes to a dead-letter queue after N attempts

Why:

The message is a poison pill: it deterministically throws, the catch block requeues it with no attempt limit, and (in a FIFO queue) it returns to the head and is retried forever — blocking every message behind it. The fix is to bound redeliveries (track an attempt count or use the broker's max-receive setting) and route the message to a dead-letter queue after the threshold, so the poison message is quarantined for inspection while the rest of the stream flows. The distractors are wrong: ack-after-process (b) is the correct at-least-once ordering, not the bug — acking first would silently lose messages on a crash; the infinite while loop (c) is the intended consume loop, not a memory leak; and changing requeue to ack on failure (d) would discard every failed message, replacing a stall with silent data loss.

System Design/sd-data/id-generation

Design a URL shortener (think Bitly / TinyURL).#

Show answer

Requirements. Two operations dominate: create (write a mapping, optionally with a custom alias and expiry) and redirect (look up a code and 30x to the long URL). The system is overwhelmingly read-heavy (100:1), so the redirect path is what we optimise. I'd confirm: are custom aliases required (yes), do links expire (optional TTL), and how precise must click counts be (approximate/eventually-consistent is fine). I'd use a 302 (temporary) redirect so we keep serving redirects through our system and can still count clicks; a 301 would let browsers cache and bypass us.

Capacity. ~40 writes/sec average, ~200/sec peak; ~4K reads/sec average, ~20K peak. Storage: ~6B links over 5 years; at ~500 bytes/row (code, URL, metadata) that's ~3 TB — comfortably shardable. Keyspace: base62 with 7 characters gives 62⁷ ≈ 3.5×10¹² codes, far more than 6B, so 7 chars (often padded to a fixed length) is plenty. The hot working set (recently/ popularly accessed links) is a small fraction of 6B, so a cache of tens of GB covers the bulk of redirect traffic.

Data model. A single mapping table/collection keyed by the short code: code (PK) → long_url, created_at, expires_at, owner_id. Point lookups by primary key suit a key-value store (DynamoDB/Redis-backed) or a sharded relational table sharded by code. Click counts live separately — incrementing a counter on every redirect would put write load on the read path — so clicks are emitted as events and aggregated asynchronously.

Short-code generation. Generate a unique 64-bit id (a distributed counter handed out in ranges per server, or a Snowflake-style id) and base62-encode it to get the short code; this guarantees uniqueness with no collision checks and no hotspot from a single shared counter. Custom aliases are written directly with a uniqueness check and stored in the same table, reserving that code. (A hash-of-URL scheme is the alternative but needs collision handling and breaks idempotency for duplicate URLs.)

Read/write paths. Write: allocate id → base62 → insert mapping → return the short URL. Redirect: a load balancer / CDN fronts stateless app servers; the server does a cache-aside lookup (code → long_url) in Redis, falling back to the store on a miss and populating the cache, then returns a 302. This keeps p99 well under 100 ms for cache hits. Expiry is enforced by TTL on both the row and the cache entry. Each redirect fires a lightweight click event onto a queue (Kafka) that a consumer aggregates into per-link counts.

Bottleneck & scaling. The bottleneck is the redirect read path at peak. We scale it with cache layers (most reads never touch the store), read replicas, and sharding the mapping store by code so lookups stay single-shard. Id generation scales by handing each server an independent id range (or using Snowflake), avoiding a single global counter as a SPOF. At 10× traffic, the cache absorbs most of it; we add cache nodes and replicas and, if needed, push redirects further to the edge.

Why:

A URL shortener is the canonical warm-up design: it forces a clean separation of a heavily read-optimised redirect path from a comparatively rare write path. The two pivotal decisions are short-code generation (a base62-encoded distributed id avoids both collisions and the hotspot of a single shared counter) and caching (cache-aside on code→URL, fronted by a CDN/LB, is what meets the latency budget when reads outnumber writes 100:1). Click counting is deliberately moved off the redirect path and made asynchronous so analytics never slow a redirect. The dominant bottleneck is the redirect read path, scaled by caching, read replicas, and sharding the mapping store by code.

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
  1. This is the hot shard (or write-hotspot) anti-pattern. Because event_id is 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.

  2. 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.

Why:

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.

Related interview questions

The other 161 questions

This page shows 10. A free account opens the rest and marks your answers, which a page of answers can't do. Miss one and it returns a few days later, then a week after that, until you stop missing it.

Start free

Free · the whole bank · 100 graded answers per 30 days · written feedback and job-ad quizzes on the paid plan

What moved, monthly

One email a month when the bulletin comes out: what moved in the markets we track, and the new question topics we published. Confirm your address to join. Unsubscribe any time.