Skip to solution
mediumSystem Design

How would you design a rate limiter?

550 views
01

Understand the problem

Explain rate limiting algorithms.

rate-limiting
02

Attempt it yourself

Sketch your approach before reading the solution — that's what interviews test.

Nudge consolestandby

Stuck? Beam a request up — the console returns a conceptual nudge that guides your logic without spoiling the implementation.

03

Study the solution

Designing a Rate Limiter

Target Audience: Junior & Senior Software Engineers preparing for System Design Interviews — no prior system design knowledge assumed. Difficulty: Easy to Medium

How to read this doc: Every concept is explained in plain language first. Right after, you'll see a callout like `�

Solution ready — 2 min read

Classified // press E to declassify

04

Read the code

Fixed Window Counter rate limiter
Run Playground
class FixedWindowCounter {
  constructor(limit, windowSize) {
    this.limit = limit;
    this.windowSize = windowSize;
    this.currentWindowId = null;
    this.count = 0;
  }

  allow(now) {
    const windowId = Math.floor(now / this.windowSize);
    if (windowId !== this.currentWindowId) {
      this.currentWindowId = windowId;
      this.count = 0;
    }
    if (this.count < this.limit) {
      this.count += 1;
      return true;
    }
    return false;
  }
}

const limiter = new FixedWindowCounter(3, 10);
const times = [0, 0, 0, 0, 9, 10, 10, 10, 10];
console.log(times.map((t) => limiter.allow(t)));
Sliding Window Log rate limiter
class SlidingWindowLog {
  constructor(limit, windowSize) {
    this.limit = limit;
    this.windowSize = windowSize;
    this.log = [];
  }

  allow(now) {
    const cutoff = now - this.windowSize;
    this.log = this.log.filter((t) => t > cutoff);
    if (this.log.length < this.limit) {
      this.log.push(now);
      return true;
    }
    return false;
  }
}

const limiter = new SlidingWindowLog(3, 10);
const times = [0, 0, 0, 0, 5, 10, 11];
console.log(times.map((t) => limiter.allow(t)));
Sliding Window Counter rate limiter
Run Playground
class SlidingWindowCounter {
  constructor(limit, windowSize) {
    this.limit = limit;
    this.windowSize = windowSize;
    this.currentWindowId = null;
    this.currentCount = 0;
    this.previousCount = 0;
  }

  allow(now) {
    const windowId = Math.floor(now / this.windowSize);
    if (windowId !== this.currentWindowId) {
      if (this.currentWindowId !== null && windowId === this.currentWindowId + 1) {
        this.previousCount = this.currentCount;
      } else {
        this.previousCount = 0;
      }
      this.currentCount = 0;
      this.currentWindowId = windowId;
    }
    const fraction = (now % this.windowSize) / this.windowSize;
    const estimated = this.currentCount + this.previousCount * (1 - fraction);
    if (estimated < this.limit) {
      this.currentCount += 1;
      return true;
    }
    return false;
  }
}

const limiter = new SlidingWindowCounter(5, 10);
const times = [0, 0, 0, 0, 0, 0, 15, 15, 15, 15];
console.log(times.map((t) => limiter.allow(t)));
Token bucket rate limiter
Run Playground
class TokenBucket {
  constructor(capacity, refillRate) {
    this.capacity = capacity;
    this.refillRate = refillRate; // tokens added per tick
    this.tokens = capacity;
    this.lastRefill = 0;
  }

  allow(now) {
    const elapsed = now - this.lastRefill;
    this.tokens = Math.min(this.capacity, this.tokens + elapsed * this.refillRate);
    this.lastRefill = now;
    if (this.tokens >= 1) {
      this.tokens -= 1;
      return true;
    }
    return false;
  }
}

const bucket = new TokenBucket(5, 1.0);
const results = [];
for (let i = 0; i < 6; i++) results.push(bucket.allow(0));
for (let i = 0; i < 4; i++) results.push(bucket.allow(3));
console.log(results);
Leaky Bucket rate limiter
Run Playground
class LeakyBucket {
  constructor(capacity, leakRate) {
    this.capacity = capacity;
    this.leakRate = leakRate;
    this.queueLevel = 0;
    this.lastLeak = 0;
  }

  allow(now) {
    const elapsed = now - this.lastLeak;
    this.queueLevel = Math.max(0, this.queueLevel - elapsed * this.leakRate);
    this.lastLeak = now;
    if (this.queueLevel < this.capacity) {
      this.queueLevel += 1;
      return true;
    }
    return false;
  }
}

const limiter = new LeakyBucket(3, 1.0);
const times = [0, 0, 0, 0, 2, 2, 2];
console.log(times.map((t) => limiter.allow(t)));
05

Join the discussion

Discussion (0)

Sign in to join the discussion.

No responses yet. Be the first to share what you think.

Transmission complete // awaiting log

KEEP THE
STREAK ALIVE.

Dossier 27 of 99 decoded in the System Design track. One more won't hurt.

Back to track