System Design ยท 5 of 5
Why a "quick" field rename in your API broke three mobile clients, and how resource modeling, pagination, versioning, and rate limiting prevent that from happening again.
Someone on your team renames a field in the /users response because the old name was confusing. Ships it. Two hours later, the mobile app โ which you don't control the release cadence of, because it's sitting in an app store review queue โ starts throwing null-pointer exceptions for every user on an older app version. The API didn't break; it changed, and something else was depending on it not changing.
This is the actual reason API design discipline exists. It's not about following REST conventions for their own sake โ it's that your API is a contract with clients you don't always control, and every decision below exists to either make that contract clearer or let it evolve without breaking anyone.
REST (Representational State Transfer) is an architectural style built on HTTP. The parts that actually matter day-to-day:
/users can guess how /orders works.Design your API around resources โ nouns โ not actions. This isn't a style preference; action-oriented endpoints tend to multiply endlessly (/createUser, /updateUserEmail, /updateUserPassword...) while resource-oriented ones compose.
POST /users # Create a user
GET /users # List all users
GET /users/{id} # Get a specific user
PUT /users/{id} # Update a user
DELETE /users/{id} # Delete a user
GET /users/{id}/posts # Get posts by a user
POST /users/{id}/posts # Create a post for a user
POST /createUser
GET /getUser/{id}
POST /updateUser
POST /deleteUser/{id}
GET /getUserPosts/{id}
Status codes exist so clients can react programmatically without parsing an error message string. Using them inconsistently is one of the fastest ways to make an API frustrating to integrate against.
| Code | Meaning | When to Use |
|---|---|---|
| 200 | OK | Successful GET, PUT, DELETE |
| 201 | Created | Successful resource creation (POST) |
| 204 | No Content | Successful request with no response body |
| 400 | Bad Request | Invalid input, malformed JSON |
| 401 | Unauthorized | Missing/invalid authentication token |
| 403 | Forbidden | Authenticated, but lacks permission |
| 404 | Not Found | Resource doesn't exist |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Internal Server Error | Unrecoverable server error |
| 503 | Service Unavailable | Server temporarily down (maintenance, overload) |
For endpoints returning large result sets, pagination isn't optional โ without it, a client that wants "recent orders" ends up asking your database to hand over a table's worth of rows.
GET /users?limit=20&offset=40
GET /users?limit=20&cursor=eyJpZCI6IDUwfQ==
{"id": 50}).This is the direct fix for the field-rename incident at the top of this chapter. Once you have external clients you don't fully control, you can't just change a response shape โ you version it, and let old clients keep working against the old version while new clients get the new one.
GET /v1/users
GET /v2/users # Different response schema
GET /users
Header: API-Version: 2
GET /users?api_version=2
Deprecation and Sunset headers so automated tooling can flag it, not just humans reading changelog emails.Rate limiting exists to protect your API from being overwhelmed by a single client โ whether that's abuse, a misbehaving retry loop, or just one customer's integration being far more active than everyone else's.
429 Too Many Requests.Tell the client where they stand instead of letting them find out by getting rejected:
HTTP/1.1 200 OK
RateLimit-Limit: 1000
RateLimit-Remaining: 999
RateLimit-Reset: 1618329600
A consistent, structured error response is what lets a client handle failures programmatically instead of showing users a raw stack trace or a generic "something broke" message.
{
"error": {
"code": "INVALID_EMAIL",
"message": "The provided email is not a valid format",
"details": {
"field": "email",
"received": "not-an-email"
}
}
}
{
"error": "Something went wrong"
}
These five chapters cover the building blocks most systems are made of: caching, event streaming, databases, load balancing, and API design. The chapters after this move up a level โ into the foundational trade-offs (the CAP theorem, consistency models, scalability patterns) and real "design X" walkthroughs (a URL shortener, a rate limiter, a notification system) that combine everything covered so far into complete systems.
Get an email when I publish the next chapter. No spam โ just new technical deep-dives.
Comments
Share feedback or questions about this blog post.
No comments yet. Be the first to share your thoughts.