THE MIGRATION LINE
INCIDENT // LYFT-2025MIGRATED

Lyft spent seven years converting its Android apps from Java to Kotlin, then added a CI lint check so no one could add Java back.

LYFT·2025·LANGUAGE / KOTLIN / ANDROID / TOOLING·✓ Sourced — eng.lyft.com
System stress over timeBreach at T+5
2018 → 2025Adoption to done
~1,000 filesLeft at Feb 2025
20+ teams, 150+ modulesSpread across
A couple modules/dayMigration pace
BASELINE

Seven years of two languages in one app

Lyft started adopting Kotlin into its Android codebase in 2018. Seven years later, in 2025, the Rider, Driver and Urban Solutions apps were finally all Kotlin. For most of the stretch in between, Lyft’s Android engineers worked in a codebase that was part Java and part Kotlin, paying the interop tax on every boundary between them.

The codebase is, in the team’s own word, huge. Lyft frequently runs automated migrations across it, and every one of them had to be taught to handle Java as well as Kotlin — a standing surcharge on every future refactor, not just this one.

REQS

What they actually needed

The requirement was blunt: all new UIs at Lyft are written in Compose, the modern declarative UI framework, and all existing UIs will eventually be migrated to Compose. Compose only supports Kotlin.

Because of that, “should we go all-Kotlin” was not really the open question — the UI direction had already decided it. What was open was how to finish, and how to keep engineers from having to think about Java-Kotlin interop while they did. There was also a hiring argument: working entirely in Kotlin is a plus for engineers considering Lyft.

OPTIONS

Know where you're standing first

Lyft runs an internal tool called Migration Tracker, which tracks every migration in progress across the Android and iOS codebases — RxJava to Coroutines, the old UI approach to the declarative one, and Java to Kotlin among them. A daily cron job updates an internal website with graphs so teams can see whether they’ll hit their migration deadlines.

The tracker gave them the number that framed the whole endgame. As of February 24, 2025, the Kotlin migration was 85% complete. The remaining 15% was about 1,000 files, scattered across more than 20 teams and more than 150 Bazel modules. That scatter is what ruled out doing it by hand, team by team.

THE CALL

Script the conversion, module by module

Developer Oleksii Zaiats built a Migration Script on top of Android Studio IDE Scripting — a powerful but rarely used facility that turned out to fit the job exactly.

For a given team, the script found all the modules that team owned. For each module, it ran Android Studio’s automatic Java-to-Kotlin conversion on every Java file, then automatically fixed some of the converter’s common imperfections. Once the whole module was Kotlin, it created a git commit on an appropriately named branch and notified the owning team to review.

COSTS

What the converter couldn't do, and the one file that stayed Java

The major pain point was that the automatic converter is not perfect. It reaches for nullable types where non-nullable ones would be fine, producing signatures like Observable<Optional<List<Ride?>?>?>?. It is unnecessarily verbose, spelling out explicit types everywhere. It isn’t smart enough to turn an explicit for-loop into a Kotlin one-liner with map or filter. And it can’t reach for lateinit var, which View-based UIs often need.

The legacy code added its own complications. Some very old code implemented a hand-written INullable interface — similar to Optional, but not quite — whose semantics forced extensive human code review every time one of those classes was touched. And the team hit one class that absolutely had to stay in Java: it implemented an interface whose signature promised non-null Floats for a touch event’s x and y, but on some devices the API contract was broken and x and y could in fact be null. Java shrugged at that. Kotlin crashed. They ended up rewriting the entire screen with a different approach that didn’t use the interface at all.

A language migration isn’t done when the last file converts — it’s done when the old language stops being a legal thing to write.

Source — read the original

A plain-language, AI-drafted and human-edited retelling of the article published on eng.lyft.com, reorganized and explained in our own structure and words, with original analysis in the editor's note above. The facts, numbers, and decisions belong to the original author and are not altered. For the full depth, read the source.

← All systems