What is Semantic Versioning? Guide and Best Practices

What is semantic versioning? Learn how SemVer uses MAJOR.MINOR.PATCH to signal breaking changes, new features, and bug fixes for reliable software releases.

Jump to section

Jump to section

Jump to section

Semantic versioning isn't just a numbering scheme—it's a contract between you and your API consumers. When you ship an API, every version number you assign communicates whether developers can safely upgrade without breaking their integrations, and this communication becomes critical as your API ecosystem grows.

This guide covers how to apply semantic versioning principles specifically to APIs and their SDKs. You'll learn when to increment major, minor, and patch versions, how to identify breaking changes that aren't immediately obvious, and how to automate the entire versioning workflow from commit messages to published packages across multiple languages.

Why semantic versioning matters

Semantic Versioning, or SemVer, is a formal convention for specifying software version compatibility using a three-part MAJOR.MINOR.PATCH number. For API developers, it's a public contract that tells consumers exactly what to expect from a new release. It signals whether a new version introduces breaking changes, adds new functionality without breaking anything, or just fixes a bug.

Adopting this standard is critical for building trust and ensuring a stable developer experience for your API consumers.

Consumer trust

When developers use your API, they build their applications on the promise that its behavior is predictable. Semantic versioning makes this promise explicit. A developer can confidently update to a new patch or minor version without fearing that their integration will suddenly break, because the version number itself guarantees backward compatibility.

This trust is the foundation of a good developer experience.

Dependency clarity

Modern development relies on package managers like npm, PyPI, and Maven to handle dependencies. These tools use semantic versioning to resolve which versions of a library or SDK are safe to install. By adhering to SemVer, you allow your users to specify version ranges, like ^1.4.0, that automatically pull in your non-breaking updates while protecting them from major changes that would require code modifications.

Release discipline

Semantic versioning forces a disciplined approach to releases. Every change to the API must be categorized as a patch, a feature, or a breaking change, which in turn dictates the version bump. This process, especially when automated, links your development workflow directly to the public contract you have with your users, ensuring that version numbers are always meaningful.

How the major minor patch format works

The core of semantic versioning is the MAJOR.MINOR.PATCH format. Each number has a specific meaning that communicates the nature of the changes in a new release. Understanding this structure is the first step to properly versioning your API and its SDKs.

Major field

The first number, X in X.Y.Z, is the major version. You increment this number only when you make incompatible API changes that are not backward-compatible. This is the most significant update, as it tells consumers they will likely need to change their code to upgrade.

  • Example: Changing an endpoint's response field from a string to an object is a breaking change. This would require a version bump from 1.7.2 to 2.0.0.

Minor field

The second number, Y in X.Y.Z, is the minor version. You increment this when you add new functionality in a backward-compatible manner. Consumers can adopt minor versions without changing their existing code.

  • Example: Adding a new, optional query parameter to an existing endpoint is a backward-compatible feature. This would warrant a version bump from 1.7.2 to 1.8.0.

Patch field

The third number, Z in X.Y.Z, is the patch version. This is for making backward-compatible bug fixes. Patch releases should be completely safe for consumers to adopt, as they only correct incorrect behavior without changing the API's features or contract.

  • Example: Fixing a bug where an endpoint incorrectly returned a 500 error for certain inputs instead of a 400 error. This would be a version bump from 1.7.2 to 1.7.3.

Pre-release tag

You can also append a hyphen to denote a pre-release version, like 1.0.0-alpha.1 or 2.0.0-beta. These versions are considered unstable and may not satisfy the intended compatibility requirements of their associated normal version. They are useful for gathering feedback on new features before a stable release.

Build metadata

Optionally, you can append a plus sign for build metadata, such as 1.0.0-alpha+001. This metadata is ignored when determining version precedence. It's useful for internal tracking, like linking a version to a specific CI build or commit hash.

When to create a major version

A major version release is a significant event for your API and its users. It signals a breaking change, requiring consumers to invest time and effort to upgrade. Because of this, you should only increment the major version when it is absolutely necessary.

Common breaking changes

A change is considered "breaking" if it can cause an existing client integration to fail.

Change Type

Example

Endpoint Removal

Deleting the GET /v1/old-feature endpoint.

Data Type Change

Changing a user id from an integer to a string, which requires similar considerations to making Java nullable fields backwards compatible.

New Required Parameter

Adding a required reason field to a POST /cancellations request.

Authentication Change

Moving from API Key authentication to OAuth 2.0.

Response Structure Change

Renaming a field in a JSON response from user_name to username.

Upgrade strategy

