Warum und wie Meta die Android-Entwicklung von Java auf Kotlin umgestellt hat
(engineering.fb.com)- Metas Android-Repo ist ein riesiges Repository, das Facebook, Instagram, Messenger, Quest usw. enthält
- Es umfasst derzeit mehr als 10 Millionen Zeilen Kotlin-Code
Gründe für die Umstellung auf Kotlin
- Neben der Popularität gab es mehrere zentrale Vorteile
- Nullability: NPEs waren bei Meta ein verbreitetes Problem, weshalb verschiedene Gegenmaßnahmen ergriffen wurden. Die eingebaute Nullability-Behandlung von Kotlin ist jedoch deutlich leistungsfähiger und einfacher zu nutzen
- Funktionale Programmierung: Kotlins Inline-Funktionen und Lambda-Ausdrücke ermöglichen einen FP-Stil ohne Einbußen bei der Ausführungsgeschwindigkeit
- Kürzerer Code
- DSL / type-safe Builder
- Natürlich gibt es auch einige nicht zu vernachlässigende Nachteile
- Mit der Einführung einer weiteren Sprache muss eine gemischte Codebasis recht lange gepflegt werden
- Kotlin ist zwar beliebt, hat in Sachen Verbreitung aber noch einen Abstand zu Java. Deshalb gibt es weniger Tools, und viele Kotlin-Tools müssen die Interoperabilität zwischen Kotlin und Java berücksichtigen, was die Implementierung komplex macht
- Die größte Sorge waren die Build-Zeiten. Von Anfang an war klar, dass Kotlin-Builds langsamer sein könnten als Java-Builds. Lange Build-Zeiten wirken sich negativ auf die Developer Experience aus
Ansatz für die Migration
- Die Umstellung auf Kotlin war überraschend einfach und zugleich sehr komplex
- Mit J2K (Java To Kotlin Converter) war es zwar bequemer, aber dennoch kompliziert
- J2K ist nicht immer präzise, und die Interoperabilität zwischen Java und Kotlin erzeugt einige Grenzfälle
- Für die Migration gab es zwei Optionen
- Nur neuen Code in Kotlin schreiben und den Großteil des Codes in Java belassen
- Der Vorteil ist der geringere Aufwand, aber durch die Mischung beider Sprachen entstehen in Kotlin Plattformtypen, was zu Null-Pointer-Dereferenzierungen und Abstürzen führen kann. Außerdem konnten Typparameter in Java (bis vor Kurzem) nicht als nullable markiert werden, und Kotlins Overloading-Regeln berücksichtigen die Null-Zulässigkeit, die Overloading-Regeln von Java jedoch nicht
- Versuchen, den gesamten Inhouse-Code in Kotlin umzuwandeln
- Nur neuen Code in Kotlin schreiben und den Großteil des Codes in Java belassen
So wurde migriert
- Beide Optionen wurden geprüft, und man entschied sich mit dem Ziel, den gesamten Code in Kotlin zu konvertieren
- Anfangs ging es etwas langsam, aber nach der Beseitigung einiger Blocker wurde eine Migration im großen Maßstab möglich
- Derzeit enthalten die Android-Apps von Facebook, Messenger und Instagram jeweils 1 Million Zeilen Kotlin-Code, und der Umstellungsgrad steigt weiter
- Die gesamte Android-Codebasis umfasst aktuell 10 Millionen Zeilen Kotlin-Code
Unblocking
- Zu Beginn der Umstellung traten einige Probleme auf
- Wegen Bytecode-Mustern musste Redex aktualisiert werden
- Einige interne Bibliotheken führten aus Performance-Gründen Bytecode-Transformationen durch, die in Kotlin nicht funktionierten
- Wer intern bereits stark optimiert hat, wird wahrscheinlich auf ähnliche Probleme stoßen
- Auch bei bestehenden Tools traten Probleme auf
- In Code-Review und Wiki wurde Kotlin-Syntax nicht hervorgehoben, daher wurde Pygments aktualisiert
- Mit Ktfmt wurde ein eigener Kotlin-Formatter entwickelt
Die Migration beschleunigen
- Die Tools waren bereit, um Code nach Kotlin zu konvertieren
- Allerdings enthielt jede einzelne Migration viel Boilerplate, die manuell bearbeitet werden musste
- J2K ist ein generisches Tool und hat kein Verständnis für den konkreten Code. Deshalb war viel Handarbeit nötig
- Zum Beispiel bei JUnit-Testregeln
- Daher wurde J2K in die Mitte einer dreistufigen Pipeline gesetzt
- Zuerst wird ein Java-Paket vorbereitet, damit es nach Kotlin konvertiert werden kann. Dazu gehören Bugfixes und Anpassungen für interne Tools
- Danach wird J2K automatisiert per Skript ausgeführt
- Anschließend werden die neuen Kotlin-Dateien nachbearbeitet. Das ist der wichtigste Schritt. Automatische Refactorings, Linter usw. laufen dabei im Headless-Modus
- Automatisierung löst nicht jedes Problem, aber die häufigsten Fälle wurden priorisiert
Was bei der Kotlin-Migration gelernt wurde
- Der Code wurde kürzer
- Die Laufzeitgeschwindigkeit blieb erhalten
- Die Build-Größe ist kein Problem
- Das Problem längerer Build-Zeiten wurde gelöst: Einsatz von KSP (Kotlin Symbol Processing API)
4 Kommentare
Vielen Dank fürs Teilen des guten Artikels. Als ich Kotlin zum ersten Mal benutzt habe, gefiel es mir persönlich, weil es in vielerlei Hinsicht komfortabler als Java war, und ich dachte auch, dass Kotlin sich künftig vielleicht zum Standard entwickeln würde. Wenn man aber eine Weile damit arbeitet, gibt es doch noch viele Punkte, bei denen ich Java weiterhin für besser halte.
Ich denke, für Android ist Kotlin eine gute Wahl, aber in anderen Umgebungen (zum Beispiel Spring usw.) scheint Java weiterhin die bessere Wahl zu sein, wenn Stabilität wichtig ist.
Könnten Sie vielleicht ein konkretes Beispiel für Stabilität nennen? Ich habe noch nicht so viel Erfahrung und bin deshalb sehr neugierig, da ich solchen Fällen bisher noch nicht begegnet bin.
Ich weiß nicht, ob es daran liegt, dass JetBrains Schlag auf Schlag Releases veröffentlicht, aber überraschenderweise gibt es viele Fixes für Compiler-Bugs.
https://github.com/JetBrains/kotlin/releases/tag/v1.7.20
Es kommt auch immer wieder vor, dass der Compiler selbst abstürzt (das ist noch einigermaßen okay, solange es vor dem Release passiert), und auch die erzeugten Artefakte enthalten gelegentlich Bugs.
Außerdem ist es bei Android so, dass auch der R8-Bytecode-Optimierer nach der Kotlin-Version unterscheidet.
Denn es gibt Optimierungsfunktionen speziell für Kotlin-Code, aber dafür gibt es keine offizielle Kompatibilitätstabelle (Android Gradle Plugin : Kotlin Version).
Deshalb sollte man vorsichtig sein, wenn man in Android-Projekten die Kotlin-Version ändert;;;
Zugehöriger Bug-Report: https://issuetracker.google.com/issues/207397158
Danke. Da gibt es wohl eine tiefe und weite Welt..