The Android version of the English learning App Duolingo was originally developed in Java and has been in use for five years. Two years later, it’s 100% Kotlin App! The migration was a huge success in terms of code maintainability and developer satisfaction.

There are already plenty of Kotlin learning resources available online, so in this article, we’ll focus on how to migrate an App with millions of users to Kotlin.

Why Kotlin

We started thinking about Kotlin in early 2018, when Android had been supporting it for less than a year and had no idea that Kotlin would become as popular as it is now, or that it would replace Java as Android’s development language of choice.

Our main expectations at that time:

  • The productive forces. Kotlin is much cleaner than Java, faster to write Kotlin code, and easier to maintain. Its seamless interoperability with Java and the way it adds new features to the language make it easy for Android developers to get started.

  • Stability. Our code base history has over 100 commits related to “fixing NullPointerException issues” from Java code. Kotlin’s Null security feature avoids most NullPointerExceptions, allowing us to focus more on other issues during code reviews.

  • Developer satisfaction. Stack Overflow’s Developer Programming Language of the Year 2018 report found Kotlin to be one of developers’ favorite programming languages, second only to Rust. Our developers have already responded positively to language upgrades for two other major platforms within the company: Swift for iOS applications and a complete rewrite of Duolingo.com with TypeScript.

Of course, there are some risks to migration, mainly the cost of developer time. Another concern is whether Kotlin, like CoffeeScript, might end up being beaten by its “shadow” language.

In the end, our Android developers agreed that Kotlin’s benefits were valuable enough to justify developing new code using Kotlin, although we weren’t ready to fully migrate all the code.

Keep developers up to speed

Every two weeks, all Android developers at Duolingo meet to discuss recent and upcoming platform changes, informal post-mortems and q&A sessions. Early sessions were dedicated to Kotlin, based on the Official Kotlin Language Guide, Kotlin Koans, the Official Android documentation, MindOrks cheat sheets, and more.

Each Android developer is then assigned some Java code to migrate to Kotlin. We put developers with relative Kotlin experience in “Kotlin Checker” roles to share best practices during code reviews. The number of this role gradually increased until all Android developers were included.

Kotlin-related development tools

From the beginning, we containerized the Kotlin tool and enforced it in code pre-commit and GitHub pull request status checks to ensure code consistency.

We use Detekt, IntelliJ Inspection, Android Lint, and our own regular express-based Splinter to check all the Kotlin code.

For automatic code formatting, we use KtLint company-wide as part of the code pre-commit hook. The other tool is IntelliJ Formatter, although we found it to run slower in Docker.

When we reduced the Java code to just 10%, we removed PMD, SpotBugs, and most of the inspection tools from the CI pipeline. Continuing to use these Java tools would slow us down and not provide much value.

Converting Java code

To make transcoding review as easy as possible, we recommend that each source file pull request contain at least three separate commits:

  • Run the IDE’s automatic converter. This reference causes lines of code to be corrupted, but does not require careful inspection because it is generally safe for the runtime, although compile-time errors may be introduced.

  • Fix compilation errors. These fixes are usually easy to make, for example, by adding @jVMStatic annotations if necessary.

  • Refactoring. Developers need to refactor the code to make it more Kotlin’s style, such as using sumBy instead of a for loop.

We found that converting Java files to Kotlin reduced the number of lines by about 30% on average, and in some cases by as much as 90%!

While porting code is not a problem for our Android platform engineers, it can be relatively difficult for our product team. Developers on the product team are encouraged to migrate frequently modified code in their free time, and gamify the process through daily leaderboard competitions. In the end, developers on the product team do half the work.

A stumbling block

Kotlin’s tool ecosystem is much smaller than Java’s. Still, it’s more than enough to meet our needs.

We still occasionally get NullPointerExceptions and IllegalArgumentExceptions from third-party Java dependent libraries (such as the Android framework itself) that don’t follow best practices and use nullable annotations, So that the Kotlin compiler has no way of knowing that some method arguments or return values can be null. The situation has improved with Google annotating their public apis.

However, Kotlin still lacks native support for some Java features, including the less common superclass static protected method calls and the arcane superclass constructor calls, which are easy to fix.

The results of

Prior to the introduction of Kotlin in early 2018, our Android codebase was growing by 46% per year. Two years on, we’ve added a lot of new features, more than doubled the number of active contributors, and our code base is now almost as big as before!

According to NPS, Android developer satisfaction increased by 129 points this time around, with most developers citing Kotlin adoption (and our tools) as the main factor. The data of NPS are as follows:

En.wikipedia.org/wiki/Net_Pr…

We now also use both Python and Java as back-end service development languages, which requires little extra work because we can reuse Java code from existing services and Kotlin tools from the Android code base.

Overall, we were very happy after moving to Kotlin. We are also excited to see its adoption rate grow both within our company and throughout the software industry!