When you release a major version, clear communication is key. Provide a detailed migration guide that explains what changed and how users can update their code. It's also a good practice to run the old and new versions of the API in parallel for a period, allowing users to migrate at their own pace.

SDK considerations

For every major version of your API, you should release a corresponding major version of your SDKs, remembering that your API isn't finished until the SDK ships. Generating and maintaining these parallel SDKs ensures that users on older versions of your API can still receive bug fixes without being forced to upgrade.

When to create a minor version

Minor versions are the lifeblood of a healthy, evolving API. They allow you to ship new features and improvements without disrupting your existing users. This is where you add value to your product in a safe, backward-compatible way.

Compatible additions

Minor releases are perfect for additive changes that expand your API's capabilities.

  • Adding a completely new API endpoint.

  • Adding new, optional fields to an existing request body.

  • Adding new fields to a response body.

  • Adding a new value to an existing enum while making Java enums forwards compatible.

Deprecation window

If you plan to remove a feature in a future major version, a minor release is the right place to mark it as deprecated. You can add a deprecated: true flag in your OpenAPI spec or include a Warning header in the API response. This gives your users advance notice to migrate away from the feature before it is removed.

Client impact

Package managers are designed to safely handle minor updates. A dependency constraint like ^1.4.0 in package.json allows npm to install any version from 1.4.0 up to, but not including, 2.0.0. This lets your users benefit from new features and bug fixes automatically without the risk of breaking their application.

When to create a patch version

Patch releases are for fixing mistakes. They are the smallest, most frequent, and safest type of release. A robust CI/CD pipeline should be able to create and publish a patch release quickly to address urgent issues like security vulnerabilities or critical bugs.

Bug fixes

The primary purpose of a patch is to correct behavior that deviates from the documented API contract.

  • Security: Fixing a vulnerability that exposes data.

  • Correctness: An endpoint returns an incorrect calculation or status code.

  • Specification: A typo in your OpenAPI spec leads to incorrectly generated SDK code.

Non-contract changes

Some changes have no effect on the API's public contract but are still worth releasing. These are also suitable for a patch release. This includes internal refactoring for better performance, improving logging, or updating dependencies, as long as these changes are invisible to the API consumer.

Rapid shipping

Because patch releases are guaranteed to be safe, you should ship them as soon as a fix is ready. Automating this process ensures that users receive critical fixes promptly. A single merged pull request with a bug fix can trigger a workflow that automatically versions, builds, and publishes updated SDKs across multiple languages.

How to automate version management

Manually managing versions across multiple SDKs is tedious and error-prone. The modern approach is to automate the entire release process, from determining the version number to publishing the packages. This ensures consistency and frees up developers to focus on building features.

Conventional commit rules

Automation starts with creating OpenAPI specs and standardizing your commit messages. The Conventional Commits specification is a lightweight convention built on top of Git commits. By prefixing your commit messages with types like fix: or feat:, you create a machine-readable history of changes.

  • fix: correct error handling for 404s signals a patch release.

  • feat: add new /users/{id}/profile endpoint signals a minor release.

  • feat: change auth from API key to OAuth\n\nBREAKING CHANGE: ... signals a major release.

semantic-release workflow

Tools like semantic-release can analyze your Git history, and based on the conventional commit messages, automatically determine the next version number. It can also generate a changelog and create a new Git tag for the release. This removes the guesswork and manual effort from versioning.

Stainless integration

This entire workflow can be managed seamlessly with the Stainless SDK generator. When you push an updated OpenAPI spec with a conventional commit message after editing configs and OpenAPI specs with branches, a CI pipeline can trigger a new build.

Frequently asked questions about semantic versioning for APIs

What qualifies as a breaking change?

Any change that could force a consumer to update their code to maintain functionality is a breaking change. This includes obvious changes like removing endpoints, but also subtle ones like changing default pagination behavior or error schemas.

Should internal microservices use semantic versioning?

Yes, it establishes clear contracts and simplifies dependency management between teams, even if the services are not public. It prevents one team's "harmless" update from breaking another team's service.

How do I move from 0.y.z to 1.0.0?

A 1.0.0 release signals a stable, production-ready API. You should move to 1.0.0 when you are confident in your API's design and are committing to not making breaking changes without a major version bump.

Can I skip minor versions?

While you technically can, it is confusing for consumers and breaks the "semantic" meaning of the version history. It is a strong best practice to increment sequentially.

How does semantic versioning interact with API gateways or proxies?

They are complementary. A gateway can route requests based on the URL path (e.g., /v1/, /v2/), which corresponds directly to the major version of your semantically versioned API and its associated SDKs.

Ready to automate your SDK releases? Get started for free.