API Design, Integration, and Interoperability
API Design, Integration, and Interoperability tests whether you can define contracts other teams can depend on: HTTP semantics, versioning that does not break consumers, pagination, rate limits, and event schemas that evolve safely.
Contract rules
- Resources are nouns; methods carry the verb. GET is safe and idempotent, PUT and DELETE idempotent, POST neither.
- Status codes are part of the contract: 201 with Location for creation, 202 for accepted async work, 409 for conflicts, 422 for semantic validation, 429 with Retry-After.
- Errors use one machine-readable envelope (RFC 9457 problem details: type, title, status, detail, instance) with a stable application error code.
- Version explicitly in the path or media type. Adding optional fields is non-breaking; removing, renaming, or retyping a field is breaking.
- Paginate large or changing collections with cursors, not offsets, and cap page size server-side.
Integration patterns
- Synchronous calls for user-facing queries; events for fan-out and decoupling.
- Publish events through a transactional outbox so the commit and the event are atomic.
- Consumers must be idempotent - delivery is at-least-once.
- Webhooks need HMAC signature verification, timestamp replay protection, backoff retries, and a dead-letter path.
- Schema registries enforce compatibility: backward (new code reads old data), forward, full.
Worked example
An order.created event gains a required currency field. Consumers deserializing with a strict schema reject every event - a breaking change despite being "just one field". Correct sequence: add currency as optional with a documented default, deploy producers and consumers in either order, backfill historical values, and only later consider enforcement. The registry's backward-compatibility mode blocks the breaking publish at the producer's CI rather than in a consumer's runtime.
Traps
- Returning 200 with an error body, so clients cannot distinguish failure from success.
- Using GET for state-changing operations, then breaking when a proxy retries it.
- Deleting a field in a minor version.
- Retrying a POST without an idempotency key.
- An unbounded default page size that times out under load.