web-dev-qa-db-fra.com

Comment supprimer en toute sécurité un morceau de code qui semble n'avoir jamais été entré?

Vous avez trouvé du code qui semble superflu, et le compilateur ne le remarque pas. Que faites-vous pour être sûr (ou aussi près que possible) que la suppression de ce code ne provoquera pas de régression.

Deux idées me viennent à l'esprit.

  1. "Simplement", utilisez la déduction selon que le code doit ou non s'exécuter. Cependant, cela peut parfois être un travail complexe et long, légèrement risqué (votre évaluation est sujette à erreur) pour aucun retour sur investissement substantiel.

  2. Placez la journalisation dans cette section de code et voyez combien de fois elle est entrée dans la pratique. Après suffisamment d'exécutions, vous devriez avoir une assurance raisonnable que la suppression du code est sûre.

Y a-t-il de meilleures idées ou quelque chose comme une approche canonique?

124
Brad Thomas

Dans mon monde fantastique parfait où j'ai une couverture de tests unitaires à 100%, je le supprimais, exécutais mes tests unitaires et quand aucun test ne devenait rouge, je le validais.

Mais malheureusement, je dois me réveiller chaque matin et faire face à la dure réalité où beaucoup de code n'a pas de tests unitaires ou quand ils sont là, on ne peut pas faire confiance pour couvrir vraiment tous les cas possibles d'Edge. Je considérerais donc le risque/récompense et arriverais à la conclusion que cela n'en vaut tout simplement pas la peine:

  • Récompense: le code est un peu plus facile à maintenir à l'avenir.
  • Risque: casser le code dans un cas Edge obscur auquel je ne pensais pas, provoquer un incident quand je m'y attendais le moins, et en être responsable parce que je devais satisfaire mon OCD de qualité de code et apporter une modification sans valeur commerciale perceptible à tout intervenant qui n'est pas un codeur lui-même.
110
Philipp

Il y a deux moitiés à ce processus. Le premier confirme que le code est bel et bien mort. La seconde consiste à comprendre les coûts d'une erreur et à s'assurer qu'ils sont correctement atténués.

De nombreuses réponses ont ici d'excellentes solutions pour la moitié précédente. Des outils comme les analyseurs statiques sont parfaits pour identifier le code mort. grep peut être votre ami dans certains cas. Une étape inhabituelle que je prends souvent consiste à essayer d'identifier l'objectif initial du code. Il est beaucoup plus facile d'affirmer que "X n'est plus une fonction de notre produit et le segment de code Y a été conçu pour prendre en charge la fonctionnalité X" que de dire "Je ne vois aucun intérêt pour le segment de code Y".

La seconde moitié est une étape clé pour briser tout blocage sur l'opportunité de supprimer le code. Vous devez comprendre quelles sont les implications d'une mauvaise réponse. Si des gens vont mourir si vous vous trompez, faites attention! C'est peut-être le bon moment pour accepter que la corruption de code se développe au fil du temps et au lieu de cela, essayez de ne pas écrire plus de corruption vous-même. Si les gens ne vont pas mourir, demandez-vous à quel point vos utilisateurs sont indulgents. Pouvez-vous leur envoyer un correctif si vous avez cassé quelque chose et maintenez votre relation client? Avez-vous une équipe de questions et réponses qui est payée pour trouver des problèmes comme celui-ci? Ces sortes de questions sont essentielles pour comprendre à quel point vous devez être certain avant d'appuyer sur la touche Suppr.

Dans les commentaires, rmun a souligné une excellente formulation du concept de compréhension de l'objectif initial du code avant de le supprimer. La citation est maintenant connue sous le nom de Chesterton’s Fence . Bien qu'il soit trop grand pour être cité directement dans un commentaire, je pense qu'il mérite d'être correctement cité ici:

En matière de réforme des choses, par opposition à leur déformation, il y a un principe clair et simple; un principe que l'on appellera probablement un paradoxe. Il existe dans un tel cas une certaine institution ou loi; disons, par souci de simplicité, une clôture ou un portail érigé en travers d'une route. Le type de réformateur le plus moderne s’adresse à lui gaiement et dit: "Je ne vois pas l’utilisation de cela; effaçons-le. " À quoi le type de réformateur le plus intelligent ferait bien de répondre: "Si vous ne voyez pas son utilisation, je ne vous laisserai certainement pas le supprimer. Va-t'en et réfléchis. Ensuite, quand vous pourrez revenir et me dire que vous en voyez l'usage, je peux vous permettre de le détruire.

