Code Smell est un indicateur superficiel dans le code qui signale un problème potentiel dans la conception ou l'architecture d'une application. Le terme a été inventé par Kent Beck et popularisé par Martin Fowler dans le livre « Refactoring: Improving the Design of Existing Code ». Selon Martin Fowler, une odeur de code ne signifie pas nécessairement un bogue, mais indique presque toujours la nécessité d'un refactoring pour améliorer la maintenabilité.
Points clés
Code Smell est une métaphore pour les symptômes dans le code source qui indiquent avec une haute probabilité des problèmes plus profonds. Le terme lui-même n'a pas de définition formelle — c'est une heuristique basée sur l'expérience des développeurs. Martin Fowler et Kent Beck ont systématisé pour la première fois 22 odeurs en 1999 dans le livre « Refactoring », et la plupart d'entre elles restent pertinentes des décennies plus tard.
Il est important de comprendre la différence entre Code Smell et un bogue. Une odeur n'est pas une erreur : le code compile, fonctionne et produit des résultats corrects. Le problème est qu'un tel code est difficile à lire, modifier et tester. Avec le temps, le coût de chaque changement augmente et la confiance dans la correction du refactoring diminue. Les outils d'analyse statique (SonarQube, Detekt, SwiftLint) détectent automatiquement de nombreuses odeurs.
La nature heuristique de Code Smell signifie que toutes les longues méthodes ne doivent pas être divisées, et toutes les grandes classes ne nécessitent pas un refactoring. La décision est prise par le développeur en évaluant le contexte : fréquence des changements, criticité du module, plans de développement. Les ingénieurs expérimentés sentent une odeur intuitivement — le code « sent mauvais » même si toutes les règles formelles sont respectées.
Fowler a identifié 22 odeurs regroupées en plusieurs catégories. Pour le développement mobile, les plus pertinentes sont les odeurs structurelles, les odeurs de conception orientée objet et les problèmes spécifiques liés aux contraintes de plateforme. Examinons chaque groupe avec des exemples concrets.
Long Method est l'odeur la plus courante dans les applications mobiles. Un écran de formulaire d'inscription contient souvent une seule méthode setupUI de 200+ lignes qui crée toutes les vues, configure les contraintes, s'abonne aux événements et gère les erreurs. Solution : diviser en méthodes par blocs logiques — configureEmailField, configurePasswordField, setupConstraints, bindViewModel.
Large Class — une Activity ou ViewController responsable de l'affichage, de la navigation, de la logique métier et des interactions réseau tout à la fois. Une telle classe viole le principe de responsabilité unique et contient des dizaines de champs et de méthodes. Sous Android, c'est souvent un Fragment de 1000+ lignes contenant la logique de différents écrans. Solution : extraire un presenter/ViewModel, déplacer le code réseau dans un repository et la navigation dans un coordinateur.
Duplicate Code — copier des blocs identiques dans différentes parties de l'application. Un exemple typique : deux écrans affichant une carte produit — dans le catalogue et dans les favoris. Si la logique d'affichage est copiée, corriger un bogue à un endroit ne le corrigera pas à l'autre. Solution : extraire la logique commune dans un composant réutilisable ou une extension.
Feature Envy — une méthode d'une classe utilise intensivement les données d'une autre classe. Sous Android, cela se manifeste quand une ViewModel accède directement aux champs d'un modèle User au lieu d'appeler une méthode du modèle. Signal : si une méthode peut être déplacée dans la classe dont elle utilise les données — déplacez-la. Switch Statements (chaînes de conditions) — une construction switch ou une chaîne if-else qui vérifie le type d'objet. Utilisez plutôt le polymorphisme ou le pattern strategy.
Data Class — une classe qui stocke uniquement des données mais ne contient pas de comportement. Les data classes en Kotlin ou les structures en Swift ne sont pas intrinsèquement une odeur. Le problème survient lorsque la logique métier qui travaille avec ces données est dispersée dans toute la base de code au lieu d'être encapsulée. Refused Bequest — une sous-classe n'utilise pas la plupart des méthodes du parent et les remplace par des stubs vides. Un signe d'héritage incorrect : remplacez l'héritage par la composition.
God Activity / God Fragment — une Activity ou Fragment qui sait tout : cycle de vie, données, navigation, permissions, DI. C'est la classe la plus coûteuse à maintenir dans une application. Solution : les patterns architecturaux MVVM, MVI ou Clean Architecture séparent les responsabilités. Giant ViewController — l'équivalent pour iOS, où un UIViewController contient toute la logique de l'écran et dépasse souvent 500 lignes.
Hardcoded Resources — chaînes, couleurs, tailles, URL d'API intégrés directement dans le code. Sous Android, cela viole le système de ressources R ; sous iOS, NSLocalizedString et Asset Catalog. Correction : déplacer toutes les chaînes dans strings.xml ou Localizable.strings, les URL dans un fichier de configuration, les tailles dans dimens. Leaking Context — conserver une référence à une Activity ou ViewController plus longtemps que la durée de vie du composant lui-même. Provoque des fuites mémoire et des crashes. Solution : références faibles, Jetpack Lifecycle, RxSwift DisposeBag.
| Odeur | Où elle apparaît | Solution |
|---|---|---|
| Long Method | Android/iOS | Extract Method, division |
| Large Class | Activity, ViewController | MVVM, VIPER, Clean Arch |
| Duplicate Code | Tout écran | Composant partagé, DRY |
| Feature Envy | ViewModel, Presenter | Move Method |
| Leaking Context | Android | Composants Lifecycle-aware |
Code review est le moyen le plus fiable de détecter les odeurs. L'œil humain remarque les constructions non naturelles que les analyseurs automatiques manquent. L'efficacité de la revue de code s'améliore lorsque l'équipe utilise une liste de contrôle des odeurs typiques. Il est recommandé de ne pas revoir plus de 200–400 lignes de code par session — après ce seuil, l'attention diminue et les odeurs commencent à échapper.
Analyse statique automatise la recherche des odeurs structurelles. Pour Android, les outils standard sont Detekt (Kotlin) et Android Lint ; pour iOS, SwiftLint et SonarQube. Ces outils trouvent les méthodes longues, les grandes classes, le code dupliqué et bien d'autres problèmes. Il est important d'adapter les règles au projet — les configurations par défaut sont souvent trop strictes ou, au contraire, laissent passer des odeurs critiques.
Métriques de code fournissent des critères objectifs : Complexité Cyclomatique (seuil >10 nécessite attention), Lignes de Code par Méthode (seuil >30), Profondeur d'Héritage (>3 — raison de réfléchir). Des outils comme CodeMetrics (Xcode) et le plugin Gradle Metrics construisent des graphiques d'évolution des métriques dans le temps. Si la complexité d'une méthode est passée de 5 à 15 après le dernier commit — c'est un signal pour refactoriser.
// Exemple : méthode avec Complexité Cyclomatique = 7 (au-dessus du seuil 5)
fun processOrder(order: Order) {
if (order.status == Status.NEW) { /* 10 lignes */ }
else if (order.status == Status.PAID) { /* 15 lignes */ }
else if (order.status == Status.SHIPPED) { /* 20 lignes */ }
else if (order.status == Status.DELIVERED) { /* 8 lignes */ }
else if (order.status == Status.CANCELLED) { /* 5 lignes */ }
else { throw IllegalStateException() }
}
// Correction : polymorphisme au lieu de switch
interface OrderHandler {
fun handle(order: Order)
}
La détection automatisée des odeurs ne remplace pas la revue de code : les analyseurs statiques ne trouvent que les problèmes structurels mais ne capturent pas les odeurs sémantiques (Feature Envy, Inappropriate Intimacy). La combinaison d'outils automatiques et de la revue humaine donne les meilleurs résultats. Configurez votre pipeline CI/CD pour que les builds échouent lorsque les seuils de complexité ou de longueur de méthode sont dépassés.
Refactoring est la méthode principale pour éliminer les odeurs de code. Fowler décrit des dizaines de techniques de refactoring, chacune applicable à une odeur spécifique. Extract Method — pour les méthodes longues, Extract Class — pour les grandes classes, Move Method — pour Feature Envy. Il est important d'effectuer le refactoring par petits pas, en maintenant le code fonctionnel après chaque changement.
Tests avant le refactoring sont obligatoires. Si le code n'est pas couvert par des tests unitaires, le refactoring se transforme en réécriture avec un résultat inconnu. Pour le code legacy sans tests, utilisez des Characterization Tests — écrivez des tests qui capturent le comportement actuel, puis refactorisez. Les tests donnent la certitude que la logique métier n'a pas été cassée après le refactoring.
Progressivité est la clé pour corriger avec succès les odeurs dans le développement mobile. N'essayez pas de réécrire une God Activity entièrement. Extrayez d'abord la couche de navigation, puis la couche de données, puis la logique d'affichage. Chaque étape doit être accompagnée d'un commit et d'une exécution de tests. Utilisez des feature toggles pour activer le refactoring pour un sous-ensemble d'utilisateurs et revenir en arrière en cas de problème.
Les outils de l'IDE automatisent de nombreuses techniques de refactoring. Android Studio et IntelliJ IDEA proposent des refactorings intégrés : Extract Method, Extract Interface, Pull Members Up, Encapsulate Fields. Xcode (à partir de la version 14) a amélioré la prise en charge du refactoring pour Swift. L'utilisation de refactorings automatiques réduit le risque d'erreurs par rapport à la copie manuelle de code.
Le développement mobile ajoute ses propres odeurs spécifiques liées aux contraintes de plateforme. Sous Android, cela inclut les fuites de Context, les curseurs non fermés et une utilisation incorrecte du Lifecycle. Sous iOS, les cycles de rétention via les closures, la manipulation incorrecte d'Auto Layout et les ViewControllers géants. Ces odeurs non seulement nuisent à la maintenabilité mais affectent directement les performances et la stabilité de l'application.
Callback Hell est une odeur caractéristique pour le code travaillant avec des opérations asynchrones. Les callbacks imbriqués rendent le code illisible et difficile à déboguer. Solution : coroutines (Kotlin), async/await (Swift 5.5+), RxJava/RxSwift ou Combine. Selon Google I/O 2023, les projets qui sont passés du style callback aux coroutines ont réduit le nombre de bogues de 30 % et accéléré l'ajout de nouvelles fonctionnalités.
Platform Coupling — couplage fort de la logique métier aux composants de la plateforme. Tester une telle logique nécessite de lancer un émulateur, ce qui ralentit le cycle de feedback. Correction : Clean Architecture sépare le code en couches Domain (Kotlin/Swift pur sans dépendances plateforme) et Data/UI (avec dépendances plateforme). La logique métier est testée sur la JVM sans émulateur.
Foire aux questions
Non — Code Smell n'est pas une erreur. Le code avec une odeur fonctionne correctement, mais il est difficile à maintenir, modifier et tester. Un bogue est un comportement incorrect ; une odeur est un avertissement de problèmes potentiels futurs.
22 odeurs dans la deuxième édition de « Refactoring » (2019). Parmi elles : Long Method, Large Class, Primitive Obsession, Data Clumps, Switch Statements, Speculative Generality et d'autres. La communauté a ajouté des dizaines de nouvelles odeurs pour les paradigmes et plateformes modernes.
Une combinaison donne le meilleur résultat : Detekt (Android/Kotlin), SwiftLint (iOS), SonarQube (les deux) pour l'analyse automatique et la revue de code pour les odeurs sémantiques. Aucun outil ne trouve 100 % des problèmes — l'expérience humaine reste décisive.
Oui, si le code change rarement ou sera complètement réécrit prochainement. Cependant, l'accumulation d'odeurs se transforme en dette technique : chaque nouveau changement devient plus difficile et le coût de correction croît de façon exponentielle.
Oui — les frameworks déclaratifs ont engendré de nouvelles odeurs : blocs @State géants, manipulation incorrecte des rendus répétés, recomposition excessive et absence d'extraction dans des vues séparées. Pour SwiftUI, une odeur typique est Massive View avec des dizaines de variables @State.
Résumé
Nous développerons une application mobile clé en main
IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.
Lisez aussi