Every February we tackle one topic in depth as a team. This year the vote was unanimous: API design. Not because we were bad at it, but because the projects where APIs were designed carelessly always came back to haunt us.
The cost of a careless contract
On a recent e-commerce project the backend team shipped endpoints as features were requested. No schema. No versioning. No documented error responses. The frontend team built against whatever came back.
Three months in, the client wanted a mobile app. The mobile developers looked at the API and immediately found inconsistencies — different date formats across endpoints, nested data in some responses and flat data in others, error codes that meant different things depending on the route.
The API refactor took longer than the original build.
Contract-first changes the conversation
Contract-first design means writing the API specification before writing a single line of implementation code. You describe endpoints, request shapes, response shapes, and error formats in a structured document — OpenAPI, JSON Schema, or even a well-organized markdown file.
This inverts the dynamic. Instead of the backend dictating what the frontend receives, both teams agree on a contract. The frontend can build against mocked data from day one. The backend can implement against a clear target.
What a good contract includes
Consistent naming. If you call it created_at in one endpoint, call it created_at everywhere. Pick a convention — camelCase or snake_case — and enforce it.
Predictable pagination. Every list endpoint should paginate the same way. We standardize on cursor-based pagination with next and previous links in a meta object.
Typed error responses. Every error returns a consistent shape: a code string for programmatic handling, a message string for logging, and an optional details array for field-level validation errors.
Versioning from the start. Even if you only have v1, prefix your routes. /api/v1/ costs nothing today and saves a painful migration later.
Tooling that enforces the contract
We validate contracts automatically. On every pull request a CI step checks that the implementation matches the OpenAPI spec. If an endpoint returns a field not in the spec, the build fails.
For TypeScript projects we generate types directly from the spec. The frontend and backend share one source of truth. A schema change produces type errors in both codebases instantly.
A practical starting point
If you have an existing API with no contract, start with what you have. Run your test suite, capture the responses, and generate a schema from them. It will be imperfect, but it documents reality. From there you can incrementally tighten the spec — fixing inconsistencies one endpoint at a time.
The best API is not the cleverest one. It is the most predictable one.