Comment pouvez-vous supprimer en toute sécurité un morceau de code qui semble ne jamais avoir été saisi?

125

Vous avez trouvé du code qui semble superflu et le compilateur ne le remarque pas. Que faites-vous pour être sûr (ou aussi proche 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 être exécuté. Toutefois, il peut parfois s’avérer un travail complexe et fastidieux, légèrement risqué (votre évaluation est entachée d’erreurs) sans aucun retour sur investissement substantiel.

  2. Placez la connexion dans cette section de code et voyez à quelle fréquence il est entré dans la pratique. Après suffisamment d'exécutions, vous devriez avoir une confiance raisonnable. Retirer le code est sans danger.

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

Brad Thomas
la source
55
Il peut être utile de consulter l'historique de contrôle de version: Quand le "code mort" a-t-il été créé? At-il semblé mort quand il a été créé? Un autre code (qui a depuis été modifié ou supprimé) a-t-il été vérifié dans lequel il était utilisé, en même temps qu'il avait été créé?
ChrisW
9
On ne peut pas faire confiance au compilateur si une forme quelconque de méta-programmation telle que la réflexion est en jeu. Un bon grep du répertoire de base est une pratique essentielle. Il est également courant d'avoir plusieurs applications partageant le code, veillez donc à grep au niveau de la couche de répertoire appropriée.
Adam Caviness
16
Je demanderais - "Pourquoi se donner la peine de le supprimer?". Si elle est utilisée dans un cas appelé une fois dans une lune bleue, vous avez cassé des choses. S'il n'est jamais utilisé et que vous le laissez là, le fichier exécutable est un peu plus gros. À moins que ce ne soit pour un appareil embarqué, c'est un gros problème? Collez un commentaire dessus et passez à une utilisation productive de votre temps.
Mickeyf
40
@ mickeyf Le problème, c'est que quelqu'un doit conserver ce code. Cela signifie qu'un développeur doit comprendre ce qu'il fait. Le développeur doit le modifier si les choses changent et que le code qui l’entoure doit maintenant faire quelque chose de différent. Croyez-moi, quand vous laissez croupir à l'affût, c'est un casse-tête majeur pour quiconque tente de comprendre plus tard.
Jpmc26
9
@ MichaelJ. Ce que je veux dire, c'est que ça n'aurait pas dû être là en premier lieu. Et si je le laisse, la situation est pire: à présent, un autre développeur doit également enquêter. Plus cela dure longtemps, plus on passe de temps à le comprendre. C'est le coût du code superflu.
jpmc26

Réponses:

112

Dans mon monde imaginaire parfait où je suis couvert à 100% par les tests unitaires, je le supprime, je fais mes tests unitaires et, quand aucun test ne passe au rouge, je le valide.

Mais malheureusement, je dois me lever tous les matins et faire face à la dure réalité: beaucoup de codes n’ont pas de tests unitaires ou ne sont pas fiables. Ils ne peuvent donc pas faire confiance à tous les cas possibles. Donc, je considérerais le risque / récompense et arriverais à la conclusion que cela ne vaut tout simplement pas la peine:

  • Récompense: Le code est un peu plus facile à gérer à l'avenir.
  • Risque: casser le code dans un cas obscur auquel je ne pensais pas, provoquer un incident quand je l'attends le moins et le prendre pour responsable parce que je devais satisfaire la qualité de mon code OCD et effectuer un changement sans valeur commerciale perceptible à tout acteur qui n'est pas un codeur.
Philipp
la source
249
Ayant observé l’état des grands projets de logiciels où des ingénieurs avaient, pendant dix à douze ans, principalement suivi l’approche «inutile» pour supprimer le code probablement superflu, je ne peux pas recommander cette approche.
Njuffa
125
De plus, les tests unitaires ne vous disent pas si ce code est réellement utilisé en production. Le code pourrait être parfaitement testé, mais pas encore utilisé en production, donc superflu.
knub
8
Selon mon expérience, l’état de tels projets n’est jamais dû à une approche "non rentable" pour corriger le code après coup, mais toujours à un mauvais code. C'est une erreur d'écrire du mauvais code, mais c'est aussi une erreur (une erreur plus avancée) de penser que le code de nettoyage en vaut toujours la peine.
Weyland Yutani
42
@Weyland Yutani Cela ne correspond pas du tout à mon expérience. La plupart du code probablement superflu que j’ai vu était suffisamment écrit et parfaitement raisonnable compte tenu des spécifications logicielles, des plates-formes matérielles ou des versions de systèmes d’exploitation existant il ya une décennie, nécessaires pour contourner les bogues ou les limitations de la chaîne d’outils ou du matériel disponible plus tôt, etc. .
njuffa
20
Answer rate l’un des avantages les plus critiques et les plus importants d’avancer et de faire le changement: apprendre une chose. Après l'avoir appris, vous pouvez ajouter une structure (commentaires, tests) pour aider les développeurs de maintenance ultérieurement. Choisir de ne pas changer quelque chose parce que vous n'en connaissez pas les conséquences est une programmation culte du fret, même si ce choix n'est pas de supprimer quelque chose.
kojiro
84

Ce processus comporte deux moitiés. La première consiste à confirmer que le code est bien mort. La seconde consiste à comprendre les coûts de l'erreur et à veiller à ce qu'ils soient correctement atténués.

Beaucoup de réponses ici ont d'excellentes solutions à la première moitié. Des outils tels que les analyseurs statiques sont parfaits pour identifier le code mort. greppeut être votre ami dans certains cas. Une démarche inhabituelle que je prends souvent consiste à essayer d'identifier le but initial du code. Il est beaucoup plus facile de dire «X n'est plus une fonctionnalité de notre produit et le segment de code Y a été conçu pour prendre en charge la fonctionnalité X» plutôt que de dire «Je ne vois aucune utilité pour le segment de code Y».

La seconde moitié est une étape clé pour résoudre tout problème lié à la suppression du code. Vous devez comprendre quelles sont les implications d'une réponse fausse. Si des gens vont mourir si la réponse est fausse, faites attention! Peut-être que c'est un bon moment pour accepter que le code cruel se développe avec le temps et essayer plutôt de ne pas écrire plus cruellement. Si les gens ne vont pas mourir, demandez-vous comment vous pardonnez à vos utilisateurs. Pouvez-vous leur envoyer un correctif si vous avez cassé quelque chose et entretenez vos relations avec la clientèle? Avez-vous une équipe Q & R qui est payée pour trouver des problèmes comme celui-ci? Ces questions sont essentielles pour comprendre votre degré de certitude 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 Clôture de Chesterton . Bien qu’il soit trop volumineux pour être cité directement dans un commentaire, je pense qu’il mérite d’être cité correctement ici:

En matière de réforme des choses, par opposition à leur déformation, il existe un principe clair et simple; un principe qui sera probablement appelé 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é sur une route. Le type le plus moderne de réformateur s’adonne gaiement et dit: «Je ne vois pas l’utilisation de cela; laissez-nous le nettoyer. »A quoi le type le plus intelligent du réformateur fera bien de répondre:« Si vous n'en voyez pas l'usage, je ne vous laisserai certainement pas le dégager. Va-t'en et réfléchis. Ensuite, lorsque vous pourrez revenir et me dire que vous en voyez l'utilisation, je peux vous permettre de le détruire.

Cort Ammon
la source
14
Chesterton's Fence part du principe que la découverte n’est faite que par la pensée et n’offre aucun recours, une philosophie plutôt grecque. La méthode scientifique est plus moderne: concevez une expérience contrôlée pour déterminer les conséquences de la suppression de la structure. Bien sûr, je ne proposerais pas nonchalamment que cette expérience soit réalisée en production. Mais s'il n'y a pas d'alternative et que le coût des essais en production est très élevé, eh bien, je quitterais plutôt rapidement un lieu de travail qui ne m'offrait aucun moyen sûr de savoir pourquoi un code existe - le risque personnel associé à un tel travail est trop élevé .
Kojiro
15
@kojiro C'est très vrai. J'aime penser que Chesterton irait bien si, en tant que "réformateur moderne", je venais dire: "Je ne sais pas pourquoi cette clôture est ici, je l'admets. Mais j'aimerais la peindre en rouge pour voir si cela me fournit de nouvelles informations ", Chesterton serait probablement heureux avec cela.
Cort Ammon
41
La méthode de décodage d’horloge en mode noyau du système d’exploitation VMS SYS $ ASCTIM contient une ligne de code permettant de corriger la dérive sidérale de l’équinoxe vernal. Aucun des tests unitaires de DEC n'exerce ce code. Il a fonctionné une fois - correctement - le 2000-02-28 à 23: 59: 59: 999 Il ne fonctionnera plus avant l'année 2400. Ne le supprimez pas.
AI Breveleri
13
@AIBreveleri J'espère qu'il y a un commentaire dans le code lui-même expliquant cela, et pas simplement ici sur StackExchange: D
Kyle Strand
11
@KyleStrand De quoi parlez-vous? Ceci est la documentation. Pouvez-vous penser à un meilleur endroit pour mettre des informations cruciales afin que d'autres personnes puissent les trouver que StackExchange? ;-)
Cort Ammon
43

J'ai aussi tendance à greprechercher le nom de fonction / classe dans le code, ce qui peut apporter des avantages supplémentaires à un analyseur de code, comme si le nom est mentionné dans un commentaire, dans un fichier de documentation ou dans un script, par exemple. Je lance grep sur les fichiers de l’arborescence des sources 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 / la 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. Ce n'est certainement pas la solution ultime, mais une belle addition à une analyse.

piwi
la source
11
Je ne suis pas un votant défavorable, mais que se passe-t-il si la section de code que vous soupçonnez de ne pas exécuter n'est pas une fonction ou une classe complète, mais une section d'une méthode / fonction?
Tulains Córdova
9
En fonction de la langue, cela peut ne pas toujours être fiable - certaines langues permettent d'effectuer des appels de méthode de manière dynamique. Par exemple php:$object->$variableMethod($arg1, $arg2);
Dezza
9
@ TulainsCórdova Alors ce n'est probablement pas une bonne solution, évidemment. Comme pour toute solution potentielle, utilisez-la si elle a du sens dans le contexte donné. Mais peut-être greping le. La fonction englobant la section de code peut donner des indices sur la manière dont la fonction est utilisée et donc sur son contenu.
piwi
7
"comme si le nom est mentionné dans un commentaire ou dans un fichier de documentation, ou dans un script" - Ou si quelqu'un utilise la réflexion pour appeler la méthode (si vous utilisez un langage de haut niveau / OO). Bien que toujours pas nécessairement précis à 100%, certaines langues supportent des méthodes très obtuses pour trouver et invoquer des méthodes.
aroth
Ceci est utile si vous avez accès à tout le code possible pouvant utiliser la fonction. Cependant, si le code fait partie d'une bibliothèque, comment saurez-vous que quelque chose d'autre dans lequel vous ne pouvez pas effectuer de recherche ne casse pas?
Kevin Shea
30
  • Identifiez le code qui semble mort (analyse statique, etc.).
  • Ajoutez un message de journal pour chaque invocation du code supposé mort. C'est facile avec les fonctions / méthodes; avec des membres statiques comme des constantes, c'est plus compliqué. Parfois, vous pouvez simplement marquer le code comme obsolète et le runtime enregistre automatiquement un message. Laissez le code intact.
    • Consignez un message lorsque le module mort est chargé: la plupart des langues disposent d'un moyen d'exécuter l'initialisation statique au moment du chargement, laquelle peut être superposée.
    • Assurez-vous que votre message de journal inclut une trace de pile raisonnable, de sorte que vous compreniez comment s'appelle 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, telles que traverser une nuit, un quart de tour, un tour d’année, etc. Consultez les journaux, mettez à jour votre compréhension de ce qui est mort. Notez que les tests unitaires peuvent tester spécifiquement le code mort, alors qu'aucun autre test unitaire ni test d'intégration ne le touche.
  • Exécutez le code modifié en production pendant quelques semaines. Assurez-vous que tous les processus périodiques, tels que les tâches périodiques 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 également 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 utilisant une API d'un service maintenant disparu), alors que d'autres ne le sont pas encore. Peut-être qu'un module / une classe entière n'est chargé que pour une constante définie dans celle-ci, et vous pouvez facilement l'utiliser.
  • Tout ce qui reste est mort en toute sécurité et peut être enlevé.
9000
la source
Il y a une première fois pour tout, donc ce n'est pas parce que le code n'a pas été exécuté auparavant qu'il ne fonctionnera jamais, surtout s'il existe un chemin d'exécution. Cela dit, décider de supprimer quelque chose qui peut s'exécuter est un processus différent de celui de déterminer s'il peut s'exécuter.
7
@nocomprende exactement ce qui se passe si ce code n'est utilisé que pour une fonction de fin d'année et que vous exécutez votre test est exécuté de février à novembre. Vous ne trouverez pas l'utilisation même si vous l'avez décrite pendant près d'une année.
Erik
1
@ 9000 Je suis d'accord en théorie, mais les systèmes existants sont truffés de dépendances cachées qui peuvent faire échec à ce type de test. Pour que votre exemple de test fonctionne, vous devez savoir qu’il existe une sorte de couplage temporel. Si le couplage temporel est externe au code que vous testez, vous ne le verrez pas dans le code et ne saurez pas que le couplage temporel est un facteur. Par exemple, le code du silo A est installé dans le GAC . Il y a des années, le silo B a demandé au silo A d'ajouter un chemin de code pour un rapport dont il a besoin pour être exécuté sur les données du silo A. cont.
Erik
1
@ 9000 cont. Maintenant, le code du silo B a un couplage temporel avec un chemin de code "mort" dans le code du silo A qui n'est pas apparent en regardant simplement le code du silo A. Comment unifieriez-vous ce couplage temporel puisque le couplage temporel est uniquement dans le code du silo B sans savoir parler au silo B? Même si le temps n’est pas un facteur important dans votre code (silo A), à quoi ressemblerait un test temporel?
Erik
2
Quelqu'un se souvient de panique de l' an 2000? Certains codes étaient corrects en 2000 car ils étaient incorrects pour 2100! Nous avons eu un problème potentiel qui pourrait se produire une fois tous les 100 ans ou même une fois tous les 400 ans.
Steve Barnes
16

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

Dans la version initiale, vous pouviez émettre un avertissement de dépréciation avec le bloc de code toujours actif. Dans la version ultérieure, vous pouvez supprimer le bloc de code mais laisser un message d'erreur permettant aux utilisateurs de constater 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 des fonctionnalités imprévues sans prévenir les utilisateurs finaux. Dans le meilleur des cas, le code ne fait vraiment rien et tout ce qui se passe est que le code inutile est conservé sur plusieurs versions avant d'être supprimé.

utilisateur3196468
la source
7
+1 J'ai vu tant de bugs et de problèmes clients que l'on aurait pu éviter si les développeurs (ou leur direction) étaient disposés à diviser un projet en deux ou trois phases sûres au lieu d'essayer de tout lancer en même temps pour rencontrer un délai arbitraire avant de passer au projet suivant.
Ruakh
Cela répond à la question posée dans le titre, mais si vous me le demandez, la question a un mauvais titre. Le titre demande comment supprimer le code, mais le corps explique comment déterminer si le code peut être supprimé en toute sécurité. Vous répondez certainement au premier, mais je ne suis pas sûr que ce soit ce que le PO demandait vraiment.
underscore_d
7

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

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

À moins que vous ne puissiez expliquer pourquoi c'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.

Adrian McCarthy
la source
Étape 1: Supprimer tout le code. Étape 2: remettez ce dont vous avez besoin. Étape 3: répétez.
1
@Adrian Puis-je attirer votre attention sur Knight Capital? en.wikipedia.org/wiki/… - juste au cas où vous voudriez renforcer votre propos par une référence. Ils ont pratiquement implosé, car un ancien code est revenu à la vie et a gentiment perdu près de 500 millions de dollars pour eux. L'enquête qui a suivi a porté sur leurs procédures de publication, mais le problème central était "la fonctionnalité morte n'a pas été supprimée".
SusanW
6

Vous pouvez utiliser les options pour changer le chemin d’exécution de votre logiciel afin d’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 désactivée. Si vous remarquez des erreurs majeures liées au code, réactivez la bascule et analysez le chemin possible.

Cette approche devrait vous donner confiance si vous ne voyez aucun problème sur une période prolongée, ainsi que la possibilité de la réactiver en cas de déploiement en temps réel. Cependant, une solution encore meilleure consisterait également à appliquer une couverture supplémentaire de journalisation et de test autour de la zone en question, ce qui fournira davantage de preuves quant à son utilisation ou non.

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

Ceinture
la source
6

L'analyse statique bien sûr ... et la bonne chose est que vous n'avez pas besoin de nouveaux outils; votre compilateur dispose de tous les outils d'analyse statique dont vous avez besoin.

Il suffit de changer le nom de la méthode (par exemple, changer DoSomethingen DoSomethingX) et ensuite exécuter une construction.

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

Si votre construction échoue, isolez la ligne de code qui l'appelle et vérifiez quelles ifinstructions entourent l'appel afin de déterminer comment le déclencher. Si vous ne trouvez aucun cas d'utilisation de données susceptible de le déclencher, vous pouvez le supprimer en toute sécurité.

Si vous souhaitez vraiment le supprimer, pensez à conserver le code, mais en le marquant avec un ObsoleteAttribute (ou l’équivalent dans votre langue). Libérez-le comme cela pour une version, puis supprimez le code après qu'aucun problème n'ait été trouvé dans la nature.

John Wu
la source
8
C'est un bon conseil pour les langages de programmation qui n'ont pas de liaison dynamique / tardive complète . Cependant, pour ceux qui en ont, c'est plus compliqué. Et, bien sûr, le code peut utiliser la méthode même s'il n'est jamais utilisé en production.
eis
La question présuppose une résolution de symbole au moment de la compilation ( vous avez donc trouvé du code qui semble superflu. Le compilateur ne le remarque pas. ). Et même les fonctions ou objets liés tardivement sont généralement définis à deux endroits, par exemple une interface ou un fichier de carte, de sorte que la construction échoue quand même.
John Wu
1
Hmm ... Je suis intrigué, je ne pense pas que ce dernier soit vrai. Ce n'est pas pour javascript vanille (non précompilé), ruby, python ou smalltalk par exemple, non? Si vous vous référez à des éléments qui n'existent pas, ceux-ci ne sont générés que lors de l'exécution.
eis
Peut-être que nous ne sommes pas d’accord sur ce que l’on entend par "liaison", ce qui pour moi indique la résolution d’une représentation logique en une représentation physique, par exemple un symbole en une adresse. Les fonctions Javascript sont stockées sous forme de paires étiquette / valeur dans l’objet conteneur, ainsi toute notion de liaison semble non séquentielle.
John Wu
1
Oui, nous sommes vraiment en désaccord. Le terme a un sens différent sur les langues qui n'ont pas de liaison tardive complète (comme je l'ai expliqué dans le premier commentaire). Lorsque vous liez des méthodes au moment de l'exécution - dans des langues pouvant ajouter et supprimer des méthodes à la volée - vous utilisez une liaison tardive comme je le comprends. Cela est vrai dans les langages de style ruby ​​/ python / smalltalk, et vous ne disposez pas de fichiers de carte séparés. Étant donné que le rubis et le python sont très largement utilisés, je ne suis pas d’accord sur votre idée de ce que cela signifierait "typiquement".
eis
4
  • Je commencerais par utiliser un analyseur de code pour vérifier si c'était du code mort
  • Je commencerais à vérifier mes tests et à essayer d’atteindre le code. Si je ne parviens pas à obtenir le code dans un scénario de test, il peut s'agir d'un code mort
  • Je supprimerais le code et lirais une exception à la place. Pour une version, l'exception est activée et dans la seconde, l'exception peut être supprimée. Pour plus de sécurité, placez un indicateur 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.