85
Cort Ammon

J'ai également tendance à grep pour le nom de la fonction/classe dans le code, ce qui peut donner des avantages supplémentaires qu'un analyseur de code pourrait ne pas, comme si le nom est mentionné dans un commentaire ou dans un fichier de documentation, ou un script, par exemple. J'exécute grep sur les fichiers dans l'arborescence source et stocke le résultat dans un fichier; généralement, le résultat donne une information condensée: nom/chemin du fichier, numéro de ligne et la ligne où le nom est rencontré, ce qui peut donner des indices sur l'endroit où la fonction/classe est appelée ou mentionnée sans aucune signification sémantique (contrairement à un analyseur de code ), et quelles que soient les extensions de fichier. Certainement pas la solution ultime mais un bel ajout à une analyse.

43
piwi
  • Identifiez le code qui semble mort (analyse statique, etc.).
  • Ajoutez un message de journal pour à chaque appel du code prétendument mort. C'est facile avec les fonctions/méthodes; avec des membres statiques comme des constantes, c'est plus délicat. Parfois, vous pouvez simplement marquer le code comme obsolète et le runtime enregistrera automatiquement un message pour vous. Laissez le code autrement intact.
    • Enregistrez un message lorsque le module mort est chargé: la plupart des langues ont un moyen d'exécuter l'initialisation statique au moment du chargement, qui peut être superposée.
    • Assurez-vous que votre message de journal comprend une trace de pile raisonnable, afin que vous compreniez quoi appelé le code prétendument mort.
  • Exécutez le code modifié dans toutes vos suites de tests. Vos suites de tests doivent également tester des heures spéciales, comme le passage de minuit, un quart de tour, un tour d'un an, etc. Regardez les journaux, mettez à jour votre compréhension de ce qui est mort. Notez que les tests unitaires peuvent spécifiquement tester le code mort, alors qu'aucun autre test unitaire et aucun test d'intégration ne le touchent.
  • Exécutez le code modifié en production pendant quelques semaines. Assurez-vous que tous les processus périodiques, comme les tâches cron ETL une fois par mois, sont exécutés pendant cette période.
  • Regardez les journaux. Tout ce qui a été enregistré n'est pas réellement mort. La fermeture transitive du graphe d'appel sur le reste du code mort est aussi potentiellement non morte, même si elle n'a pas été invoquée. Analysez-le. Peut-être que certaines branches sont mortes en toute sécurité (par exemple, en travaillant avec une API d'un service désormais éteint), tandis que d'autres ne le sont pas encore. Peut-être qu'un module/classe entier n'est chargé que pour une constante définie dans celui-ci, et vous pouvez facilement le désactiver.
  • Tout ce qui reste est mort en toute sécurité et peut être retiré.
30
9000

En plus des réponses existantes mentionnées, vous pouvez également supprimer le code de manière itérative sur plusieurs versions.

Dans la version initiale, vous pouviez faire un avertissement de dépréciation avec le bloc de code fonctionnant toujours. Dans la version suivante, vous pouvez supprimer le bloc de code, mais laisser un message d'erreur indiquant aux utilisateurs que la fonctionnalité est obsolète et n'est plus disponible. Dans la version finale, vous supprimeriez le bloc de code et tous les messages.

Cela peut aider à identifier les fonctionnalités imprévues sans avertissement pour les utilisateurs finaux. Dans le meilleur des cas, le code ne fait rien et tout ce qui se passe, c'est que le code inutile est conservé sur plusieurs versions avant d'être supprimé.

16
user3196468

Beaucoup de gens suggèrent que la chose "sûre" à faire est de laisser le code si vous ne pouvez pas prouver qu'il n'est pas utilisé.

Mais le code n'est pas un atout, c'est un passif.

À moins que vous ne puissiez expliquer pourquoi il est important et indiquer ses tests, l'alternative "plus sûre" pourrait très bien être de le supprimer.

