Non-Breaking API Evolution
An API can evolve by adding optional capabilities while preserving existing client contracts. The goal is to let old clients continue working while new clients adopt additional behavior at their own pace.
Non-breaking API evolution means changing an API without invalidating requests, responses, or assumptions made by existing clients. A common approach is to add optional request fields, new endpoints, or additional response fields while retaining the existing contract.
For JSON APIs, adding a response property is generally safer than removing or renaming one, because well-behaved clients typically ignore fields they do not understand. Similarly, a newly introduced request field should normally be optional and have a sensible default so older clients remain valid.
Breaking changes include removing fields, changing their meaning or type, making an optional field required, changing authentication requirements incompatibly, or altering documented behavior that clients depend on. Compatibility therefore covers more than URL syntax; it includes schemas, semantics, error behavior, and operational expectations.
In a Java backend, DTOs and serialization configuration should be designed with compatibility in mind. API contracts should be tested against representative old clients, and changes should be documented so consumers can migrate deliberately when a breaking change is unavoidable.
Versioning is still useful when incompatible behavior must be introduced. However, versioning an API does not itself make a change non-breaking; the key property is whether existing consumers can continue using their established contract unchanged.
Key points
- Prefer additive changes such as optional request fields and additional response fields.
- Do not remove, rename, or change the type or meaning of fields used by existing clients.
- Give newly added request fields backward-compatible defaults when appropriate.
- Treat semantics, validation, errors, authentication, and documented behavior as part of the API contract.
- Use a new API version when an incompatible contract genuinely cannot be avoided.
Interview questions
Is adding a new field to a JSON response always non-breaking?
No. It is usually non-breaking for clients that ignore unknown properties, but that behavior depends on the client's parser and contract. A client configured to reject unknown fields can fail when the server adds a property. Therefore, compatibility should be verified against actual consumer behavior and documented serialization rules.
What are common examples of breaking API changes?
Removing or renaming an existing field is breaking because clients may depend on it. Changing a field's type or documented meaning can also invalidate existing code. Making a previously optional request parameter mandatory is another common breaking change. Changes to authentication, validation, error contracts, or side effects can also break integrations.
What makes an API change backward compatible?
A change is backward compatible when existing clients can continue making their existing requests and correctly process the responses. Additive changes are commonly safer because clients can ignore information they do not use. Removing fields or changing their meaning can break consumers even when the endpoint URL remains unchanged.
How would you evolve a Java REST API without breaking existing consumers?
I would first identify the existing consumer contract, including schemas, validation, errors, authentication, and documented semantics. I would prefer additive changes, such as optional request properties or additional response fields, while preserving existing defaults and behavior. I would verify compatibility with contract tests and representative older clients before deployment. If an incompatible requirement is unavoidable, I would introduce a separately managed contract or API version and provide a migration path.
Can changing an API's behavior be breaking even when its schema stays identical?
Yes. A schema describes structure, but clients can also depend on semantics and operational behavior. For example, changing a successful operation into an idempotent or non-idempotent one, altering authorization rules, or changing error semantics can affect existing integrations. Compatibility analysis therefore needs to examine both data contracts and behavioral contracts.