Ein im Code-Review erkannter Fehler kostet Minuten. Derselbe Fehler, der nach einem Play-Store-Rollout entdeckt wird, kostet Ihre absturzfreie Nutzerrate, Ihr Store-Ranking und manchmal Ihr Wochenende. Google reduziert die Sichtbarkeit jeder App, die eine 1,09%ige nutzerperzipierte Absturzrate überschreitet, was das Review zum letzten ruhigen Kontrollpunkt macht, bevor diese Offenlegung öffentlich wird.
Diese Android-Code-Review-Checkliste ist diejenige, die unsere Reviewer verwenden, wenn ein Kunde einen Pull Request auf einer Android-Codebasis öffnet. Sie kodifiziert die Code-Review-Best-Practices, die unser Team in Produktions-App-Audits verfeinert hat. Eine solide Code-Review-Checkliste beginnt mit universellen Prinzipien wie geschichteter Architektur, Benennung und Testabdeckung. Auf Android treffen diese Prinzipien auf eine spezifische Reihe von Fehlermodi rund um Lifecycle, Threading und den Play-Store-Release-Pfad.
Vor Beginn des Reviews
Eine produktive Android-Code-Review beginnt, bevor jemand den Code öffnet. Der Reviewer braucht Kontext, einen sauberen Build und einen überschaubaren Umfang. Wir bestätigen drei Dinge im Voraus:
- Umfang und Ziel. Ein Bugfix, ein Feature-Branch, ein Refactoring und ein Release-Kandidaten-Audit verdienen jeweils eine andere Betrachtungsweise. Wir fragen den Autor, was sich geändert hat, welches Risiko es birgt und was zu priorisieren ist.
- CI ist grün. Lint, Detekt, Ktlint und Unit-Tests müssen bestehen, bevor ein Mensch den Code berührt.
- Das Änderungsset ist überprüfbar. Ciscos klassische Studie zum Peer-Code-Review zeigte, dass die Fehlererkennung bei mehr als 400 Zeilen pro Sitzung stark abnimmt. Ein 3.000-Zeilen-Review wird zum Gummistempel.
Wir lesen auch zuerst das Ticket und die Commit-Nachrichten. Die Hälfte des Lärms bei den meisten Reviews kommt von Reviewern, die „warum?” fragen, wenn die Antwort in Jira war.
Architektur-Review
Architektur ist die Schicht, wo kleine Fehler zu quartalsübergreifenden Neuentwicklungen werden. Die meisten Probleme, die wir bei Kundenaudits finden, lassen sich auf eines von drei Mustern zurückführen: Business-Logik, die in die UI-Schicht leckt, umgangene Dependency Injection oder ein „ViewModel”, das still zu einem God-Object geworden ist.
Schichtentrennung
Domain-Logik gehört in Use Cases oder Interactors. Activity, Fragment und Composable-Funktionen sollten frei von Business-Regeln bleiben. Repositories verwalten Datenquellen, und ViewModels koordinieren den Zustand. Eine Activity, die Retrofit direkt aufruft, wird immer zum Refactoring-Kandidaten.
Dependency Injection
Hilt oder Koin sollte den Dependency-Graph verdrahten. Die manuelle Instanziierung von Abhängigkeiten innerhalb von Produktionsklassen ist ein Warnsignal, insbesondere für alles, was isoliert getestet werden muss. Zentralisiertes DI macht auch Legacy-Code-Modernisierung später günstiger, weil der Austausch von Implementierungen zu einer Änderung auf Modulebene wird, anstatt zu einer Suche durch die gesamte Codebasis.
Modularisierungs-Warnsignale
Feature-Module sollten von :core und :domain abhängen. Feature-übergreifende Abhängigkeiten laufen durch eine gemeinsame Schicht. Zirkuläre Abhängigkeiten sollten den Build zum Scheitern bringen. Wir kennzeichnen auch die architektonischen Anti-Patterns, die wir am häufigsten in KI-gestützten Code-Reviews sehen: Composables, die kompilieren, aber Composes Stabilitätsvertrag brechen, Repositories, die die Schichtenarchitektur umgehen, und ViewModels, die Logik absorbieren, die die Use-Case-Schicht besitzen sollte.
Kotlin-Idiome im Android-Code
Kotlin gibt Reviewern Tools, die Java nicht hatte. Die meisten modernen Android-Codebasen mischen die beiden Sprachen, insbesondere nach einer Migration. Für die Java-Seite einer gemischten Codebasis deckt unsere Java Code-Review-Checkliste die sprachspezifischen Prüfungen ab. Was folgt, zielt auf die Kotlin-Patterns, die mit Android-Lifecycle und Threading zusammenhängen.
Wir suchen nach diesen Idiomen:
valstandardmäßig,varnur dort, wo sich der Zustand tatsächlich verändert- Null-Sicherheit ohne
!!. Sichere Aufrufe, der Elvis-Operator undrequireNotNullmit einer aussagekräftigen Meldung sind die Standards - Sealed Classes oder Sealed Interfaces für den UI-Zustand. Eine
data classmit sieben nullable Feldern ist ein Refactoring-Kandidat - Scope-Funktionen werden verwendet, um die Absicht zu verdeutlichen, nicht um vier Ebenen tief zu verschachteln
- Collections werden nach dem Zugriffsmuster ausgewählt. Ein linearer Scan einer
Listin einer Hot-Loop sollte einSetsein
sealed interface OrderListUiState {
data object Loading : OrderListUiState
data class Ready(val orders: List) : OrderListUiState
data class Error(val message: String) : OrderListUiState
}
class OrderListViewModel(
private val repository: OrderRepository
) : ViewModel() {
private val _uiState = MutableStateFlow(OrderListUiState.Loading)
val uiState: StateFlow = _uiState.asStateFlow()
fun refresh() {
viewModelScope.launch {
_uiState.value = runCatching { repository.getOrders() }
.fold(
onSuccess = { OrderListUiState.Ready(it) },
onFailure = { OrderListUiState.Error(it.message.orEmpty()) }
)
}
}
}
Das Legacy-Äquivalent verwendet typischerweise LiveData<Order>?, einen nullable Fehler-String und ein !! auf value. Die Aufgabe des Reviewers ist es, dieses Muster zu erkennen, bevor es gemergt wird.
Jetpack-Compose-Review
Compose hat verändert, wonach ein Reviewer sucht. Die XML- und Fragment-Patterns sind für ältere Screens noch relevant, aber neuer Code rückt Rekompositionskosten, State-Hoisting und die Behandlung von Seiteneffekten in den Fokus.
Für neuen Compose-Code prüfen wir:
- Der Zustand ist auf die richtige Ebene gehoistet. Ein
TextFieldsollte seinen eigenen Zustand nicht besitzen, wenn der übergeordnete Screen ihn benötigt - Keine Business-Logik innerhalb von
@Composable-Funktionen. Composables beschreiben die UI für einen gegebenen Zustand. Sie rufen keine Repositories auf und führen kein I/O aus LaunchedEffect,DisposableEffectundSideEffectwerden korrekt verwendet. Eine Coroutine, die in einem Composable-Body ohneLaunchedEffectgestartet wird, leckt bei der nächsten RekompositionrememberundderivedStateOfschützen aufwändige Berechnungen vor der erneuten BerechnungLazyColumnundLazyRowverwenden stabile Keys. EinLazyColumnohnekey = { it.id }mischt Composables bei jeder Datenänderung neu- Previews existieren für Lade-, Erfolgs- und Fehlerzustände
Für XML-basierte Screens gilt die Regel unverändert: ViewBinding statt findViewById, Layouts flach halten, keine Business-Logik innerhalb von onCreateView.
Async-Arbeit-Review
Async-Bugs sind die teuerste Klasse von Android-Defekten, die wir bei Audits antreffen. Sie sind in der QA intermittierend, in der Produktion deterministisch und häufig in Logs unsichtbar. Die Aufgabe des Reviewers ist es, sie zur Review-Zeit aufzudecken.
Worauf wir achten:
- No
GlobalScopeim Produktionscode.viewModelScope,lifecycleScopeoder ein injizierter Scope, der an eine Hilt-Komponente gebunden ist, sind die richtigen Optionen Dispatchersentsprechen der Arbeit und werden injiziert, damit Tests sie ersetzen können- Abbruch wird berücksichtigt. Langwierige Arbeit verwendet abbrechbare Suspending-APIs oder prüft
isActivein Schleifen - No
runBlockingim Haupt-Thread. Niemals StateFlowfür den UI-Zustand,SharedFlowfür einmalige Ereignisse, kaltes Flow für Repository-Lesevorgänge- Exceptions überqueren Coroutine-Grenzen sicher.
CoroutineExceptionHandlerundsupervisorScopesind bewusste Entscheidungen, die der Reviewer absichtlich eingesetzt sehen sollte
Ein Reviewer, der viewModelScope.launch(Dispatchers.IO) { repository.fetch() } lesen und die vier darin enthaltenen Fehler erkennen kann, ist bei jedem Release wertvoll.
Speicher- und Performance-Review
Performance-Probleme verstecken sich in kleinen Fehlern. Speicherlecks verstecken sich an Stellen, die isoliert korrekt aussehen. Ein erfahrener Reviewer liest Code mit einem Profiler im Kopf.
Lifecycle-Lecks
Langlebige Objekte, wie Singletons, Application-Scope-Repositories oder statische Felder, sollten niemals eine Referenz auf eine Activity, ein Fragment oder eine View halten. Innere Klassen innerhalb von Lifecycle-Ownern sind ein häufiger Täter. Alles, was Sie in einem Lifecycle-Callback registrieren, muss auch abgemeldet werden, wenn dieser Lifecycle endet.
ViewBinding-Nullsetzung
In Fragmenten ist das Setzen von _binding = null in onDestroyView obligatorisch. Dieses einzelne Muster verhindert das häufigste Speicherleck im modernen Android-Code. Der Reviewer sollte es in jedem Fragment sehen, das ViewBinding verwendet, ohne Ausnahmen.
Wir prüfen auch den Rest der Performance-Schicht. Das Laden von Bildern sollte eine echte Bibliothek (Coil, Glide, Picasso) verwenden, anstatt einem handgestrickten Decoder, der den Haupt-Thread blockiert. RecyclerView-Adapter sollten DiffUtil oder ListAdapter anstelle von notifyDataSetChanged() verwenden. LeakCanary gehört in jeden Debug-Build, und jedes offene Leck, das es aufdeckt, wird behoben, bevor die Änderung ausgeliefert wird.
Sicherheits-Review
Mobile Sicherheit ist einer jener Bereiche, wo ein einziges Versehen mehr kostet als ein Jahr sorgfältiger Arbeit. Der Verizon 2025 Mobile Security and Breach Report ergab, dass 85 % der Organisationen zunehmende Angriffe auf mobile Geräte meldeten, und 88 % der Anwendungs-Verstöße durch gestohlene Anmeldedaten ermöglicht werden. Geheimnisverwaltung und Berechtigungs-Scoping sind nicht verhandelbare Teile jeder Code-Review-Checkliste für Android.
Manifest und Berechtigungen
android:exported sollte bei jeder Activity, jedem Service, Receiver und Provider mit einem Intent-Filter explizit gesetzt werden. Android 12 und später erfordern es, und der sichere Standard ist false. Jeder <uses-permission>-Eintrag sollte einer Funktion entsprechen, die die App tatsächlich verwendet. networkSecurityConfig sollte in Produktions-Builds keinen Klartext-Traffic erlauben.
Geheimnisse und Datenspeicherung
API-Schlüssel, Signing-Keys, OAuth-Client-Secrets und Firebase-Server-Keys gehören in CI-Secret-Stores. gradle.properties in Git eingecheckt zu haben, ist ein Leak-Pfad, den wir häufig bei Code-Review-Services-Engagements aufdecken. Sensitive Laufzeitdaten verwenden EncryptedSharedPreferences für Anmeldedaten, Android Keystore für kryptografische Schlüssel und internen Speicher für app-private Dateien. TLS 1.2 ist das Minimum.
R8 sollte für Release-Builds mit aktiviertem Schrumpfen und Obfuskierung aktiviert sein. ProGuard-Regeln müssen auf übermäßig breite -keep-Direktiven überprüft werden, und Debug-only-Code muss durch Build-Variante abgesichert sein, damit er nie den Release erreicht.
Release-Bereitschaft
Einige Review-Bedenken gelten nur für Änderungen, die den Release-Pfad berühren. Wir geben ihnen einen separaten Durchgang, weil die Kosten eines Fehlers hoch sind und Teams sie normalerweise spät erkennen.
Wir überprüfen Folgendes:
minSdkVersion,targetSdkVersionundcompileSdkVersionstimmen mit den aktuellen Play-Store-Anforderungen überein. Google erhöht die Mindestwerte fürtargetSdkVersionjährlich, und das Verpassen des Stichtags blockiert neue Uploads- Das Release-Artefakt ist ein Android App Bundle (
.aab), das erforderliche Upload-Format für neue Apps - R8 verkleinert das Bundle in CI sichtbar. Ein 20-MB-Sprung bei einer kleinen Änderung verdient fünf Minuten Untersuchung
- Baseline-Profile werden regeneriert, wenn startup-kritischer Code sich ändert
- Crashlytics, Firebase Performance Monitoring und Play Console Pre-Launch-Berichte sind in die Release-Variante eingebunden
- Strings sind für jede Locale übersetzt, in der die App ausgeliefert wird. Die Lint-Regel für fehlende Übersetzungen sollte Release-Builds scheitern lassen, anstatt eine Warnung auszugeben, die das Team ignorieren lernt
Statische Analysetools
Automatisierte Tools erkennen die Probleme, mit denen Menschen keine Zeit verschwenden sollten. Vier gehören in jede Android-CI-Pipeline. Sie überschneiden sich, ergänzen sich gegenseitig, keines ersetzt die anderen. Code-Review-Richtlinien funktionieren am besten, wenn diese Tools die mechanischen Prüfungen übernehmen und Reviewer sich auf Architektur und Absicht konzentrieren.
Android Lint
Nativer Android-Gradle-Plugin-Checker. Scannt Kotlin, Java, XML und Ressourcen auf Plattform-Level-Probleme.
Berechtigungen, veraltete APIs, Layout-Probleme, fehlende Übersetzungen, Manifest-Probleme.
Detekt
Statische Analyse für Kotlin. Parst den Kotlin-AST, versteht also Coroutinen, Sealed Classes und moderne Sprachfeatures.
Code-Gerüche, zyklomatische Komplexität, Namenskonventionen, benutzerdefinierte Regelsets.
Ktlint
Meinungsstarker Formatter und Style-Checker für Kotlin. Minimale Konfiguration, vernünftige Standards.
Formatierungskonsistenz, Import-Reihenfolge, Zeilenlänge. Wird eher mit Detekt kombiniert als es zu ersetzen.
R8
Code-Shrinker, Optimierer und Obfuskierer von Google. Ersetzt ProGuard.
Größenreduzierung von Release-Builds, Dead-Code-Eliminierung, Bezeichner-Obfuskierung.
Kombinieren Sie diese mit Android Studios integrierten Inspektionen, und Warnungen erscheinen inline beim Review, wo Reviewer sie tatsächlich sehen.
Warum ein frischer Blick wichtig ist
Internes Code-Review ist eine Kultur. Externes Code-Review ist ein Service. Die obige Checkliste wirkt am stärksten, wenn sie von jemandem außerhalb des Teams angewendet wird, das den Code geschrieben hat, weil interne Reviewer Patterns normalisieren, mit denen die Codebasis seit Monaten lebt.
Drei Momente, in denen das Hinzuziehen eines externen Reviewers die Investition zurückzahlt:
- Vor einem großen Release, wenn das Team zu nah am Code ist, um zu erkennen, was es gewohnt geworden ist.
- Nach der Übernahme einer Codebasis von einem früheren Anbieter, einem Freelancer oder einer Akquisition.
- Wenn KI-unterstützter Code schnell gemergt wurde und das Team einen Härtungsdurchgang benötigt, bevor der Produktionsverkehr eintrifft.
Wenn Sie eine Android-Codebasis haben, auf die Sie einen Senior-Blick werfen möchten, kontaktieren Sie uns und wir werden ein Review auf Basis dieser Checkliste planen.
Erfahren Sie, wie wir eine plattformübergreifende Netzwerk-Mapping-App vor dem Release auditiert und 90 % Code-Wartbarkeit erzielt haben