Semantic Versioning for SDK Releases: An Engineering Guide

Semantic versioning for SDKs release please guide: Use release-please to automate version bumps, changelogs and releases for consistent SDK publishing.

Jump to section

Jump to section

Jump to section

Semantic versioning for SDKs is fundamentally different from API versioning. While your API version tracks changes to HTTP contracts, your SDK version must reflect any modification that could break a developer's existing code, including type changes, method renames, or structural refactoring.

Most teams manually version their SDKs, which becomes unsustainable once you're shipping multiple languages. This guide covers how to identify breaking changes in generated code, automate semantic versioning across language ecosystems, coordinate multi-repository releases, and maintain the developer trust that comes from predictable, well-documented version bumps.

Semantic versioning for SDKs is not about your API's HTTP contract; it's about your user's code. A change is "breaking" if it forces a developer to change their code to upgrade, even if the underlying API is stable. Automating this process across multiple languages is critical for maintaining developer trust and is achievable with the right tooling and practices.

Identify breaking changes in an SDK

A breaking change in an SDK is any modification that could cause a consumer's existing code to fail at compile-time or runtime. This is a wider definition than a breaking API change, because it includes changes to the SDK's own code structure, types, and helper methods, not just the underlying HTTP requests and responses. Understanding this distinction is the first step to versioning your SDKs correctly.

Define SDK and API boundaries

The API contract and the SDK contract are two different things. Your API can remain perfectly stable while your SDK introduces a breaking change. This happens more often than you'd think.

For example, these are all breaking SDK changes, even if the API's JSON payload is untouched:

  • Renaming a generated type from Transaction to PaymentTransaction.

  • Changing a field's type from string | null to just string, removing its nullability. In languages like Java, making Java nullable fields backwards compatible requires special attention.

  • Refactoring a method from client.users.create() to client.users.add().

  • Altering a pagination helper to return an async iterator instead of a simple list.

A robust SDK generation process will provide diagnostics that flag these kinds of potentially breaking changes before they are released.

Detect breaks in generated code

When you regenerate an SDK from an updated OpenAPI spec, the generator itself can introduce breaking changes. Modern tools are announcing the Stainless SDK generator capabilities to help detect these issues early.

Good SDK generators mitigate this by using a three-way merge process. This preserves any custom code you've added to the SDK while clearly highlighting conflicts between your customizations and newly generated code, forcing a manual review. This prevents accidental breaking changes from slipping through.

Automate semantic versioning for SDKs

Once you're shipping SDKs in more than one language, manual versioning and changelog management becomes unsustainable. It's tedious, error-prone, and a huge time sink. Automation is the only scalable solution for maintaining release hygiene and developer trust.

Compare release tools

Two popular tools for release automation are semantic-release and release-please.

  • semantic-release: A powerful tool that analyzes commits on your main branch, determines the version bump, and immediately publishes the package. It's great for single-package repositories where direct pushes to main trigger releases.

  • release-please: This tool works by opening pull requests that propose a version bump and a generated changelog. The release only happens when you merge the PR. This PR-based workflow is ideal for SDKs, as it gives you a chance to review, and it works beautifully for managing releases across many repositories at once.

We find the pull request model of release-please provides a crucial review step that is perfect for managing a suite of SDKs.

Configure continuous releases

The automated release workflow is straightforward and powerful. It starts with developers using a standardized commit message format.

The flow looks like this:

  1. A developer pushes a commit with a message like feat(users): add support for user profiles or fix: correct pagination logic.

  2. An automation tool, like release-please, runs in your CI pipeline. It parses the commit history since the last release.

  3. The tool determines the next version (e.g., a feat commit triggers a minor bump) and opens a pull request with the proposed version and an updated CHANGELOG.md.

  4. Your team reviews the release PR. Once merged, the CI pipeline tags the release and publishes the new version to the package manager.

Use pre-release versions effectively

Pre-release versions are essential for communicating the stability of your SDKs. Using tags like -alpha and -beta manages user expectations, especially when an SDK is new or undergoing significant changes.

Start in alpha for new SDKs

