API Design Principles & Evolution
Mastering resource-oriented URI design, versioning strategies, and documentation as executable code.
URI Design & Resource Modeling
A well-designed REST API is intuitive because it relies on resource-oriented routing rather than Remote Procedure Calls (RPC). The Uniform Resource Identifier (URI) should represent a noun (the entity), while the HTTP method acts as the verb (the action).
Avoid embedding actions in the URL. Instead, structure URIs hierarchically to represent physical or logical relationships:
- Anti-Pattern (RPC):
/getAllUsers,/updateOrder?id=5,/deleteAccount - Professional Standard:
GET /users,PUT /orders/5,DELETE /accounts/123
To build highly discoverable systems, advanced APIs implement HATEOAS (Hypermedia as the Engine of Application State). Instead of hardcoding URLs in the client application, the server includes navigation links within the JSON response, allowing the client to dynamically discover available state transitions.
/users/5/orders/12/items/3) become brittle and difficult to cache. A best practice is to limit nesting to two levels. If you need item 3, fetch it directly via /items/3.Versioning Strategies
APIs are bound to evolve, but modifying an active contract will break downstream clients. You must introduce versioning before your first production deployment. There are two dominant strategies:
- URI Versioning (
/v1/orders): Extremely visible and easy to route via an API Gateway. However, it violates strict REST principles because a resource should theoretically have only one universal identifier. - Header Versioning (
Accept: application/vnd.api.v1+json): Architecturally pure. The URI remains constant, and the client requests a specific representation of the resource via HTTP headers.
Documentation as Executable Code
Treating API documentation as an afterthought is an engineering failure. Modern teams use specifications like OpenAPI (Swagger) to define the API contract in YAML or JSON before writing any backend code.
By defining the contract first, teams can generate:
- Interactive documentation automatically.
- Client SDKs in multiple languages.
- Mock servers for frontend teams to build against immediately.
Test Your Understanding
Q:A client application hardcodes the endpoint `/api/v1/checkout` to process payments. When the backend team upgrades the billing microservice, they move the endpoint to `/api/v2/checkout`, instantly breaking the client. How would implementing HATEOAS have prevented this outage? Reveal ▾
_links object containing the current URL for the checkout action. When the backend team migrated to v2, they would simply update the link provided in the cart response. The client would dynamically follow the new link, entirely unaware that the underlying routing had changed.