Si vous n'êtes toujours pas sûr, assurez-vous au moins d'ajouter des tests pour exercer le code zombie.

7
Adrian McCarthy

Vous pouvez utiliser des bascules de fonction pour modifier le chemin d'exécution de votre logiciel pour ignorer complètement le code en question.

De cette façon, vous pouvez déployer votre modification en toute sécurité avec le code non utilisé et la bascule. Si vous remarquez des défauts majeurs liés au code, réactivez la bascule et recherchez le chemin possible vers celui-ci.

Cette approche devrait vous donner confiance si vous ne voyez aucun problème sur une période prolongée ainsi que la possibilité de le réactiver en direct sans déploiement. Cependant, un meilleur moyen serait également d'appliquer une journalisation supplémentaire et une couverture de test autour de la zone en question, ce qui fournira plus de preuves qu'elle soit utilisée ou non.

En savoir plus sur les bascules ici: https://martinfowler.com/articles/feature-toggles.html

6
Sash

L'analyse statique bien sûr ... et ce qui est bien, c'est que vous n'avez pas besoin de nouveaux outils; votre compilateur possède tous les outils d'analyse statique dont vous avez besoin.

Modifiez simplement le nom de la méthode (par exemple, changez DoSomething en DoSomethingX), puis exécutez une génération.

Si votre build réussit, la méthode n'est évidemment utilisée par rien. Coffre-fort à supprimer.

Si votre génération échoue, isolez la ligne de code qui l'appelle et vérifiez quelles instructions if entourent l'appel pour déterminer comment le déclencher. Si vous ne trouvez aucun cas d'utilisation de données possible qui le déclenche, vous pouvez le supprimer en toute sécurité.

Si vous êtes vraiment inquiet de le supprimer, pensez à conserver le code mais à le marquer avec un ObsoleteAttribute (ou l'équivalent dans votre langue). Libérez-le comme ça pour une version, puis supprimez le code après qu'aucun problème n'a été trouvé dans la nature.

6
John Wu
  • Je commencerais à utiliser un analyseur de code pour vérifier s'il s'agit d'un code mort
  • Je commençais à vérifier mes tests et à essayer d'atteindre le code. Si je ne parviens pas à accéder au code dans un scénario de test, il peut s'agir d'un code mort
  • Je supprimerais le code et lèverais une exception à la place. Pour une version, l'exception est activée et dans la deuxième version, l'exception peut être supprimée. Pour être sûr, placez un drapeau d'urgence (en Java, une propriété système) pour pouvoir activer le code d'origine, lorsqu'un client remarque l'exception. Ainsi, l'exception peut être désactivée et le code d'origine peut être activé dans un environnement de production.
4
Markus Lausberg

Suppression du code inaccessible

Dans un langage typé statiquement basé sur des principes, vous devez toujours savoir si le code est réellement accessible ou non: supprimez-le, compilez, s'il n'y a pas d'erreur, il n'était pas accessible.

Malheureusement, toutes les langues ne sont pas typées statiquement, et toutes les langues typées statiquement ne sont pas fondées sur des principes. Les choses qui pourraient mal tourner comprennent (1) la réflexion et (2) la surcharge sans principes.

Si vous utilisez un langage dynamique ou un langage avec une réflexion suffisamment puissante pour que le morceau de code examiné soit potentiellement accessible au moment de l'exécution via la réflexion, vous ne pouvez pas compter sur le compilateur. Ces langages incluent Python, Ruby ou Java.

Si vous utilisez un langage avec une surcharge sans principes, la simple suppression d'une surcharge peut simplement basculer la résolution de surcharge sur une autre surcharge silencieusement. Certains de ces langages vous permettent de programmer un avertissement/erreur de compilation associé à l'utilisation du code, sinon vous ne pouvez pas compter sur le compilateur. Ces langages incluent Java (utilisez @Deprecated) ou C++ (utilisez [[deprecated]] ou = delete).

Donc, à moins que vous n'ayez la chance de travailler avec des langages stricts (Rust vient à l'esprit), vous pouvez vraiment vous tirer une balle dans le pied en faisant confiance au compilateur. Et malheureusement, les suites de tests sont généralement incomplètes, ce qui ne nous aide pas beaucoup non plus.

