An SDK version keeps its identity from beta to stable release

When you develop an SDK, its version number initially looks like a simple piece of technical information. You change some code, publish a package, increment a number, and move on.

Then people start using the SDK.

One team is waiting for a new API. Another needs to schedule its integration. Support wants to know which version introduced a particular behavior. External partners are preparing their own upgrade plans. At that point, the version number no longer describes only an artifact: it becomes a coordination tool.

And once several teams have aligned around a number, changing it is much harder than you might think.

MayoSDK, the SDK with extra sauce

When I was at Ketchapp, I managed an SDK called MayoSDK.

Yes, the name was deliberate: Ketchapp, mayo… We decided to commit to the joke. Thank you, Jonathan, for coming up with it.

MayoSDK was distributed as a Unity Package Manager, or UPM, package. It was initially deployed internally, then adopted by the many external studios we worked with. Its version therefore had to follow the major.minor.patch format—for example, 1.9.10—with the option of adding a prerelease suffix. Unity relies on Semantic Versioning rules to compare versions and resolve dependencies.

On paper, everything looks simple:

  • the major version signals an incompatible change;
  • the minor version adds backward-compatible functionality;
  • the patch version contains fixes without API changes.

In practice, for an SDK used in several hundred games, we also needed to represent the maturity level of a single release.

Before considering a feature stable, we would publish it in a small number of games to validate it under real-world conditions. A first beta might reveal one problem, and a second beta might fix another. Once the result looked solid enough, we moved to a release candidate and then to a stable release. Even after that release, an urgent fix could still be necessary.

The code would therefore change several times while, from the perspective of the teams waiting for it, it was still the same feature and the same release.

When a version number becomes a brand

Imagine announcing the following to every department:

Feature A will be available in MayoSDK 1.9.10.

From that moment on, 1.9.10 becomes the name of that release. It appears in tickets, roadmaps, Slack messages, integration documents, and sometimes in commitments made to a partner.

If every beta then consumes a new number—1.9.10, followed by 1.9.11, then 1.9.12—the stable version might eventually ship as 1.9.15. Nothing technically prevents this. From a human perspective, however, the message has become much less clear.

Some people will remember the number announced at the beginning. Others will use the number of the beta they tested. The rest will refer to the version that was ultimately released. Everyone will be discussing the same feature using a different identifier.

The problem is not merely how to number packages. It is how to preserve a shared vocabulary throughout the release cycle.

Freeze the target number before the first beta

After several attempts, I settled on a simple convention: we selected the number of the future stable version before publishing the first beta, then kept it unchanged throughout validation.

In our MayoSDK convention:

  • the first number represented the major generation of the SDK; in practice, it always remained 1;
  • the second number changed only for a structural evolution, such as significant new APIs or an incompatible change;
  • the third number identified a specific functional release;
  • the suffix indicated its maturity level and published iteration.

The lifecycle of release 1.9.10 looked like this:

  1. 1.9.10-beta0001: first publication to a limited number of games;
  2. 1.9.10-beta0002: a new beta incorporating the first fixes;
  3. 1.9.10-rc0001: the release candidate we considered ready to become stable;
  4. 1.9.10: the stable release;
  5. 1.9.10-hotfix1: in our historical convention, an urgent fix explicitly tied to that release.

Release cycle of a MayoSDK version, from beta to hotfix

The number 1.9.10 carried the functional promise. The suffixes only described how far we had progressed in validating it.

There was nothing magical about the four-digit counter. It simply made publication order immediately visible, including in tools that used lexicographical sorting. Another team could quite reasonably prefer beta.1, beta.2, and rc.1, which follow the conventional SemVer notation. What matters is that the convention is documented, always moves forward, and is applied automatically by the release pipeline.

A common language for every stakeholder

This naming system allowed us to keep communication fluid throughout the release.

In most conversations, the various stakeholders simply talked about version 1.9.10. Development teams knew which feature they needed to integrate. QA knew which scope it had to validate. Product, support, and partners could organize their work around the same reference point. They did not need to know the details of every beta, every release candidate, or every new package publication.

