Kotlin compatibility QuickSheet
4 min read
Edit: Blog post updated for Kotlin 1.7, see the last paragraph for details.
Kotlin 1.6 has just been released 🎉 (blog post). This is great news for everyone in the Kotlin ecosystem. As with every feature release, there are new features, new deprecations and other changes that might (but hopefully not too much) break your code.
The Kotlin documentation has two awesome pages to go into the implications of new feature releases:
- The Kotlin evolution - compatibility tools page
- The Compatibility modes page
Additionally, a lot of additional information can be found in the Kotlin issue tracker, like this issue about Kotlin native compatibility
In practice though, I often have a hard time choosing what
kotlin-stdlib version to use, etc... This post is an attempt to list common scenarios to help build a better mental model of what's happening under the hood. And also answer the question of:
Should you use Kotlin 1.6 in a library?
Let's try to answer this question.
If you prefer reading code than blog posts, the source for this post is available at: github.com/martinbonnin/kotlin-compatibilit..
project compiled with 1.3 using a lib compiled with 1.6
e: /Users/mbonnin/git/kotlin-compatibility/lib-1-6/build/libs/lib-1-6.jar! /META-INF/lib-1-6.kotlin_module: Module was compiled with an incompatible version of Kotlin. The binary version of its metadata is 1.6.0, expected version is 1.1.16.
That sounds appropriate. The evolution principles state that:
Preferably (but we can't guarantee it), the binary format is mostly forwards compatible with the next feature release, but not later ones (in the cases when new features are not used, e.g. 1.3 can understand most binaries from 1.4, but not 1.5).
Here, the 1.3 compiler cannot read the 1.6 metadata which sounds expected. Let's try with 1.4
project compiled with 1.4 using a lib compiled with 1.6
> Task :app-1-4:compileKotlin FAILED e: /Users/mbonnin/git/kotlin-compatibility/lib-1-6/build/libs/lib-1-6.jar! /META-INF/lib-1-6.kotlin_module: Module was compiled with an incompatible version of Kotlin. The binary version of its metadata is 1.6.0, expected version is 1.4.2.
Good 😌. This was expected. Note how 1.4 expects 1.4.2 metadata while 1.3 was expecting 1.1.16 so something changed but it's still not enough.
Let's try with 1.5
project compiled with 1.5 using a lib compiled with 1.6
> Task :app-1-5:compileKotlin BUILD SUCCESSFUL in 5s
Huge success 🙌. Everything works as expected.
Now let's assume that we are a library author and we want our new shiny lib to use 1.6 while still allowing 1.3 users. Is that possible?
Make the lib use apiVersion=1.3, languageVersion=1.3
e: Language version 1.3 is no longer supported; please, use version 1.4 or greater.
Fair enough. The 1.6 release notes state that:
Starting with Kotlin 1.6.0, you can now develop using three previous API versions instead of two (along with the current stable one). Currently, this includes API versions 1.3, 1.4, 1.5, and 1.6.
That doesn't say anything about
languageVersion(Update: Starting with 1.7,
languageVersion will also support three previous versions). Let's try with just
Make the lib use apiVersion=1.3, languageVersion=1.6
e: /Users/mbonnin/git/kotlin-compatibility/lib-1-6-api-1-3-language-1-6 /build/libs/lib-1-6-api-1-3-language-1-6.jar!/META-INF /lib-1-6-api-1-3-language-1-6.kotlin_module: Module was compiled with an incompatible version of Kotlin. The binary version of its metadata is 1.6.0, expected version is 1.1.16.
We're back to step one. The new metadata format cannot be read by the old compiler. Maybe if we could tell the 1.6 compiler to output "backward" compatible metadata that could help. Let's see if
languageVersion=1.4 can help
> Task :lib-1-6-api-1-3-language-1-4:compileKotlin BUILD SUCCESSFUL in 1s
🎉 That worked! So looks like
languageVersion also affects the metadata format
As a library author wanting to use Kotlin 1.6, my current understanding is that:
if your users are compiling against your lib with Kotlin 1.n
languageVersionshould be set to n+1:
apiVersiondoesn't really matter as
kotlin-stdlibshould be resolved to
1.6, which is backward compatible with any previous
if your users run your lib with a fixed
kotlin-stdlib:1.pat runtime (like Gradle)
apiVersionshould be set to n:
If you bump to
1.6 without configuring anything else, your consumers will have to update to Kotlin 1.5.
Note: The other way around (backward compatibility?) is a way better story: if you don't bump to
1.6 and keep using
1.5 in your library, then all consumers can upgrade to
1.6 and continue using your lib (including native ones).
Update for Kotlin 1.7
The same is still true. If you bump to Kotlin 1.7 in your lib, your consumers are forced into compiling with 1.6 by default (including for native).
You can keep compatibility with the 1.5 compiler with
languageVersion="1.5" but that will only go so far because
kotlin-stdlib itself contains 1.7 metadata so unless you're going to great length to not expose
kotlin-stdlib:1.7 transitively, setting the languageVersion is most likely not going to change much.