Your very first release should be an alpha version, like 0.1.0-alpha.1. This signals to developers that the SDK is experimental and its public interface is not yet stable. Users understand they should pin the version precisely and expect potential breaking changes even between alpha releases.

Graduate to stable versions

Moving from a pre-release to a stable 1.0.0 version should be a deliberate decision. It tells your users that you consider the SDK's public surface to be stable and will follow semantic versioning rules strictly.

Before you move to 1.0.0, you should have a simple checklist:

  • Are there any generator errors or diagnostics?

  • Have all warnings been reviewed and acknowledged?

  • Is the README.md polished and are the examples working? Have you started to integrate SDK snippets with your API docs?

Only after meeting this quality bar should you release a 1.0.0 version.

Keep multi-language SDKs in sync

Managing five or more SDK repositories is a significant coordination challenge. Deciding on a versioning strategy upfront will save you headaches down the line.

Choose synchronized or independent versions

You have two main options for versioning your suite of SDKs.

  • Synchronized: All SDKs (TypeScript, Python, Go, etc.) share the same version number. If you release v1.5.0, all SDKs get a v1.5.0 release. This is simple to communicate and makes documentation easier.

  • Independent: Each SDK has its own version. The Python SDK could be v2.1.0 while the Go SDK is v1.8.3. This allows for language-specific fixes without requiring a release for every other language.

For most teams, starting with synchronized versioning is the simpler and better approach.

Coordinate cross-repo releases

A modern release process can automate cross-repository coordination. The ideal flow uses a central next branch to stage upcoming changes for all SDKs, similar to how you can edit configs and OpenAPI specs with branches before merging to main.

When a change is made, it's pushed to the next branch in every SDK repository. This automatically generates a "Release PR" for each language, all proposing the same version bump. This gives you a single, unified view of the upcoming release across your entire SDK ecosystem.

Follow proven SDK versioning practices

Adopting a few key practices will dramatically improve the quality and reliability of your SDK releases. These habits form the foundation of a trustworthy, automated release pipeline.

Adopt conventional commits everywhere

Conventional Commits are a specification for commit messages that provides a simple, human-readable structure that machines can also parse. This is the engine that drives automated versioning and changelog generation.

Commit Type

Version Bump

Description

feat

Minor

A new feature for the user.

fix

Patch

A bug fix for the user.

docs

None

Changes to documentation only.

refactor

None

Code changes that neither fix a bug nor add a feature.

BREAKING CHANGE

Major

A footer in any commit type that signals a breaking change.

Ship changelogs users trust

A great changelog is clear, accurate, and easy to find. When you automate your releases based on conventional commits, you get a high-quality CHANGELOG.md file for free. Because it's generated directly from your commit history, developers can trust that it's a complete and accurate record of what's changed.

Support older versions responsibly

You can't support every old version of your SDK forever. A common and reasonable policy is to provide bug fixes (patch releases) only for the latest major version. Critical security fixes may be backported to a previous major version for a limited time. This strategy is easy to manage with a main branch for the current release and a separate maintenance branch for the older version.

Frequently asked questions about SDK versioning

Here are answers to some common questions engineers have when setting up semantic versioning for their SDKs.

Should SDK and API versions match?

No, they should track different things. The SDK version signals breaking changes in the client-side code, while the API version signals breaking changes in the HTTP contract.

When do dependency updates change the SDK version?

A dependency update that fixes a security vulnerability should trigger a patch release. If a dependency introduces its own breaking change that you expose through your SDK, that requires a major version bump for your SDK.

How do I version SDK-only helpers like retries?

Treat them as part of the SDK's public surface. Adding a new helper method is a feature (feat), resulting in a minor bump. Changing the signature of an existing helper is a BREAKING CHANGE, requiring a major bump.

What version should I start with for a brand-new SDK?

A great starting point is 0.1.0-alpha.1. This clearly communicates that the SDK is new and its API is not yet stable, encouraging users to be cautious with upgrades.

How does Stainless update versions for me?

When you make changes, a pull request is automatically created with a suggested version in the title, based on your commits. You can edit this title to override the version, and merging the PR automatically tags the release and publishes it for you.

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