The Kotlin ecosystem might not choose a strict 'SemVer' notation
When practical engineering requires overcoming idealism

While other ecosystems (such as Rust or the Web) seem to have well-established conventions for package and library versioning, even a quick glance at the Kotlin ecosystem reveals disagreement. Without any official authority providing guidance, each company or team is tasked to choose whatever fits their needs. Even we at JetBrains failed to establish a common convention for versioning our Kotlin packages. For some, this might just be an aesthetic issue, merely a small inconsistency when looking at their project’s build files. For others, the inability to build an intuition around version strings leads to frustration.

There are several reasons why such inconsistencies happen. Take Ktor, for example: Until recently, the project belonged to a very different department than Kotlin itself. Compose Multiplatform, on the other hand, has good reasons matching the versions from Google’s Jetpack Compose. And kotlinx libraries might just have suffered from a lack of defined conventions and the nature of such issues: They will never become the #1 top priority and always stay within the realm of ‘would be nice to address eventually’.
I previously invited the community to a debate about a proposal attempting to unify the versioning schema used by the Kotlin ecosystem. As of writing this article, there are two top contenders:
Strict SemVer1.0.0-alpha.1, 1.0.0-beta.1, 1.0.0-rc.1
Google / AndroidX1.0.0-alpha01, 1.0.0-beta01, 1.0.0-rc01
While some audiences prefer the ‘Google Notation’, seasoned library developers often argue for the ‘Strict Semver’. Interestingly, my very first draft of a versioning proposal, shared with the Kotlin department, also proposed this notation. It seems like a decent convention used by a very significant share of our industry. It has clearly defined semantics and ‘easy to understand’ rules. I fully agree with Martin Bonnin and would also call it a ‘great versioning scheme for Kotlin libraries’.
The ‘ideal’ diversion
I am really fascinated by the concept of ‘engineering emotions’, and I strongly believe that all good engineers eventually have to overcome many of their ‘guiding feelings’: The idealism trying to satisfy our sense of beauty and order, the subtle irritation we notice when reading code that certainly works, but somehow does not fit our philosophy.
I failed to manage my emotions when I initially proposed that the Kotlin Department adopt a strict SemVer (1.0.0-alpha.1) notation. I noticed the inconsistency within our company’s libraries when starting a new project and had to choose a versioning schema. After realising we do not have a written convention, I considered using the same schema as other libraries and noticed the lack of consistency, which motivated me to address this. I had clear and simple goals:
I wanted to find a versioning schema/notation which
Works predictably for practical use cases (e.g., is handled well by Build Systems like Maven, Gradle, and Amper)
Is able to convey compatibility semantics for all our projects
Can be adopted by many projects to eventually increase the entire ecosystem’s consistency
And I found that ‘SemVer’ already defines clear rules for pre-release classifiers (alpha, beta, rc), and I consider those rules very beautiful. The fact that other ecosystems seem to converge on this notation made me very excited, and I got started writing up a proposal to the team, not knowing that what I had taken to be an ‘ideal’ would, very quietly, change my initial goals! I wanted to move our Kotlin ecosystem toward a more consistent, predictable state. My proposal, however, aimed to align Kotlin with a supposedly ‘industry standard,’ well knowing that many of our projects and key ecosystem libraries would not be able to adapt to it. The entire ‘androidx’ ecosystem of libraries, including Compose, a vital part of our Kotlin Multiplatform technology, has already settled on the ‘Google’/’AndroidX’ notation. My initial goals, therefore, cannot be achieved by my very own proposal, and this is not the first time this has happened. During my research, I found basically no practical differences between the two notations. Build Systems handle them well; both look good, and both have easy-to-explain rules. The worst part: While aggressively seeking good reasons to switch to an entirely new notation (SemVer), I even found situations where the Google notation objectively works better. I still ended up writing a proposal, ignoring my initial goals and even ignoring my own research. All in order to satisfy my own sense of beauty and ‘greater ideal’.
My Proposal 1.0.0-rc01
As I noticed how my emotions guided me into missing my very objectives, my fix is easy:
I’ll correct my proposal to use the ‘Google’ notation. It works just as well and can be adopted across all projects within the Kotlin Ecosystem Department (speaking Kotlin, kotlinx, ktor, …). The notation is well known by many of our developers and has some nice properties
Still ‘SemVer’ compliant
From the official guide
A pre-release version MAY be denoted by appending a hyphen and a series of dot separated identifiers immediately following the patch version. Identifiers MUST comprise only ASCII alphanumerics and hyphens [0-9A-Za-z-]. Identifiers MUST NOT be empty. Numeric identifiers MUST NOT include leading zeroes
Note that only numeric identifiers are not allowed to start with a 0 (e.g. 1.0.0-0 ) would be illegal, whereas 1.0.0-alpha01 is legal and is compliant with ordering rules for up to 99 alpha releases (Again, real-world build systems will handle even more releases just fine)
When 1.0.0-rc01 is better than 1.0.0-rc.1
Please keep in mind that the entire section here is me falling into an emotional trap again: Post-Hoc Rationalization: Coming up with reasons why my decision makes sense, after making it, that is. I still include it, because it’s fun to talk about. While I acknowledge the trap, I am still convinced that the decision between the two styles is simple, since only one can justify the original goals
Naive Version Parsers
While ‘SemVer’ offers a robust set of rules, we have to acknowledge that many real-life projects may need to quickly parse version numbers and compare them. I have written such code many times to manage integration tests, depending on the Kotlin version, or to manage builds. Since most of those implementations are naive or even lazy, I am a little too embarrassed to link them; have mercy on me! Such code usually works by splitting the version by the . character, converting major, minor and patch to integers, then comparing -pre-release-classifier alphabetically. While I agree that the implementation is wrong, we have to give credit to Google Notations’ more lenient properties towards such mistakes!
In this case, 2.14.3-alpha.20 would be wrongfully evaluated as ‘lower’ compared to the -alpha.5 counterpart


This effect is also visible in systems that do not care about library versions at all, such as simple indices on webservers or even filesystems. Again: I am not saying that the notation, therefore, is better, but we have to acknowledge its strengths.
Our release philosophy
During a call about Google’s versioning schema, Aurimas Liutikas also mentioned that the notation more closely aligns with how we actually think about our pre-releases, and I can see a lot of beauty in this argument: The ‘SemVer’ notation might lead to interpreting pre-releases within a given maturity level as ‘rolling’. E.g. 1.0.0-alpha.1 only being the first published build with the alpha maturity and 1.0.0-alpha.2 being the second public build with alpha maturity. The alpha, therefore, carries the maturity semantics independently of the build number (.1, .2, .3, …). AndroidX, however, wants to convey a publication as ‘complete’. alpha01 therefore being more distinct from alpha02. This comes in handy, as AndroidX already provides ABI-compatibility guarantees for pre-releases!
Summary
I think the last two arguments presented do not matter much (but were fun to make). What matters is a consistent schema across the ecosystem that works and can be adopted by many projects. I will proceed with my proposal, using the Google notation. Multiple people within JetBrains will review this proposal. I will keep you up to date on any changes to it. Once approved, you can expect a public, official KEEP to be created to promote this proposal beyond JetBrains.