Repérez la section suivante ...


Suppression du code potentiellement inutilisé

Plus probablement, le code est en fait référencé, mais vous soupçonnez qu'en pratique les branches de code qui le référencent ne sont jamais prises.

Dans ce cas, quel que soit le langage, le code est manifestement accessible et seule l'instrumentation d'exécution peut être utilisée.

Dans le passé, j'ai utilisé avec succès une approche en 3 phases pour supprimer un tel code:

  1. Sur chaque branche suspectée de NE PAS être prise, enregistrez un avertissement.
  2. Après un cycle, lancez une exception/retournez une erreur en entrant le morceau de code spécifique.
  3. Après un autre cycle, supprimez le code.

Qu'est-ce qu'un cycle? C'est le cycle d'utilisation du code. Par exemple, pour une application financière, je m'attendrais à un cycle mensuel court (avec des salaires payés à la fin du mois) et à un cycle annuel long. Dans ce cas, vous devez attendre au moins un an pour vérifier qu'aucun avertissement n'est jamais émis car l'inventaire de fin d'année peut utiliser des chemins de code qui autrement ne sont jamais utilisés.

Espérons que la plupart des applications ont des cycles plus courts.

Je vous conseille de mettre un commentaire TODO, avec une date, pour vous indiquer quand passer à l'étape suivante. Et un rappel dans votre calendrier.

3
Matthieu M.

Supprimer le code de la production, c'est comme nettoyer la maison. Au moment où vous jetez un objet du grenier, votre femme vous tuera le lendemain pour avoir jeté le cadeau de retour du troisième neveu de son arrière-grand-mère à leur voisine décédée en 1923.

Sérieusement, après une analyse rapide à l'aide de divers outils que tout le monde a déjà mentionnés, et après avoir utilisé l'approche de dépréciation par étapes déjà mentionnée, gardez à l'esprit que vous pouvez être absent de l'entreprise lorsque le retrait effectif a lieu. Laisser le code subsister et faire consigner son exécution et faire en sorte que toute alerte de son exécution vous soit définitivement communiquée (ou à vos successeurs et ayants droit) est essentiel.

Si vous ne suivez pas cette approche, vous risquez d'être tué par votre femme. Si vous conservez le code (comme chaque souvenir), vous pouvez vous reposer votre conscience dans le fait que la loi de Moore vient à votre secours et le coût de l'espace disque indésirable utilisé par le code qui ressemble à un "rocher dans ma chaussure", réduit chaque année et vous ne risquez pas de devenir le centre de plusieurs potins de refroidisseur d'eau et de regards étranges dans les couloirs.

PS: Pour clarifier en réponse à un commentaire .. La question d'origine est "Comment voulez-vous en toute sécurité supprimer .." bien sûr, le contrôle de version est supposé dans ma réponse. Tout comme creuser dans la poubelle pour trouver le précieux est supposé. Aucun organisme sensé ne jette le code et le contrôle de version ne devrait faire partie de la rigueur de chaque développeur.

Le problème concerne le code qui peut être superflu. Nous ne pouvons jamais savoir qu'un morceau de code est superflu à moins que nous puissions garantir que 100% des chemins d'exécution ne l'atteignent pas. Et il est supposé qu'il s'agit d'un logiciel suffisamment grand pour que cette garantie soit presque impossible. Et à moins que je ne lise mal la question, la seule fois où tout ce fil de conversation est pertinent est pour les cas pendant la production où le code supprimé pourrait être appelé et donc nous avons un problème d'exécution/production. Le contrôle de version ne sauve personne derrière en raison d'échecs de production, donc le commentaire sur "Contrôle de version" n'est pas pertinent pour la question ou mon point d'origine, c'est-à-dire qu'il est obsolète correctement sur une période extrêmement longue si vous le devez vraiment, sinon ne le faites pas ' t vous inquiétez pas car le code superflu ajoute un coût relativement faible en termes de saturation d'espace disque.

À mon humble avis, le commentaire est superflu et est un candidat à la suppression.

2
LMSingh

Peut-être que le compilateur le fait remarque que cela ne fait pas de bruit.

