2026-08-06 17:06:04.850824+08 / restful-api

Compatibility

关于 API 向后兼容、破坏性变更、版本策略和消费者健壮性的设计指南。

Restful ApiApi DesignOpenapi

Compatibility

1. Overview

Backward compatibility is one of the most important principles in API evolution.

An API is a contract between service providers and consumers. API providers should avoid breaking existing clients through unilateral changes because consumers usually have independent release cycles and require system stability.

When evolving an API, prefer compatible extensions instead of introducing incompatible changes.

If an incompatible change is unavoidable, a new major API version should be introduced, allowing existing consumers to continue using the previous version during an agreed migration period.


2. Backward Compatibility Within Major Versions

APIs MUST NOT introduce breaking changes within the same major version.

Compatible API evolution should follow these principles:

  • Existing clients should continue working without modification.
  • New functionality should be introduced through additive changes.
  • API consumers should be able to ignore unknown fields or values introduced by future compatible versions.

Breaking changes should only be introduced by increasing the major version.

Example:

text
/api/orders/v1/orders

A breaking change requires a new major version:

text
/api/orders/v2/orders

3. Prefer Compatible Extensions

API providers SHOULD prefer backward-compatible extensions.

The following practices help maintain compatibility.

3.1 Add Optional Fields Only

New fields added to request or response objects should always be optional.

Good:

json
{
  "id": "123",
  "name": "example",
  "description": "optional field"
}

Adding description does not break existing clients.

Avoid:

json
{
  "id": "123",
  "name": "example",
  "mandatoryField": "new-value"
}

Adding required fields forces clients to update.


3.2 Do Not Change Field Meaning

The semantic meaning of existing fields MUST NOT change.

Example:

Before:

json
{
  "customerNumber": "10001"
}

Changing the meaning of customerNumber from one identifier type to another breaks consumers.

If a new concept is required, introduce a new field instead.


3.3 Keep Existing Validation Rules

Input validation rules should not become more restrictive in compatible versions.

Example:

Existing API:

text
username length: 1-100 characters

Changing it to:

text
username length: 5-20 characters

may break existing clients.

Validation constraints should be clearly documented.


4. Enum Compatibility

Enums require special attention during API evolution.

Response Enum

Response values should not be extended unexpectedly.

Example:

Existing response:

json
{
  "status": "ACTIVE"
}

Allowed values:

text
ACTIVE
INACTIVE

Adding:

text
PENDING

may break clients that assume only existing values.


Request Enum

Request enums can be extended.

Example:

Client sends:

json
{
  "type": "STANDARD"
}

A future API version may allow:

text
STANDARD
PREMIUM
ENTERPRISE

Existing clients remain compatible.


5. Response Structure Compatibility

API responses should be designed for future extensions.

Always Return JSON Objects

The top-level response structure SHOULD always be a JSON object.

Recommended:

json
{
  "items": [
    {
      "id": "1",
      "name": "example"
    }
  ]
}

Avoid:

json
[
  {
    "id": "1",
    "name": "example"
  }
]

Because arrays cannot easily be extended with additional metadata.

For example, pagination requires additional fields:

json
{
  "items": [
    {
      "id": "1"
    }
  ],
  "nextPage": "/items?page=2"
}

6. OpenAPI Extension Rules

OpenAPI schemas should be considered extensible by default.

Clients consuming API responses:

  • MUST NOT assume objects are closed.
  • MUST ignore unknown fields.
  • SHOULD tolerate additional properties.

Example:

Current response:

json
{
  "id": "123",
  "name": "test"
}

Future response:

json
{
  "id": "123",
  "name": "test",
  "createdAt": "2026-08-06"
}

Old clients should continue working.


7. Consumer Robustness Principle

API consumers SHOULD follow the robustness principle:

Be conservative when sending requests, but tolerant when processing responses.

Consumers should:

Ignore Unknown Fields

Example:

