Le code mort — ce sont des fragments de programme qui ne sont jamais exécutés et n'affectent pas le résultat, mais restent physiquement dans les fichiers sources du projet. Contrairement aux parties commentées, le code mort est compilé et se retrouve dans le binaire, augmentant sa taille et compliquant la navigation. Selon une étude de TIOBE Index (2025), un projet commercial moyen contient entre 10 et 25 pour cent de code qui n'est jamais appelé. Le code zombie est un sous-type de code mort qui fonctionnait dans le passé, mais après refactorisation a perdu sa pertinence et ne fait plus que prendre de la place. Le nettoyage régulier de ces fragments réduit la charge cognitive des développeurs et diminue le risque d'erreurs lors des modifications.
Points clés
Le code mort (dead code) — est le code source inclus dans le programme, mais qui n'est jamais exécuté dans aucun scénario d'utilisation. Le compilateur ou l'interpréteur le traite, mais à l'exécution, le contrôle n'atteint jamais ces parties.
Exemples classiques de code mort : des variables qui reçoivent une valeur mais ne sont jamais lues ; des fonctions ou méthodes qui ne sont appelées nulle part ; des branches de condition qui ne deviennent jamais vraies (if(false)) ; des boucles dont le corps n'est jamais exécuté.
Selon le rapport SonarQube State of Code Quality (2025), environ 15 pour cent de tous les avertissements dans les projets Java commerciaux sont liés à des méthodes et champs privés inutilisés. Dans les projets JavaScript, la proportion de code inutilisé peut atteindre 30 pour cent en raison de la nature dynamique du langage et de l'abondance de bibliothèques tierces.
Vérifiez régulièrement votre projet pour du code mort — surtout après des refactorisations majeures et la suppression de fonctionnalités. Un import oublié ou une fonction inutilisée aujourd'hui peut se transformer demain en code zombie qui induit en erreur les nouveaux membres de l'équipe.
Le code zombie (zombie code) — est un cas particulier de code mort qui se distingue par son contexte historique. Le code zombie a fonctionné à un moment donné, mais après des changements dans le système, il est devenu inaccessible, mais n'a pas été supprimé, mais plutôt conservé «au cas où».
La différence entre le code mort et le code zombie réside dans l'origine. Le code mort peut avoir été écrit par erreur (n'a jamais fonctionné), tandis que le code zombie est un ancien code vivant qui a perdu sa pertinence après une refactorisation. Par exemple, une fonction de calcul de remise selon une ancienne logique métier qui a été remplacée par une nouvelle, mais l'ancienne méthode n'a pas été supprimée — au cas où on aurait besoin de revenir en arrière.
Le danger principal du code zombie — l'illusion d'une fonctionnalité opérationnelle. Un nouveau développeur voit une fonction, lit sa documentation, suppose qu'elle est appelée quelque part — et perd du temps à étudier un artefact. En essayant de l'appeler directement, il peut découvrir qu'elle dépend d'entités supprimées ou d'API obsolètes.
Suivez le code zombie via l'historique git : si une fonction n'a pas été modifiée depuis deux ans et n'est pas utilisée — c'est un zombie. Supprimez-le sans hésitation, car git conserve l'historique, et le code peut toujours être restauré si nécessaire.
La première cause et la plus fréquente — le développement itératif avec une refactorisation incomplète. L'équipe ajoute de nouvelles fonctionnalités remplaçant les anciennes, mais ne supprime pas les modules remplacés. Les sprints accumulent ces «queues», et après un an, le projet se couvre d'une couche de code mort.
La deuxième cause — les tests A/B et les feature toggle. Les conditions d'activation d'une nouvelle fonctionnalité peuvent se figer avec le temps (par exemple, toujours true), mais la branche else avec la logique alternative reste dans le code. Les développeurs craignent de la supprimer de peur de casser accidentellement le système si le toggle est réactivé.
La troisième cause — la génération automatique et le copier-coller. Les générateurs de code (IDE, moteurs de templates) créent des squelettes avec des méthodes que le développeur ne remplit pas ou n'utilise pas. Le code copié d'un autre projet contient souvent des blocs entiers non pertinents pour le nouveau contexte.
La quatrième cause — la peur de supprimer. Dans les grands projets, les développeurs craignent de supprimer du code parce qu'ils ne sont pas sûrs qu'il ne soit vraiment utilisé nulle part. Cette peur est aggravée par un système de tests faible : s'il n'y a pas de vérification automatique, la suppression peut entraîner des bugs qui ne sont découverts qu'en production.
Le code mort affecte directement quatre aspects de la qualité du projet : les performances du build, la taille de l'artefact, la charge cognitive de l'équipe et la fiabilité de la refactorisation.
Augmentation du temps de compilation : le compilateur traite les fichiers inutilisés, analyse les dépendances et génère du bytecode ou du code machine pour des fragments qui ne seront jamais exécutés. Dans les grands projets, cela ajoute des minutes à chaque build. Pour les langages interprétés (JavaScript, Python), le temps de chargement du module et la consommation mémoire augmentent.
Risque de bugs lors des modifications : un développeur modifie du code sans se douter que la fonction n'est utilisée que dans une branche morte. Après refactorisation, le code mort ne compile plus ou génère des erreurs — l'équipe perd du temps à diagnostiquer un problème qui n'affecte pas le fonctionnement de l'application.
La charge cognitive — le facteur le plus coûteux. Chaque fonction inutilisée demande de l'attention lors de la lecture du code. Le développeur dépense de l'énergie mentale pour comprendre pourquoi ce code existe et où il est appelé. Une étude du Developer Productivity Lab (2025) a montré : supprimer 20 pour cent du code mort réduit le temps d'intégration (onboarding time) de 18 pour cent en moyenne.
Supprimez le code mort immédiatement après sa découverte. Chaque jour de retard augmente la probabilité qu'un membre de l'équipe perde des heures à étudier un artefact qui aurait dû être supprimé hier.
La recherche de code mort s'effectue par deux méthodes principales : l'analyse statique (sans exécution du programme) et l'analyse dynamique (profilage de couverture à l'exécution). Chaque approche est efficace pour différents types de code mort.
Les analyseurs statiques prennent en charge tous les langages de programmation populaires. Pour Java et Kotlin — SonarQube, IntelliJ IDEA Inspections, SpotBugs. Pour JavaScript et TypeScript — ESLint avec les règles no-unused-vars et no-unused-modules. Pour Swift — SwiftLint avec la règle unused_declaration. Pour Python — pylint avec l'option unused-import et vulture pour la recherche approfondie.
// build.gradle.kts - configuration ProGuard pour Android
android {
buildTypes {
release {
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
}
// proguard-rules.pro - conserver uniquement les classes nécessaires
-keep class com.example.app.** { *; }
-assumenosideeffects class Timber {
static <methods>;
}
ProGuard supprime non seulement les classes et méthodes inutilisées, mais minimise également les noms dans le build de release. Un build avec ProGuard activé montre automatiquement quelles classes et méthodes sont considérées comme inutilisées — dans le rapport usage.txt, tout le code supprimé est listé.
Les outils de couverture de code (JaCoCo pour Java, XCTest coverage pour Swift, Istanbul pour JavaScript) montrent quelles lignes et branches sont exécutées pendant les tests. Les méthodes avec une couverture nulle sont candidates au code mort. Cependant, l'absence de couverture ne garantit pas que le code n'est pas appelé en production — pour une certitude totale, utilisez une combinaison d'analyse statique et dynamique.
Configurez votre pipeline CI pour que le build échoue en cas de dépassement du seuil de déclarations inutilisées. La Quality Gate de SonarQube avec la règle «Proportion de code privé inutilisé ne dépassant pas 3 %» empêche l'accumulation de code mort au niveau du processus de développement.
Le processus de suppression du code mort comprend quatre étapes : trouver, vérifier, supprimer, vérifier à nouveau. Sauter une étape augmente le risque de régression.
Première étape — recherche de candidats via un analyseur statique. Obtenez un rapport des déclarations inutilisées : fonctions, classes, variables, imports. Filtrez les faux positifs — les analyseurs se trompent parfois avec la réflexion, le chargement dynamique de classes ou les appels cachés via la sérialisation.
Deuxième étape — vérification via git blame et l'historique des modifications. Regardez quand et pourquoi le code a été écrit. Si le code faisait partie d'une fonctionnalité désactivée par un feature toggle — assurez-vous que le toggle est fixé et ne sera pas réactivé. Commentez le code que vous hésitez à supprimer et laissez un TODO avec un ticket pour une re-vérification dans un mois.
Troisième étape — suppression dans une branche séparée avec exécution complète des tests. Si les tests passent — la probabilité de régression est faible. Si les tests échouent — le code est encore utilisé et il faut comprendre dans quel scénario.
// before - code mort et code zombie dans le même fichier
int calculateV1(int price) { // n'appelé nulle part
int tax = price * 0.18;
return price + tax;
}
int calculateV2(int price, double rate) {
return static_cast<int>(price * (1 + rate));
}
// after - code mort supprimé, code zombie nettoyé
int calculatePrice(int price, double rate) {
return static_cast<int>(price * (1 + rate));
}
Quatrième étape — revue de code des modifications. Le relecteur doit confirmer que le code est vraiment mort. Si le relecteur n'est pas sûr — laissez un commentaire dans le code et reportez la suppression jusqu'à l'analyse complète. Après le merge de la branche — supprimez la branche pour ne pas proliférer le code zombie dans le dépôt git.
Appliquez la règle : aucune pull request ne doit contenir de nouveau code mort. Ajoutez un linter dans les hooks de pre-commit qui bloque le commit en présence de variables ou d'imports inutilisés. La prévention est toujours moins chère que le nettoyage.
Questions fréquentes
Oui, si le code mort contient des erreurs de syntaxe ou référence des types supprimés. Les compilateurs modernes vérifient toujours les branches mortes, donc une erreur dans un bloc if(false) entraînera un échec du build. C'est une protection : le code ne doit pas être assez mort pour que le compilateur ne le vérifie pas.
Le code zombie induit en erreur : un nouveau développeur voit une fonction avec documentation et suppose qu'elle est utilisée. Il perd du temps à étudier du code qui ne fonctionne pas et peut accidentellement lier une nouvelle logique à une entité obsolète, créant ainsi un bug difficile à tracer.
Utilisez ESLint avec les règles no-unused-vars et no-unused-modules, ainsi que l'utilitaire knip — il analyse les exports et imports dans tout le projet, trouvant les fichiers, fonctions et dépendances inutilisés. Pour les grands monorepos, knip donne l'image la plus complète.
Il est préférable de supprimer avant la release, mais pas au dernier moment. La suppression de code mort est un travail technique qui se planifie dans le sprint séparément. Juste avant la release, la suppression peut apporter de l'instabilité si le code n'est pas aussi mort qu'il y paraissait.
Oui, les compilateurs modernes et les minifieurs (ProGuard, R8, Terser, Closure Compiler) suppriment le code inaccessible au niveau de Dead Code Elimination. Cependant, cela ne dispense pas de nettoyer les sources : le compilateur retire le code du binaire, mais pas du dépôt — les développeurs continuent de buter dessus à la lecture.
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