Exécutez une génération avec des optimisations complètes du compilateur, orientées vers la taille. Supprimez ensuite le code suspect et réexécutez la génération.

Comparez les binaires. S'ils sont identiques, le compilateur a remarqué et supprimé le code en silence. Vous pouvez le supprimer de la source en toute sécurité.

Si les binaires sont différents ... alors ce n'est pas concluant. Ce pourrait être un autre changement. Certains compilateurs incluent même la date et l'heure de compilation dans le binaire (et peut-être peuvent-elles être configurées ailleurs!)

1

J'ai en fait récemment rencontré cette situation exacte, avec une méthode appelée "deleteFoo". Nulle part dans la base de code entière, cette chaîne n'a été trouvée autre que la déclaration de méthode, mais j'ai sagement écrit une ligne de journal en haut de la méthode.

PHP:

public function deleteFoo()
{
    error_log("In deleteFoo()", 3, "/path/to/log");
}

Il s'avère que le code a été utilisé! Certains AJAX appelle la "méthode: delete, elem = Foo" qui est ensuite concaténée et appelée avec call_user_func_array () .

En cas de doute, connectez-vous! Si vous avez passé suffisamment de temps sans voir le journal rempli, envisagez de supprimer le code. Mais même si vous supprimez le code, laissez le journal en place et un commentaire avec la date pour faciliter la recherche du commit dans Git pour le gars qui doit le maintenir!

1
dotancohen

Si vous savez ce que fait le code qui l'entoure, testez-le pour voir s'il se casse. C'est le moyen le plus simple et le plus rentable de refactoriser un morceau de code sur lequel vous travaillez, et devrait être fait de toute façon dans le but de développer toute modification du code sur lequel vous travaillez en premier lieu.

Si, d'autre part, vous refactorisez un morceau de code où vous ne savez pas ce qu'il fait, ne le supprimez pas. Comprenez-le d'abord, puis refactorisez-le si vous déterminez qu'il est sûr (et seulement si vous pouvez déterminer que le refactoring serait une bonne utilisation de votre temps).

