Clean APIs and sensible rate limiting protect your system and make it pleasant to build on. Both come up constantly in system design discussions, especially for public-facing services.
REST API principles
- Resource-oriented URLs (
/users/123/orders) with HTTP verbs (GET, POST, PUT, DELETE). - Statelessness — each request carries what it needs; no server-side session state.
- Proper status codes — 2xx success, 4xx client error, 5xx server error.
- Versioning —
/v1/…so you can evolve without breaking clients.
Idempotency
An operation is idempotent if doing it multiple times has the same effect as doing it once. GET, PUT and DELETE should be idempotent; POST usually isn't. For critical writes (like payments), use an idempotency key so a retried request doesn't charge twice.
Pagination
Never return unbounded lists. Use offset-based pagination (?page=2&limit=20) for simplicity, or cursor-based pagination for large, changing datasets where offsets drift and get slow.
Rate limiting algorithms
Rate limiting caps how many requests a client can make in a window, protecting the system from abuse and overload:
- Token bucket — tokens refill at a fixed rate; each request spends one. Allows bursts. Most popular.
- Leaky bucket — requests drain at a constant rate; smooths bursts.
- Fixed window — count per fixed interval; simple but bursty at edges.
- Sliding window — smooths the fixed-window edge problem for fairer limiting.
In a distributed system, the counters usually live in a shared store like Redis so limits apply across all servers.