API design decisions that cost you two years later
The expensive API mistakes are all made in week one and paid for in year two.
An API is the one part of a system you cannot quietly refactor, because other people's code depends on the exact shape of it. That makes a handful of early decisions unusually expensive to get wrong — not because they are hard, but because they are permanent.
1. Versioning, decided before the first consumer
Put a version in the path from day one, even when there is only one version. Retrofitting versioning onto a live API means breaking every consumer once to gain the ability to never break them again, and that migration never happens at a convenient time.
2. Identifiers that do not leak or collide
- Sequential integer IDs expose your record counts and make enumeration trivial. Use opaque identifiers in public APIs.
- Never reuse an identifier after deletion. Somewhere a cache still holds the old meaning.
- Decide early whether IDs are globally unique or unique per type, and never change the answer.
3. Errors a client can act on
A correct status code, a stable machine-readable error code, a human-readable message, and a field pointing at what was wrong. Clients build logic on the machine-readable code, so it must never change meaning — which means it has to be designed, not generated from whatever the exception happened to be.
4. Pagination that survives writes
Offset pagination breaks when records are inserted mid-traversal — consumers silently skip or duplicate rows and nobody notices for months. Cursor pagination costs slightly more to implement and does not have this class of bug.
5. Time, money and nulls
- All timestamps in UTC, in a single ISO format, with the timezone included. Ambiguity here produces bugs that appear only twice a year.
- Money as integer minor units with an explicit currency, never as a float.
- A documented distinction between null, absent and empty. Clients will infer one if you do not state it, and each will infer differently.
6. Rate limits and idempotency
Publish the limits and return the remaining quota in headers, so a consumer can behave well. Accept an idempotency key on anything that creates or charges, because networks fail after the server has already acted and the client will retry.
7. Documentation generated from the code
Hand-written API documentation is wrong within a quarter. Generate it from the definition, publish the schema, and treat a documentation mismatch as a bug rather than a chore.
None of these is difficult on day one. All of them are expensive on day five hundred, which is the entire point.
The services behind this post.
Frequently asked questions.
REST for public APIs with many unknown consumers and cacheable resources; GraphQL when a small number of known clients need flexible, varied queries over connected data.
Long enough for your slowest consumer to migrate, announced in advance with a firm date. Indefinite support is how organisations end up maintaining four versions.
Talk to the team that does this every day.
Tell us what you're trying to grow. You'll get a plan, not a pitch.
Contact Us