When more precision was required, the SDK team used the full name: 1.9.10-beta0002 or 1.9.10-rc0001. That complexity remained limited to the people who actually needed it.

We therefore had two levels of conversation:

  • 1.9.10 identified the functional release in conversations between teams;
  • the suffix identified the exact artifact in conversations about development, deployment, or validation.

This separation prevented us from imposing the SDK’s internal lifecycle on the entire company. Someone could ask, “When will 1.9.10 be available?” or announce, “This game is moving to 1.9.10,” without having to specify whether the version currently under test was the second beta or the first release candidate. The functional context remained stable even while the packages continued to evolve.

Teams could begin integrating a beta without having to learn a new name when the release candidate arrived. Tickets and documentation remained attached to the same objective. We could prepare the changelog under a single heading. Finally, the move to stable was immediately understandable: the promise did not change; we simply removed the suffix.

It also improved external communication. We could announce that a feature would be available in 1.9.10, give partners who wanted to integrate it early access to 1.9.10-rc0001, and then publish 1.9.10 without having to revise the entire message along the way. In practice, betas remained limited to our internal validation: only release candidates were made available for early access.

This may sound like nothing more than a vocabulary convenience. In reality, communication around an SDK is a challenge in its own right. The more consumers an SDK has, the more its stakeholders differ in their roles, constraints, and levels of technical knowledge. The version number must connect these people without requiring all of them to understand the entire publication process.

A good naming convention makes that complexity almost invisible. But this simplicity does not happen by itself: it must be designed, documented, and shared with as much care as the release cycle itself.

The special case of a hotfix

There is, however, an important subtlety. Under Semantic Versioning, everything after a hyphen is a prerelease identifier. Consequently, 1.9.10-hotfix1 is considered earlier than 1.9.10, not a newer update.

Our hotfix1 suffix was useful as an operational convention in a controlled ecosystem where versions were selected explicitly. However, you should not expect a SemVer-compatible package manager to automatically offer 1.9.10-hotfix1 to a project that already uses 1.9.10.

For an SDK published widely or consumed using automatic dependency resolution, the correct solution is to publish the fix as a new stable patch:

1.9.10              version that introduced the feature
1.9.11              corrected stable version, now recommended

The shared reference point does not need to disappear from the communication. We simply need to be more precise:

Feature A is available starting with version 1.9.10. The recommended stable version is 1.9.11.

The version that introduced a feature and the currently recommended version answer different questions. Distinguishing between them keeps the message accurate without abandoning the number that teams already recognize.

Internal or external, make the rules explicit

An internal SDK allows more room for custom conventions because its team knows the consumers, often controls the registry, and can support each upgrade. That proximity does not make versioning less important. On the contrary, an SDK shared by many internal projects can create just as many dependencies and coordination challenges as a public product.

For an external SDK, tools and users do not know the team’s habits. They interpret versions according to established standards. The rules therefore need to be much stricter: a breaking API change increments the major version, a backward-compatible feature increments the minor version, and a fix increments the patch version. Suffixes remain reserved for releases that precede the stable version.

In both cases, a few rules prevent a great deal of confusion:

  • choose the target number before the first prerelease;
  • keep that number throughout the beta and RC phases;
  • never replace the contents of an already published version;
  • document exactly what each segment and suffix means;
  • publish release notes with the package;
  • automate version generation and validation;
  • say “available starting with” when referring to the version that introduced a feature.

Versioning is part of the product

A sound versioning strategy does more than serve the package manager. It helps development, QA, support, product teams, partners, and users talk about the same thing.

This is particularly apparent with an SDK: its version appears in its consumers’ code, but also in their schedules and decisions. It becomes a technical contract as well as a human reference point.

With MayoSDK, freezing the target number before the first beta allowed us to evolve a release without diluting its identity. The suffixes indicated its maturity; the main number preserved the promise.

Ultimately, versioning an SDK correctly is not only about knowing which number to increment. It is about making sure that when a team says the name of a version, everyone knows exactly what they are talking about.