json
{
  "id": "123",
  "name": "test",
  "newField": "future extension"
}

Clients should process known fields and ignore unknown ones.


Handle New Enum Values

Clients should not fail when receiving new optional enum values.

Example:

Existing:

text
ACTIVE
DISABLED

Future:

text
ACTIVE
DISABLED
SUSPENDED

Unknown values should be handled gracefully.


Handle Unknown HTTP Status Codes

HTTP status codes are extensible.

Clients should provide fallback handling.

Example:

Known:

text
200 OK
400 Bad Request
404 Not Found

Future:

text
422 Unprocessable Entity

Clients should handle unexpected codes appropriately.


8. Breaking Changes

A breaking change requires clients to modify their implementation.

Typical breaking changes include:

Rename Resource or Method

Before:

text
GET /users/{id}

After:

text
GET /customers/{id}

Existing clients fail.


Change URL Structure

Before:

text
/orders/v1/items

After:

text
/orders/v1/order-items

The old client path no longer works.


Remove Existing Fields

Before:

json
{
  "id": "1",
  "name": "test"
}

After:

json
{
  "id": "1"
}

Removing name breaks consumers.


Rename Existing Fields

Before:

json
{
  "customerId": "100"
}

After:

json
{
  "clientId": "100"
}

Field renaming is a breaking change.


Change Response Codes

Before:

text
404 Not Found

After:

text
400 Bad Request

Clients depending on status codes may fail.


Change Error Structure

Error responses are part of the API contract.

Changing:

json
{
  "errorCode": "USER_NOT_FOUND",
  "message": "User does not exist"
}

to:

json
{
  "code": "404",
  "reason": "missing"
}

may break error handling logic.


9. Versioning Strategy

Use semantic versioning:

text
MAJOR.MINOR.PATCH

Example:

text
1.2.3

Meaning:

  • MAJOR: incompatible changes
  • MINOR: backward-compatible features
  • PATCH: backward-compatible fixes

10. API Version in URI

The major version should be included in the URI.

Example:

text
https://api.example.com/order-service/v1/orders

Breaking change:

text
https://api.example.com/order-service/v2/orders

Only major version should appear in stable API paths.


11. Content Negotiation

When supporting multiple representations, APIs should support content negotiation.

Example:

Request:

text
GET /orders/v1/orders/123
Accept: application/json

Response:

text
Content-Type: application/json

Alternative:

text
GET /orders/v1/orders/123
Accept: application/yaml

Response:

text
Content-Type: application/yaml

12. Schema Version Negotiation

When multiple schema versions are supported, clients may request a specific version.

Example:

Request:

text
GET /orders/v1/orders/123

Accept-Version: 1.2.0

Response:

text
HTTP/1.1 200 OK

Content-Type: application/json
Schema-Version: 1.2.0

If the requested version is unavailable:

text
HTTP/1.1 406 Not Acceptable

should be returned.


13. Schema Location

APIs SHOULD provide a link to the schema definition.

Example:

Response:

text
Schema-Version: 1.2.0
Schema-Location: https://api.example.com/schema/order.json

This allows consumers to discover the exact payload definition.


14. URI Naming Guidelines

Use Lowercase Host Names

Recommended:

text
https://api.example.com

Avoid:

text
https://API.Example.COM

Recommended Host Format

text
api.<namespace>.<domain>

Example:

text
api.order.example.com

Summary

A well-designed REST API should evolve without breaking existing consumers.

Core principles:

  1. Prefer additive changes over breaking changes.
  2. Never remove or rename existing API contracts.
  3. Add optional fields only.
  4. Keep response objects extensible.
  5. Make clients tolerant of future extensions.
  6. Introduce a new major version only when compatibility cannot be maintained.
  7. Treat API schemas, URLs, status codes, and error formats as long-term contracts.

Backward compatibility is not only a versioning strategy; it is a fundamental API design principle.

Compatibility | Remi Resume