Code Smell ist ein oberflächliches Merkmal im Code, das auf ein potenzielles Problem im Design oder der Architektur einer Anwendung hinweist. Der Begriff wurde von Kent Beck geprägt und von Martin Fowler im Buch „Refactoring: Improving the Design of Existing Code“ populär gemacht. Laut Martin Fowler bedeutet ein Code-Geruch nicht unbedingt einen Fehler, weist aber fast immer auf die Notwendigkeit einer Refaktorisierung zur Verbesserung der Wartbarkeit hin.
Wichtige Erkenntnisse
Code Smell ist eine Metapher für Symptome im Quellcode, die mit hoher Wahrscheinlichkeit auf tiefere Probleme hinweisen. Der Begriff selbst hat keine formale Definition — es ist eine Heuristik, die auf der Erfahrung von Entwicklern basiert. Martin Fowler und Kent Beck systematisierten 1999 erstmals 22 Gerüche im Buch „Refactoring“, und die meisten davon sind Jahrzehnte später noch relevant.
Es ist wichtig, den Unterschied zwischen Code Smell und einem Fehler zu verstehen. Ein Geruch ist kein Fehler: Der Code kompiliert, funktioniert und liefert korrekte Ergebnisse. Das Problem ist, dass solcher Code schwer zu lesen, zu ändern und zu testen ist. Mit der Zeit steigen die Kosten für jede Änderung und das Vertrauen in die Korrektheit der Refaktorisierung sinkt. Statische Analysetools (SonarQube, Detekt, SwiftLint) erkennen viele Gerüche automatisch.
Der heuristische Charakter von Code Smell bedeutet, dass nicht jede lange Methode aufgeteilt werden muss und nicht jede große Klasse refaktorisiert werden muss. Die Entscheidung trifft der Entwickler unter Bewertung des Kontexts: Änderungshäufigkeit, Kritikalität des Moduls, Entwicklungspläne. Erfahrene Ingenieure riechen einen Geruch intuitiv — der Code „riecht schlecht“, auch wenn alle formalen Regeln eingehalten werden.
Fowler identifizierte 22 Gerüche, die in mehrere Kategorien unterteilt sind. Für die mobile Entwicklung sind strukturelle Gerüche, objektorientierte Design-Gerüche und spezifische Probleme im Zusammenhang mit Plattformbeschränkungen am relevantesten. Lassen Sie uns jede Gruppe anhand von Beispielen aus der Praxis untersuchen.
Long Method ist der häufigste Geruch in mobilen Anwendungen. Ein Registrierungsformular-Bildschirm enthält oft eine einzige setupUI-Methode mit 200+ Zeilen, die alle Views erstellt, Constraints einrichtet, Ereignisse abonniert und Fehler behandelt. Lösung: Aufteilung in Methoden nach logischen Blöcken — configureEmailField, configurePasswordField, setupConstraints, bindViewModel.
Large Class — eine Activity oder ViewController, die für Anzeige, Navigation, Geschäftslogik und Netzwerkinteraktionen gleichermaßen verantwortlich ist. Eine solche Klasse verletzt das Single-Responsibility-Prinzip und enthält Dutzende von Feldern und Methoden. In Android ist dies oft ein Fragment mit 1000+ Zeilen, das Logik verschiedener Bildschirme enthält. Lösung: einen Presenter/ViewModel extrahieren, Netzwerkcode in ein Repository verschieben und Navigation an einen Coordinator delegieren.
Duplicate Code — Kopieren identischer Blöcke in verschiedenen Teilen der Anwendung. Ein typisches Beispiel: zwei Bildschirme, die eine Produktkarte anzeigen — im Katalog und in den Favoriten. Wenn die Anzeigelogik kopiert wurde, behebt die Korrektur eines Fehlers an einer Stelle ihn nicht an der anderen. Lösung: gemeinsame Logik in eine wiederverwendbare Komponente oder Erweiterung auslagern.
Feature Envy — eine Methode einer Klasse verwendet intensiv Daten einer anderen Klasse. In Android zeigt sich dies, wenn eine ViewModel direkt auf Felder eines User-Modells zugreift, anstatt eine Methode des Modells aufzurufen. Signal: Wenn eine Methode in die Klasse verschoben werden kann, deren Daten sie verwendet — verschieben Sie sie. Switch Statements (Bedingungsketten) — eine switch-Konstruktion oder if-else-Kette, die den Objekttyp prüft. Verwenden Sie stattdessen Polymorphismus oder das Strategy-Pattern.
Data Class — eine Klasse, die nur Daten speichert, aber kein Verhalten enthält. Data Classes in Kotlin oder Strukturen in Swift sind an sich kein Geruch. Das Problem entsteht, wenn die mit diesen Daten arbeitende Geschäftslogik über die gesamte Codebasis verstreut ist, anstatt gekapselt zu sein. Refused Bequest — eine Unterklasse verwendet die meisten Methoden der Elternklasse nicht und überschreibt sie mit leeren Stubs. Ein Zeichen falscher Vererbung: Ersetzen Sie Vererbung durch Komposition.
God Activity / God Fragment — eine Activity oder Fragment, die alles weiß: Lebenszyklus, Daten, Navigation, Berechtigungen, DI. Dies ist die wartungsintensivste Klasse in einer Anwendung. Lösung: Architekturmuster wie MVVM, MVI oder Clean Architecture trennen Zuständigkeiten. Giant ViewController — das Äquivalent für iOS, bei dem ein UIViewController die gesamte Bildschirmlogik enthält und oft 500 Zeilen überschreitet.
Hardcoded Resources — Zeichenfolgen, Farben, Größen, API-URLs, die direkt in den Code eingebettet sind. In Android verstößt dies gegen das R-Ressourcensystem, in iOS gegen NSLocalizedString und Asset Catalog. Behebung: Alle Zeichenfolgen in strings.xml oder Localizable.strings verschieben, URLs in eine Konfigurationsdatei, Größen in dimens. Leaking Context — Halten einer Referenz auf eine Activity oder ViewController länger als die Komponente selbst lebt. Führt zu Speicherlecks und Abstürzen. Lösung: schwache Referenzen, Jetpack Lifecycle, RxSwift DisposeBag.
| Geruch | Wo es auftritt | Lösung |
|---|---|---|
| Long Method | Android/iOS | Extract Method, Aufteilung |
| Large Class | Activity, ViewController | MVVM, VIPER, Clean Arch |
| Duplicate Code | Beliebige Bildschirme | Shared Component, DRY |
| Feature Envy | ViewModel, Presenter | Move Method |
| Leaking Context | Android | Lifecycle-aware Komponenten |
Code-Review ist der zuverlässigste Weg, Gerüche zu erkennen. Das menschliche Auge bemerkt unnatürliche Konstruktionen, die automatische Analysewerkzeuge übersehen. Die Effektivität von Code-Reviews steigt, wenn das Team eine Checkliste typischer Gerüche verwendet. Es wird empfohlen, nicht mehr als 200–400 Codezeilen pro Sitzung zu überprüfen — nach dieser Schwelle lässt die Aufmerksamkeit nach und Gerüche beginnen zu entgehen.
Statische Analyse automatisiert die Suche nach strukturellen Gerüchen. Für Android sind die Standardwerkzeuge Detekt (Kotlin) und Android Lint, für iOS SwiftLint und SonarQube. Diese Werkzeuge finden lange Methoden, große Klassen, doppelten Code und viele andere Probleme. Es ist wichtig, die Regeln an das Projekt anzupassen — Standardkonfigurationen sind oft zu streng oder übersehen im Gegenteil kritische Gerüche.
Code-Metriken liefern objektive Kriterien: Zyklomatische Komplexität (Schwelle >10 erfordert Aufmerksamkeit), Codezeilen pro Methode (Schwelle >30), Vererbungstiefe (>3 — Grund zum Nachdenken). Werkzeuge wie CodeMetrics (Xcode) und das Gradle Metrics Plugin erstellen Diagramme der Metrikveränderungen im Laufe der Zeit. Wenn die Komplexität einer Methode nach dem letzten Commit von 5 auf 15 gestiegen ist — das ist ein Signal zur Refaktorisierung.
// Beispiel: Methode mit zyklomatischer Komplexität = 7 (über Schwelle 5)
fun processOrder(order: Order) {
if (order.status == Status.NEW) { /* 10 Zeilen */ }
else if (order.status == Status.PAID) { /* 15 Zeilen */ }
else if (order.status == Status.SHIPPED) { /* 20 Zeilen */ }
else if (order.status == Status.DELIVERED) { /* 8 Zeilen */ }
else if (order.status == Status.CANCELLED) { /* 5 Zeilen */ }
else { throw IllegalStateException() }
}
// Lösung: Polymorphismus statt switch
interface OrderHandler {
fun handle(order: Order)
}
Die automatisierte Geruchserkennung ersetzt kein Code-Review: Statische Analysewerkzeuge finden nur strukturelle Probleme, erfassen aber keine semantischen Gerüche (Feature Envy, Inappropriate Intimacy). Eine Kombination aus automatischen Werkzeugen und menschlicher Überprüfung liefert die besten Ergebnisse. Konfigurieren Sie Ihre CI/CD-Pipeline so, dass Builds fehlschlagen, wenn Komplexitäts- oder Methodenlängenschwellen überschritten werden.
Refaktorisierung ist die primäre Methode zur Beseitigung von Code-Gerüchen. Fowler beschreibt Dutzende von Refaktorisierungstechniken, die jeweils auf einen bestimmten Geruch anwendbar sind. Extract Method — für lange Methoden, Extract Class — für große Klassen, Move Method — für Feature Envy. Es ist wichtig, die Refaktorisierung in kleinen Schritten durchzuführen und den Code nach jeder Änderung funktionsfähig zu halten.
Tests vor der Refaktorisierung sind obligatorisch. Wenn der Code nicht durch Unit-Tests abgedeckt ist, wird die Refaktorisierung zu einer Neuschreibung mit unbekanntem Ergebnis. Für Legacy-Code ohne Tests verwenden Sie Characterization Tests — schreiben Sie Tests, die das aktuelle Verhalten erfassen, und refaktorisieren Sie dann. Tests geben die Gewissheit, dass die Geschäftslogik nach der Refaktorisierung nicht beschädigt wurde.
Schrittweises Vorgehen ist der Schlüssel zur erfolgreichen Behebung von Gerüchen in der mobilen Entwicklung. Versuchen Sie nicht, eine God Activity vollständig neu zu schreiben. Extrahieren Sie zuerst die Navigationsschicht, dann die Datenschicht, dann die Anzeigelogik. Jeder Schritt sollte von einem Commit und einem Testlauf begleitet werden. Verwenden Sie Feature Toggles, um die Refaktorisierung für eine Teilmenge von Benutzern zu aktivieren und bei Problemen zurückzusetzen.
IDE-Werkzeuge automatisieren viele Refaktorisierungstechniken. Android Studio und IntelliJ IDEA bieten integrierte Refaktorisierungen: Extract Method, Extract Interface, Pull Members Up, Encapsulate Fields. Xcode (ab Version 14) verbesserte die Refaktorisierungsunterstützung für Swift. Die Verwendung automatischer Refaktorisierungen verringert das Risiko von Fehlern im Vergleich zum manuellen Kopieren von Code.
Mobile Entwicklung fügt ihre eigenen spezifischen Gerüche hinzu, die mit Plattformbeschränkungen zusammenhängen. In Android gehören dazu Context-Lecks, nicht geschlossene Cursor und unsachgemäße Lifecycle-Nutzung. In iOS — retain cycles durch Closures, falsche Handhabung von Auto Layout, riesige ViewController. Diese Gerüche beeinträchtigen nicht nur die Wartbarkeit, sondern wirken sich direkt auf Leistung und Stabilität der Anwendung aus.
Callback Hell ist ein charakteristischer Geruch für Code, der mit asynchronen Operationen arbeitet. Verschachtelte Callbacks machen Code unlesbar und schwer debuggbar. Lösung: Koroutinen (Kotlin), async/await (Swift 5.5+), RxJava/RxSwift oder Combine. Laut Google I/O 2023 reduzierten Projekte, die vom Callback-Stil auf Koroutinen umgestiegen sind, die Fehleranzahl um 30% und beschleunigten die Hinzufügung neuer Funktionen.
Platform Coupling — starke Kopplung der Geschäftslogik an Plattformkomponenten. Das Testen solcher Logik erfordert das Starten eines Emulators, was den Feedback-Zyklus verlangsamt. Lösung: Clean Architecture trennt Code in Domain- (reines Kotlin/Swift ohne Plattformabhängigkeiten) und Data/UI- (mit Plattformabhängigkeiten) Schichten. Geschäftslogik wird auf der JVM ohne Emulator getestet.
Häufig gestellte Fragen
Nein — Code Smell ist kein Fehler. Code mit Geruch funktioniert korrekt, ist aber schwer zu warten, zu ändern und zu testen. Ein Fehler ist falsches Verhalten; ein Geruch ist eine Warnung vor potenziellen zukünftigen Problemen.
22 Gerüche in der zweiten Ausgabe von „Refactoring“ (2019). Darunter Long Method, Large Class, Primitive Obsession, Data Clumps, Switch Statements, Speculative Generality und andere. Die Gemeinschaft hat Dutzende neuer Gerüche für moderne Paradigmen und Plattformen hinzugefügt.
Eine Kombination liefert das beste Ergebnis: Detekt (Android/Kotlin), SwiftLint (iOS), SonarQube (beide) für die automatische Analyse und Code-Review für semantische Gerüche. Kein einzelnes Werkzeug findet 100% der Probleme — menschliche Erfahrung bleibt entscheidend.
Ja, wenn der Code selten geändert wird oder in Kürze vollständig neu geschrieben wird. Die Anhäufung von Gerüchen wird jedoch zu technischen Schulden: Jede neue Änderung wird schwieriger und die Kosten der Behebung wachsen exponentiell.
Ja — deklarative Frameworks haben neue Gerüche hervorgebracht: riesige @State-Blöcke, unsachgemäße Handhabung wiederholter Renderings, übermäßige Rekomposition und fehlende Extraktion in separate Views. Für SwiftUI ist ein typischer Geruch Massive View mit Dutzenden von @State-Variablen.
Zusammenfassung
Wir entwickeln eine mobile Applikation schlüsselfertig
IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.
Lesen Sie auch