{"id":1271,"date":"2026-07-11T23:49:53","date_gmt":"2026-07-11T18:19:53","guid":{"rendered":"https:\/\/lastroundai.com\/blog\/?post_type=iq&#038;p=1271"},"modified":"2026-07-19T09:23:29","modified_gmt":"2026-07-19T03:53:29","slug":"system-design","status":"publish","type":"iq","link":"https:\/\/lastroundai.com\/interview-questions\/system-design","title":{"rendered":"System Design Interview Questions (2026): Must-Know Q&#038;A"},"content":{"rendered":"<p>Eric Brewer sketched what became the CAP theorem as a conjecture during a keynote at the 2000 ACM Symposium on Principles of Distributed Computing. He didn&#8217;t prove it. Two years later, MIT researchers Seth Gilbert and Nancy Lynch did, formalizing consistency, availability, and partition tolerance into an actual theorem and showing mathematically that you can&#8217;t guarantee all three once a network partition happens (<a href=\"https:\/\/dl.acm.org\/doi\/10.1145\/564585.564601\" target=\"_blank\" rel=\"noopener noreferrer\">Gilbert and Lynch, ACM SIGACT News, 2002<\/a>). Most candidates can recite &#8220;CAP&#8221; on demand. Fewer can explain what &#8220;partition tolerance&#8221; formally means, or why the real decision in a 2026 interview almost never comes down to choosing C over A, it comes down to how much staleness one specific feature can tolerate before it breaks something a user actually notices.<\/p>\n<p>Here&#8217;s an opinion that might be wrong: I think most system design prep spends too much time on box-and-arrow diagrams and not nearly enough on what happens when one of those boxes goes down at 2am. You can draw a clean architecture for a URL shortener and still lose the round if you freeze the moment an interviewer asks what happens when your database&#8217;s primary node dies mid-write. Drawing boxes is the easy 60 percent of a system design interview. Failure modes, data models, and trade-offs are the other 40, and that&#8217;s usually the part that decides whether the offer is senior or mid-level.<\/p>\n<p>This page covers system design interview questions across nine areas: scalability and the latency-versus-throughput split, load balancing, caching, databases (SQL vs NoSQL, indexing, sharding, replication), the CAP theorem and consistency models, message queues, rate limiting, API design, and five worked &#8220;design X&#8221; scenarios you&#8217;re actually likely to get asked, a URL shortener, a news feed, a chat app, a rate limiter, and a notification system. If you want a minute-by-minute framework for running a live 45-minute design round instead of a question bank, LastRoundAI&#8217;s <a href=\"\/blog\/system-design-interview-guide\">system design interview guide<\/a> covers that separately. This page answers the &#8220;what will they actually ask&#8221; question. That one answers the &#8220;how do I structure my 45 minutes&#8221; question.<\/p>\n<div class=\"iq-stats not-prose\"><div class=\"iq-stat\"><span class=\"iq-stat__value\">55<\/span><span class=\"iq-stat__label\">Questions<\/span><\/div><div class=\"iq-stat\"><span class=\"iq-stat__value\">Trade-offs Over Diagrams<\/span><span class=\"iq-stat__label\">Core Topic<\/span><\/div><div class=\"iq-stat\"><span class=\"iq-stat__value\">Whiteboard + Verbal<\/span><span class=\"iq-stat__label\">Format<\/span><\/div><div class=\"iq-stat\"><span class=\"iq-stat__value\">2002<\/span><span class=\"iq-stat__label\">CAP Proof Year<\/span><\/div><\/div>\n<h2>Scalability, latency, and throughput: the fundamentals interviewers check first<\/h2>\n<p>Every loop opens here, senior or junior. Get this section wrong and the interviewer recalibrates downward for the rest of the round, whether they say so or not.<\/p>\n<div class=\"iq-dsec iq-dsec--easy\"><div class=\"iq-dsec__row\"><h2 class=\"iq-dsec__h\" id=\"easy\"><span class=\"iq-dsec__dot\" aria-hidden=\"true\"><\/span>Easy questions<\/h2><span class=\"iq-dsec__n\">16<\/span><\/div><div class=\"iq-dsec__bar\" aria-hidden=\"true\"><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">What does &#039;scalability&#039; actually mean in a system design interview, beyond &#039;handling more users&#039;?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Fundamentals<\/span><span class=\"iq-badge iq-badge--easy\">Easy<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>A system is scalable if it can absorb a growing load, more users, more data, more requests, mostly by adding resources rather than by a full redesign. That last part is the piece candidates skip. A system that technically handles 10x traffic but only after a six-month rewrite isn&#8217;t scalable in any interview-relevant sense, it&#8217;s just rebuildable.<\/p>\n<p>Interviewers usually want you to name which dimension is actually growing (requests per second, data volume, number of concurrent connections, geographic spread) before you propose a fix, since the right answer changes depending on which one is the actual bottleneck.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">What&#039;s the actual difference between latency and throughput, and why do interviewers ask about both instead of just one?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Fundamentals<\/span><span class=\"iq-badge iq-badge--easy\">Easy<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>Latency is how long a single request takes, start to finish. Throughput is how many requests a system processes per unit of time. They sound related and often move together, but they can trade off against each other directly.<\/p>\n<p>Batching writes to a database is the clean example: group 200 writes into one transaction and throughput climbs, since you&#8217;re paying the per-transaction overhead once instead of 200 times, but the 199th write in that batch now waits longer than it would have alone, so its individual latency gets worse. An interviewer asking &#8220;what&#8217;s your P99 latency&#8221; right after you&#8217;ve proposed batching is checking whether you noticed that trade-off yourself or whether they have to point it out.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">What&#039;s the difference between a stateless and a stateful service, and why do interviewers keep pushing designs toward stateless?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Fundamentals<\/span><span class=\"iq-badge iq-badge--easy\">Easy<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>A stateless service keeps no client-specific data between requests, everything it needs arrives in the request itself (a token, a session ID it looks up elsewhere) or lives in a shared store like Redis or a database. A stateful service holds something in memory between requests, a session, a cached computation, an open connection, that only that specific instance knows about.<\/p>\n<p>Stateless services are the easier default because any instance can handle any request, which makes horizontal scaling and failover close to free, kill an instance and the load balancer just sends the next request somewhere else. Stateful services need sticky routing or replication of that state, which is exactly the extra complexity interviewers want to hear you flag before you casually add a stateful component to your diagram.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">What does &#039;99.99% uptime&#039; actually mean in minutes, and why do interviewers ask you to do that math out loud?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Fundamentals<\/span><span class=\"iq-badge iq-badge--easy\">Easy<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>99.9% uptime, three nines, allows about 8.76 hours of downtime a year. 99.99%, four nines, allows about 52 minutes. 99.999%, five nines, allows about 5 minutes. Each additional nine is roughly a 10x reduction in allowed downtime, and in production, each additional nine tends to cost a lot more than 10x the engineering effort, which is exactly why almost nobody actually needs five nines and plenty of teams claim to want it anyway.<\/p>\n<p>Interviewers ask you to do this math because it forces a concrete conversation instead of a vague one. &#8220;Highly available&#8221; means nothing on its own. &#8220;This needs to support 52 minutes of downtime a year, split across planned maintenance and incidents&#8221; is a number you can actually design against, and it changes whether a five-minute failover is fine or a genuine problem.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">What are the common load balancing algorithms, and which one do interviewers expect you to default to?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Load Balancing<\/span><span class=\"iq-badge iq-badge--easy\">Easy<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>Round robin cycles through servers in order. Least connections routes to whichever server currently has the fewest active connections. Weighted round robin accounts for servers with different capacities. Consistent hashing (or IP hash) routes the same client to the same server repeatedly, useful when a server holds session state or a warm cache for that client.<\/p>\n<p>Default to least connections when request cost varies a lot, one request might take 5ms and another 500ms, and to round robin when requests are roughly uniform in cost. Naming &#8220;round robin&#8221; as your only answer, without acknowledging it breaks down under uneven request costs, is a common tell that someone learned the term without the reasoning behind it.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">Cache-aside, write-through, and write-back: what&#039;s the actual difference in when data gets written where?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Caching<\/span><span class=\"iq-badge iq-badge--easy\">Easy<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>Cache-aside (also called lazy loading) has the application check the cache first, and on a miss, read from the database and populate the cache for next time. Writes go straight to the database, and the cache entry gets invalidated or updated separately. Write-through writes to the cache and the database at the same time, synchronously, so the cache is never stale but every write pays the cache-write cost too. Write-back writes to the cache immediately and flushes to the database later, asynchronously, which is fast but risks losing data if the cache dies before the flush happens.<\/p>\n<p>Cache-aside is the default most teams reach for. Write-back shows up mostly where write volume is very high and some data loss on a crash is genuinely acceptable, an analytics counter, not a payment record.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">How does a CDN actually work, and what&#039;s the real difference between a push CDN and a pull CDN?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Caching<\/span><span class=\"iq-badge iq-badge--easy\">Easy<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>A CDN is a network of edge servers, geographically distributed, that cache content closer to the end user so a request doesn&#8217;t have to travel all the way back to your origin server for every asset. The first request for a given file at a given edge location is a cache miss that fetches from origin, and every subsequent request from a nearby user hits the edge cache instead.<\/p>\n<p>A pull CDN fetches content from your origin the first time it&#8217;s requested at each edge location, lazily, and caches it there for a TTL. A push CDN has you actively upload content to the CDN ahead of time, which guarantees it&#8217;s already warm everywhere before the first user asks for it, at the cost of you managing what gets pushed and when. Pull is the default for most web traffic since it needs no extra deployment step. Push shows up more for large, infrequently-changing assets, a new app build, a video file, where you&#8217;d rather pay the upload cost once than risk a slow first request in some region nobody&#8217;s tested from.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">What is a write-around cache, and how is it different from cache-aside?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Caching<\/span><span class=\"iq-badge iq-badge--easy\">Easy<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>Write-around writes go straight to the database and skip the cache entirely, the cache only gets populated later, on a read, the same way cache-aside populates it on a miss. The difference from cache-aside is subtle but real: in cache-aside, a write typically also invalidates or updates the existing cache entry for that key. In write-around, a write doesn&#8217;t touch the cache at all, so if that key was already cached, it&#8217;s now silently stale until its TTL expires or something explicitly evicts it.<\/p>\n<p>Write-around is the right shape for data that&#8217;s written often but read rarely right after the write, an audit log, a bulk import job, where populating the cache immediately would just be evicting something else useful for data nobody&#8217;s about to ask for. It&#8217;s the wrong shape for anything read again immediately after being written, a user&#8217;s own comment they expect to see reload instantly, where the first read after write becomes an avoidable cache miss.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">SQL vs NoSQL: what&#039;s the actual decision criteria in 2026?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Databases<\/span><span class=\"iq-badge iq-badge--easy\">Easy<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>&#8220;NoSQL scales better&#8221; was mostly true a decade ago and is a weaker argument today. PostgreSQL crossed 58.2 percent adoption among professional developers in the 2025 Stack Overflow survey, up sharply from the year before, and a lot of that growth is workloads that would&#8217;ve defaulted to MongoDB or DynamoDB five years ago (<a href=\"https:\/\/survey.stackoverflow.co\/2025\/technology\" target=\"_blank\" rel=\"noopener noreferrer\">Stack Overflow Developer Survey, 2025<\/a>). Modern Postgres handles JSONB documents, full-text search, and horizontal read scaling well enough that &#8220;we need flexibility&#8221; isn&#8217;t the slam-dunk case for NoSQL it used to be.<\/p>\n<p>The real decision criteria: does your data have relationships that need joins and multi-row transactional guarantees (pick SQL), or is it naturally key-value or document-shaped with access patterns you can predict in advance and denormalize around (NoSQL gets easier)? My take, and I could be wrong about where this settles: start with Postgres unless you have a specific, named reason not to. Reach for a purpose-built NoSQL store when you actually hit its use case, not because a system design template says every design needs one.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">What is database connection pooling, and what actually breaks if you skip it?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Databases<\/span><span class=\"iq-badge iq-badge--easy\">Easy<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>Opening a new database connection isn&#8217;t cheap, TCP handshake, authentication, session setup, often tens of milliseconds before your query even runs. A connection pool opens a fixed number of connections up front and hands them out to requests as needed, returning each one to the pool when the request finishes instead of closing it.<\/p>\n<p>Skip pooling and open a fresh connection per request, and under any real load two things go wrong at once: every request pays that connection-setup latency on top of the query itself, and the database itself has a hard limit on concurrent connections, Postgres defaults to 100, that a spike in traffic can exhaust in seconds, at which point every new connection attempt just fails outright instead of queueing. A pool with a sensible max size, and a queue for requests that arrive when every connection is checked out, turns a hard failure into a brief wait instead.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">Why introduce a message queue at all instead of having services call each other directly?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Messaging<\/span><span class=\"iq-badge iq-badge--easy\">Easy<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>A direct call couples the caller to the callee&#8217;s availability, if the downstream service is slow or down, the caller is stuck waiting or failing too. A queue decouples them in time: the producer drops a message and moves on, and the consumer processes it whenever it&#8217;s actually ready, which smooths out traffic spikes instead of forwarding them straight into a downstream service that can&#8217;t absorb the burst.<\/p>\n<p>It also buys you retry semantics almost for free, an unprocessed message just sits in the queue until a consumer is available, instead of the caller having to build its own retry-and-backoff logic for every downstream dependency it talks to directly.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">What is a dead-letter queue, and what happens to a message that keeps failing if you don&#039;t have one?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Messaging<\/span><span class=\"iq-badge iq-badge--easy\">Easy<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>A dead-letter queue is a separate holding queue for messages that a consumer has failed to process some number of times in a row, three retries, five, whatever threshold you set. Instead of retrying forever, the message gets moved there for a human or a separate process to inspect later.<\/p>\n<p>Without one, a poison message, malformed data, a bug in the consumer that throws on one specific input, gets retried indefinitely, and depending on how your retry logic is written, it can block every message behind it in the same partition or queue from ever being processed, turning one bad message into a full outage for that entire channel. A dead-letter queue turns that into a single message quietly quarantined for review instead of a stuck pipeline nobody notices until the backlog alerts fire.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">Token bucket vs sliding window: what&#039;s the actual difference in behavior under bursty traffic?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Rate Limiting<\/span><span class=\"iq-badge iq-badge--easy\">Easy<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>A token bucket holds a fixed number of tokens that refill at a steady rate, and every request consumes one token. It allows short bursts up to the bucket&#8217;s capacity even if the sustained rate is lower, which matches how real traffic actually behaves, rarely perfectly smooth.<\/p>\n<p><div class=\"iq-code not-prose\"><div class=\"iq-code__bar\"><span class=\"iq-code__lang\">python<\/span><button class=\"iq-code__copy\" type=\"button\">Copy<\/button><\/div><pre><code class=\"language-python\">\n\nclass TokenBucket:\n\n    def __init__(self, capacity, refill_rate):\n\n        self.capacity = capacity\n\n        self.tokens = capacity\n\n        self.refill_rate = refill_rate  # tokens per second\n\n        self.last_check = time.time()\n    def allow(self):\n\n        now = time.time()\n\n        elapsed = now \u2013 self.last_check\n\n        self.tokens = min(self.capacity, self.tokens + elapsed * self.refill_rate)\n\n        self.last_check = now\n\n        if self.tokens &gt;= 1:\n\n            self.tokens -= 1\n\n            return True\n\n        return False\n<\/code><\/pre><\/div><\/p>\n<p>A fixed window counter, by contrast, resets the count to zero at the start of every window, which lets a client send its full quota right at the boundary of one window and its full quota again right at the start of the next, doubling the effective burst right at the seam. A sliding window log or sliding window counter smooths that out by weighting the previous window&#8217;s count into the current one instead of hard-resetting.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">How do you rate limit per-user instead of per-IP, and what actually breaks if you only rate limit by IP?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Rate Limiting<\/span><span class=\"iq-badge iq-badge--easy\">Easy<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>Rate limiting by IP address alone breaks in both directions. A single user behind a corporate NAT or a mobile carrier&#8217;s shared IP pool can get throttled because hundreds of unrelated people share that same address, punishing users who did nothing wrong. And an attacker with access to a botnet or a pool of proxies can spread requests across thousands of IPs and never trip a per-IP limit at all, defeating the entire point of the limit.<\/p>\n<p>Rate limiting by an authenticated identity, a user ID or an API key, ties the limit to the actual entity you care about regardless of which IP they&#8217;re calling from this minute. The realistic answer most production systems land on is both, a looser per-IP limit as a blunt defense against unauthenticated abuse and basic scraping, and a tighter per-user or per-API-key limit as the real enforcement mechanism for anything behind auth.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">REST, GraphQL, or gRPC: how do you actually decide, beyond personal preference?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">API Design<\/span><span class=\"iq-badge iq-badge--easy\">Easy<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>REST is the default for public APIs because it&#8217;s the most broadly understood, cacheable at the HTTP layer, and easy to debug with nothing more than curl. GraphQL solves a specific problem REST doesn&#8217;t, a client that needs data from several resources in one round trip instead of chaining several REST calls, which matters a lot for mobile clients on a slow connection and less for a server-to-server call on the same internal network. gRPC trades human-readability for speed, binary Protocol Buffers over HTTP\/2, and shows up mostly for internal service-to-service calls where both sides control the client and the schema.<\/p>\n<p>Picking GraphQL for an internal microservice-to-microservice call, or gRPC for a public API third parties need to hit with curl and a browser, is usually a sign someone picked the technology before thinking about who&#8217;s actually calling it.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">How does a distributed system actually detect that a node has failed, and what&#039;s the trade-off in how aggressively it checks?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Resilience<\/span><span class=\"iq-badge iq-badge--easy\">Easy<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>Two related mechanisms usually do this. A heartbeat is the node itself periodically announcing &#8220;I&#8217;m alive&#8221; to some coordinator or its peers. A health check is the reverse, something else actively pings the node and expects a response within a timeout. Miss enough heartbeats, or fail enough health checks in a row, and the node gets marked down and pulled out of rotation.<\/p>\n<p>The trade-off is entirely in the timing. Check too aggressively, a 1-second timeout, mark down after one miss, and a node that&#8217;s just briefly slow, a garbage collection pause, a momentary network blip, gets falsely marked dead and yanked out of rotation for no real reason. Check too loosely, a 60-second timeout, five misses in a row, and a genuinely dead node keeps receiving traffic for minutes before anything routes around it. Most production systems land somewhere around a few seconds per check with 2 to 3 consecutive failures required before marking a node down, fast enough to react, slow enough to not overreact to a single blip.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-dsec iq-dsec--medium\"><div class=\"iq-dsec__row\"><h2 class=\"iq-dsec__h\" id=\"medium\"><span class=\"iq-dsec__dot\" aria-hidden=\"true\"><\/span>Medium questions<\/h2><span class=\"iq-dsec__n\">24<\/span><\/div><div class=\"iq-dsec__bar\" aria-hidden=\"true\"><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">Vertical scaling vs horizontal scaling: when does &#039;just get a bigger box&#039; actually stop working?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Fundamentals<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>Vertical scaling means a bigger machine, more CPU, more RAM, faster disks. Horizontal scaling means more machines sharing the load. Vertical is simpler because you keep every distributed-systems problem, network partitions, partial failures, coordination, out of the picture entirely. It stops working for two reasons: cost stops scaling linearly past a certain instance size, and there&#8217;s a hard ceiling, the biggest machine cloud providers will actually rent you.<\/p>\n<p>Horizontal scaling has no real ceiling but isn&#8217;t free. The moment you have more than one machine, you inherit coordination problems a single box never had, which node owns this write, what happens if two nodes disagree, how do you route a request to the right one. My honest take: default to vertical scaling for as long as it holds. Reaching for horizontal scaling before you actually need it buys you distributed-systems complexity you didn&#8217;t have to pay for yet.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">How do you actually do back-of-envelope capacity estimation in a system design interview, and which numbers are worth memorizing?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Fundamentals<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>The estimation step exists to catch designs that fall apart under real numbers, not to test your mental math. Start from what the interviewer gave you, say, 100 million daily active users, and derive request volume: if each user opens the app twice a day and each open triggers 5 reads, that&#8217;s roughly 1 billion reads a day, or about 11,600 requests per second on average, and 3 to 5 times that at peak. That single number tells you whether a single Postgres instance is even in the conversation or whether you&#8217;re sharding from day one.<\/p>\n<p>A handful of reference numbers are worth actually memorizing instead of deriving each time: an in-memory cache read is around 1 microsecond, a round trip within the same data center is roughly 0.5 milliseconds, a round trip across regions is 100 to 150 milliseconds, and a disk seek on spinning media, rare now, but still asked, is about 10 milliseconds. Jeff Dean&#8217;s &#8220;Numbers Everyone Should Know&#8221; talk from Google is where most of this table originally comes from, and it&#8217;s still the fastest way to sanity-check an estimate that feels off by an order of magnitude.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">What is a single point of failure, and how do you actually remove one instead of just naming it on a whiteboard?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Fundamentals<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>A single point of failure is any component where, if it goes down, the whole system goes down with it, not degrades, fully stops. Candidates are usually good at spotting them (&#8220;the database is a single point of failure&#8221;) and much weaker at proposing a fix that isn&#8217;t just &#8220;add more of it.&#8221;<\/p>\n<p>Removing one actually means three separate decisions: redundancy (more than one instance of the component), a way to detect that an instance failed (health checks, heartbeats), and a way to route around the failed one automatically (a load balancer pulling it from rotation, a replica getting promoted to primary). Naming redundancy alone without the detection and routing pieces is a common half-answer, you can have five database replicas and still go down for two minutes if nothing automatically promotes one when the primary dies.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">Layer 4 vs Layer 7 load balancing, what&#039;s the real difference and why would you pick one over the other?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Load Balancing<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>A Layer 4 load balancer (like AWS&#8217;s Network Load Balancer) makes routing decisions using only IP address and port, it never looks inside the packet. A Layer 7 load balancer (an nginx reverse proxy, AWS&#8217;s Application Load Balancer) terminates the connection and reads the actual HTTP request, path, headers, cookies, before deciding where it goes.<\/p>\n<p>L4 is faster and handles more raw throughput because it&#8217;s doing less work per packet. L7 is what lets you do content-based routing, send \/api\/* to one service and \/static\/* to another, or terminate TLS at the load balancer instead of on every backend server. Pick L4 when you need raw speed and don&#8217;t care about request content. Pick L7 the moment routing needs to depend on anything inside the request itself.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">What&#039;s a sticky session, and why do interviewers treat it as a design smell rather than a clean solution?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Load Balancing<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>A sticky session routes every request from a given client to the same backend server, usually via a cookie or the client&#8217;s IP hashed to a specific instance. It&#8217;s the easy fix for &#8220;my server holds session state in memory,&#8221; and it works, right up until that one server goes down and every user pinned to it gets logged out or loses their in-progress state at once.<\/p>\n<p>The cleaner fix interviewers usually want named is moving session state out of the server entirely, into Redis or a database that every instance can read, so any server can handle any request and stickiness stops being load-bearing. Sticky sessions aren&#8217;t wrong for every case, some real-time systems use them deliberately, but proposing them as your default answer to &#8220;how do you scale this stateful service&#8221; usually reads as not having considered the alternative.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">How does DNS-based load balancing differ from a load balancer sitting in front of your servers?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Load Balancing<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>A traditional load balancer sits inline, every request physically passes through it on the way to a backend. DNS-based load balancing, also called Global Server Load Balancing or GSLB, works a level up: when a client resolves your domain name, the DNS server returns a different IP address depending on the client&#8217;s location, current server health, or a round-robin rotation, and after that the client talks directly to whichever data center it was handed.<\/p>\n<p>The practical use is routing users to the nearest region, a user in Singapore gets an IP in your Asia data center, a user in Ohio gets one in the US data center, cutting cross-region latency before a single request even hits your infrastructure. The catch is DNS caching: clients and resolvers cache a DNS answer for whatever TTL you set, so failing a region out of rotation doesn&#8217;t take effect instantly everywhere, it takes effect as fast as the DNS TTL expires, which is why GSLB failover is measured in minutes, not the milliseconds a normal load balancer manages.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">What is consistent hashing, and what problem does it actually solve that a simple hash-mod-N doesn&#039;t?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Consistent Hashing<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>With plain hash-mod-N, you hash a key and take the result modulo the number of servers to decide which one owns it. It works fine until the number of servers changes. Add or remove one server and N changes, which changes almost every single key&#8217;s assigned server, because the modulo of nearly every key against the new N is different from its modulo against the old N. In a cache, that means almost every cached entry suddenly maps to the wrong server and gets treated as a miss all at once.<\/p>\n<p>Consistent hashing fixes this by hashing both the servers and the keys onto the same fixed ring, 0 to 2^32 typically, and assigning each key to the next server clockwise from its position on the ring. Adding or removing a server only affects the keys between that server and its neighbor on the ring, roughly 1\/N of all keys, not nearly all of them. That&#8217;s the whole reason DynamoDB, Cassandra, and most distributed caches use it instead of modulo arithmetic.<\/p>\n<p><div class=\"iq-code not-prose\"><div class=\"iq-code__bar\"><span class=\"iq-code__lang\">python<\/span><button class=\"iq-code__copy\" type=\"button\">Copy<\/button><\/div><pre><code class=\"language-python\">\n\nimport bisect\n\nimport hashlib\nclass ConsistentHashRing:\n\n    def __init__(self, nodes=None, replicas=100):\n\n        self.replicas = replicas\n\n        self.ring = {}\n\n        self.sorted_keys = []\n\n        for node in nodes or []:\n\n            self.add_node(node)\n    def _hash(self, key):\n\n        return int(hashlib.md5(key.encode()).hexdigest(), 16)\n    def add_node(self, node):\n\n        for i in range(self.replicas):\n\n            h = self._hash(f\u201d{node}:{i}\u201d)\n\n            self.ring[h] = node\n\n            bisect.insort(self.sorted_keys, h)\n    def get_node(self, key):\n\n        h = self._hash(key)\n\n        idx = bisect.bisect(self.sorted_keys, h) % len(self.sorted_keys)\n\n        return self.ring[self.sorted_keys[idx]]\n<\/code><\/pre><\/div><br \/>\n<\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">Which cache eviction policy would you pick and why: LRU, LFU, or TTL-based?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Caching<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>LRU (least recently used) evicts whatever hasn&#8217;t been touched in the longest time, and it&#8217;s the right default for most access patterns because recent access is usually the best cheap predictor of near-future access. LFU (least frequently used) evicts by access count instead of recency, which handles a specific case LRU gets wrong: an item that&#8217;s genuinely popular but happened to go quiet for an hour shouldn&#8217;t get evicted just because something else was touched five minutes ago and only once.<\/p>\n<p>TTL-based eviction solves a different problem: correctness, not popularity. Some data just shouldn&#8217;t live in cache past a certain age, no matter how often it gets read. A stock price cached for 500ms and a user&#8217;s profile picture cached for a day are both TTL decisions. Most production caches actually combine TTL with LRU rather than picking one.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">Phil Karlton&#039;s famous line is that there are only two hard problems in computer science: cache invalidation and naming things. What actually makes cache invalidation hard?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Caching<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>The hard part isn&#8217;t expiring a cache entry after a fixed TTL, that&#8217;s a timer. The hard part is knowing precisely when cached data has become wrong before its TTL runs out, and telling every cache that might be holding a stale copy, not just the one your current request happens to touch.<\/p>\n<p>A user updates their display name. Their profile is cached. Their profile also appears embedded in every comment they&#8217;ve ever posted, in search results, possibly in another service&#8217;s cache entirely that pulled a copy of it last week. Missing even one of those means some users see the old name for minutes or hours after the update, and there&#8217;s no generic fix, the answer depends on whether you can afford eventual consistency there (most display names, yes) or need the invalidation to be immediate and complete (a permissions change, usually no). Most production systems land on a mix: short TTLs for things that are hard to invalidate precisely, and explicit event-driven invalidation, publish a &#8220;user updated&#8221; event, every service holding a cached copy of that user subscribes and evicts, for things that can&#8217;t tolerate staleness.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">Redis vs Memcached: when does the choice actually matter, and when is it just a preference?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Caching<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>For the simplest case, a plain key-value cache of short-lived strings, they&#8217;re close enough that the choice barely matters and picking whichever your team already runs is the right call. The difference shows up once you need more than &#8220;get and set a blob.&#8221;<\/p>\n<p>Redis supports richer data structures natively, sorted sets for a leaderboard, lists for a queue, hashes for a partial object update without re-serializing the whole thing, plus optional persistence to disk and built-in pub-sub. Memcached is deliberately simpler, pure key-value, multithreaded per instance in a way that can matter for raw throughput on a single large box, and nothing to configure around persistence because it doesn&#8217;t have any, a restart empties it. Pick Memcached when you want the simplest possible cache and genuinely don&#8217;t need anything beyond get and set. Pick Redis the moment you need a data structure, a pub-sub channel, or the ability to survive a restart with data intact.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">What does a database index actually do, and why isn&#039;t the answer just &#039;add more indexes&#039;?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Databases<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>An index is a separate, sorted data structure (usually a B-tree) that maps a column&#8217;s values to the rows containing them, so a lookup doesn&#8217;t have to scan every row in the table. It turns a full table scan, O(n), into something closer to O(log n).<\/p>\n<p>The catch: every index has to be updated on every write to the table it&#8217;s attached to. More indexes means slower writes and more disk space, on top of whatever speed you gained on reads. A table with twelve indexes on it pays that cost on every single insert. Interviewers want to hear you name that trade-off unprompted, index the columns you actually filter and join on, not every column that seems like it might someday be useful.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">Sharding vs replication: what problem does each actually solve?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Databases<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>Replication copies the same data onto multiple machines, usually one primary that handles writes and several replicas that serve reads. It solves availability (a replica can take over if the primary dies) and read throughput (spread reads across replicas), but every replica still holds the full dataset, so it doesn&#8217;t help once your data itself is too big for one machine.<\/p>\n<p>Sharding splits the data itself across machines, each shard holds a different slice, by user ID range, by geography, by a hash of some key. It solves the &#8220;too big for one machine&#8221; problem that replication can&#8217;t touch, but it introduces a new one: a query that needs data from two shards, a join across shard boundaries, either becomes expensive or becomes impossible without an application-level workaround. Most systems that reach real scale eventually run both together, sharded for write capacity and dataset size, replicated within each shard for availability and read throughput.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">ACID vs BASE: what&#039;s each one actually optimizing for, and is &#039;pick one&#039; even the right framing?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Databases<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>ACID, atomicity, consistency, isolation, durability, is the guarantee a traditional relational database makes, a transaction either fully happens or fully doesn&#8217;t, and once committed, its effects survive a crash. It optimizes for correctness first, and you pay for that with coordination overhead that limits how far a single ACID-compliant system scales.<\/p>\n<p>BASE, basically available, soft state, eventually consistent, is the looser guarantee most large-scale NoSQL stores make instead, the system stays available even during a partition, and any given read might return a slightly stale value that will converge given enough time. It optimizes for availability and scale first, accepting that &#8220;correct right now&#8221; is sometimes replaced by &#8220;correct soon.&#8221; The honest framing isn&#8217;t ACID vs BASE as a single system-wide choice, it&#8217;s picking per component: a payments table usually wants ACID guarantees even inside an otherwise BASE-leaning architecture, and a view counter on a video usually doesn&#8217;t need ACID at all.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">Optimistic locking vs pessimistic locking: how do you actually decide which one to use for a given write path?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Databases<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>Pessimistic locking grabs a lock on a row before reading it, so no other transaction can touch that row until the first one finishes, guaranteed correctness, at the cost of every concurrent writer to that row waiting in line. Optimistic locking assumes conflicts are rare, reads the row along with a version number, and only checks at write time whether the version has changed since the read, if it has, the write is rejected and the caller retries.<\/p>\n<p>Pessimistic locking makes sense when contention on the same row is actually high, several transactions really do compete for the same seat, the same inventory unit, and retrying under optimistic locking would just mean most attempts fail and retry anyway. Optimistic locking is the better default when conflicts are the exception, most rows aren&#8217;t being fought over concurrently, and paying a lock&#8217;s overhead on every single read to guard against a rare conflict is wasted cost. A seat-booking system for a single popular concert, high contention on specific seats, leans pessimistic. A user editing their own profile, almost never contended, leans optimistic.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">What is replication lag, and what actually breaks for a user when they read from a lagging replica?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Databases<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>Replication lag is the delay between a write landing on the primary and that same write showing up on a given replica. It&#8217;s usually milliseconds, but under load, a slow replica, or a large batch write, it can stretch to seconds or worse, and it&#8217;s never exactly zero in an asynchronously replicated system.<\/p>\n<p>The failure mode users actually notice is read-after-write inconsistency: someone updates their profile picture, the write lands on the primary, and the very next request, a page reload that happens to get routed to a replica that hasn&#8217;t caught up yet, shows the old picture. The fix isn&#8217;t eliminating lag, that&#8217;s usually not realistic without giving up the throughput benefit of read replicas in the first place, it&#8217;s routing a user&#8217;s own immediately-following reads to the primary for a short window after they write, or reading their own writes from a cache that got updated synchronously, while everyone else&#8217;s reads still go to replicas.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">What does the CAP theorem actually say, and why do so many candidates misstate it?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">CAP Theorem<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>During a network partition, a distributed system has to choose between consistency (every read gets the most recent write) and availability (every request gets a response, even a possibly stale one). You can&#8217;t have both once nodes genuinely can&#8217;t talk to each other. Partition tolerance isn&#8217;t really optional. It&#8217;s a property any distributed system running across more than one machine has to accept, because networks partition whether you plan for it or not.<\/p>\n<p>The common misstatement is treating CAP as a permanent, system-wide setting, &#8220;we&#8217;re a CP system&#8221; or &#8220;we&#8217;re an AP system,&#8221; as if the choice applies to every operation forever. In practice the choice is per-operation and often per-feature. A checkout flow inside an e-commerce system might demand consistency for the payment write and tolerate a stale product recommendation on the same page. Talking about CAP at the operation level instead of the whole-system level is usually the signal that separates a strong answer from a memorized one.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">Why does a distributed system need leader election, and what actually happens if two nodes both believe they&#039;re the leader at once?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Consistency<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>A lot of coordination problems get much simpler if exactly one node is responsible for making a decision at any given moment, who gets the next ID, which node&#8217;s write actually lands first. Leader election is the process a cluster uses to agree on which node holds that role, and to notice and replace it if that node dies.<\/p>\n<p>The failure case is split-brain, a network partition convinces one group of nodes their old leader is dead and elects a new one, while the old leader is actually still alive and still thinks it&#8217;s in charge, on the other side of the partition. Now two nodes are both accepting writes as &#8220;the leader&#8221; at the same time, and once the partition heals, those two histories can directly conflict. Real systems avoid this with a quorum requirement built into the election protocol itself, Raft and Paxos both require a majority of nodes to agree before a new leader is confirmed, so a minority partition can never successfully elect a leader of its own, it can only ever fail to elect one until it can see a majority of the cluster again.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">At-least-once vs exactly-once delivery in a queue-based system, what&#039;s the practical difference for how you write your consumer?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Messaging<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>At-least-once means a message might get delivered and processed more than once, so your consumer has to be idempotent, processing the same message twice needs to produce the same end state as processing it once, usually via a unique message ID checked against what&#8217;s already been applied. Exactly-once is a stronger and much harder guarantee to actually deliver end to end, most systems that claim it only guarantee it within their own boundary, not once a side effect (an email, a charge) leaves that boundary.<\/p>\n<p>My honest advice: design for at-least-once and idempotent consumers by default. It&#8217;s more work upfront but it&#8217;s work you actually control, versus relying on an exactly-once guarantee that quietly stops applying the moment your consumer calls an external API. If you want the deeper mechanics of how this works inside a real queue, LastRoundAI&#8217;s <a href=\"\/interview-questions\/kafka\">Kafka interview questions<\/a> page covers idempotent producers and delivery semantics in more depth than fits here.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">How do Kafka partitions and consumer groups actually let you scale message consumption horizontally?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Messaging<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>A Kafka topic is split into partitions, and each partition is an ordered, append-only log. Messages within one partition keep strict order, but there&#8217;s no ordering guarantee across different partitions of the same topic, which is the trade-off that makes parallelism possible at all.<\/p>\n<p>A consumer group is a set of consumers that split up a topic&#8217;s partitions between them, each partition gets consumed by exactly one consumer within that group at a time, so adding more consumers, up to the number of partitions, increases throughput almost linearly. Go past that number and the extra consumers just sit idle, since there aren&#8217;t any more partitions left to assign them, which is exactly why the partition count is a capacity-planning decision made up front, not something you casually bump later without a rebalance.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">What is backpressure, and how does a queue-based system apply it when a consumer can&#039;t keep up with a producer?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Messaging<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>Backpressure is a signal, explicit or implicit, that tells a producer to slow down because whatever&#8217;s downstream can&#8217;t keep pace. Without it, a fast producer just keeps filling a queue that a slower consumer is draining, and the queue grows without bound until it runs out of memory or disk, or messages start getting dropped.<\/p>\n<p>A message queue applies backpressure a few different ways: bounding the queue&#8217;s size and blocking or rejecting new writes once it&#8217;s full, exposing consumer lag as a metric a producer, or an autoscaler watching that metric, can react to, or, in reactive-streams-style systems, having the consumer explicitly request the next batch of items rather than the producer pushing whatever it wants whenever it wants. The honest failure mode to watch for in an interview answer is proposing an unbounded queue as the whole fix for &#8220;producer is faster than consumer,&#8221; an unbounded queue doesn&#8217;t solve the mismatch, it just delays the moment it becomes an outage.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">Leaky bucket vs token bucket: what&#039;s the actual behavioral difference under sustained overload?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Rate Limiting<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>Both use a bucket metaphor and both are named constantly in the same breath, but they enforce different things. A token bucket, covered earlier on this page, allows bursts up to the bucket&#8217;s capacity as long as tokens are available, then falls back to the steady refill rate. A leaky bucket instead enforces a strictly constant output rate no matter how bursty the input is, requests get queued and processed, or &#8220;leaked out,&#8221; at a fixed rate, and if the bucket, the queue, fills up before it can drain, new requests get dropped.<\/p>\n<p>The practical difference is what happens to a burst. Token bucket lets a legitimate burst through immediately, up to its capacity, then throttles. Leaky bucket smooths every burst into the same constant trickle regardless of size, which is exactly right for protecting a downstream system that genuinely can&#8217;t handle spikes at all, a legacy service with no headroom, but overly strict for an API where a client bursting occasionally is normal and fine.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">What makes an API endpoint idempotent, and why do interviewers care so much about it for a POST request?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">API Design<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>An idempotent endpoint produces the same result no matter how many times you call it with the same input, calling it once and calling it five times leave the system in the same state. GET, PUT, and DELETE are idempotent by the HTTP spec&#8217;s own definition. POST isn&#8217;t, by default, which is exactly the problem: a client on a flaky connection that retries a POST after a timeout, without knowing whether the first attempt actually succeeded, risks creating the same resource, or charging the same card, twice.<\/p>\n<p>The standard fix is an idempotency key, the client generates a unique ID per logical operation and sends it in a header, and the server stores which keys it&#8217;s already processed, returning the cached result instead of re-executing the operation for a key it&#8217;s seen before. Stripe&#8217;s payment API is the example most candidates already know without realizing it&#8217;s exactly this pattern.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">What is exponential backoff with jitter, and why does the &#039;jitter&#039; part matter as much as the &#039;exponential&#039; part?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Resilience<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>Exponential backoff means each retry waits longer than the last, 1 second, then 2, then 4, then 8, instead of retrying immediately or at a fixed interval. It exists because retrying instantly against a service that&#8217;s already struggling just adds more load to the exact thing that&#8217;s failing.<\/p>\n<p>Jitter adds randomness on top of that schedule, instead of every client waiting exactly 1, 2, 4, 8 seconds, each client waits somewhere in a range around those values. Without jitter, every client that failed at the same moment, because the service went down at the same moment for everyone, retries at exactly the same moment too, producing a synchronized wave of requests that can look almost like a second outage. AWS&#8217;s own architecture guidance specifically calls out &#8220;full jitter&#8221; as outperforming plain exponential backoff in their load tests, spreading retries out is what actually gives the recovering service room to catch up, not just slowing them down on average.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">What is a circuit breaker, and what are its three states?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Resilience<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>A circuit breaker sits in front of a call to another service and stops making that call entirely once it detects the downstream is failing, instead of letting every request keep timing out against something that&#8217;s already down. It&#8217;s named after the electrical equivalent for the same reason, once tripped, it stops current, requests, from flowing until it&#8217;s safe again.<\/p>\n<p>Closed is the normal state, requests pass through and failures are counted. Once failures cross a threshold, it moves to open, every call fails immediately without even attempting the downstream request, which protects both the caller (no more waiting on doomed timeouts) and the struggling downstream (no more load piling on). After a cooldown period, it moves to half-open, letting a small number of test requests through to check if the downstream has recovered, closing again if they succeed or snapping back to open if they still fail. Skipping half-open and jumping straight from open back to closed is the version that tends to cause a second outage right as the first one was recovering.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-dsec iq-dsec--hard\"><div class=\"iq-dsec__row\"><h2 class=\"iq-dsec__h\" id=\"hard\"><span class=\"iq-dsec__dot\" aria-hidden=\"true\"><\/span>Hard questions<\/h2><span class=\"iq-dsec__n\">7<\/span><\/div><div class=\"iq-dsec__bar\" aria-hidden=\"true\"><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">What are virtual nodes in consistent hashing, and why does almost every real implementation need them?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Consistent Hashing<\/span><span class=\"iq-badge iq-badge--hard\">Hard<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>Plain consistent hashing with one point per physical server on the ring has an uneven-distribution problem: with only a handful of points scattered randomly on a ring of 2^32 positions, some servers end up owning a much bigger arc than others purely by chance, the same way flipping a coin 10 times doesn&#8217;t reliably give you 5 heads. A server that happens to land on a big gap gets hammered with a disproportionate share of the keys.<\/p>\n<p>Virtual nodes fix this by mapping each physical server to many points on the ring instead of one, often 100 to 200 per server, each hashed independently, server-A-1, server-A-2, and so on. With enough virtual nodes, the law of large numbers takes over and each physical server ends up owning roughly its fair share of the ring, regardless of where its individual virtual points happened to land. It also makes rebalancing smoother when a server leaves, its virtual nodes are scattered across the ring, so the keys it was holding get redistributed across many remaining servers instead of dumping the entire load onto whichever single neighbor happened to be next to it.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">How do you avoid the cache stampede problem when a hot key expires and thousands of requests hit the database at once?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Caching<\/span><span class=\"iq-badge iq-badge--hard\">Hard<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>This is what AWS calls the thundering herd problem, and their own caching guidance names request coalescing as the fix: when a cache miss happens, only one request is allowed to actually go fetch from the database, and every other concurrent request for that same key waits on the result of that one fetch instead of triggering its own (<a href=\"https:\/\/aws.amazon.com\/builders-library\/caching-challenges-and-strategies\/\" target=\"_blank\" rel=\"noopener noreferrer\">AWS Builders&#8217; Library, Caching Challenges and Strategies<\/a>).<\/p>\n<p>A second, complementary fix is staggering TTLs, adding a small random jitter to expiration times so thousands of keys cached at the same moment (a deploy, a cold start) don&#8217;t all expire in the same millisecond. Neither trick eliminates the problem alone. Together they turn &#8220;10,000 requests hit Postgres at once&#8221; into &#8220;one request hits Postgres, 9,999 wait 40ms for the answer,&#8221; which is a very different incident to be paged for.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">You need to shard a database that&#039;s already in production with live traffic. What&#039;s the actual risk, and how do you migrate without downtime?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Databases<\/span><span class=\"iq-badge iq-badge--hard\">Hard<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>The sharding logic itself is rarely the actual risk. A naive hash-mod-N scheme reshuffles almost every key the moment you add or remove a shard, the same problem key-based partitioning has in a message queue. Consistent hashing limits the blast radius: adding a shard only moves roughly 1\/N of the keys instead of nearly all of them, since each shard owns a slice of a hash ring rather than a range determined by modulo arithmetic.<\/p>\n<p>For the live migration itself, the pattern that actually avoids downtime is dual writes plus a backfill: start writing every new record to both the old single database and the new sharded setup, backfill historical data into the shards in the background, verify the two are consistent, then cut reads over to the sharded system and stop writing to the old one. Skipping the verification step is where most sharding migrations actually go wrong, not the sharding math itself. I&#8217;ve personally seen more incidents caused by an unverified backfill than by a bad hash function.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">What&#039;s a quorum-based read and write, and how does it let a system tune consistency instead of picking CP or AP outright?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Databases<\/span><span class=\"iq-badge iq-badge--hard\">Hard<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>In a quorum system with N replicas, a write is only considered successful once it&#8217;s acknowledged by W replicas, and a read is only considered successful once it&#8217;s confirmed by R replicas. As long as W plus R is greater than N, every read is guaranteed to overlap with at least one replica that has the latest write, which gives you strong consistency without requiring every single replica to be involved in every operation.<\/p>\n<p>This is the mechanism Amazon&#8217;s Dynamo paper popularized in 2007 and that systems like Cassandra still use today. It&#8217;s tunable per operation: a system running with N=3 can do W=1, R=1 for fast, loosely consistent reads and writes when that&#8217;s acceptable, or W=2, R=2, satisfying W+R greater than N=3, when it needs the stronger guarantee, without redeploying anything, just changing the consistency level requested on that specific query. Most candidates know quorum exists. Fewer can explain that the actual guarantee comes from the arithmetic of W+R>N, not from quorum being a synonym for &#8220;majority vote.&#8221;<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">Strong consistency, eventual consistency, and causal consistency: what does each actually guarantee, and which would you pick for a shopping cart vs a bank balance?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Consistency<\/span><span class=\"iq-badge iq-badge--hard\">Hard<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>Strong consistency guarantees every read sees the most recent write, immediately, everywhere, which usually means coordinating across nodes on every operation and paying for it in latency. Eventual consistency drops that guarantee: replicas will converge to the same value eventually, but a read right after a write might return stale data. Causal consistency sits between them, it guarantees that causally related operations (a reply shown after the comment it&#8217;s replying to) appear in the right order everywhere, without requiring every unrelated operation to coordinate globally.<\/p>\n<p>A bank balance needs something close to strong consistency, or at least strict serializability on the specific account row, since two concurrent withdrawals both reading a stale balance is a real money bug, not a UX inconvenience. A shopping cart usually tolerates eventual consistency fine, if an item added on your phone takes 200ms to show up on your laptop&#8217;s open tab, nobody notices or cares. The mistake I see most often in mock interviews is candidates defaulting to strong consistency everywhere because it sounds safer, without noticing the latency and availability cost that choice carries into every single request.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">What is the PACELC theorem, and why do some interviewers consider it more complete than CAP alone?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">CAP Theorem<\/span><span class=\"iq-badge iq-badge--hard\">Hard<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>CAP only describes what happens during a network partition. Daniel Abadi pointed out in a 2012 paper that this leaves out the more common case, when there&#8217;s no partition at all, and a system still has to make a trade-off between latency and consistency on every single operation, because coordinating across replicas to guarantee consistency simply takes time even when every node can reach every other node fine.<\/p>\n<p>PACELC extends CAP into: if there&#8217;s a Partition, choose between Availability and Consistency, that&#8217;s the original CAP, Else, no partition, choose between Latency and Consistency. A system can be AP during a partition and still choose lower latency over strict consistency the rest of the time, which is a decision CAP alone doesn&#8217;t even have a vocabulary for. Naming PACELC unprompted, when asked a CAP question, is usually enough on its own to signal you&#8217;ve read past the one-paragraph explanation most people stop at.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">How do you enforce a rate limit consistently across multiple servers instead of just per-instance?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Rate Limiting<\/span><span class=\"iq-badge iq-badge--hard\">Hard<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>Per-instance rate limiting, counting requests in memory on each server, breaks the moment you have more than one server behind a load balancer. Ten servers each independently enforcing &#8220;100 requests per minute&#8221; gives an effective limit of 1,000, not 100. The fix is a shared, centralized counter, usually Redis, with an atomic increment-and-check operation (INCR plus EXPIRE, or a Lua script that does both atomically to avoid a race between the two calls).<\/p>\n<p>The trade-off you&#8217;re accepting: every rate-limited request now makes a network call to Redis before it can proceed, which adds latency and makes Redis itself a dependency your entire API&#8217;s availability now rests on. Some systems accept slightly loose enforcement, a local in-memory count with periodic sync to the shared store, to avoid that dependency on the hot path, trading strict accuracy for lower latency and one less thing that can take the whole API down if it&#8217;s unavailable.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-dsec iq-dsec--scenario\"><div class=\"iq-dsec__row\"><h2 class=\"iq-dsec__h\" id=\"scenario\"><span class=\"iq-dsec__dot\" aria-hidden=\"true\"><\/span>Real-time scenario questions<\/h2><span class=\"iq-dsec__n\">8<\/span><\/div><div class=\"iq-dsec__bar\" aria-hidden=\"true\"><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">Design a URL shortener like bit.ly. Walk through your approach.<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">System Design<\/span><span class=\"iq-badge iq-badge--hard\">Hard<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>Requirements: given a long URL, return a short one, and given the short one, redirect to the original, fast, at high read volume, since reads (redirects) massively outnumber writes (new short URLs created).<\/p>\n<p>Components: a write path that takes a long URL and generates a short code, a key-value store mapping short codes to long URLs, and a read path that looks up the code and issues an HTTP redirect. Data model: a single table or key-value store, short_code as the key, long_url plus creation timestamp and optional expiry as the value. Generate the short code with a base62-encoded counter (or a hash of the URL, truncated and checked for collisions) rather than a random string, since a counter guarantees no collisions without a retry loop.<\/p>\n<p>Scale and bottlenecks: at real traffic, redirects are the hot path, cache the short-code-to-long-URL mapping aggressively (it almost never changes after creation) and put a CDN or edge cache in front of the redirect endpoint so most reads never touch your database at all. Trade-offs: a counter-based approach makes short codes sequential and guessable, which is fine for most use cases but a real problem if URLs need to stay unguessable, in which case you add a random component and accept the small chance of needing a collision retry.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">Design a social media news feed like Twitter&#039;s timeline. How do you handle a celebrity account with 50 million followers?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">System Design<\/span><span class=\"iq-badge iq-badge--hard\">Hard<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>Requirements: a user&#8217;s feed shows recent posts from accounts they follow, roughly chronological, generated fast enough that opening the app doesn&#8217;t feel like waiting on a database query.<\/p>\n<p>The two textbook approaches are fan-out-on-write (when someone posts, immediately push that post into every follower&#8217;s precomputed feed) and fan-out-on-read (store posts once, and build each user&#8217;s feed by querying everyone they follow at read time). Fan-out-on-write makes reads nearly instant, you&#8217;re just reading a precomputed list, but a celebrity with 50 million followers turns one post into 50 million write operations, which can take minutes and hammer your write path.<\/p>\n<p>The real answer almost every large platform actually ships is a hybrid: fan-out-on-write for accounts under some follower threshold, and fan-out-on-read for the small number of accounts above it, merging both sources at read time for any user who follows a mix of regular and celebrity accounts. Interviewers want to see you name that hybrid unprompted, instead of picking one extreme and defending it as if the other never comes up in practice.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">Design a one-on-one and group chat system like WhatsApp. What&#039;s the hardest part?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">System Design<\/span><span class=\"iq-badge iq-badge--hard\">Hard<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>Requirements: real-time message delivery between users, messages persist even if the recipient&#8217;s offline, delivery and read receipts, and group chats fan a single message out to multiple recipients.<\/p>\n<p>Components: a persistent connection layer (WebSockets, since the server needs to push new messages to a client the instant they arrive, instead of waiting for the client to ask), a message service that writes every message durably before attempting delivery, and a presence service tracking who&#8217;s currently connected to which server. Data model: messages keyed by conversation ID plus a monotonic sequence number, so ordering within a conversation is well-defined even if messages arrive at the server slightly out of order due to network jitter.<\/p>\n<p>The happy path is the easy part. What actually separates a working design from a broken one is the offline case: the message has to persist in a queue or store tied to that user, and get delivered, with ordering preserved, the moment they reconnect, without duplicating anything already delivered before they went offline. Group chats add a second hard problem on top, delivering to 200 members means 200 separate delivery attempts, and you need per-recipient delivery tracking so one member&#8217;s flaky connection doesn&#8217;t block or duplicate delivery to the other 199.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">Design a rate limiter for a public API gateway serving multiple clients with different tiers.<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">System Design<\/span><span class=\"iq-badge iq-badge--hard\">Hard<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>Requirements: enforce different limits per client tier (free, paid, enterprise), enforce them consistently across every gateway instance, and reject over-limit requests fast, without adding meaningful latency to requests that are within their limit.<\/p>\n<p>This is the token bucket and centralized-Redis pattern from earlier on this page, applied end to end: the gateway checks a client&#8217;s remaining tokens in Redis before forwarding the request, keyed by client ID with a tier-specific bucket capacity and refill rate loaded from a config service, not hardcoded. Components: the gateway itself, a Redis cluster holding the token buckets, and a config service or table mapping client ID to tier.<\/p>\n<p>Scale and bottlenecks: Redis has to handle a check-and-decrement on nearly every request across the whole platform, so that single Redis cluster becomes both a hot path and a single point of failure if it isn&#8217;t itself replicated. Trade-offs: reject-with-429 the instant Redis is unreachable (fail closed, protects your backend but blocks legitimate traffic during a Redis outage) versus allow-through when Redis is unreachable (fail open, protects availability but leaves you with zero rate limiting exactly when something&#8217;s already going wrong). Most production systems I&#8217;ve seen fail open for a short grace period and alert loudly, rather than picking one policy forever.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">Design a notification system that sends push, email, and SMS, and needs to survive a 30-minute outage of one provider.<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">System Design<\/span><span class=\"iq-badge iq-badge--hard\">Hard<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>Requirements: accept a notification request from any internal service, deliver it through the right channel (push, email, SMS), retry on transient failure, and never double-send if a retry happens after the first attempt actually succeeded.<\/p>\n<p>Components: an ingestion API that internal services call, a queue per channel (or one queue with channel as a message attribute), a set of channel-specific workers that each talk to one external provider (APNs or FCM for push, an email provider, an SMS provider like Twilio), and a delivery log tracking status per notification ID. Idempotency matters here as much as it did in the API design section, every notification needs a unique ID the worker checks before sending, so a retried message doesn&#8217;t become a duplicate text on someone&#8217;s phone.<\/p>\n<p>Surviving a 30-minute outage of one provider means the worker for that channel needs a dead-letter queue or a retry-with-backoff loop that doesn&#8217;t block the other channels, a down email provider shouldn&#8217;t stall push notifications, and a circuit breaker that stops hammering a provider that&#8217;s clearly down, backing off and retrying on a schedule instead of retrying every failed send instantly and making the outage worse for everyone still trying to use that provider.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">Design a distributed cache like Redis that can hold more data than fits on one machine.<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">System Design<\/span><span class=\"iq-badge iq-badge--hard\">Hard<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>Requirements: sub-millisecond get and set operations, data sharded across multiple nodes since the whole dataset doesn&#8217;t fit on one, and some tolerance for a node dying without losing everything cached on it.<\/p>\n<p>Components: consistent hashing (covered earlier on this page) to decide which node owns which key, so adding or removing a node only reshuffles a small fraction of the keyspace instead of nearly all of it, a client-side or proxy-based router that hashes the key and forwards the request to the right node, and, per node, an in-memory hash table as the actual store. Data model is closer to a filesystem than a database: no schema, just a key, a value (string, list, hash, whatever structure the cache supports), and an optional TTL.<\/p>\n<p>Scale and bottlenecks: a single popular key, a hot key, can overwhelm the one node that owns it under consistent hashing even if every other node is idle, since consistent hashing distributes keys evenly but says nothing about access frequency per key. The standard fix is detecting hot keys and replicating just that key&#8217;s value across multiple nodes, or caching it an extra layer up, in the application process itself, rather than assuming even key distribution means even load. Trade-offs: replicate each node for durability (survives a node dying, costs double the memory) or accept that a node dying loses whatever it was holding and let it repopulate from the source of truth on the next read, which is often the right call for a cache specifically, since a cache miss is supposed to be recoverable by definition.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">Design a system that generates unique IDs across many machines, without a central database or coordination on every request.<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">System Design<\/span><span class=\"iq-badge iq-badge--hard\">Hard<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>Requirements: every generated ID must be unique across the entire distributed system, IDs should ideally be roughly sortable by creation time, and generation has to be fast, no round trip to a central coordinator for every single ID.<\/p>\n<p>The approach Twitter&#8217;s engineering team introduced in 2010 under the name Snowflake, and that plenty of other companies have since copied under different names, splits a 64-bit integer into three parts: a timestamp (milliseconds since some fixed epoch), a machine or worker ID (assigned once per node, so no two machines share one), and a per-millisecond sequence number that increments if the same machine generates more than one ID in the same millisecond. Concatenate the three and you get an ID that&#8217;s globally unique without any machine ever talking to another one to generate it, since uniqueness comes from the combination of machine ID and local sequence, not from checking against every other machine&#8217;s history.<\/p>\n<p>The trade-off worth naming: this scheme depends on clock accuracy. If a machine&#8217;s clock jumps backward, from an NTP correction, say, it can generate an ID that collides with or predates one it already issued. Production implementations guard against this by refusing to generate new IDs for a short window if the local clock is detected moving backward, rather than silently risking a collision.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">Design a distributed key-value store like DynamoDB that stays available even when some nodes are unreachable.<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">System Design<\/span><span class=\"iq-badge iq-badge--hard\">Hard<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>Requirements: simple get and put operations by key, the system keeps accepting writes even during a network partition (this is explicitly an AP system, not a CP one), and it tolerates individual node failures without losing data.<\/p>\n<p>Components: consistent hashing with virtual nodes to spread keys evenly across the cluster and keep rebalancing cheap when nodes join or leave, quorum-based reads and writes (from earlier on this page) so you can tune the consistency-versus-latency trade-off per operation instead of picking one setting for the whole system, and a gossip protocol, nodes periodically exchange state with a few random peers, so every node eventually learns the health and membership of the whole cluster without a central coordinator that would itself become a single point of failure.<\/p>\n<p>The hard part specifically is conflict resolution: because writes are accepted even during a partition, two different replicas can end up with two different values for the same key once the partition heals. Amazon&#8217;s actual Dynamo paper handles this with vector clocks, tracking which replica&#8217;s history a given value descended from, and when automatic resolution isn&#8217;t possible (two genuinely concurrent, conflicting writes), pushing the conflict up to the application to resolve, the classic example being a shopping cart where the resolution is just merging both versions&#8217; items together rather than picking one and discarding the other.<\/p>\n<p><\/div><\/div><\/div>\n<h2>How to prepare for a system design interview in 2026<\/h2>\n<p>Skip another pass through static architecture diagrams. Build something small with two or three real moving parts, an API behind a load balancer, a Redis cache in front of Postgres, a queue feeding a worker, then break it on purpose. Kill the cache and watch what happens to your database&#8217;s load. Stop the worker and watch the queue back up. Reading about a stale cache is nothing like watching your own API&#8217;s latency spike because you didn&#8217;t set a TTL.<\/p>\n<p>Across mock interview sessions run through LastRoundAI tagged systems or backend, the CAP theorem and consistency-model question trips up more candidates than the design scenarios do, even though the design scenarios get most of the prep time. My guess is that everyone drills the URL shortener and the news feed because they&#8217;re concrete and fun to draw, and skips consistency models because they read as abstract theory, right up until an interviewer asks what happens to a shopping cart write during a network partition. I don&#8217;t have a clean percentage to put on that pattern, only that it comes up often enough in review to flag here.<\/p>\n<p>If you want the format for actually running a 45-minute design round, clarifying requirements first, budgeting your time across sections, knowing when to go deep versus when to move on, that&#8217;s a different problem than knowing the material, and LastRoundAI&#8217;s <a href=\"\/blog\/system-design-interview-guide\">system design interview guide<\/a> covers it in more depth than a question bank like this one can.<\/p>\n<h2>Explain your design before an interviewer finds the hole in it<\/h2>\n<p>Reading an answer to a sharding question is not the same as defending it live once an interviewer changes one number on you, ten times the traffic, half the budget, one fewer server. <a href=\"\/products\/mock-interviews\">LastRoundAI&#8217;s mock interview mode<\/a> runs system design rounds with follow-up questions that adapt to what you actually said instead of a fixed script, and the free plan includes 15 credits a month that reset monthly rather than piling up. Starter is $19\/mo if fifteen sessions a month isn&#8217;t enough runway.<\/p>\n<p>If the harder part of the job hunt right now is finding enough backend, platform, or systems roles rather than passing the interview once you land one, <a href=\"\/products\/auto-apply\">Auto-Apply<\/a> queues tailored applications for your review, 10 a month on the free plan, up to 400 a month on the Ultimate plan, and nothing goes out until you approve it.<\/p>\n<p>Questions about either product go to contact@lastroundai.com. That&#8217;s the only inbox we check.<\/p>\n<div class=\"iq-sources not-prose\"><div class=\"iq-sources__title\">Sources &amp; further reading<\/div><ul class=\"iq-sources__list\"><li><a href=\"https:\/\/dl.acm.org\/doi\/10.1145\/564585.564601\" target=\"_blank\" rel=\"nofollow noopener\">Gilbert and Lynch, ACM SIGACT News (2002): Brewer&#039;s Conjecture and the Feasibility of Consistent, Available, Partition-Tolerant Web Services<\/a><\/li><li><a href=\"https:\/\/aws.amazon.com\/builders-library\/caching-challenges-and-strategies\/\" target=\"_blank\" rel=\"nofollow noopener\">AWS Builders&#039; Library: Caching Challenges and Strategies<\/a><\/li><li><a href=\"https:\/\/survey.stackoverflow.co\/2025\/technology\" target=\"_blank\" rel=\"nofollow noopener\">Stack Overflow Developer Survey 2025<\/a><\/li><\/ul><\/div>\n","protected":false},"excerpt":{"rendered":"<p>Eric Brewer sketched what became the CAP theorem as a conjecture during a keynote at the 2000 ACM Symposium on Principles of Distributed Computing. He didn&#8217;t prove it. Two years later, MIT researchers Seth Gilbert and Nancy Lynch did, formalizing consistency, availability, and partition tolerance into an actual theorem and showing mathematically that you can&#8217;t&#8230;<\/p>\n","protected":false},"author":7,"featured_media":1710,"comment_status":"open","ping_status":"closed","template":"","meta":{"_kad_post_transparent":"","_kad_post_title":"","_kad_post_layout":"","_kad_post_sidebar_id":"","_kad_post_content_style":"","_kad_post_vertical_padding":"","_kad_post_feature":"","_kad_post_feature_position":"","_kad_post_header":false,"_kad_post_footer":false,"_kad_post_classname":"","footnotes":""},"tags":[],"class_list":["post-1271","iq","type-iq","status-publish","has-post-thumbnail","hentry"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.8 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>System Design Interview Questions (2026): Must-Know Q&amp;A | LastRoundAI<\/title>\n<meta name=\"description\" content=\"System design interview questions for 2026: scalability, caching, CAP theorem, databases, and 5 worked designs like URL shorteners and news feeds.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/lastroundai.com\/interview-questions\/system-design\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"System Design Interview Questions (2026): Must-Know Q&amp;A | LastRoundAI\" \/>\n<meta property=\"og:description\" content=\"System design interview questions for 2026: scalability, caching, CAP theorem, databases, and 5 worked designs like URL shorteners and news feeds.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/lastroundai.com\/interview-questions\/system-design\" \/>\n<meta property=\"og:site_name\" content=\"LastRound AI\" \/>\n<meta property=\"article:modified_time\" content=\"2026-07-19T03:53:29+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/lastroundai.com\/blog\/wp-content\/uploads\/2026\/07\/iq-system-design-og.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"630\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"24 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/lastroundai.com\\\/interview-questions\\\/system-design\",\"url\":\"https:\\\/\\\/lastroundai.com\\\/interview-questions\\\/system-design\",\"name\":\"System Design Interview Questions (2026): Must-Know Q&A | LastRoundAI\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/lastroundai.com\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/lastroundai.com\\\/interview-questions\\\/system-design#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/lastroundai.com\\\/interview-questions\\\/system-design#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/lastroundai.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/iq-system-design-og.png\",\"datePublished\":\"2026-07-11T18:19:53+00:00\",\"dateModified\":\"2026-07-19T03:53:29+00:00\",\"description\":\"System design interview questions for 2026: scalability, caching, CAP theorem, databases, and 5 worked designs like URL shorteners and news feeds.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/lastroundai.com\\\/interview-questions\\\/system-design#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/lastroundai.com\\\/interview-questions\\\/system-design\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/lastroundai.com\\\/interview-questions\\\/system-design#primaryimage\",\"url\":\"https:\\\/\\\/lastroundai.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/iq-system-design-og.png\",\"contentUrl\":\"https:\\\/\\\/lastroundai.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/iq-system-design-og.png\",\"width\":1200,\"height\":630,\"caption\":\"System Design interview questions \u2014 LastRoundAI\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/lastroundai.com\\\/interview-questions\\\/system-design#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/lastroundai.com\\\/blog\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Interview Questions\",\"item\":\"https:\\\/\\\/lastroundai.com\\\/interview-questions\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"System Design Interview Questions (2026): Must-Know Q&#038;A\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/lastroundai.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/lastroundai.com\\\/blog\\\/\",\"name\":\"LastRound AI\",\"description\":\"Interview Assistant prep, tech careers and AI tools\",\"publisher\":{\"@id\":\"https:\\\/\\\/lastroundai.com\\\/blog\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/lastroundai.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/lastroundai.com\\\/blog\\\/#organization\",\"name\":\"LastRound AI\",\"url\":\"https:\\\/\\\/lastroundai.com\\\/blog\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/lastroundai.com\\\/blog\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/lastroundai.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/06\\\/lastroundai-transprant-logo-optimized-BxEo2Wtq.png\",\"contentUrl\":\"https:\\\/\\\/lastroundai.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/06\\\/lastroundai-transprant-logo-optimized-BxEo2Wtq.png\",\"width\":400,\"height\":400,\"caption\":\"LastRound AI\"},\"image\":{\"@id\":\"https:\\\/\\\/lastroundai.com\\\/blog\\\/#\\\/schema\\\/logo\\\/image\\\/\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"System Design Interview Questions (2026): Must-Know Q&A | LastRoundAI","description":"System design interview questions for 2026: scalability, caching, CAP theorem, databases, and 5 worked designs like URL shorteners and news feeds.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/lastroundai.com\/interview-questions\/system-design","og_locale":"en_US","og_type":"article","og_title":"System Design Interview Questions (2026): Must-Know Q&A | LastRoundAI","og_description":"System design interview questions for 2026: scalability, caching, CAP theorem, databases, and 5 worked designs like URL shorteners and news feeds.","og_url":"https:\/\/lastroundai.com\/interview-questions\/system-design","og_site_name":"LastRound AI","article_modified_time":"2026-07-19T03:53:29+00:00","og_image":[{"width":1200,"height":630,"url":"https:\/\/lastroundai.com\/blog\/wp-content\/uploads\/2026\/07\/iq-system-design-og.png","type":"image\/png"}],"twitter_card":"summary_large_image","twitter_misc":{"Est. reading time":"24 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/lastroundai.com\/interview-questions\/system-design","url":"https:\/\/lastroundai.com\/interview-questions\/system-design","name":"System Design Interview Questions (2026): Must-Know Q&A | LastRoundAI","isPartOf":{"@id":"https:\/\/lastroundai.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/lastroundai.com\/interview-questions\/system-design#primaryimage"},"image":{"@id":"https:\/\/lastroundai.com\/interview-questions\/system-design#primaryimage"},"thumbnailUrl":"https:\/\/lastroundai.com\/blog\/wp-content\/uploads\/2026\/07\/iq-system-design-og.png","datePublished":"2026-07-11T18:19:53+00:00","dateModified":"2026-07-19T03:53:29+00:00","description":"System design interview questions for 2026: scalability, caching, CAP theorem, databases, and 5 worked designs like URL shorteners and news feeds.","breadcrumb":{"@id":"https:\/\/lastroundai.com\/interview-questions\/system-design#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/lastroundai.com\/interview-questions\/system-design"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/lastroundai.com\/interview-questions\/system-design#primaryimage","url":"https:\/\/lastroundai.com\/blog\/wp-content\/uploads\/2026\/07\/iq-system-design-og.png","contentUrl":"https:\/\/lastroundai.com\/blog\/wp-content\/uploads\/2026\/07\/iq-system-design-og.png","width":1200,"height":630,"caption":"System Design interview questions \u2014 LastRoundAI"},{"@type":"BreadcrumbList","@id":"https:\/\/lastroundai.com\/interview-questions\/system-design#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/lastroundai.com\/blog"},{"@type":"ListItem","position":2,"name":"Interview Questions","item":"https:\/\/lastroundai.com\/interview-questions"},{"@type":"ListItem","position":3,"name":"System Design Interview Questions (2026): Must-Know Q&#038;A"}]},{"@type":"WebSite","@id":"https:\/\/lastroundai.com\/blog\/#website","url":"https:\/\/lastroundai.com\/blog\/","name":"LastRound AI","description":"Interview Assistant prep, tech careers and AI tools","publisher":{"@id":"https:\/\/lastroundai.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/lastroundai.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/lastroundai.com\/blog\/#organization","name":"LastRound AI","url":"https:\/\/lastroundai.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/lastroundai.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/lastroundai.com\/blog\/wp-content\/uploads\/2026\/06\/lastroundai-transprant-logo-optimized-BxEo2Wtq.png","contentUrl":"https:\/\/lastroundai.com\/blog\/wp-content\/uploads\/2026\/06\/lastroundai-transprant-logo-optimized-BxEo2Wtq.png","width":400,"height":400,"caption":"LastRound AI"},"image":{"@id":"https:\/\/lastroundai.com\/blog\/#\/schema\/logo\/image\/"}}]}},"_links":{"self":[{"href":"https:\/\/lastroundai.com\/blog\/wp-json\/wp\/v2\/iq\/1271","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/lastroundai.com\/blog\/wp-json\/wp\/v2\/iq"}],"about":[{"href":"https:\/\/lastroundai.com\/blog\/wp-json\/wp\/v2\/types\/iq"}],"author":[{"embeddable":true,"href":"https:\/\/lastroundai.com\/blog\/wp-json\/wp\/v2\/users\/7"}],"replies":[{"embeddable":true,"href":"https:\/\/lastroundai.com\/blog\/wp-json\/wp\/v2\/comments?post=1271"}],"version-history":[{"count":3,"href":"https:\/\/lastroundai.com\/blog\/wp-json\/wp\/v2\/iq\/1271\/revisions"}],"predecessor-version":[{"id":1524,"href":"https:\/\/lastroundai.com\/blog\/wp-json\/wp\/v2\/iq\/1271\/revisions\/1524"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/lastroundai.com\/blog\/wp-json\/wp\/v2\/media\/1710"}],"wp:attachment":[{"href":"https:\/\/lastroundai.com\/blog\/wp-json\/wp\/v2\/media?parent=1271"}],"wp:term":[{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/lastroundai.com\/blog\/wp-json\/wp\/v2\/tags?post=1271"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}