<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[DevInterviewMaster Blog]]></title><description><![CDATA[DevInterviewMaster Blog]]></description><link>https://devinterviewmasterhq.hashnode.dev</link><image><url>https://cdn.hashnode.com/uploads/logos/6adc59f15860f38338b0e5a1/dfba4d96-51db-4dd7-8444-c7442183eeda.png</url><title>DevInterviewMaster Blog</title><link>https://devinterviewmasterhq.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Tue, 03 Nov 2026 02:49:32 GMT</lastBuildDate><atom:link href="https://devinterviewmasterhq.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Designing a Rate Limiter for SDE-2 Interviews: Token Bucket vs Sliding Window, With the Trade-offs Spelled Out]]></title><description><![CDATA[Four algorithms, where the counter lives, what happens when Redis is slow, and how to talk through it all in 35 minutes]]></description><link>https://devinterviewmasterhq.hashnode.dev/designing-a-rate-limiter-for-sde-2-interviews-token-bucket-vs-sliding-window-with-the-trade-offs-spelled-out</link><guid isPermaLink="true">https://devinterviewmasterhq.hashnode.dev/designing-a-rate-limiter-for-sde-2-interviews-token-bucket-vs-sliding-window-with-the-trade-offs-spelled-out</guid><category><![CDATA[System Design]]></category><category><![CDATA[interview preparation]]></category><category><![CDATA[backend]]></category><category><![CDATA[distributed systems]]></category><category><![CDATA[Redis]]></category><dc:creator><![CDATA[DevInterviewMaster]]></dc:creator><pubDate>Sat, 31 Oct 2026 11:29:05 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6adc59f15860f38338b0e5a1/1742501f-e1fb-4e51-a75b-8fc13d9fb1f6.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>"Design a rate limiter" is one of the most common system design questions in SDE-2 loops, and it's easy to underestimate. Almost everyone can say "use a token bucket". Fewer people can explain why they chose it over a sliding window, where the counters live when you have twenty API servers, or what the API should do when the rate-limit store itself is down.</p>
<p>That second half is what the interviewer is actually grading. Here's how a strong candidate walks through it: requirements, then the algorithm with honest trade-offs, then the distributed parts that turn a toy into a real design.</p>
<h2>Step 1: Pin down the requirements (3-5 minutes)</h2>
<p>Don't start drawing. Ask questions that change the design:</p>
<ol>
<li><strong>Client-side or server-side?</strong> Assume server-side. You can't trust clients to throttle themselves.</li>
<li><strong>Throttle by what?</strong> User ID or API key for logged-in traffic, IP as the fallback. Many users can share one office or mobile-carrier IP.</li>
<li><strong>What are the rules?</strong> For example: 100 requests per minute per user, plus a stricter 5 login attempts per 15 minutes per account. Rules differ per endpoint, so they should be configuration, not code.</li>
<li><strong>How strict?</strong> Is it acceptable if a user occasionally gets 105 requests through instead of 100? For most APIs, yes. For OTP sends or payment attempts, maybe not. This one answer decides a lot.</li>
<li><strong>Where does it run?</strong> As middleware in each service, or centrally at the API gateway?</li>
<li><strong>Scale?</strong> Rough users, peak requests per second and number of API servers.</li>
</ol>
<p>Then say your assumptions out loud: "Distributed limiter at the gateway, keyed by user ID with IP as fallback, rules stored in config, a small amount of over-admission is acceptable for normal endpoints but not for login and OTP."</p>
<h2>Step 2: Pick an algorithm, and say why</h2>
<p>There are four algorithms worth knowing. You don't need to implement all of them, but you should be able to compare them in one breath.</p>
<h3>Fixed window counter</h3>
<p>Split time into fixed windows (say, each calendar minute). Keep one counter per user per window. Increment on each request; reject once it passes the limit.</p>
<ul>
<li><strong>Good:</strong> dead simple. One integer per user. In Redis it's an <code>INCR</code> plus an <code>EXPIRE</code>.</li>
<li><strong>Bad:</strong> the boundary problem. With a limit of 100 per minute, a user can send 100 requests at 10:00:59 and another 100 at 10:01:00. That's 200 requests in about one second, and both windows look fine.</li>
</ul>
<h3>Sliding window log</h3>
<p>Store a timestamp for every request. On each new request, drop timestamps older than the window, count what's left, and allow the request only if the count is under the limit.</p>
<ul>
<li><strong>Good:</strong> exact. No boundary problem at all.</li>
<li><strong>Bad:</strong> memory. A limit of 1,000 requests per hour means up to 1,000 timestamps per user. Multiply by a large user base and it gets expensive. In Redis this is usually a sorted set, so each check costs a removal plus a count instead of one increment.</li>
</ul>
<h3>Sliding window counter</h3>
<p>The practical middle ground. Keep the counter for the current fixed window and the previous one, and estimate the rolling count with a weighted sum:</p>
<pre><code>estimated = current_count + previous_count × (fraction of previous window still inside the rolling window)
</code></pre>
<p>Say the limit is 100 per minute, it's 15 seconds into the current minute, the previous minute had 80 requests and the current one has 30. Then 45 seconds of the previous window still fall inside the rolling 60 seconds, so the estimate is 30 + 80 × 0.75 = 90. The request is allowed.</p>
<ul>
<li><strong>Good:</strong> two counters per user, and it smooths out the boundary spike.</li>
<li><strong>Bad:</strong> it's an approximation. It assumes requests in the previous window were evenly spread. Fine for most APIs, not something to use where the limit is a hard contract.</li>
</ul>
<h3>Token bucket</h3>
<p>Each user has a bucket with a capacity (say 10 tokens) that refills at a steady rate (say 1 token per second). Each request takes one token. No token, no request.</p>
<p>You don't need a background job to refill buckets. Store two values, <code>tokens</code> and <code>last_refill_time</code>, and compute the refill lazily when a request arrives:</p>
<pre><code>now = current_time()
tokens = min(capacity, tokens + (now - last_refill_time) × refill_rate)
last_refill_time = now
if tokens &gt;= 1:
    tokens -= 1
    allow()
