Interview replay
Full round replay — rate limiting
Timed verbal replay with pass/fail criteria per follow-up.
How to run this
The opener
“How would you implement a rate limiter?”
Budget: 45 seconds. Going long here is itself a fail signal.
Follow-ups
1. “Why not just a counter you reset every minute?”
Testing: Whether they know the boundary burst.
Scoring
Pass: That is a fixed window, and it allows twice the limit across a boundary — the full limit in the last instant of one minute and again in the first instant of the next. Clock-aligned traffic lands exactly there: cron jobs, retry backoffs that round to the minute, dashboards refreshing.
Fail: Treats it as correct, or calls the burst a rare edge case.
2. “Token bucket or leaky bucket?”
Testing: Whether the distinction is real to them.
Scoring
Pass: Token bucket allows a burst up to the bucket size and limits the average rate; leaky bucket drains at a fixed rate and smooths output completely. Token for public APIs where clients are allowed to be bursty, leaky when a downstream system needs an even feed.
Fail: Says they are the same, or cannot say what changes.
3. “Does the token bucket need a background thread to refill it?”
Testing: A common misunderstanding that costs a lot at scale.
Scoring
Pass: No. The refill is computed from elapsed time when a request arrives, so idle keys cost nothing. A timer per key would be a million scheduled tasks for a million users.
Fail: Describes a scheduler topping up buckets.
4. “How much memory does each algorithm need per key?”
Testing: Whether they can rule one out with a number.
Scoring
Pass: Fixed window and token bucket are constant. A sliding log stores one timestamp per request in the window — a limit of 10,000 with a million users is ten billion timestamps. A sliding counter is two integers and tracks the exact algorithm's rate to about a percent.
Fail: Recommends the sliding log for a large key count without costing it.
5. “Now run it on ten instances.”
Testing: The real question.
Scoring
Pass: An in-memory limiter becomes a limit ten times higher, and it loosens further every scale-out — so protection weakens exactly when load is highest. The counter has to be shared: Redis with an atomic script, or the gateway.
Fail: Says it still works, or has not considered it.
6. “Why not just divide the limit by the number of instances?”
Testing: Whether they see the hidden assumption.
Scoring
Pass: It assumes even routing, and nothing enforces that — sticky sessions, hashing on user id, or an instance restarting all skew it. Under skew a customer inside their quota gets refused with no error logged anywhere. It also has to be re-tuned on every scale event.
Fail: Offers it as the answer with no caveat.
7. “Why must the distributed check be atomic?”
Testing: Read-modify-write under concurrency.
Scoring
Pass: Otherwise two instances read the same count and both allow. A Lua script, INCR with an expiry, or a CAS loop makes it indivisible. Without it the limiter is approximately right, which for a paid quota is wrong.
Fail: Proposes a GET followed by a SET.
8. “Redis is down. What does your limiter do?”
Testing: Whether they know it is a decision.
Scoring
Pass: It is a choice, per endpoint. Fail open keeps the API up and drops the protection during exactly the incident you need it in; fail closed keeps the guarantee and turns a Redis blip into an outage. Fail closed with a generous local fallback for anything protecting a fragile backend, fail open with an alert for public reads.
Fail: Has no answer, or assumes the store is always available.
9. “What key do you count against?”
Testing: A defect that reaches production often.
Scoring
Pass: Whatever the limit is sold against — usually the API key or tenant. Source IP puts every customer behind one NAT gateway on a shared quota. IP is still useful as a separate, coarser layer against volumetric abuse, but it is a different limit.
Fail: Says IP without qualification.
10. “A million users hit your service once each and never come back. What happens to memory?”
Testing: The leak nobody tests for.
Scoring
Pass: Without an expiry the key map retains all of them — on a public endpoint that is attacker-controlled growth. Use a cache with expiry and a max size, or PEXPIRE in Redis. The expiry has to exceed the window, because an evicted key returns with a full bucket and that is a free burst.
Fail: Does not consider eviction, or expires at exactly the window.
11. “What do you return to the client?”
Testing: The detail that separates a working limiter from an outage report.
Scoring
Pass: 429 with Retry-After, plus RateLimit-Limit, -Remaining and -Reset so a well-behaved client can pace itself instead of discovering the limit by hitting it. Not 503 — that says the service is broken and invites retries against something healthy.
Fail: Says 503, or returns 429 with no headers.