Hermes Wiki
Developer/CommunicationPatterns/Protocols/REST/Fundamentals/rest-architectural-constraints-and-the-richardson-maturity-model

REST Architectural Constraints and the Richardson Maturity Model

Concept

REST (Representational State Transfer) is not a protocol or a format — it's an architectural style defined by Roy Fielding in his 2000 doctoral dissertation, derived by identifying the constraints that made the web itself scale. Fielding names six: client-server separation (UI and storage evolve independently), statelessness (every request carries all context needed to process it — the server holds no per-client session between requests), cacheability (responses declare whether they can be cached, enabling intermediaries to serve repeat requests without hitting the origin), uniform interface (a small, standardized set of operations — the real core of the style, expanded below), layered system (a client can't tell whether it's talking to the origin server or an intermediary — proxies, gateways, load balancers — which lets those layers be added transparently), and code-on-demand (optional — servers can extend client behavior by transferring executable logic, e.g. JavaScript).

Of these, the uniform interface constraint is what most people actually mean when they say "REST," and it further decomposes into four sub-constraints: identification of resources (each resource has a stable URI), manipulation through representations (clients act on a representation — JSON, XML — not the resource itself), self-descriptive messages (a message contains enough metadata, like Content-Type, to be processed without out-of-band knowledge), and HATEOAS — hypermedia as the engine of application state, meaning a client should navigate the API by following links returned in responses rather than hardcoding URI templates it constructed itself.

That last sub-constraint is the one almost no production "REST API" actually implements, which is why Leonard Richardson proposed a maturity model (popularized by Martin Fowler) to distinguish "uses HTTP" from "is actually RESTful": Level 0 — a single URI, a single HTTP method (usually POST), the payload carries the real routing logic (RPC- or SOAP-style tunneled through HTTP). Level 1 — multiple URIs, one per resource, but still using a single HTTP method for everything. Level 2 — resources plus proper HTTP verbs (GET/POST/PUT/DELETE) and status codes conveying outcome, not just 200 with an error field in the body. Level 3 — HATEOAS: responses embed links (_links, or a custom hypermedia format) telling the client what it can do next, so the client doesn't need prior knowledge of the API's URI structure. The overwhelming majority of APIs people call "REST" — including most public APIs from major companies — sit at Level 2 and stop there deliberately.

Tradeoffs

Level What it buys What it costs
0 — single endpoint, tunneled RPC Nothing over plain RPC; often the honest starting point for internal tools No HTTP semantics at all — no caching, no verb-based authorization, opaque to any HTTP-aware tooling
1 — resources, one verb URIs become meaningful and cacheable individually Still can't distinguish read from write by verb; every request risks side effects
2 — resources + verbs + status codes HTTP caching works for free (GET is cacheable by URL), intermediaries and tooling (browsers, curl, API gateways, monitoring) understand it without custom logic, safe default for public APIs Client still must know the URI structure and construct URLs itself; API evolution can break clients that hardcoded paths
3 — HATEOAS Client decouples from URI structure — server can restructure URLs, add new transitions, or change available actions per-resource-state without breaking clients that follow links Real implementation and design cost; almost no client tooling or generated SDKs actually consume hypermedia controls, so the benefit is rarely realized in practice

When to use / when not to

  • Level 2 is the correct default for the overwhelming majority of public and internal APIs — it gets HTTP caching, standard status-code semantics, and universal tooling support for a modest design cost, which is exactly why it's what "REST API" means colloquially.
  • Reach for Level 3 HATEOAS only when client-server URI coupling is a genuine, recurring pain — e.g. a long-lived API surface consumed by many independent client teams where the server needs freedom to restructure resource relationships, or a resource with a real state machine (an order that can be paid, shipped, cancelled depending on state) where embedding "what actions are valid right now" in the response removes a whole class of client-side business logic duplication. Payment processors (Stripe's expandable/linked objects, PayPal's HATEOAS-driven Orders API) are the clearest real examples.
  • Don't force pure resource modeling onto operations that are inherently actions, not nouns — login, search, bulk-import, "send password reset email." Most Level-2 REST APIs pragmatically break the pure model here with verb-shaped endpoints (POST /password-resets, treating the action itself as a resource) rather than distorting the domain to fit CRUD.
  • Don't call an API "RESTful" if it keeps session state server-side between requests (sticky sessions, in-memory auth state) — that violates statelessness, the constraint that actually enables horizontal scaling and load-balancer transparency, regardless of how resource-shaped the URLs look.

Common pitfall

Treating "uses JSON over HTTP with resource-shaped URLs" as sufficient to call something REST, while quietly keeping server-side session state or ignoring HTTP status codes (returning 200 OK with {"success": false} in the body). The failure isn't cosmetic: an API that isn't actually stateless can't be load-balanced without sticky sessions, defeating the layered-system constraint that makes REST's caching and intermediary story work in the first place — and clients or proxies that trust HTTP status codes (retry on 5xx, don't retry on 4xx, cache on 200) silently misbehave against an API that always returns 200.

Engineering Lens

Statelessness, not resource-shaped URLs, is the constraint that actually pays for itself at scale — it's what lets any request land on any server instance with zero coordination, which is the entire mechanism behind trivial horizontal scaling and rolling deploys. HATEOAS is the constraint nearly everyone consciously skips, and that's mostly fine: the cost (real hypermedia design, client tooling that can consume it) rarely clears the bar against the benefit (URI-structure decoupling) for APIs with a small number of well-coordinated client teams. The design review question worth asking isn't "are we RESTful" as a purity check — it's "which of these six constraints does our actual scaling and evolution story depend on," since that's usually just statelessness and cacheability, with the uniform interface's Level 2 subset doing the rest.

Sources

Hermes Wiki