APIs and data
API design
Section titled “API design”- Contract first: keep an OpenAPI (or GraphQL) schema in the repo and generate types or validate against it.
- Plural nouns, lowercase hyphenated paths, standard methods and status codes (200, 201, 204, 400, 401, 403, 404, 409, 422, 429).
- One error format everywhere (for example RFC 9457 problem details): a stable error code, a human message and field errors. Never leak stack traces, queries or internals.
- Validate bodies, path params and query strings against the schema. Reject unknown fields on writes.
- Paginate every list with a maximum page size; use cursors for large or changing sets. Filter and sort only on indexed fields.
- Make retries safe:
PUTandDELETEare idempotent, and creates or payments accept an idempotency key. - Additive changes need no new version. Breaking changes get a new version and a deprecation period for consumers.
- Authenticate every non-public endpoint and authorise per object. Rate-limit and return 429 with
Retry-After. - Webhooks: verify signatures, tolerate duplicates and out-of-order delivery, respond fast and process in the background.
- Timestamps are ISO 8601 in UTC. Money is an integer in minor units plus a currency code. Public IDs are opaque (UUID or ULID) where enumeration matters.
Data modelling
Section titled “Data modelling”- Enforce invariants in the database, not only in code:
NOT NULL, unique, foreign key and check constraints. - Store UTC timestamps and UTF-8 text. Money is never a float.
- Wrap multi-step writes in a transaction. Handle concurrent updates with optimistic locking or row locks.
- Use soft delete only when restore or audit needs it, and filter it everywhere.
- Never store passwords or secrets in plain text. Encrypt sensitive fields where the law or risk requires it.
Migrations
Section titled “Migrations”- Every schema change is a committed, reviewed migration. Never edit an applied one.
- Keep migrations compatible with the running code: expand, migrate the data, then contract in a later deploy. Avoid long locks on large tables.
- Data migrations are idempotent and run in batches. Plan a rollback or forward fix before running a risky one.
Backups and retention
Section titled “Backups and retention”- Production databases have automated backups with point-in-time recovery. Test a restore at least every quarter.
- Define a retention period for each kind of personal data, and delete or anonymise it when that period ends.
- Never copy production personal data into development or test environments without anonymising it.