Maintenant, il peut y avoir des circonstances (je sais que je l'ai rencontré moi-même) où le code qui est "mort" n'est en fait connecté à rien . Telles que des bibliothèques de code développées par un entrepreneur qui n'ont jamais été implémentées, car elles sont devenues obsolètes immédiatement après la livraison de l'application (pourquoi oui, j'ai un exemple spécifique en tête, comment le saviez-vous?).

Personnellement, j'ai fini par supprimer tout ce code, mais c'est un énorme risque que je ne recommande pas de prendre à la légère - selon la quantité de code le changement pourrait potentiellement avoir un impact (car il y a toujours le potentiel que vous avez oublié quelque chose), vous devez être extrêmement prudent et exécuter des tests unitaires agressifs pour déterminer si la suppression de ce l'ancien code hérité cassera votre application.

Maintenant tout cela étant dit, cela ne vaut probablement pas votre temps ou votre raison pour essayer de supprimer du code comme ça. Non, sauf si cela devient un problème sérieux avec votre application (par exemple, ne pas être en mesure d'effectuer des analyses de sécurité à cause du ballonnement de l'application ... pourquoi oui, j'ai toujours un exemple en tête), donc je ne le recommanderais pas à moins que vous n'atteigniez une telle situation où le code a vraiment commencé à pourrir de manière percutante.

Donc en bref - si vous savez ce que fait le code, testez-le d'abord. Si vous ne le faites pas, vous pouvez probablement le laisser tranquille. Si vous savez que vous ne pouvez pas le laisser tranquille, consacrez votre temps à tester le changement de manière agressive avant de l'implémenter.

0
Zibbobz

Utilisez grep ou tout autre outil dont vous disposez en premier, vous pourriez trouver des références au code. (Pas à l'origine mes instructions/conseils.)

Ensuite, décidez si vous voulez risquer de supprimer le code, ou si ma suggestion pourrait venir à être utilisée:

Vous pouvez modifier la fonction/bloc de code pour écrire qu'il a été utilisé dans un fichier journal. Si aucun message de ce type n'est "jamais" enregistré dans le fichier journal, vous pouvez probablement supprimer le code de journalisation.

Deux problèmes:

  1. Récupération du journal des autres utilisateurs. Vous pouvez peut-être définir un indicateur global qui plantera le programme à la sortie et demander à l'utilisateur de vous envoyer le journal des erreurs.

  2. Tracer l'appelant. Dans certaines langues de haut niveau (par exemple, Python), vous pouvez facilement obtenir une trace. Mais dans les langues compilées, vous devrez enregistrer l'adresse de retour dans le journal et forcer un vidage de mémoire à la sortie (point 1).
    . il suffit de lire l'adresse de retour de la pile (peut ou peut ne pas nécessiter de langage d'assemblage en ligne).

L'enregistrement des arguments dans le fichier journal peut être utile ou non.

0
Oskar Skog

Mon approche, que j'ai toujours supposée être la norme de l'industrie dans ce cas, n'a étrangement pas été mentionnée jusqu'à présent:

Obtenez une deuxième paire d'yeux.

Il existe différents types de "code inutilisé" et dans certains cas, la suppression est appropriée, dans d'autres cas, vous devez le refactoriser, et dans d'autres cas, vous vous enfuyez autant que vous le pouvez et ne regardez jamais en arrière. Vous devez déterminer lequel il s'agit, et pour ce faire, il est préférable de vous associer à un second - expérimenté! - développeur, qui connaît très bien la base de code. Cela réduit considérablement le risque d'erreur inutile et vous permet d'apporter les améliorations nécessaires pour maintenir la base de code dans un état maintenable. Et si vous ne le faites pas, à un moment donné, vous n'avez plus de développeurs qui connaissent très bien la base de code.

J'ai également vu l'approche de journalisation - pour être plus précis, j'ai vu le code de journalisation: encore plus de code mort qui a été laissé là pendant 10 ans. L'approche de journalisation est assez décente si vous parlez de supprimer des fonctionnalités entières, mais pas si vous supprimez simplement un petit morceau de code mort.

0
Peter

La réponse simple à votre question est que vous ne pouvez pas supprimer en toute sécurité le code que vous ne comprenez pas, ce qui inclut de ne pas comprendre comment il est appelé. Il y aura un certain risque.

Vous devrez juger de la meilleure façon d'atténuer ce risque inévitable. D'autres ont mentionné l'exploitation forestière. La journalisation peut bien sûr prouver qu'elle est utilisée, mais ne peut pas prouver qu'elle ne l'est pas. Étant donné que le problème majeur avec le code mort est qu'il est maintenu, vous pourrez peut-être vous en sortir en ajoutant un commentaire disant qu'il s'agit d'un code mort suspect, et ne pas effectuer de maintenance sur celui-ci.

Si le code est sous contrôle de source, vous pouvez toujours savoir quand il a été ajouté et déterminer comment il a été appelé.

En fin de compte, soit vous le comprenez, vous le laissez tranquille, soit vous tentez.

0
jmoreno

Dans le cas où le développeur du code en question est toujours disponible, passez en revue la suppression du code avec lui . En outre, lire les commentaires dans le code .

Vous obtiendrez l'explication appropriée, pourquoi ce code a été ajouté et à quelle fonction il sert. Cela peut facilement être un support pour le cas d'utilisation spécifique, ou vous pouvez même ne pas avoir suffisamment d'expertise pour juger si ce n'est vraiment pas nécessaire. Dans les cas extrêmes, on peut supprimer toutes les instructions gratuites d'un programme C et ne rien remarquer de mal (cela peut prendre quelques minutes pour manquer de mémoire).

C'est vraiment une mauvaise culture lorsque l'auteur du code est assis à côté de vous, mais vous ne voyez aucune raison de parler parce que vous êtes sûr qu'il écrit juste du mauvais code, et le vôtre est si parfait. Et, bien sûr, il écrit juste des mots au hasard dans les commentaires, pas besoin de perdre du temps à lire.

L'une des raisons les plus importantes pour écrire un commentaire dans le code est d'expliquer pourquoi il a été implémenté de cette façon. Vous pouvez être en désaccord avec la solution, mais vous devez prendre le problème en considération.

La discussion avec l'auteur peut également révéler que le code est le vestige historique de quelque chose supprimé ou jamais terminé, il est donc sûr de le supprimer.

0
h22