· 4 min read

SemVer is a great versioning scheme for Kotlin libraries

Both Maven and Gradle support strict SemVer

Both Maven and Gradle support strict SemVer

Edit 2026-01-17: Sebastian Sellmair wrote a comprehensive response here. As he points out, both versioning schemes are SemVer compatible! I have edited this post to be about “strict” vs “zero-padded” SemVer.

The question came up lately what versioning scheme to use for Kotlin libraries (KotlinLang slack, Twitter, BlueSky).

This is an interesting question, with lots of nuances.

The closest we have to an authoritative answer is Semantic Versioning (or SemVer). SemVer defines a grammar for version numbers and rules for ordering.

SemVer allows for a wide range of variation, though. Two variants are widely used, we’ll call them ‘strict’ and ‘zero-padded’:

  • Strict SemVer looks like 1.0.0-alpha.1
  • Zero-padded SemVer looks like 1.0.0-alpha01

Strict SemVer is used in many NPM and Rust packages as well as some Kotlin libraries. Zero-padded SemVer is used by AndroidX and a lot of Kotlin libraries.

With zero-padded SemVer, versions are padded with zeroes to ensure a fixed number of digits and a natural lexicographic order (think filesystem-like).

I’ve been wrestling with this question for many years. In fact, I was asking the same question 5 years ago. At that time, I landed on zero-padded SemVer.

I have changed my mind. This post explains why.

Two versioning schemes

To make things very explicit, let’s take a look at examples of what it looks like in real life.

Both schemes allow a vast range of options, but we’ll limit ourselves to alphas, betas, rcs, and stable:

Zero-paddedStrict
0.1.00.1.0
0.1.10.1.1
0.1.100.1.10
1.0.0-alpha.011.0.0-alpha.1
1.0.0-alpha.101.0.0-alpha.10
1.0.0-beta.011.0.0-beta.1
1.0.0-beta.101.0.0-beta.10
1.0.0-rc.011.0.0-rc.1
1.0.0-rc.101.0.0-rc.10
1.0.11.0.1
1.0.101.0.10
1.1.01.1.0
2.0.02.0.0
10.0.010.0.0

Maven and Gradle both support strict SemVer

Versions are important because they carry meaning (they are semantic!).

Your usual build tool will resolve dependencies conflicts based on a version number.

You want stable releases to be chosen over release candidates, then betas, then alphas releases. You also want versions without -SNAPSHOT to take precedence over the SNAPSHOTs.

When a conflict arises, sensible build tools will choose the higher version (nonsense build tools will do something else, but this is another topic).

The whole Maven ecosystem is based on that premise.

Sonatype warns about this in their documentation:

One gotcha for release version numbers is the ordering of the qualifiers.
Take the version release numbers “1.2.3-alpha-2” and “1.2.3-alpha-10,” 
[...]
Maven is going to sort “alpha-10” before “alpha-2” due to a known issue 
in the way Maven handles version numbers.

If build tools were using lexicographical sorting, you could see how strict SemVer is a problem because 1.0.0-alpha10 would be considered lower than 1.0.0-alpha2. Not good 💥

Thankfully, Maven 3 (released in 2010) and above have this bug fixed. You can try for yourself using maven-artifact:

@file:DependsOn("org.apache.maven:maven-artifact:3.8.6")

import org.apache.maven.artifact.versioning.ComparableVersion

println(ComparableVersion("1.0.0-alpha.10") > ComparableVersion("1.0.0-alpha.2"))
// true

Similarly, Gradle has been sorting strict SemVer versions fine since Gradle 1 (released in 2012) (test script)

There are some differences in the implementation, especially around -dev versions and case sensitivity. But none of that is relevant if you follow the versioning scheme above.

If you’re curious, you can browse their source code:

The bottom line is unless you want to support really old build tools (hint: there’s no real reason for this), you can use both strict or zero-padding and your dependency resolution will work as expected.

The case for strict SemVer

One strong argument for zero-padding is that it sorts lexicographically. If you have ever browsed Maven Central, zero-padding makes it easier to find your libs.

On the other hand, strict SemVer:

  • Allows arbitrary numbers of alphas. Want to do 1.0.0-alpha.100? You can!
  • Is generally more consistent. Just like major, minor, and patch versions are not padded, the pre-release version uses the same pattern.

Both Maven and Gradle have been correctly sorting strict SemVer versions for over 14 years.

The rest of the tooling (HTML indices, anything else?) will improve so that sorting can be made more consistently. But this will only happen if we align on the best practices.

OkHttp uses strict SemVer, Apollo too.

Next time you have to choose a versioning scheme, consider strict SemVer.

Comment on this post on Bluesky or Mastodon

PS: we should also start our versions at ‘0’, story for another time!


Photo by Mika Baumeister

    Share:
    Back to Blog