Rate Limiter
Level: Advanced 60–90 minConcepts: AlgorithmsMockingBoundariesStateDesign Patterns
Solutions: C# | TypeScript | Python
Implement a rate limiter that restricts the number of requests a client can make within a time window.
Requirements
- Create a
RateLimiterwith a configurable max requests and time window (in seconds) allowRequest(clientId)— returnstrueif the request is allowed,falseif rate limited- Each client is tracked independently
- Use a sliding window — requests older than the window are no longer counted
- The limiter should accept an injectable clock/time source for testability
Test Cases
Given a rate limiter configured for 3 requests per 10 seconds:
| Time (s) | Client | Action | Result |
|---|---|---|---|
| 0 | ”alice” | allowRequest | true (1/3) |
| 1 | ”alice” | allowRequest | true (2/3) |
| 2 | ”alice” | allowRequest | true (3/3) |
| 3 | ”alice” | allowRequest | false (limit reached) |
| 3 | ”bob” | allowRequest | true (bob is separate, 1/3) |
| 11 | ”alice” | allowRequest | true (first request expired) |
| 11 | ”alice” | allowRequest | true (second request expired) |
| 11 | ”alice” | allowRequest | false (third request still in window) |
Bonus
- Implement token bucket algorithm as an alternative strategy
- Add
remainingRequests(clientId)— returns how many requests the client has left - Add
retryAfter(clientId)— returns seconds until the next request will be allowed - Add burst allowance — permit short bursts above the rate limit with a separate burst quota
- Support tiered limits — different rate limits for different client tiers (free, pro, enterprise)
Reference Walkthrough
Full C#, TypeScript, and Python implementations live at tddbuddy-reference-katas/rate-limiter. Nineteen scenarios across rule construction, allow-under-limit, reject-at-limit with retryAfter, window-expiry reset, per-key isolation, and an end-to-end worked example — shared across all three languages — with a Limiter aggregate, Rule value type, Decision union (Allowed | Rejected(retryAfter)), Clock collaborator, LimiterBuilder + FixedClock test doubles, and LimiterRuleInvalidException for invalid rule config.
- C# (.NET 8, xUnit, FluentAssertions) — walkthrough
- TypeScript (Node 20, Vitest, strict types) — walkthrough
- Python (3.11, pytest, frozen dataclasses, Protocol clock) — walkthrough
This kata ships in Agent Full-Bake (F3) mode: one commit per language with the full domain design landing together. The walkthroughs read as design rationale — why Decision is a union, why Rule is a value type, why Clock is injected (same pattern as memory-cache and circuit-breaker).
The reference uses the fixed-window algorithm rather than the sliding window described in the brief above. Fixed window collapses to a two-variable state machine (windowStart, count) per key and is the simplest algorithm to teach — every behavior in the scenario list (allow, reject, reset, retryAfter, isolation) is exercisable against it, and the end-to-end worked example in WorkedExampleTests walks the full cycle for alice and bob at 3 requests per 10 seconds. The brief’s sliding-window variant is a natural next step: swap WindowState for a Deque<DateTime> of request timestamps and prune on entry. The bonus items (token bucket, remainingRequests, burst allowance, tiered limits) are deliberately out of scope — the Rule and Decision types are ready to extend without the reference hiding seams under premature abstraction. See the repo’s Gears section for when middle gear is the right call.
Related reading
- Katas Are Rehearsal, Not Performance
You don't practice TDD on production code. You practice on katas and bring the muscle memory to production. The gap between knowing TDD and doing TDD is reps. - Test Deletion Is a Privileged Operation
The cheapest way for an agent to make a failing test pass is to delete it. That is logical for the agent and catastrophic for the codebase. Tests are append-only by default. Deletion needs a human author, a separate commit, and a separate review. - The Instruction File Is Not the Discipline
Teams that want their agent to do TDD reach for the instruction file. The TDAD paper measured this directly: adding a 'do TDD' instruction raised regressions by nearly two-thirds. Contextual test discovery cut them by seventy percent. The instruction is theatre. The codebase's test surface is the discipline.