MediumRate Limiting in Spring Boot: The Interview Question That Tests More Than You Think | by curiouswomen | Medium
Sitemap
curiouswomen

curiouswomen

I am here to share my experience being it a technical, non-technical or daily life experience that I am curious and thrive a positive change.

Rate Limiting in Spring Boot: The Interview Question That Tests More Than You Think

4 min readJul 21, 2026

--

A backend interview question I recently came across looked deceptively simple:

“Build a Spring Boot application with CRUD operations and implement an in-memory rate limiter. If a user exceeds 10 requests per minute on any endpoint, return HTTP 429 (Too Many Requests).”

At first glance, this sounds like a straightforward coding exercise.

In reality, it evaluates your understanding of backend architecture, concurrency, HTTP semantics, and clean design.

Let’s break down what the interviewer is actually testing.

What Is Rate Limiting?

Rate limiting is a mechanism that restricts how many requests a client can make within a given period.

For example:

Limit:
10 requests / minute
Request #11
↓
HTTP 429 Too Many Requests

Without rate limiting, a single client could overwhelm your application by sending thousands of requests, affecting every other user.

Rate limiting protects:

  • APIs from abuse
  • Server resources
  • Downstream services
  • Overall application stability

What This Interview Question Is Really Testing

Although the requirement is small, it touches multiple backend concepts.

1. REST API Fundamentals

Can you build a clean Spring Boot CRUD application?

This verifies that you’re comfortable with:

  • Controllers
  • Services
  • Repositories
  • DTOs
  • REST conventions

2. HTTP Status Codes

Many developers only think about:

  • 200 OK
  • 400 Bad Request
  • 404 Not Found
  • 500 Internal Server Error

A production backend should also know when to return:

429 Too Many Requests

This status tells the client:

“Your request is valid, but you’ve exceeded the allowed request rate.”

3. Algorithm Selection

There isn’t just one rate-limiting algorithm.

Common approaches include:

Fixed Window

Simple counter per time window.

Pros:

  • Easy to implement

Cons:

  • Burst traffic at window boundaries

Sliding Window

Stores request timestamps.

On every request:

  • Remove timestamps older than the window.
  • Count remaining requests.
  • Reject if limit exceeded.

Pros:

  • More accurate

Cons:

  • Slightly more memory usage

Token Bucket

A bucket contains tokens.

Each request consumes one token.

Tokens refill gradually over time.

Pros:

  • Allows controlled bursts
  • Widely used in production
  • Used internally by libraries such as Resilience4J

Cons:

  • Slightly more complex

Why Thread Safety Matters

Imagine two requests arrive simultaneously.

Both read:

Current Count = 9

Both increment:

10

You just allowed 11 requests.

This is called a race condition.

Using a regular HashMap is unsafe because multiple threads may modify it simultaneously.

Instead, use:

ConcurrentHashMap

which provides thread-safe operations for concurrent access.

Why the Rate Limiter Should Not Be Inside the Controller

A common beginner implementation looks like this:

@PostMapping("/users")
public ResponseEntity<?> createUser() {
    if(rateExceeded()){
return ResponseEntity.status(429).build();
}
...
}

It works.

Get curiouswomen’s stories in your inbox

Join Medium for free to get updates from this writer.

But now every controller contains duplicated rate-limiting logic.

Tomorrow you’ll need to copy the same code into:

  • UserController
  • ExpenseController
  • GroupController
  • BalanceController

This violates the DRY (Don’t Repeat Yourself) principle.

The Better Design

Rate limiting is a cross-cutting concern.

It should be handled before the request reaches the controller.

In Spring Boot, the request flow looks like this:

Incoming Request
│
▼
Servlet Filter
│
▼
DispatcherServlet
│
▼
HandlerInterceptor
│
▼
Controller
│
▼
Service

A custom Filter (or OncePerRequestFilter) can intercept every request and decide whether it should proceed.

This keeps controllers focused solely on business logic.

A Simple In-Memory Design

One possible approach is:

ConcurrentHashMap<
ClientIdentifier,
Queue<RequestTimestamp>
>

Where:

  • Key = client (IP, API key, or user)
  • Value = queue of request timestamps

For every incoming request:

  1. Remove timestamps older than 60 seconds.
  2. Check queue size.
  3. If size >= 10 → return HTTP 429.
  4. Otherwise add the current timestamp and continue.

This is essentially a Sliding Window implementation.

Why This Design Is Good

  • Thread-safe
  • Centralized
  • Easy to extend
  • No duplicated controller logic
  • Easy to replace later with Redis or Resilience4J

In-Memory vs Production

An in-memory implementation is excellent for interviews and learning.

However, in production it has limitations.

Problems

  • Doesn’t work across multiple application instances.
  • Loses data after restart.
  • Doesn’t scale horizontally.

Production systems usually use:

  • Redis
  • API Gateway rate limiting
  • Cloud load balancers
  • Resilience4J (for outbound calls)

An Important Distinction

Many developers confuse two different concepts.

API Rate Limiting

Protects your application.

Client
↓
Spring Boot API
↓
HTTP 429

Resilience4J RateLimiter

Protects external dependencies.

Example:

Spring Boot
↓Google Maps API

Suppose Google allows only 5 requests per second.

Resilience4J prevents your application from exceeding that limit.

Although both are called “Rate Limiting,” they solve different problems.

What the Interviewer Is Actually Looking For

The best solution is not the shortest one.

A strong answer demonstrates:

  • Clean REST architecture
  • Correct HTTP semantics
  • Thread-safe programming
  • Separation of concerns
  • Understanding of request lifecycle
  • Awareness of production trade-offs

In other words, one seemingly simple question evaluates far more than just coding ability.

Final Thoughts

Whenever you encounter a backend interview question, don’t rush into writing code.

First ask yourself:

  • Where does this feature belong?
  • Is it a business concern or a cross-cutting concern?
  • Will this design still work if my application scales tomorrow?

That shift — from simply solving the problem to designing the solution — is what differentiates a backend developer from a backend engineer.

--

--

curiouswomen

Written by curiouswomen

I am here to share my experience being it a technical, non-technical or daily life experience that I am curious and thrive a positive change.