The Case for Boring APIs: Why Consistency Beats Cleverness
A cleverly-designed API endpoint — one that's unusually elegant, unusually flexible, unusually tailored to its specific use case — is often a worse addition to a codebase than a boring, conventional one, for a reason that has nothing to do with the clever design being wrong: it's the one endpoint out of fifty that doesn't behave the way the other forty-nine do, and every client integrating against your API now has to remember, specifically, that this one is different.
What "boring" actually buys you
A boring API is one where, having learned how endpoint #1 behaves, a developer can correctly guess how endpoint #30 behaves, without reading its documentation first. That's a genuinely valuable property, and it only survives if the API is relentlessly consistent about things that feel like small, independent decisions in the moment:
- The same pagination shape everywhere — not offset params on one endpoint and cursor params on another because two different people built them at different times with different opinions.
- The same error shape everywhere — one consistent structure for "what went wrong," not a bespoke format per endpoint depending on who wrote it.
- The same casing and naming conventions everywhere — not
created_aton one resource andcreatedAton another. - The same treatment of nulls, empty arrays, and missing fields everywhere — deciding once whether "no items" is an empty array or a missing field, and never relitigating that decision per endpoint.
None of these decisions is inherently better than its alternative. The value isn't in which convention you pick — it's in picking one and applying it everywhere, so a developer's mental model built from one part of the API transfers correctly to every other part.
Why "clever" tends to break this
A genuinely clever, tailored design for one specific endpoint's specific use case is often clever precisely because it deviates from the general pattern to better fit that one case — a bespoke filtering syntax that's more powerful than the standard query params used elsewhere, a response shape that omits the usual envelope because "this one doesn't need it." Each of these can be a locally good decision and a globally bad one, because the cost isn't paid by the endpoint's designer — it's paid by every future developer who has to remember it's the exception.
Where this connects to mocking and testing
A useful, if slightly indirect, way to catch inconsistency before it ships: if generating a mock for a new endpoint from your existing conventions feels awkward — the pagination doesn't fit the pattern your mock tooling already assumes, the error shape needs special-casing — that friction is itself a signal. A stateful CRUD route that "just works" the same way every other resource in your mock does is evidence the underlying API design is consistent enough that tooling built for the general case handles it without complaint. An endpoint that needs bespoke handling in your test tooling is very often the same endpoint that will need bespoke handling in every client that ever integrates with it — the mock is just often the first place that friction becomes visible.
Xerat02
Building MockBase.
More from the blog
Testing File Uploads Without a Real File Server
Oversized files, slow connections, and partial transfers are all normal real-world conditions that a lot of upload code never actually sees before launch.
What Actually Happens When Two Requests Race to Create the Same Thing
A double-click, two open tabs, or a retry that fires while the original is still processing all produce the same scenario: two near-simultaneous requests trying to create one thing. Most "create if it doesn't exist" code gets this wrong.