The versioning contract
What you promise
When you publish an API, you’re making an implicit promise to every developer who uses it: “If you build against this API, it will keep working.” That sounds obvious, but it’s surprisingly easy to break that promise without realizing it.
Versioning makes this promise explicit. Each version is a contract, a guarantee that the request and response shapes won’t change in breaking ways. If you need to make a breaking change, you create a new version. The old one keeps working.
Backward compatibility
A change is backward compatible if existing clients continue to work without any modifications after the change. The rule from the previous lesson applies directly here: additive changes are backward compatible, subtractive changes are not.
But there’s an important nuance. Backward compatibility is a one-way promise. You promise that old clients work with new API versions. You do not promise that new clients work with old API versions. That second thing is called forward compatibility, and it’s a much harder promise to make.
A client built for v2 can’t call v1 if v2 has features that v1 doesn’t support. That’s expected. The promise only flows backward: old clients always work.
Semantic versioning for APIs
You might have seen version numbers like 2.3.1 on npm packages. That’s semantic versioning, and it uses three numbers: MAJOR.MINOR.PATCH.
MAJOR (v1 to v2): Breaking changes. Removed fields, changed types, restructured responses. Clients must update their code to use this version.
MINOR (v1.1 to v1.2): New features that are backward compatible. Added fields, new endpoints, added optional parameters. Existing clients keep working without changes.
PATCH (v1.1.0 to v1.1.1): Bug fixes with no API changes. A bug in the rating calculation gets fixed, but the response shape stays the same.
In practice, most APIs only version with the major number: v1, v2, v3. Minor and patch changes happen within a version without bumping the version number, because they’re backward compatible and don’t require any client changes. You just deploy them, and everything keeps working.
The contract per version
Let’s make this concrete with our book catalog API. Each version has a specific contract that clients depend on:
v1 contract:
GET /v1/booksreturns[{ id, title, author_name, rating }]POST /v1/booksaccepts{ title, author_id }, returns201with{ id, title }author_nameis a string,ratingis a number
v2 contract:
GET /v2/booksreturns[{ id, title, author: { id, name }, ratings: { average, count } }]POST /v2/booksaccepts{ title, authorId }, returns201with{ id, title, author }authoris a nested object,ratingsis a nested object
Both contracts exist at the same time. v1 clients call v1 endpoints. v2 clients call v2 endpoints. Neither breaks. That’s the whole point of versioning.
When to create a new version
You should create a new major version when you need to make a breaking change that can’t be done additively. Here are the common triggers:
Restructuring responses: Flattening nested objects, changing from arrays to objects, adding pagination wrappers.
Renaming fields: Changing from snake_case to camelCase, or from abbreviated names to full names.
Changing authentication: Moving from API keys to OAuth, changing token formats.
Changing error formats: Restructuring error responses, changing error codes.
What about adding fields, adding endpoints, adding optional parameters, fixing bugs, or improving performance? Those are all backward compatible. They belong in the current version. Don’t create a new version for things that won’t break anyone.
[!NOTE] The REST API Design course established conventions for response formats, pagination, and error shapes. Changing any of those conventions for existing endpoints requires a new version.
Next, we’ll set up the project we’ll use throughout the rest of this course, a book catalog API that already has clients depending on it.
Exercises
Exercise 1: Write the contract for a v1 API with 3 endpoints. Specify the request and response shapes.
Exercise 2: Identify three changes that would require a v2. Identify three changes that fit within v1.
Exercise 3: A client was built against v1 six months ago. They haven’t updated since. What do they expect from the API?
When should you create a new major API version?