Let me start with a confession.
The first “REST API” I designed wasn’t RESTful. It wasn’t elegant. It wasn’t scalable.
It worked.
Until it didn’t.
If you’ve ever shipped an API that started clean and slowly turned into a jungle of endpoints like this, you know what I mean:
/createUser/deleteUser/updateUserDetails/getUserInfoByEmail
That is what I call API entropy.
This is not a theoretical explanation of REST. These are practical lessons learned while building APIs in real systems, from enterprise platforms to customer-facing cloud services where mistakes are expensive.
The Moment I Realised JSON over HTTP Isn’t REST
Early in my career, I thought REST meant three things:
- HTTP
- JSON
- Done
Then I actually read Roy Fielding’s original work.
REST is not about JSON. It is an architectural style built on constraints that give you important system properties.
| Constraint | What You Get |
|---|---|
| Statelessness | Horizontal scalability |
| Uniform interface | Evolvability |
| Cacheability | Lower cost and better performance |
| Layered system | Security and abstraction boundaries |
When you ignore these constraints, your API still works. Over time, it becomes fragile.
The Day Verbs Betrayed Me
I once inherited an API that looked like this:
POST /approveVendorPOST /rejectVendorPOST /validateVendor
It felt expressive. It quickly became difficult to maintain.
Every new workflow required a new verb. Over time, the surface area grew unnecessarily.
We redesigned it by modelling resources instead of actions:
POST /vendorsGET /vendors/{id}PATCH /vendors/{id}
Approval became state instead of a dedicated endpoint:
PATCH /vendors/{id}{ "status": "APPROVED"}
Once you think in nouns, the system becomes predictable. Predictability makes scaling teams and integrations much easier.
Idempotency Is Not Academic
We once had a simple endpoint:
POST /orders
Under load, clients retried requests. Sometimes the network dropped after submission.
The result was duplicate orders.
Nothing clarifies idempotency faster than explaining to finance why someone was charged twice.
HTTP semantics matter.
- PUT is idempotent.
- POST is not.
- Retries will happen whether you plan for them or not.
In distributed systems, idempotency is a resilience feature.
Pagination and the Cost of Growth
An endpoint worked perfectly returning a few hundred records.
Then a large customer onboarded and suddenly there were hundreds of thousands of rows.
Response times increased. Memory pressure increased. Database scans became painful.
We started with offset pagination:
GET /users?page=400&limit=50
At high offsets, performance degraded significantly.
We moved to cursor-based pagination:
GET /users?cursor=eyJpZCI6MTIzfQ==
It required more careful implementation but scaled much better.
Status Codes Are Operational Signals
Returning 200 OK for every response might seem harmless.
200 OK{ "error": "Invalid email"}
Monitoring systems rely on status codes to detect problems. If everything is 200, dashboards become misleading.
Correct usage matters:
- 201 Created for successful creation
- 400 for validation issues
- 404 when a resource does not exist
- 409 for conflicts
Versioning Is Inevitable
Every API begins as version one.
Eventually requirements change, and the original contract no longer fits.
In practice, URL versioning has been the simplest to manage:
/v1/users/v2/users
It is straightforward to route, document, and deprecate.
Caching Is Often Ignored
In one system, the majority of requests were read operations. We were not caching.
After implementing proper headers:
Cache-ControlETagIf-None-Match
Database load dropped noticeably and latency improved.
REST explicitly supports caching. It is one of the simplest performance improvements available.
Design Errors for Humans and Machines
{ "error": "Something went wrong"}
This tells neither the developer nor the system anything useful.
{ "code": "USER_ALREADY_EXISTS", "message": "A user with this email already exists.", "correlationId": "abc-123"}
Structured errors make debugging and monitoring significantly easier.
Security Is Part of the Design
Every API is an attack surface.
Use proper authentication standards, scope tokens carefully, expire them appropriately, and log access. Security decisions should be made at design time.
What I Eventually Realised
Good API design is less about cleverness and more about discipline.
When done well, an API becomes predictable, observable, evolvable, and stable.
The best APIs feel almost invisible. They do not surprise you. They scale with your organisation.
Most of these lessons are learned by cleaning up earlier mistakes. That is part of the process.