Markus Lausberg
la source
6
-1. Il est absolument déconseillé d’apporter ces modifications à un code de production, à un indicateur de sécurité ou non. Ce n'est que lorsque l'on est sûr que le code en question n'est pas utilisé qu'il devrait être supprimé du code productif. Ce genre de problème est exactement la raison pour laquelle il devrait toujours y avoir différents environnements de production et de test.
Johannes Hahn
3
Cela fonctionnera à merveille lorsque la couverture de test atteindra près de 90%, mais que se passe-t-il si vous avez un projet avec 100 000 lignes de code. C’est la raison pour laquelle j’ai décrit comment essayer de tester le code et si aucun test n’est en mesure d’atteindre le code ... il est peut-être mort.
Markus Lausberg
IMHO Si vous n'êtes pas assez sûr de vous empêcher de supprimer complètement le code - vous ne devriez pas lancer d'exceptions en production - la journalisation suggérée par l'OP est une bien meilleure alternative, vous obtiendrez le même résultat et vous pourrez obtenir des diagnostics au lieu de vous fier aux clients
Sebi
@JohannesHahn Je pense que vous voulez dire par "code de production", c'est-à-dire un code qui s'exécute dans un environnement de production.
jpmc26
@ jpmc26 Oui, bien sûr.
Johannes Hahn
3

