By the end of the lesson, you can decide whether an API change is backward compatible or breaking, you know the options for versioning, and you can say what usable API documentation must contain.
Why do changes to an API have to be managed?
For several clients an API is a long-term contract. The server, however, changes: new features, renames, fixes. Clients do not update at the same moment: a browser interface immediately, a mobile app after weeks or months, a partner's system perhaps never. If the provider changes the contract overnight, the old clients break, while the network and HTTP work flawlessly. Versioning, compatibility, and documentation make change traceable and manageable.
Renaming a field with several clients
So far the API of Kurzuspont has sent a cim field. A new server development would rename it to megnevezes. The new interface would read the new field, but the old mobile app still expects cim. Tried, an old and a newer client with the same three responses:
constregiKliens = v => "Kurzus: " + v.cim;
constujKliens = v => "Kurzus: " + (v.megnevezes ?? v.cim);
The rename broke the old client (undefined), the extension did not. The third solution temporarily sends the two fields together, so both clients work.
flowchart LR
R[Old client: cim] --> A[API response]
U[New client: megnevezes] --> A
A --> D[Compatibility decision]
D --> M[Temporary coexistence or a new version]
Compatible and breaking changes
Change
Effect on the old client
Typically
New, optional response field
The old client can ignore it
backward compatible
New endpoint
The old client does not use it
compatible
Removing a mandatory field
The client cannot find it
breaking
Renaming a field
As above
breaking
Changing the type of a field (number → string)
May cause a type error (see 5.2)
breaking
Changing the meaning of a field while keeping its name
Silently wrong behavior
breaking, especially insidious
New mandatory request field
The old client does not send it
breaking
Whether a modification is really compatible depends on the actual contract and the behavior of the clients. The error responses (status code, error format) are also parts of the contract: if the client has so far counted on a given status and error shape, a change can break the operation there too.
Versioning options
A new version can be distinguished in several ways:
in the path:/api/v2/kurzusok;
in a request header: for example the client signals the requested version in a header or in the media type;
in the schema: with GraphQL, introducing new fields and marking the old ones as deprecated, without a version number.
There is no single mandatory form. The goal is that the client knows which contract it is using, and that there is a transition if the old version is later retired.
What should the documentation contain?
A usable API description says:
what the service is for and what access is needed for it;
where the operations can be reached, with which method;
what input is needed, which fields are mandatory or optional;
what successful and failed response is expected (status code and body);
what the fields mean, what type they are, what can be missing or null;
how the contract has changed, what is being retired.
Examples help, but they do not replace the description of the rules: a sample response does not say what happens with missing data or an authorization error.
flowchart TD
S[API contract] --> M[Operations and inputs]
S --> V[Responses and errors]
S --> A[Meaning of data fields]
S --> K[Compatibility and changes]
The OpenAPI Specification is a language- and vendor-neutral description format for HTTP APIs: according to the specification, both people and machines can understand from it what the service can do, without knowing the source code, the documentation, or the network traffic. The description is also suitable for machine processing (for example for generating client or test code). According to the site, the latest release of the specification is 3.2.1. Good documentation, however, is not merely the product of a file format: the exact meaning of the fields and the terms of use are needed too. With GraphQL, the schema and the field descriptions fill the same role.
A short OpenAPI excerpt for the GET /api/kurzusok/{id} operation of Kurzuspont (illustrative, we did not check the syntax with a validator):
The cooperation of provider and client is worth checking from the viewpoint of both the old and the new client: does the old client still receive the needed fields? Does it react understandably to a missing resource? Has the meaning of a field changed while keeping its name? These questions examine compatibility concretely, not just the existence of a version number.
Worked example: v2 of Kurzuspont
The operator of Kurzuspont wants to rename the cim field, and to introduce a szabadHely field next to ferohely.
Step
Decision
Adding szabadHely
An optional extension, old clients ignore it: no new version is needed.
Renaming cim to megnevezes
A breaking change. Temporarily both fields are sent (the third row above), and cim is marked "deprecated" in the documentation.
Retirement
The documentation gives the deadline; after the deadline cim disappears in the new version.
Documentation
An OpenAPI description, a changelog, a description of the error format.
This way there is time to update the old mobile app, and nobody sees the text undefined overnight.
Common misconceptions
Statement
Clarification
"With a new server version all clients update at once."
Clients change at different paces.
"A new field is always a breaking change."
An optional extension can in many cases be handled for old clients.
"A version number by itself preserves compatibility."
Maintaining the old contract and a transition are also needed.
"A sample response is complete documentation."
Errors, conditions, and field meanings need separate description.
"An OpenAPI file is good documentation."
It is an important tool, but the meaning of the fields and the conditions are separate text.
In brief
An API contract is long-lived, and clients update at different paces.
An extension is generally compatible; removal, renaming, and a change of type or meaning are breaking.
The version can be signaled by the path, a header, or the schema; a transition and a retirement plan are needed with it.
Good documentation describes the operations, fields, errors, and changes too.
OpenAPI is a machine-readable description, but it does not replace describing the meaning of the fields.
Review questions
Why is renaming a field a breaking change, and why is a new optional field not?
How can a rename be introduced gradually?
What does a sample response not tell about the API?
Why is silently changing the meaning of a field dangerous?
API versioning, compatibility, and documentation
Objectives
By the end of the lesson, you can decide whether an API change is backward compatible or breaking, you know the options for versioning, and you can say what usable API documentation must contain.
Why do changes to an API have to be managed?
For several clients an API is a long-term contract. The server, however, changes: new features, renames, fixes. Clients do not update at the same moment: a browser interface immediately, a mobile app after weeks or months, a partner's system perhaps never. If the provider changes the contract overnight, the old clients break, while the network and HTTP work flawlessly. Versioning, compatibility, and documentation make change traceable and manageable.
Renaming a field with several clients
So far the API of Kurzuspont has sent a
cimfield. A new server development would rename it tomegnevezes. The new interface would read the new field, but the old mobile app still expectscim. Tried, an old and a newer client with the same three responses:const regiKliens = v => "Kurzus: " + v.cim; const ujKliens = v => "Kurzus: " + (v.megnevezes ?? v.cim);{"id":"webprog","cim":"Webprogramozás 1."}(old)Kurzus: Webprogramozás 1.Kurzus: Webprogramozás 1.{"id":"webprog","megnevezes":"Webprogramozás 1."}(renamed)Kurzus: undefinedKurzus: Webprogramozás 1.{"id":"webprog","cim":"...","megnevezes":"...","nyelv":"hu"}(extended)Kurzus: Webprogramozás 1.Kurzus: Webprogramozás 1.The rename broke the old client (
undefined), the extension did not. The third solution temporarily sends the two fields together, so both clients work.flowchart LR R[Old client: cim] --> A[API response] U[New client: megnevezes] --> A A --> D[Compatibility decision] D --> M[Temporary coexistence or a new version]Compatible and breaking changes
Whether a modification is really compatible depends on the actual contract and the behavior of the clients. The error responses (status code, error format) are also parts of the contract: if the client has so far counted on a given status and error shape, a change can break the operation there too.
Versioning options
A new version can be distinguished in several ways:
/api/v2/kurzusok;There is no single mandatory form. The goal is that the client knows which contract it is using, and that there is a transition if the old version is later retired.
What should the documentation contain?
A usable API description says:
null;Examples help, but they do not replace the description of the rules: a sample response does not say what happens with missing data or an authorization error.
flowchart TD S[API contract] --> M[Operations and inputs] S --> V[Responses and errors] S --> A[Meaning of data fields] S --> K[Compatibility and changes]The OpenAPI Specification is a language- and vendor-neutral description format for HTTP APIs: according to the specification, both people and machines can understand from it what the service can do, without knowing the source code, the documentation, or the network traffic. The description is also suitable for machine processing (for example for generating client or test code). According to the site, the latest release of the specification is 3.2.1. Good documentation, however, is not merely the product of a file format: the exact meaning of the fields and the terms of use are needed too. With GraphQL, the schema and the field descriptions fill the same role.
A short OpenAPI excerpt for the
GET /api/kurzusok/{id}operation of Kurzuspont (illustrative, we did not check the syntax with a validator):paths: /api/kurzusok/{id}: get: summary: Egy kurzus lekérése parameters: - name: id in: path required: true schema: { type: string } responses: "200": { description: A kurzus } "404": { description: Nincs ilyen kurzus }Testing the contract
The cooperation of provider and client is worth checking from the viewpoint of both the old and the new client: does the old client still receive the needed fields? Does it react understandably to a missing resource? Has the meaning of a field changed while keeping its name? These questions examine compatibility concretely, not just the existence of a version number.
Worked example:
v2of KurzuspontThe operator of Kurzuspont wants to rename the
cimfield, and to introduce aszabadHelyfield next toferohely.szabadHelycimtomegnevezescimis marked "deprecated" in the documentation.cimdisappears in the new version.This way there is time to update the old mobile app, and nobody sees the text
undefinedovernight.Common misconceptions
In brief
Review questions
Learning resources