a
a
Weather:
No weather information available
HomeEngineeringMigrating Lyft’s Android Codebase to Kotlin

Migrating Lyft’s Android Codebase to Kotlin

Migrating Lyft’s Android Codebase to Kotlin
Migrating Lyft’s Android Codebase to Kotlin

Introduction

Lyft started adopting Kotlin into our Android codebase in 2018. Fast forward 7 years, and we are finally done! Lyft Rider, Driver and Lyft Urban Solutions apps are now fully Kotlin-based.

I joined Lyft in 2022, so this post will describe the efforts undertaken after that.

Our motivation included several points:

  • Kotlin code is more concise, and oftentimes, way more concise than Java. In some cases, 10 lines of Java could be turned into a 1-liner in Kotlin.
  • We get compile speed benefits by using the newest K2 compiler.
  • All the new UIs in Lyft are written using Compose — the modern declarative UI framework approach which is the industry standard. All the existing UIs will eventually be migrated to Compose. That also means Kotlin was the only option, as Compose only supports Kotlin.
  • We started adopting Coroutines — the structured concurrency framework which greatly simplifies writing asynchronous code. Coroutines are part of the Kotlin standard library, so that was an extra argument to adopt Kotlin faster.
  • Our codebase is huge, so we often run automated migrations, which require adopting migration scripts for Java as well.
  • Working entirely in Kotlin is a big plus for engineers considering Lyft.

Pre-migration

The first thing which needs to be done when undertaking a project this large is to know where we are standing. To achieve this, Lyft has an internal tool called Migration Tracker, which tracks all of the migrations in both Android and iOS codebases. Examples of migrations are:

  • Migrating from RxJava to Coroutines
  • Switching from our old UI approach to the new declarative one
  • Eliminating uses of Java in favor of Kotlin

A daily cron job runs the Migration Tracker and updates an internal website, presenting graphs which help us ensure we meet the migration deadlines.

As of Feb 24, 2025, the Kotlin migration was 85% ready. That means we still needed to migrate about 1,000 files scattered across 20+ teams and 150+ Bazel modules.

Migration

Fellow developer Oleksii Zaiats built a tool which greatly sped up the migration process: the Migration Script. It leveraged Android Studio IDE Scripting, which is a powerful but rarely used tool, fitting perfectly for this kind of task.

Put simply, the script flow was as follows:

  • For a given team, it found all modules owned by the team.
  • For the given module, it ran the automatic migration mechanism for each Java file.
  • It automatically fixed some common imperfections of Android Studio’s built-in Java to Kotlin converter.
  • After all the files in the module were migrated to Kotlin, it created a git commit with all the changes, naming the git branch appropriately.
  • The owning team was notified of the changes and reviews were requested.

This simple approach was not without its flaws, but it gave us a real productivity boost and allowed us to migrate a couple of modules per day.

Challenges and Caveats

The major pain point in the migration process was that the automatic migration tool was not perfect:

  • In many cases, it uses nullable-types where non-nullable types are fine, resulting in code like Observable<Optional<List<Ride?>?>?>?
  • It is unnecessarily verbose, using explicit types everywhere.
  • It is not smart enough to convert an explicit for loop into a Kotlin one-liner using map or filter.
  • It cannot automatically use lateinit var which is often needed when writing View-based UIs.

In addition, the structure of the legacy code itself presented additional complications. We had some very old code implementing a hand-written INullable interface, which was similar to Optional but not quite. The semantics of INullable required us to do extensive code review each time we touched one of these classes.

Last but not least, once we encountered a class which absolutely had to be written in Java! It was implementing an interface with a signature like so:

public void onTouchEvent(@NonNull Float x, @NonNull Float y)

However, on some devices, the API contract was broken, so x and y could in fact be null! In Java this was totally fine, but in Kotlin this resulted in a crash. Thankfully, we have rewritten the entire screen using another approach, not using this interface anymore.

Post-migration

After we were finally done, we needed to ensure that no one accidentally adds a Java file to our codebase. To achieve this, we have added a Lint check, integrated in our CI system, which checks every pull request and explicitly prohibits Java code.

Conclusion

After concluding this years-long effort, Lyft developers can skip the hassle of Java-Kotlin interop and concentrate on solving the problem that actually matters — providing our users with the world’s best transportation!

Lyft is hiring! If you’re passionate about working on a Kotlin-only app, visit Lyft Careers to see our openings.


Migrating Lyft’s Android Codebase to Kotlin was originally published in Lyft Engineering on Medium, where people are continuing the conversation by highlighting and responding to this story.

No comments

Sorry, the comment form is closed at this time.

Translate »