15 Modifying an API
APIs rarely remain static, so modifying them demands the same care as designing them from scratch: continue to meet needs while staying user-friendly, interoperable, secure, efficient, and aligned with context. The chapter shows how seemingly simple changes—like replacing a single-value field with a collection or restructuring a price into value-plus-currency—can break consumers if deployed abruptly. It emphasizes planning and communication, favoring backward-compatible adjustments when possible, and, when not, managing change through clear versioning and transition paths. A robust initial design significantly reduces the risk and cost of future changes.
The chapter maps where breaking changes arise and how to avoid them: output and input data (only add or widen limits to remain compatible), resource paths and operations (renames, removals, and method swaps break), HTTP status codes (strict checks and behavior shifts can fail clients), and multi-step flows (inserting new steps can silently break use cases). It calls out the “invisible contract” where consumers rely on undocumented behaviors, and advises preventing unintended changes via OpenAPI-aware diffs, CI/CD checks, and thorough testing. Security changes require special care: replacing schemes (for example, API key to mutual TLS) is necessarily breaking; scope changes can break or introduce breaches if misaligned. Versioning is framed as an interface concern distinct from implementation; semantic versions are preferred (with date-based as an alternative), path-level version signaling is the default, and choosing scheme and representation early avoids confusion. Sub-API-level versioning (per resource or operation) is discouraged for complexity and interoperability reasons.
To limit future breakage, the chapter promotes extensible design: provide ready-to-use, interpretable data; accept extra input gracefully; design flexible flows; and keep APIs right-sized rather than monolithic. It recommends learning from past trade-offs and adopting patterns that evolve well: always wrap bodies in objects, prefer arrays of objects over atomics, use interpretable standards (such as ISO durations), group similar data in arrays, favor enumerations over booleans, and expose operations instead of hard-coded enumerations that change frequently. When breaking is justified, evaluate impacts by identifying consumer types and usage, determine if multiple versions can run concurrently (e.g., adapters), align with versioning policy, and balance cost against consumer and business value. Communicate changes with OpenAPI: set the API version, mark deprecated elements, maintain a changelog, and, at runtime, surface deprecation with a Sunset header to guide smooth migrations.
Modifying a consumed API is "design as usual" with the constraint of determining how modifications will affect consumers and making trade-offs, if necessary. The initial design can facilitate later modifications.
Caring for design modification starts when creating an API. Modifying an API is "design as usual" with the addition of caring for breaking changes and modification logs.
Breaking changes can occur in headers and bodies of any success or error response. Only add data or modify it within the limits of the original one to be backward-compatible.
The breaking changes in output data require updating the consuming applications' code. Backward-compatible changes have no impact on non-updated consumers, but they won’t use the newly added data.
Breaking changes are introduced while enhancing an error data model. The error list is renamed, and specific error types are replaced by generic ones.
Breaking changes can occur in request bodies, headers, query parameters, and path (including path parameters). Only make required data optional, add new optional data, and expand the limits of existing data to be backward-compatible.
The breaking changes in the data model require updating the consumer code. Backward-compatible changes have no impact on consumers. However, non-updated applications will continue to send removed data and never send newly added data.
We version the API from the consumer perspective. Implementation modifications that don’t affect the interface contract are not reflected in the API version numbering.
Semantic versioning indicates the type of change, while date-based versioning doesn’t.
The API version can be indicated in the path, domain name, query parameter, header, media type, or an out-of-the-band configuration.
We can indicate the API version or API plus document version in info.version.
Use the deprecated flag to mark elements that should no longer be used but must be kept to maintain backward compatibility.
Leverage Markdown to add changelogs to the API and any element that supports description. Consider adding tags to highlight modified operations.
The Design of Web APIs, Second Edition ebook for free