else:
    reject()
</code></pre>
<ul>
<li><strong>Good:</strong> allows short bursts while enforcing a long-run average. That matches real behaviour: a page load fires several calls at once, then nothing. Small state.</li>
<li><strong>Bad:</strong> two parameters to tune instead of one. If you set the capacity too high, bursts can still hurt a fragile downstream service.</li>
</ul>
<p>The <strong>leaky bucket</strong> queues requests and drains them at a fixed rate. It smooths traffic rather than rejecting it, at the cost of queueing delay. Mention it and move on.</p>
<h3>The comparison you should be able to say out loud</h3>
<table>
<thead>
<tr>
<th>Algorithm</th>
<th>State per key</th>
<th>Allows bursts?</th>
<th>Accuracy</th>
<th>Typical use</th>
</tr>
</thead>
<tbody><tr>
<td>Fixed window</td>
<td>1 counter</td>
<td>Yes, up to 2× at the boundary</td>
<td>Low at edges</td>
<td>Internal tools, rough quotas</td>
</tr>
<tr>
<td>Sliding window log</td>
<td>1 timestamp per request</td>
<td>No</td>
<td>Exact</td>
<td>Low-volume, strict limits (login, OTP)</td>
</tr>
<tr>
<td>Sliding window counter</td>
<td>2 counters</td>
<td>Smoothed</td>
<td>Approximate</td>
<td>General API limits</td>
</tr>
<tr>
<td>Token bucket</td>
<td>Token count + timestamp</td>
<td>Yes, up to the capacity</td>
<td>Exact for its model</td>
<td>Public APIs, per-user limits</td>
</tr>
</tbody></table>
<p>A good answer sounds like: "For the general API I'll use a token bucket, because it tolerates normal bursts and keeps state small. For login and OTP, where the limit is a security control and volume is low, I'll use a sliding window log, because exactness matters more than memory there."</p>
<p>That one sentence shows you know there isn't a single right answer.</p>
<h2>Step 3: Where do the counters live?</h2>
<p>With one server, an in-memory map is enough. With twenty API servers behind a load balancer, in-memory counters break: each server sees only part of a user's traffic, so the user effectively gets twenty times the limit.</p>
<p>The standard answer is a <strong>shared, fast store, usually Redis</strong>:</p>
<ul>
<li>Key: something like <code>rl:{user_id}:{rule_name}</code></li>
<li>Value: a counter, a sorted set, or a small hash (tokens, last refill), depending on the algorithm</li>
<li>TTL on every key, so idle users don't pile up in memory forever</li>
</ul>
<h3>The race condition interviewers like to poke at</h3>
<p>Two requests from the same user hit two servers at the same moment. Both read <code>tokens = 1</code>, both decide to allow, both write <code>tokens = 0</code>. You've let through two requests on one token.</p>
<p>The fix is to make read-check-update <strong>atomic</strong>:</p>
<ul>
<li>For a fixed window, <code>INCR</code> is already atomic. Check the returned value instead of reading first.</li>
<li>For a token bucket or sliding window, put the logic in a <strong>Lua script</strong> that Redis executes atomically, so the read, the refill calculation and the write happen as one operation.</li>
</ul>
<p>Saying "I'd use a Lua script so the check-and-decrement is atomic" is a small sentence that signals real experience.</p>
<h3>Scaling the store</h3>
<ul>
<li><strong>Shard by key.</strong> Redis Cluster spreads keys across nodes by hash slot. Each limiter key lives on exactly one node, so every check is a single-shard operation and the Lua script still works.</li>
<li><strong>Hot keys.</strong> One huge customer can overload a single shard. Give them a local pre-limiter on each gateway node, or split their budget across sub-keys.</li>
<li><strong>Latency budget.</strong> Every request now includes a network call to Redis. Keep the store close to the gateway (same region, ideally same zone) and use connection pooling.</li>
</ul>
<h2>Step 4: Failure modes (where SDE-2 answers stand out)</h2>
<h3>What if Redis is down or slow?</h3>
<p>You have to choose:</p>
<ul>
<li><strong>Fail open:</strong> allow requests when the limiter can't decide. Users aren't hurt, but you lose protection exactly when something is already wrong.</li>
<li><strong>Fail closed:</strong> reject requests. Safe for the backend, but your rate limiter has just become a single point of failure for the whole API.</li>
</ul>
<p>A common, defensible answer: <strong>fail open for general endpoints, with a generous local in-memory limit as a backstop, and fail closed for sensitive endpoints</strong> like login and OTP. Also put a tight timeout on the Redis call (a few milliseconds), so a slow limiter can't drag down every request.</p>
<h3>Precision vs latency</h3>
<p>A purely local limiter needs no network call but is inaccurate when traffic is uneven across nodes. A central one is accurate but adds a hop. A middle path syncs local counts to the central store every few hundred milliseconds, trading slight over-admission for speed.</p>
<h2>Step 5: The client-facing contract</h2>
<p>Interviewers like candidates who remember that someone is on the other end:</p>
<ul>
<li>Return <strong>HTTP 429 Too Many Requests</strong> when a request is rejected.</li>
<li>Include a <strong><code>Retry-After</code></strong> header so well-behaved clients know when to try again.</li>
<li>Many APIs also send headers showing the limit, how many requests remain and when the window resets. Naming conventions vary, so say "remaining-quota headers" rather than inventing an exact standard.</li>
<li>Log rejections with the rule that triggered them, so "your API is blocking us" takes minutes to answer, not hours.</li>
</ul>
<h2>A 35-minute plan for the interview</h2>
<table>
<thead>
<tr>
<th>Time</th>
<th>What to cover</th>
</tr>
</thead>
<tbody><tr>
<td>0-5 min</td>
<td>Requirements: key, rules, strictness, placement, rough scale</td>
</tr>
<tr>
<td>5-15 min</td>
<td>Algorithm comparison and your choice, with the reason</td>
</tr>
<tr>
<td>15-25 min</td>
<td>High-level design: gateway middleware, Redis, rules config, atomic Lua script</td>
</tr>
<tr>
<td>25-32 min</td>
<td>Failure modes: fail open vs closed, timeouts, hot keys</td>
</tr>
<tr>
<td>32-35 min</td>
<td>Client contract (429, Retry-After), monitoring, and what you'd improve</td>
</tr>
</tbody></table>
<p>If the interviewer wants to go deep somewhere, follow them. The plan just stops you spending 25 minutes on the algorithm and never reaching the distributed part.</p>
<h2>Common mistakes</h2>
<ul>
<li><strong>Naming an algorithm without a reason.</strong> "Token bucket" isn't an answer. "Token bucket, because bursts are normal for our clients and the state is small" is.</li>
<li><strong>Forgetting the multi-server problem.</strong> If your counters are in local memory and you have more than one server, the limit doesn't hold.</li>
<li><strong>Ignoring atomicity.</strong> Read-then-write on a shared counter is a race condition.</li>
<li><strong>Treating the limiter as infallible.</strong> It's a dependency. Decide what happens when it fails.</li>
<li><strong>One rule for everything.</strong> Login, search and file upload deserve different limits and possibly different algorithms.</li>
</ul>
<h2>Quick checklist</h2>
<ul>
<li><input disabled type="checkbox" /> Throttle key decided (user ID / API key, IP fallback)</li>
<li><input disabled type="checkbox" /> Rules are per endpoint and live in config</li>
<li><input disabled type="checkbox" /> Algorithm chosen and justified, with an alternative named</li>
<li><input disabled type="checkbox" /> Shared store with TTLs and atomic updates</li>
<li><input disabled type="checkbox" /> Fail-open vs fail-closed decided per endpoint, with a timeout</li>
<li><input disabled type="checkbox" /> 429 plus Retry-After, and rejections are logged</li>
</ul>
<p>Practise this once end to end, out loud, and the question stops being scary. Thoda structure, thoda trade-off talk, and you're already ahead of most answers.</p>
<hr />
<p><em>I build DevInterviewMaster, an interview-prep site for developers that teaches in English, Hindi or Hinglish. A rate limiter is one stop on a bigger path (requirements, estimation, high-level design, deep dives). If you want that whole flow laid out, the system design framework pages are free to read at <a href="https://devinterviewmaster.com/system-design">devinterviewmaster.com/system-design</a>.</em></p>
]]></content:encoded></item><item><title><![CDATA[How to Estimate in a System Design Interview: Back-of-the-Envelope Math That Actually Helps]]></title><description><![CDATA[Five numbers to memorise, a four-step method and a worked example, so estimation drives your design instead of eating your time]]></description><link>https://devinterviewmasterhq.hashnode.dev/how-to-estimate-in-a-system-design-interview-back-of-the-envelope-math-that-actually-helps</link><guid isPermaLink="true">https://devinterviewmasterhq.hashnode.dev/how-to-estimate-in-a-system-design-interview-back-of-the-envelope-math-that-actually-helps</guid><category><![CDATA[interview preparation]]></category><category><![CDATA[System Design]]></category><category><![CDATA[backend]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[Career]]></category><dc:creator><![CDATA[DevInterviewMaster]]></dc:creator><pubDate>Thu, 29 Oct 2026 11:19:40 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6adc59f15860f38338b0e5a1/6bf1d1c8-3382-484c-b1f9-1e80b81abf7d.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Almost every system design interview has a moment where the interviewer says, "Okay, how much traffic are we talking about?" Many candidates then do one of two things. They skip estimation entirely and jump to drawing boxes, or they spend ten minutes multiplying numbers on the whiteboard and never use the result.</p>
<p>Both miss the point. Estimation isn't a maths test. It's how you decide whether you need one database or twenty, whether a cache is optional or essential, and whether the bottleneck is reads, writes or storage. Done well, it takes three to five minutes and makes every later decision easier to defend.</p>
<p>Here's a practical way to do it.</p>
<h2>What the interviewer is actually checking</h2>
<ol>
<li><strong>Can you turn vague requirements into numbers?</strong> "A lot of users" isn't a design input. "About 1,000 writes per second at peak" is.</li>
<li><strong>Are your assumptions explicit?</strong> Interviewers care far more that you <em>state</em> an assumption than that it's perfectly accurate.</li>
<li><strong>Do you use the numbers?</strong> An estimate that never shows up again in your design is wasted time.</li>
<li><strong>Is your order of magnitude right?</strong> Being off by 2× is fine. Being off by 1,000× changes the architecture.</li>
</ol>
<p>Precision is not on the list. Round aggressively.</p>
<h2>The five numbers worth memorising</h2>
<p>You don't need a table of fifty constants. These cover most interviews.</p>
<p><strong>1. Seconds in a day: 86,400, so call it ~100,000 (10^5).</strong>
This one number turns "per day" into "per second". 1 million requests a day is roughly 10 requests per second. 100 million a day is roughly 1,000 per second. Using 10^5 slightly underestimates QPS, which you'll correct with a peak factor anyway.</p>
<p><strong>2. Seconds in a month: about 2.5 million.</strong>
Useful when the requirement is stated per month (30 × 86,400 ≈ 2.6 million).</p>
<p><strong>3. Powers of two and data sizes.</strong></p>
<table>
<thead>
<tr>
<th>Power</th>
<th>Approx. value</th>
<th>Unit</th>
</tr>
</thead>
<tbody><tr>
<td>2^10</td>
<td>thousand</td>
<td>KB</td>
</tr>
<tr>
<td>2^20</td>
<td>million</td>
<td>MB</td>
</tr>
<tr>
<td>2^30</td>
<td>billion</td>
<td>GB</td>
</tr>
<tr>
<td>2^40</td>
<td>trillion</td>
<td>TB</td>
</tr>
<tr>
<td>2^50</td>
<td>quadrillion</td>
<td>PB</td>
</tr>
</tbody></table>
<p>So "1 billion records of 1 KB each" is about 1 TB. That shortcut alone saves a lot of fumbling.</p>
<p><strong>4. Latency orders of magnitude.</strong>
These are the widely cited reference figures (often called "latency numbers every programmer should know"). Exact values vary by hardware; the ratios are what matter.</p>
<ul>
<li>Main memory reference: ~100 nanoseconds</li>
<li>Random read from SSD: ~100 microseconds</li>
<li>Round trip within the same data centre: ~0.5 milliseconds</li>
<li>Disk seek on a spinning HDD: ~10 milliseconds</li>
<li>Round trip between continents: ~150 milliseconds</li>
</ul>
<p>The takeaway: memory is roughly 1,000× faster than SSD, and a cross-continent call costs more than a thousand in-datacenter calls. That's why caches and regional deployments exist.</p>
<p><strong>5. Peak vs average.</strong>
Traffic isn't flat. A common assumption is peak ≈ 2–3× the average. For spiky Indian workloads such as a cricket final, a flash sale or exam results day, say out loud that peaks can be far higher, and design for that peak.</p>
<h2>The four-step method</h2>
<p>Do these in order and write each line on the board.</p>
<h3>Step 1: Pin down the users and actions</h3>
<p>Start from daily active users (DAU) and what each user does.</p>
<blockquote>
<p>"Let's assume 50 million DAU. Each user reads about 20 items and creates about 1 item per day."</p>
</blockquote>
<p>If the interviewer gives you numbers, use theirs. If not, propose round ones and ask, "Does that sound reasonable?" This turns estimation into a conversation instead of a monologue.</p>
<h3>Step 2: Convert to QPS (reads and writes separately)</h3>
<ul>
<li>Writes/day = DAU × writes per user</li>
<li>Write QPS ≈ writes/day ÷ 10^5</li>
<li>Read QPS ≈ reads/day ÷ 10^5</li>
<li>Peak QPS ≈ average × 2 or 3</li>
</ul>
<p>Keeping reads and writes separate matters. The <strong>read:write ratio</strong> is often the most useful single number in the interview. A 100:1 read-heavy system pushes you towards caching and read replicas. A write-heavy system pushes you towards partitioning, queues and append-friendly storage.</p>
<h3>Step 3: Storage</h3>
<ul>
<li>Storage/day = writes/day × size per record</li>
<li>Total = storage/day × 365 × years of retention</li>
<li>Multiply by the replication factor (3 is a common default)</li>
</ul>
<p>Estimate record size by listing fields: an ID (8 bytes), a short URL or username (tens of bytes), a timestamp (8 bytes), some text (a few hundred bytes). Round up to a clean number such as 500 bytes or 1 KB. Media (images, video) should be estimated separately because it dominates everything else and usually goes to object storage.</p>
<h3>Step 4: Bandwidth and memory</h3>
<ul>
<li>Incoming bandwidth ≈ write QPS × record size</li>
<li>Outgoing bandwidth ≈ read QPS × response size</li>
<li>Cache size: if a small fraction of items gets most of the traffic (the 80/20 rule is a common working assumption), caching around 20% of a day's read volume is a reasonable starting estimate.</li>
</ul>
<p>Stop there. Four steps, about five lines of maths.</p>
<h2>A worked example: a URL shortener</h2>
<p>Assumptions (stated aloud and agreed with the interviewer):</p>
<ul>
<li>100 million new short URLs per month</li>
<li>Read:write ratio of 100:1 (people click links far more often than they create them)</li>
<li>Store links for 5 years</li>
<li>About 500 bytes per record (short code, long URL, owner ID, timestamps)</li>
</ul>
<p><strong>Writes:</strong>
100M per month ÷ 2.5M seconds per month ≈ <strong>40 writes/second</strong> on average, so about 100/second at peak.</p>
<p><strong>Reads:</strong>
40 × 100 = <strong>4,000 reads/second</strong> on average, about 10,000/second at peak.</p>
<p><strong>Storage:</strong>
100M per month × 12 × 5 = 6 billion records.
6 billion × 500 bytes = 3 TB. With 3× replication, about <strong>9 TB</strong>.</p>
<p><strong>Cache:</strong>
Reads per day ≈ 4,000 × 10^5 = 400 million. If we cache 20% of daily reads at 500 bytes each: 80 million × 500 bytes = <strong>40 GB</strong>, which fits comfortably in memory across a small cache cluster.</p>
<p><strong>Key space (bonus):</strong>
With a base-62 alphabet (a–z, A–Z, 0–9), 7 characters give 62^7 ≈ 3.5 trillion combinations. That's far more than 6 billion, so 7 characters are plenty.</p>
<p>Now <strong>use</strong> the numbers:</p>
<ul>
<li>100 writes/second is modest; a single primary database can usually handle this, so sharding for writes isn't the first problem.</li>
<li>10,000 reads/second at peak with a 100:1 ratio says the design should be cache-first. Most redirects should never touch the database.</li>
<li>9 TB over five years is large for a single machine but manageable with partitioning by short code, which you can plan for later rather than on day one.</li>
<li>Since key space is ample, you can choose between a counter-based ID with base-62 encoding and random codes with collision checks, and discuss the trade-offs.</li>
</ul>
<p>That is what estimation is for. Each number led to a design decision.</p>
<h2>Common mistakes</h2>
<ul>
<li><strong>Doing maths with no assumptions on the board.</strong> If the interviewer can't see where 4,000 came from, they can't give you credit for it.</li>
<li><strong>False precision.</strong> Writing 1,157.4 QPS signals you're optimising the wrong thing. Say "about 1,000–1,200".</li>
<li><strong>Forgetting peak traffic.</strong> Design for the peak, not the average.</li>
<li><strong>Mixing units.</strong> Bits vs bytes and per-day vs per-second errors are the most common source of 1,000× mistakes. Write units on every line.</li>
<li><strong>Ignoring replication and indexes.</strong> Raw data is rarely the full storage cost.</li>
<li><strong>Never using the result.</strong> End the section with one sentence: "So this is read-heavy, around 10K QPS at peak, a few TB, and caching is the main lever."</li>
</ul>
<h2>How to practise (without a whiteboard partner)</h2>
<ol>
<li>Pick a familiar product: a chat app, a food-delivery tracker, a UPI-style payment notification service, a photo feed.</li>
<li>Set a five-minute timer and run the four steps on paper.</li>
<li>Write the one-sentence conclusion and the two design decisions it implies.</li>
<li>Compare your assumptions with a friend's. If yours differ by 10× or more, find out why.</li>
</ol>
<p>Do this for ten systems and the steps become automatic. In the interview, you'll spend your energy on the design itself, which is where the real discussion happens. Thoda practice, and the numbers stop feeling scary.</p>
<h2>Quick checklist</h2>
<ul>
<li><input disabled type="checkbox" /> DAU and actions per user stated and agreed</li>
<li><input disabled type="checkbox" /> Read and write QPS calculated separately, with a peak factor</li>
<li><input disabled type="checkbox" /> Storage with retention and replication</li>
<li><input disabled type="checkbox" /> Bandwidth and cache size, roughly</li>
<li><input disabled type="checkbox" /> Units written on every line</li>
<li><input disabled type="checkbox" /> A one-sentence summary that drives at least two design decisions</li>
</ul>
<hr />
<p><em>Estimation is the step between requirements and architecture, and it fits into a bigger interview framework: clarify requirements, estimate, sketch the high-level design, then dive deep. If you want a structured overview of that whole flow, the system design framework pages on DevInterviewMaster are free to read at <a href="https://devinterviewmaster.com/system-design">devinterviewmaster.com/system-design</a>.</em></p>
]]></content:encoded></item><item><title><![CDATA[How to answer "Tell me about a production bug you fixed" in interviews]]></title><description><![CDATA[It sounds like a soft question, but "Tell me about a production bug you fixed" is one of the most revealing questions in a backend interview. In two…]]></description><link>https://devinterviewmasterhq.hashnode.dev/how-to-answer-tell-me-about-a-production-bug-you-fixed-in-interviews</link><guid isPermaLink="true">https://devinterviewmasterhq.hashnode.dev/how-to-answer-tell-me-about-a-production-bug-you-fixed-in-interviews</guid><category><![CDATA[interview]]></category><category><![CDATA[backend]]></category><category><![CDATA[Career]]></category><category><![CDATA[Beginner Developers]]></category><dc:creator><![CDATA[DevInterviewMaster]]></dc:creator><pubDate>Mon, 26 Oct 2026 11:20:40 GMT</pubDate><content:encoded><![CDATA[<p>It sounds like a soft question, but "Tell me about a production bug you fixed" is one of the most revealing questions in a backend interview. In two minutes the interviewer learns how you debug, how you communicate under pressure, whether you understand systems beyond your own code, and whether you learn from mistakes.</p>
<p>It's also the question many developers handle worst: either a vague story ("there was a bug, I fixed it") or a ten-minute ramble. Here's a structure that works, with a worked example.</p>
<h2>What the interviewer is actually evaluating</h2>
<ol>
<li><strong>Ownership:</strong> did you drive it, or watch?</li>
<li><strong>Debugging method:</strong> hypotheses, evidence, elimination, or random changes?</li>
<li><strong>Systems understanding:</strong> do you know what's beneath your code (database, network, infra)?</li>
<li><strong>Impact awareness:</strong> did you think about users and the business?</li>
<li><strong>Learning:</strong> did anything change afterwards so it can't recur?</li>
</ol>
<h2>The structure: S-I-H-F-P</h2>
<ul>
<li><strong>S: Situation.</strong> One or two lines of context. What system, what scale.</li>
<li><strong>I: Impact.</strong> What users saw, how many, for how long.</li>
<li><strong>H: Hypotheses.</strong> Your first guess, why it was wrong, and what evidence led you to the real cause. This is the most important part.</li>
<li><strong>F: Fix.</strong> The immediate mitigation, then the permanent fix.</li>
<li><strong>P: Prevention.</strong> Tests, alerts, runbooks or design changes afterwards.</li>
</ul>
<p>Aim for about two minutes, then let the interviewer dig in.</p>
<h2>A worked example</h2>
<blockquote>
<p><strong>Situation:</strong> "I worked on the order service of an e-commerce backend: Spring Boot, Postgres, around 200 requests per second at peak."</p>
<p><strong>Impact:</strong> "During a sale, checkout requests started timing out. About 30% of checkouts failed for roughly 25 minutes."</p>
<p><strong>Hypotheses:</strong> "We first assumed the database was overloaded, but DB CPU was only 35%. Then we suspected a bad deploy, but nothing had shipped that day. Application metrics showed the HikariCP pool at 100% utilisation with requests waiting for a connection. A thread dump showed many threads blocked on <code>getConnection()</code>, and a few holding connections while waiting on an external payment API call made <em>inside</em> the transaction."</p>
<p><strong>Fix:</strong> "As a mitigation we raised the payment API timeout handling and restarted to free connections. The real fix was moving the external call outside the database transaction, so we no longer held a connection while waiting on a third party."</p>
<p><strong>Prevention:</strong> "We added an alert on pool utilisation above 80%, a connection-acquisition timeout so requests fail fast instead of hanging, and a code-review checklist item: no network calls inside transactions."</p>
</blockquote>
<p>Why this works: there's a specific number (or an honest approximation), a wrong first hypothesis, concrete evidence (metrics, thread dump), a two-stage fix, and lasting prevention. That's what a senior engineer sounds like.</p>
<p><em>(The example is illustrative. Use your own real incident and your own real numbers. Never invent metrics in an interview; interviewers probe.)</em></p>
<h2>Common mistakes</h2>
<ul>
<li><strong>No impact.</strong> "There was a bug in the API" gives the interviewer nothing to evaluate.</li>
<li><strong>Skipping the investigation.</strong> Jumping from symptom to fix hides your best skill.</li>
<li><strong>Blaming others.</strong> "The other team's service was bad" reads poorly. Focus on what you did.</li>
<li><strong>Too much jargon, no clarity.</strong> Explain it so a non-specialist on the panel could follow.</li>
<li><strong>No learning.</strong> Always end with what changed afterwards.</li>
</ul>
<h2>"I've never had a production bug". What then?</h2>
<p>This is common for freshers and developers from service companies. Options, in order of strength:</p>
<ol>
<li><strong>A bug from an internship, college project or side project</strong> that had real users, even ten.</li>
<li><strong>A bug you investigated as part of a team,</strong> with an honest description of your part.</li>
<li><strong>A well-studied incident pattern</strong>, framed honestly: "I haven't hit this in production, but I've studied connection-pool exhaustion closely. Here's how it happens, how I'd detect it, and how I'd fix it." Interviewers usually respect this far more than a made-up story.</li>
</ol>
<p>To prepare for option 3, learn the most common incident patterns deeply: N+1 queries, connection-pool exhaustion, deadlocks, replication lag, memory leaks, race conditions, retry storms, cascading failures, OOM kills and DNS or config issues. For each, know the symptom, how you'd detect it, the root cause, the fix and the prevention.</p>
<h2>A quick prep checklist</h2>
<ul>
<li><input disabled type="checkbox" /> Pick 2â€“3 real stories (or studied patterns) and write each in S-I-H-F-P.</li>
<li><input disabled type="checkbox" /> Attach at least one honest number to each (users, duration, latency, error rate).</li>
<li><input disabled type="checkbox" /> Practise each out loud in under two minutes.</li>
<li><input disabled type="checkbox" /> Prepare for follow-ups: "How would you detect this sooner?", "What if the fix didn't work?", "How would this change at 10Ã— scale?"</li>
<li><input disabled type="checkbox" /> Keep one story about a bug <em>you caused</em>. Owning it shows maturity.</li>
</ul>
<hr />
<p><em>If you want realistic material to practise with, DevInterviewMaster has 57 production incidents written up as symptom â†’ wrong hypothesis â†’ root cause â†’ buggy vs fixed code â†’ prevention, the same shape as a good interview answer. A few in the Database, Backend and Frontend categories are free at <a href="https://devinterviewmaster.com/production-bugs">devinterviewmaster.com/production-bugs</a>.</em></p>
]]></content:encoded></item><item><title><![CDATA[From SOLID to System Design: a practical roadmap for LLD and HLD interviews]]></title><description><![CDATA[SOLID, design patterns, LLD, then HLD: the order that makes system design interviews click.]]></description><link>https://devinterviewmasterhq.hashnode.dev/from-solid-to-system-design-a-practical-roadmap-for-lld-and-hld-interviews</link><guid isPermaLink="true">https://devinterviewmasterhq.hashnode.dev/from-solid-to-system-design-a-practical-roadmap-for-lld-and-hld-interviews</guid><category><![CDATA[System Design]]></category><category><![CDATA[interview]]></category><category><![CDATA[Career]]></category><category><![CDATA[Software Engineering]]></category><dc:creator><![CDATA[DevInterviewMaster]]></dc:creator><pubDate>Sun, 25 Oct 2026 03:04:43 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6adc59f15860f38338b0e5a1/6f08d57f-e989-4acb-8ee3-52273eee595b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>"Learn system design" is advice that sounds useful and gives you nothing to do on Monday morning. The topic is huge, YouTube is full of 45-minute "design Netflix" videos, and it's easy to watch twenty of them and still freeze when an interviewer says "design a parking lot".</p>
<p>The fix is to learn it in the right order. Design skill stacks: object design sits on principles, and architecture sits on object design and fundamentals. Here's the roadmap I'd follow, and the one DevInterviewMaster's learning path is built around: <strong>SOLID → Design Patterns → Low-Level Design → High-Level Design.</strong></p>
<h2>Stage 1: SOLID principles (1–2 weeks)</h2>
<p>SOLID isn't trivia to recite. It's a vocabulary for talking about the cost of change.</p>
<ul>
<li><strong>Single Responsibility:</strong> a class should have one reason to change. If your <code>OrderService</code> also formats emails and talks to the payment gateway, every change risks everything.</li>
<li><strong>Open/Closed:</strong> add behaviour by adding code, not by editing a growing <code>if/else</code> chain.</li>
<li><strong>Liskov Substitution:</strong> a subclass must honour the parent's contract. The classic square/rectangle problem is the interview favourite.</li>
<li><strong>Interface Segregation:</strong> many small interfaces beat one fat one that forces empty implementations.</li>
<li><strong>Dependency Inversion:</strong> depend on abstractions, so you can swap a real payment gateway for a fake one in tests.</li>
</ul>
<p><strong>Exit test:</strong> take a 200-line class you wrote and explain which principle it breaks and what that will cost you later.</p>
<h2>Stage 2: Design patterns (2–3 weeks)</h2>
<p>Don't memorise a catalogue. For each pattern, learn <strong>the problem it solves and its trade-off</strong>. Prioritise the ones that come up again and again in LLD rounds:</p>
<ul>
<li><strong>Strategy:</strong> swap algorithms at runtime (pricing rules, payment methods).</li>
<li><strong>Factory / Abstract Factory:</strong> centralise object creation.</li>
<li><strong>Observer:</strong> notify subscribers of events (notifications, order status).</li>
<li><strong>State:</strong> behaviour that changes with state. Vending machines, elevators and order lifecycles are exactly this.</li>
<li><strong>Decorator:</strong> add behaviour without subclass explosion.</li>
<li><strong>Singleton:</strong> know it, and know why it's often a smell (global state, testing pain).</li>
<li><strong>Builder, Adapter, Command:</strong> frequent supporting cast.</li>
</ul>
<p><strong>Exit test:</strong> given "design a vending machine", you can say which pattern you'd use and why, in two sentences.</p>
<h2>Stage 3: Low-Level Design (3–4 weeks)</h2>
<p>LLD rounds ask you to turn requirements into classes, interfaces and working code. A repeatable approach:</p>
<ol>
<li><strong>Clarify use cases and invariants.</strong> "Two cars can never be assigned the same spot" is an invariant, and your design must protect it.</li>
<li><strong>Identify entities and responsibilities.</strong> Which object owns each rule?</li>
<li><strong>Define interfaces and relationships.</strong> Composition over inheritance by default.</li>
<li><strong>Implement one core flow end-to-end.</strong> Interviewers want running logic, not just a class diagram.</li>
<li><strong>Test the design against change.</strong> "Now add electric-vehicle charging spots": how many classes do you touch?</li>
<li><strong>Discuss concurrency</strong> if relevant. Two requests reserving the same spot at once is a race condition (see stage 4 thinking).</li>
</ol>
<p>Classic practice problems: parking lot, elevator system, vending machine, library management, splitwise, rate limiter (LLD version), LRU cache, BookMyShow-style seat booking.</p>
<h2>Stage 4: High-Level Design (4–6+ weeks)</h2>
<p>Now zoom out to services, data flow and failure. Use the same six steps for every question:</p>
<ol>
<li><strong>Clarify requirements:</strong> users, core actions, scale, and what's explicitly out of scope.</li>
<li><strong>Estimate scale:</strong> QPS, storage, read/write ratio, latency targets, with units.</li>
<li><strong>Design APIs and the data model</strong> before drawing boxes.</li>
<li><strong>Draw the core flow:</strong> trace one request end-to-end.</li>
<li><strong>Handle scale and failure:</strong> caching, queues, partitioning, replication, retries. Add each only when you can say why it's needed.</li>
<li><strong>Defend trade-offs:</strong> consistency vs availability, cost vs complexity, and what would change your mind.</li>
</ol>
<p>Fundamentals to cover: load balancing, caching strategies and invalidation, SQL vs NoSQL, indexing, replication and partitioning, consistent hashing, message queues and event-driven design, rate limiting, CDNs, idempotency, and observability.</p>
<p>Start with these five problems, because together they cover most building blocks: <strong>URL shortener, rate limiter, notification service, chat system, file storage.</strong></p>
<h2>What changes by level</h2>
<ul>
<li><strong>SDE-1:</strong> fundamentals, small object designs, clean code. LLD matters more than HLD.</li>
<li><strong>SDE-2:</strong> both rounds; expect deeper trade-offs and failure handling.</li>
<li><strong>Senior:</strong> HLD dominates, with emphasis on reliability, operability and cost. Real production incidents become great material here.</li>
</ul>
<h2>A realistic 12-week plan</h2>
<table>
<thead>
<tr>
<th>Weeks</th>
<th>Focus</th>
</tr>
</thead>
<tbody><tr>
<td>1–2</td>
<td>SOLID, with code-review exercises on your own code</td>
</tr>
<tr>
<td>3–4</td>
<td>Core design patterns, one small implementation each</td>
</tr>
<tr>
<td>5–8</td>
<td>LLD: two problems per week, coded end-to-end</td>
</tr>
<tr>
<td>9–12</td>
<td>HLD: two problems per week, explained out loud with estimates</td>
</tr>
</tbody></table>
<p>Throughout, practise <strong>explaining out loud</strong>. System design interviews are conversations. Record yourself, or explain to a friend (or a tutor) and see where you hand-wave.</p>
<hr />
<p><em>DevInterviewMaster follows exactly this path, SOLID → design patterns → LLD (70 topics) → HLD (248 topics), with an AI tutor for HLD and LLD that you can talk to in English, Hindi or Hinglish. The overview is free at <a href="https://devinterviewmaster.com/system-design">devinterviewmaster.com/system-design</a>.</em></p>
]]></content:encoded></item><item><title><![CDATA[12 production incidents every backend developer should understand (from a library of 57)]]></title><description><![CDATA[Symptom, root cause and fix for 12 classic outages, and how to talk about them in interviews]]></description><link>https://devinterviewmasterhq.hashnode.dev/12-production-incidents-every-backend-developer-should-understand-from-a-library-of-57</link><guid isPermaLink="true">https://devinterviewmasterhq.hashnode.dev/12-production-incidents-every-backend-developer-should-understand-from-a-library-of-57</guid><category><![CDATA[backend]]></category><category><![CDATA[System Design]]></category><category><![CDATA[interview]]></category><category><![CDATA[Devops]]></category><dc:creator><![CDATA[DevInterviewMaster]]></dc:creator><pubDate>Sat, 24 Oct 2026 07:15:31 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6adc59f15860f38338b0e5a1/9a4b2787-059d-444d-9778-baad66e62d27.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you've ever been on-call, you know textbook distributed systems and 3 AM distributed systems are different things. Interviewers know it too. That's why "tell me about a production issue you handled" now shows up in so many backend and SDE-2 loops.</p>
<p>The good news: most production incidents are variations of a small set of patterns. While writing up 57 incidents across databases, backend services, frontends, infrastructure and distributed systems, we kept seeing the same shapes. Here are twelve worth knowing, grouped by layer, each with the symptom, the root cause and the usual fix.</p>
<h2>Database layer</h2>
<p><strong>1. The N+1 query problem</strong></p>
<p><em>Symptom:</em> A list endpoint that took 40 ms in development takes 4 seconds in production. CPU on the app server looks fine.</p>
<p><em>Root cause:</em> The ORM loads a list of parent rows with one query, then lazily loads a relationship for each row, so N extra queries. With 10 rows nobody notices. With 2,000 the database is doing 2,001 round trips.</p>
<p><em>Fix:</em> Eager loading (<code>JOIN</code>, <code>select_related</code>, <code>include</code>), batching, and a test that asserts the query count for critical endpoints so the regression can't sneak back.</p>
<p><strong>2. Connection pool exhaustion</strong></p>
<p><em>Symptom:</em> The app "hangs". Requests time out, yet CPU and memory are low. Restarting fixes it for an hour.</p>
<p><em>Root cause:</em> Connections are borrowed and not returned (a missing <code>finally</code>, an exception path, a long-running transaction), or a slow query holds them. Once the pool is empty, every request waits.</p>
<p><em>Fix:</em> Always release in <code>finally</code>/context managers, set acquisition timeouts so callers fail fast, alert on pool utilisation, and size pools against the database's <code>max_connections</code> across all instances.</p>
<p><strong>3. Deadlocks</strong></p>
<p><em>Symptom:</em> Intermittent errors like "deadlock detected" under load, usually around payments or inventory.</p>
<p><em>Root cause:</em> Two transactions lock the same rows in opposite orders and each waits for the other.</p>
<p><em>Fix:</em> Lock rows in a consistent order, keep transactions short, avoid user I/O inside transactions, and retry the transaction on deadlock errors.</p>
<p><strong>4. Replication lag</strong></p>
<p><em>Symptom:</em> A user changes a setting, the page reloads, and the old value shows. Support tickets say "your app doesn't save".</p>
<p><em>Root cause:</em> Writes go to the primary but reads go to a replica that is a few hundred milliseconds behind.</p>
<p><em>Fix:</em> Read-your-writes consistency. After a write, route that user's reads to the primary for a short window, or use session-level consistency tokens.</p>
<h2>Backend services</h2>
<p><strong>5. Memory leaks</strong></p>
<p><em>Symptom:</em> Memory climbs steadily for days, then the process is OOM-killed and restarts. The graph looks like a sawtooth.</p>
<p><em>Root cause:</em> Unbounded in-memory caches, event listeners that are never removed, or growing global maps keyed by user or request.</p>
<p><em>Fix:</em> Bound every cache (size and TTL), take heap snapshots before and after a load test, and alert on memory trend rather than just the absolute level.</p>
<p><strong>6. Race conditions</strong></p>
<p><em>Symptom:</em> Very occasionally a coupon is redeemed twice, or a seat is booked by two users.</p>
<p><em>Root cause:</em> A check-then-act sequence ("is it available? then reserve it") without atomicity.</p>
<p><em>Fix:</em> Push the invariant into the database (unique constraints, conditional updates like <code>UPDATE ... WHERE available = true</code>), or use proper locking or idempotency keys.</p>
<p><strong>7. Thread starvation</strong></p>
<p><em>Symptom:</em> Latency spikes across all endpoints, not just one, whenever a particular downstream service slows down.</p>
<p><em>Root cause:</em> Blocking calls occupy every worker thread, so even cheap requests can't get a thread.</p>
<p><em>Fix:</em> Timeouts on every outbound call, separate pools (bulkheads) for slow dependencies, or non-blocking I/O.</p>
<p><strong>8. Retry storms</strong></p>
<p><em>Symptom:</em> A brief slowdown in one service turns into a full outage that lasts far longer than the original blip.</p>
<p><em>Root cause:</em> Every caller retries immediately, several times. Load on the struggling service multiplies just when it can least handle it.</p>
<p><em>Fix:</em> Exponential backoff with jitter, retry budgets, and circuit breakers that stop calling a failing dependency for a while.</p>
<h2>Frontend</h2>
<p><strong>9. Infinite re-renders</strong></p>
<p><em>Symptom:</em> The tab freezes and the fan spins. React warns "Maximum update depth exceeded".</p>
<p><em>Root cause:</em> State updated inside an effect whose dependency changes on every render (often a new object or array literal).</p>
<p><em>Fix:</em> Stable dependencies (<code>useMemo</code>/<code>useCallback</code>), and derive values during render instead of syncing them into state.</p>
<p><strong>10. Hydration mismatch</strong></p>
<p><em>Symptom:</em> A server-rendered page flickers, or console warnings say the server and client HTML differ.</p>
<p><em>Root cause:</em> Rendering something non-deterministic on the server (dates, <code>Math.random()</code>, <code>window</code> checks) that comes out different on the client.</p>
<p><em>Fix:</em> Render deterministic markup on the server and move client-only values into effects.</p>
<h2>Infrastructure and distributed systems</h2>
<p><strong>11. DNS and config drift</strong></p>
<p><em>Symptom:</em> One region or environment behaves differently, or a deploy works in staging and fails in production.</p>
<p><em>Root cause:</em> Manually changed config, stale DNS records or TTLs, and environments that slowly diverge.</p>
<p><em>Fix:</em> Infrastructure as code, config in version control, drift detection, and sensible DNS TTLs before planned migrations.</p>
<p><strong>12. Split brain and cascading failures</strong></p>
<p><em>Symptom:</em> After a network partition, two nodes both accept writes as "leader", and data diverges. Or one failing dependency takes down services that were healthy.</p>
<p><em>Root cause:</em> Leader election without quorum, missing timeouts, and no isolation between components.</p>
<p><em>Fix:</em> Quorum-based consensus (Raft and similar), fencing tokens, timeouts everywhere, bulkheads, and graceful degradation.</p>
<h2>How to use this in an interview</h2>
<p>When an interviewer asks about a production bug, walk through it in this order:</p>
<ol>
<li><strong>Symptom and impact</strong>: what users saw, how many, for how long.</li>
<li><strong>First hypothesis, and why it was wrong</strong>: this makes the story credible.</li>
<li><strong>How you found the real cause</strong>: metrics, logs, traces, a reproduction.</li>
<li><strong>The fix</strong>: the immediate mitigation, then the real fix.</li>
<li><strong>Prevention</strong>: tests, alerts or guardrails so it can't recur.</li>
</ol>
<p>Even if you've never been on-call, you can study these patterns well enough to discuss them honestly ("I haven't hit this in production, but here's how I'd detect and fix it"). Interviewers respect that far more than a vague story.</p>
<hr />
<p><em>We wrote all 57 incidents up, each with root-cause analysis, buggy vs fixed code and prevention steps, on <a href="https://devinterviewmaster.com/production-bugs">DevInterviewMaster</a>. The first two in the Database, Backend and Frontend categories are free to read. Which classic incident would you add to this list?</em></p>
]]></content:encoded></item></channel></rss>