Validate at the boundary
Every piece of data entering your service should be validated and converted into a known shape before any business logic touches it. Python's typing and validation libraries make this straightforward.
A service that validates at the boundary can trust its own internals. One that validates ad hoc throughout is where the subtle bugs live.
Consistent errors
- Meaningful status codes — not 200 with an error in the body
- A single error shape used everywhere
- A machine-readable code alongside the human message
- Field-level detail for validation failures
- No internal detail leaked in production error messages
Version before you have consumers
Adding versioning after the first consumer means either breaking them or maintaining an unversioned path forever. A path prefix decided at the start costs nothing.
Then be explicit about what constitutes a breaking change, and communicate deprecations with real dates.
Document from the code
- Generated from the schemas, so it cannot drift
- Example requests and responses for every endpoint
- Every error code listed with its cause
- Authentication explained with a working example
- Rate limits stated explicitly
The test of good API documentation is whether someone can build against it without contacting you. Most fail that test.
Operational requirements
| Requirement | Why |
|---|---|
| Health endpoint | Load balancers and monitoring need it |
| Request identifiers | Tracing a single call through logs |
| Structured logging | Searchable when something goes wrong |
| Rate limiting | Including for internal consumers |
| Graceful shutdown | Deployments without dropped requests |