API Design for Products That Keep Evolving
· 8 min read
Build interfaces that support change without turning every release into a coordinated migration.
Model the domain, not the screen
Interfaces change faster than the concepts behind them. An API built around a specific screen tends to expose presentation state and force duplicate endpoints as new clients appear. Start with stable domain resources, their lifecycle, and the operations the business recognizes.
This does not require theoretical purity. A product-facing API can aggregate data for a journey, but the aggregation should have a clear purpose and ownership. Avoid leaking database tables directly; storage structure is an implementation detail that will eventually need to change.
Compatibility is a feature
Consumers deploy on different schedules, especially mobile applications and external integrations. Additive changes are usually safer: new optional fields, new resources, and new enum values with tolerant readers. Removing or redefining existing behavior requires a migration plan, telemetry, and a published end date.
Versioning is useful when semantics genuinely diverge, but version numbers do not replace compatibility discipline. A versioned API can still surprise consumers through changed ordering, error behavior, limits, or authorization. Maintain a machine-readable schema and test representative consumers against it.
- Treat unknown enum values safely
- Document nullability and defaults
- Use contract tests for critical consumers
- Measure deprecated-field usage before removal
Mutations need identity
Retries are unavoidable across unreliable networks. For important mutations, accept an idempotency key scoped to the caller and operation. Store the result so a repeated request returns the original outcome rather than performing the action again.
Long-running work should return an operation resource with explicit states. Clients can poll or subscribe without holding a fragile request open. This also improves support: the system can explain whether work is queued, active, completed, or failed and why.
Authorization belongs in the contract
Authentication establishes identity; authorization decides whether that identity may perform an operation on a resource. Enforce this on the server at the narrowest meaningful boundary. Hiding a button in the client is interface behavior, not access control.
Multi-tenant systems need tenant context that cannot be freely supplied and trusted by the client. Derive scope from verified membership, validate ownership on every resource access, and log administrative actions with enough context for audit and investigation.
Optimize for consumer understanding
Consistent naming, predictable pagination, useful errors, examples, and a clear change log reduce integration time more than clever protocol choices. An API is successful when consumers can use it correctly without learning its internal history.
Design reviews should include client engineers and operational scenarios. The interface will live longer than the first implementation, so spend precision on the parts that are hardest to change: identifiers, semantics, authorization, and lifecycle.
Published on September 18, 2024 by Berktug Berke Ates.