Supprimer le code inaccessible

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

Malheureusement, toutes les langues ne sont pas typées statiquement, et toutes les langues statiquement typées ne sont pas régies par des principes. Les choses qui pourraient mal tourner incluent (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 l'élément de code examiné puisse éventuellement être consulté au moment de l'exécution via la réflexion, vous ne pouvez pas vous fier au compilateur. Ces langages incluent Python, Ruby ou Java.

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

Donc, à moins que vous ne soyez très chanceux de travailler avec des langages stricts (on pense à Rust), 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, donc pas beaucoup plus d’aide non plus.

Cue la prochaine section ...


Supprimer le code potentiellement inutilisé

Plus probablement, le code est effectivement 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, peu importe la langue, le code est manifestement accessible et seule une instrumentation d'exécution peut être utilisée.

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

  1. Enregistrez un avertissement sur chaque branche suspectée de ne pas être prise.
  2. Après un cycle, émettez une exception / retournez une erreur lors de la saisie du code spécifique.
  3. Après un autre cycle, supprimez le code.

C'est quoi 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 les salaires versé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 émis pour l'inventaire de fin d'année. Il 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 conseille de mettre un commentaire TODO, avec une date, indiquant quand passer à la prochaine étape. Et un rappel dans votre calendrier.

Matthieu M.
la source
1
En réalité, en C ++, vous pouvez marquer votre surcharge suspecte comme [[deprecated]]identifiant les sites qui appellent cette version; vous pouvez ensuite les inspecter pour voir si leur comportement va changer. Vous pouvez également définir votre surcharge de la manière suivante = delete: dans ce cas, vous devrez explicitement transtyper des arguments pour utiliser l'une des autres versions.
Toby Speight
@TobySpeight: C'est vrai, et une bonne alternative aussi. Laissez-moi éditer ça.
Matthieu M.
"Docteur, ça fait mal quand je fais ça." "Eh bien, ne faites pas ça! " N'utilisez pas d'approches qui rendent le code ingérable.
2

Retirer le code de la production revient à nettoyer une maison. Au moment où vous jetez un objet du grenier, votre femme vous tuera le lendemain pour avoir jeté le cadeau de retour au pays du troisième neveu de son arrière grand-mère à son voisin décédé en 1923.

Sérieusement, après une analyse sommaire à l'aide de divers outils que tout le monde a déjà mentionné, et après avoir utilisé l'approche par dépréciations par étapes déjà mentionnée, gardez à l'esprit que vous risquez peut-être de vous retirer de l'entreprise lorsque la suppression effective a lieu. Laisser le code rester et faire en sorte que son exécution soit consignée et s’assurer que toutes les alertes concernant son exécution vous sont définitivement communiquées (ou à vos successeurs et ayants droit).

Si vous ne suivez pas cette approche, vous serez probablement tué par votre femme. Si vous conservez le code (comme chaque souvenir), vous pourrez vous reposer sur le fait que la loi de Moore vient à votre secours et que le coût de l'espace disque 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 nombreux commérages de refroidisseur d’eau et de regards étranges dans les couloirs.

PS: Pour clarifier en réponse à un commentaire. La question initiale est "Comment effacez -vous en toute sécurité .." bien sûr, le contrôle de version est supposé dans ma réponse. Tout comme creuser dans la corbeille 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 pourrons jamais savoir qu'un morceau de code est superflu à moins de pouvoir garantir que 100% des chemins d'exécution ne l'atteignent pas. Et il est supposé que ce logiciel est suffisamment volumineux pour que cette garantie soit quasiment impossible. Et à moins que je ne lise mal la question, le seul moment où ce fil de discussion est pertinent concerne les cas de production où le code supprimé peut être appelé, ce qui pose un problème d'exécution / de production. Le contrôle de version ne sauve pas le retard de tous en raison d’échecs de production. Le commentaire sur "Contrôle de version" n’est donc pas pertinent pour la question ni pour mon argument initial, c’est-à-dire que nous vous déconseillons d’être obsolète sur une très longue période si vous en avez vraiment besoin, sinon ne le faites pas.

IMHO, le commentaire est superflu et est un candidat à la suppression.

LMSingh
la source
12
Si j'avais l'équivalent du contrôle de version , l'objection de ma femme serait facilement inversée. De même avec la suppression de code: ce n'est pas une infraction meurtrière. Le référentiel n'est jamais jeté.
JDługosz
Je pensais que la programmation orientée objet était supposée réduire la portée dans laquelle une méthode pouvait être appelée. Cela devrait donc conduire à un code plus facile à comprendre et à gérer, non? Sinon, pourquoi le faisons-nous de cette façon?
@nocomprende Nulle part dans cette discussion, il n’est indiqué que la discussion se limite aux langues et à la conception POO. Je ne sais pas pourquoi vous pensez que c'est pertinent.
underscore_d
1

Peut - être que le compilateur ne remarque que, ça ne fait pas de chichi.

Exécutez une construction avec des optimisations complètes du compilateur, orientées vers la taille. Supprimez ensuite le code suspect et relancez la construction.

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 fichiers binaires sont différents ... alors cela n'est pas concluant. Cela pourrait être un autre changement. Certains compilateurs incluent même la date et l'heure de compilation dans le binaire (et peut-être que cela peut être configuré à distance!)

Emilio M Bumachar
la source
1
Toutes les langues ne sont pas compilées, tous les compilateurs n'ont pas d'optimisation et toutes les optimisations ne sont pas égales. Beaucoup ne suppriment pas le code non invoqué au cas où ce code existe pour une raison. Si le code réside dans une bibliothèque / DLL partagée, il ne sera pas supprimé.
Steve Barnes
1
@SteveBarnes Eh bien oui, si vous ne faites pas de LTO et n'associez pas de liens statiques à tous les objets, vous souffrirez. C'est une donnée.
Navin
1

En fait, j'ai 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 la 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 appels de méthode AJAX "méthode: delete, elem = Foo" sont ensuite concaténés et appelés 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!

dotancohen
la source
Il existe des bibliothèques pour PHP et Perl appelées Tombstone (php: github.com/scheb/tombstone ) qui peuvent être utiles ici.
Alister Bulman
0

Utilisez grepou quels que soient les outils disponibles en premier, vous pouvez trouver des références au code. (Pas à l'origine mes instructions / conseils.)

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

Vous pouvez modifier la fonction / le 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. Obtenir le 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. Retracer l'appelant. Dans certains langages de haut niveau (par exemple, Python), vous pouvez facilement obtenir une trace. Mais dans les langages compilés, vous devrez enregistrer l'adresse de retour dans le journal et forcer un vidage mémoire à la sortie (point 1).
    En C, cela devrait être assez simple: utilisez un bloc conditionnel (par exemple, #ifdef ... #endif) pour l'utiliser uniquement lorsque vous savez que cela fonctionnera (et que vous l'avez testé) et lisez simplement l'adresse de retour. de la pile (peut nécessiter ou non un langage d'assemblage intégré).

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

Oskar Skog
la source
0

Si vous savez ce que fait le code qui l’entoure, testez-le pour voir s’il tombe. Il s’agit du moyen le plus simple et le plus rentable de modifier le code sur lequel vous travaillez et vous devriez le faire de toute façon si vous souhaitez modifier le code sur lequel vous travaillez.

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

Maintenant, il peut y avoir des circonstances (je sais que je l'ai moi-même rencontré) où le code "mort" n'est en réalité connecté à rien . Telles des bibliothèques de code développées par un fournisseur et qui n'ont jamais été implémentées nulle part, 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 le code, mais c’est un risque énorme que je ne recommande pas de prendre à la légère - en fonction de la quantité de code que ce changement pourrait avoir un impact (car il ya toujours le potentiel que vous avez oublié doit être extrêmement prudent et exécuter des tests unitaires agressifs pour déterminer si la suppression de cet ancien code héritera votre application.

Tout cela étant dit, votre temps et votre santé mentale ne valent probablement pas la peine d'essayer de supprimer du code comme celui-là. Pas à moins que cela ne devienne un problème sérieux avec votre application (par exemple, ne pas être en mesure de réaliser des analyses de sécurité en raison de la surcharge de l'application ... pourquoi oui, j'ai encore un exemple en tête), je ne le recommanderais donc pas, sauf si vous atteignez une telle situation où le code a vraiment commencé à pourrir de manière percutante.

En bref, si vous savez ce que fait le code, commencez par le tester. Si vous ne le faites pas, vous pouvez probablement le laisser seul. Si vous savez que vous ne pouvez pas en rester là, consacrez votre temps à tester le changement de manière agressive avant de le mettre en œuvre.

Zibbobz
la source
0

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 la reformuler, et dans d'autres cas, vous vous enfuyez aussi loin que vous le pouvez et vous ne regardez jamais en arrière. Vous devez déterminer lequel il s’agit et, pour ce faire, vous ferez le mieux avec un second - expérimenté! - développeur, quelqu'un 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 serez à court de développeurs qui connaissent très bien la base de code.

J'ai aussi 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 tout à fait décente si vous parlez de supprimer des fonctionnalités entières, mais pas si vous supprimez simplement un petit morceau de code mort.

Peter
la source
0

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

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 évidemment prouver qu’elle est utilisée, mais ne peut pas prouver que ce n’est pas le cas. Étant donné que le principal problème du code mort est qu'il est maintenu, vous pouvez peut-être vous en tirer en ajoutant un commentaire disant qu'il s'agit d'un code mort présumé, et ne pas effectuer de maintenance dessus.

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

En fin de compte, vous pouvez le comprendre, le laisser tranquille ou tenter votre chance.

jmoreno
la source
0

Si le développeur du code en question est toujours disponible, examinez la suppression du code avec lui . En outre, lire les commentaires dans le code .

Vous obtiendrez la bonne explication, pourquoi ce code a été ajouté et à quelle fonction sert-il. Il peut facilement s'agir d'une prise en charge pour un cas d'utilisation spécifique ou même ne pas disposer de suffisamment d'expertise pour juger si cela n'est vraiment pas nécessaire. Dans les cas extrêmes, il est possible de supprimer toutes les instructions libres d’un programme C et de ne rien remarquer de mal (cela peut prendre quelques minutes pour manquer de mémoire).

C’est vraiment une mauvaise culture que l’auteur du code se trouve à 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 simplement des mots au hasard dans les commentaires, inutile 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 en compte le problème.

Une discussion avec l'auteur peut également révéler que le code est le reste historique de quelque chose qui a été enlevé ou qui n'a jamais été fini. Il est donc sûr de le supprimer.

h22
la source
-1

Je suis tout à fait d'accord avec le premier point de Markus Lausberg: le code doit absolument être analysé à l'aide d'outils. Les cerveaux de singe ne doivent pas être confiés à ce genre de tâche!

La plupart des langages de programmation standard peuvent être utilisés dans les IDE et tous les IDE qui méritent d'être appelés ont une fonctionnalité "trouver pour tous les usages" qui est exactement le bon outil pour ce type de situation. (Ctrl + Shift + G dans Eclipse par exemple)

Bien sûr: les outils de l'analyseur statique ne sont pas parfaits. Il faut être conscient des situations plus complexes dans lesquelles la décision d'appeler la méthode en question n'est prise qu'au moment de l'exécution (appels via Reflection, appels aux fonctions "eval" dans les langages de script, etc.).

Johannes Hahn
la source
5
Peut-être que les cerveaux de singes ne devraient pas écrire de code? " Insensible aux mains humaines " était une expression marketing. J'ai toujours imaginé les mains de gorille faire le travail.
3
(Les cerveaux des singes ont aussi écrit les outils) (Chut! Vous allez gâcher l'histoire!)
1
Je pense que vous manquez tous les deux mon point. J'ai clairement dit qu'il n'y avait aucune garantie que l'analyse de code statique trouve chaque invocation d'une méthode ou d'une classe. Comme il est dit sur l’étain, il s’agit d’une analyse statique. On ne peut pas et on ne doit pas s'attendre à ce que des invocations dynamiques soient trouvées via réflexion, eval (), etc. Mon point est que l'analyse de code statique doit être considérée comme une condition préalable nécessaire à la suppression d'un morceau de code. Et plus le projet est important, moins il est probable qu'un cerveau humain soit même capable de faire cette analyse, encore moins de le faire correctement ...
Johannes Hahn
1
... l'analyse de code statique est un travail fastidieux et répétitif, mieux effectué par un ordinateur. C'est exactement le genre de tâche qui peut être exécutée de manière fiable par un ordinateur, mais qui est notoirement peu fiable lorsqu'elle est exécutée par un cerveau singe. Ce dernier ne devrait être utilisé que pour les tâches qu'un ordinateur ne peut faire, comme trouver ces réflexions difficiles et ces appels eval.
Johannes Hahn
1
Encore une fois, rater le point. Même si les outils ne sont pas parfaits, car ils ont été écrits par des cerveaux de singes (et je ne suis même pas sûr de devoir leur accorder cela! Calculer des hiérarchies d'appels statiques n'est pas si difficile), ils sont toujours bien supérieurs à l'exécution de tâches manuelle. Ce n'est pas une situation du type tout ou rien. Un outil fiable à 99% est toujours préférable à un cerveau qui commence peut-être à 99% mais tombe rapidement à des taux beaucoup plus bas après les mille premières lignes de code.
Johannes Hahn
-1

Deux possibilités:

  1. Vous avez une couverture de test de 99% + : supprimez le code et terminez-le.
  2. Vous n'avez pas de couverture de test digne de confiance: la solution consiste alors à ajouter un avertissement de dépréciation . C'est-à-dire que vous ajoutez du code à cet endroit qui rend quelque chose d'intempestif (connectez un avertissement à un endroit facilement visible qui n'entre pas en conflit avec l'utilisation quotidienne de votre application, par exemple). Puis laissez mijoter pendant quelques mois / années. Si vous n'en avez jamais trouvé l'occurrence, remplacez la dépréciation par quelque chose qui lève une exception. Laissons cela pendant un moment. Enfin, supprimez tout complètement.
AnoE
la source
2
cela ne semble rien offrir de substantiel par rapport aux points soulevés et expliqués dans les 17 réponses précédentes. Les deux avertissement de couverture et deprecation ont été discutées là - bas déjà
moucheron
@gn, vous avez raison. Lorsque j’ai parcouru les réponses pour la première fois, j’ai pensé, pour une raison quelconque, que j’en avais vu plusieurs qui, au sommet, n’arrivaient pas à ces deux points de manière succincte et succincte. Au milieu des réponses, certaines sont comme ça.
AnoE