APIs are contracts. Once external clients depend on them, changing them without versioning breaks things. The design decisions you make early get baked into client code, documentation, and muscle memory of everyone using your API.
Here are the mistakes I have made or seen made, and what the better version looks like.
Inconsistent Response Shapes
When success responses, error responses, and edge cases all have different shapes, API consumers have to write defensive parsing code everywhere.
Choose a consistent envelope and stick to it:
{
"data": { ... },
"error": null,
"meta": { "total": 100, "page": 1 }
}Errors always come in the same place. Data always comes in the same place. Clients can write one response handler instead of per-endpoint logic.
Using HTTP Status Codes Wrong
200 OK with { "success": false, "error": "Not found" } in the body is worse than just returning a 404. Clients checking status codes (which all HTTP clients can do natively) get wrong signals.
The basics:
200— success, resource returned201— success, resource created400— client error (bad input)401— not authenticated403— authenticated but not authorized404— resource does not exist422— validation errors on correct structure429— rate limited500— server error
Premature Nesting in URLs
/api/users/:userId/projects/:projectId/tasks/:taskId/comments/:commentIdDeep nesting like this makes every URL hard to read and harder to cache. Two levels of nesting is usually the right limit. Beyond that, flatten:
GET /api/comments?taskId=123Not Versioning From Day One
Even if you are the only consumer right now, add a version prefix to your API path from the beginning:
/api/v1/shares
/api/v1/usersWhen you need to make breaking changes (and you will), /api/v2 can exist alongside /api/v1 while clients migrate. Without versioning, every breaking change is an emergency.