Je traite avec une assez grosse base de code et on m'a donné quelques mois pour refactoriser le code existant. Le processus de refactorisation est nécessaire car nous aurons bientôt besoin d'ajouter de nombreuses nouvelles fonctionnalités à notre produit et, pour l'instant, nous ne sommes plus en mesure d'ajouter aucune fonctionnalité sans casser autre chose. En bref: un code énormément volumineux, complexe, que beaucoup d’entre nous ont vu dans leur carrière.
Pendant le refactoring, je rencontre de temps en temps la classe, la méthode ou les lignes de code qui ont des commentaires comme
Délai d'attente défini pour donner au module A un peu de temps pour faire des choses. Si ce n'est pas chronométré comme ça, ça va casser.
ou
Ne changez pas cela. Croyez-moi, vous allez casser des choses.
ou
Je sais que l'utilisation de setTimeout n'est pas une bonne pratique, mais dans ce cas, je devais l'utiliser
Ma question est la suivante: dois-je reformuler le code lorsque je rencontre de tels avertissements de la part des auteurs (non, je ne peux pas contacter les auteurs)?
la source
Réponses:
Il semble que vous refactoring "au cas où", sans savoir exactement quelles parties de la base de code seront modifiées en détail lors du développement de la nouvelle fonctionnalité. Autrement, vous sauriez s'il est vraiment nécessaire de refactoriser les modules fragiles ou si vous pouvez les laisser tels quels.
Pour dire ceci directement: je pense que c'est une stratégie de refactoring condamnée . Vous investissez temps et argent dans votre entreprise pour des projets dans lesquels personne ne sait si cela va réellement rapporter un avantage, et vous êtes sur le point de faire empirer les choses en introduisant des bogues dans le code qui fonctionne.
Voici une meilleure stratégie: utilisez votre temps pour
ajoutez des tests automatiques (probablement pas des tests unitaires, mais des tests d'intégration) aux modules à risque. Surtout les modules fragiles que vous avez mentionnés nécessiteront une suite de tests complète avant de modifier quoi que ce soit.
refactorisez uniquement les éléments nécessaires à la mise en place des tests. Essayez de minimiser les changements nécessaires. La seule exception est lorsque vos tests révèlent des bogues - corrigez-les immédiatement (et modifiez-les au degré requis pour le faire en toute sécurité).
Apprenez à vos collègues le "principe du scoutisme" (AKA, "refactoring opportuniste" ). Ainsi, lorsque l'équipe commencera à ajouter de nouvelles fonctionnalités (ou à corriger des bugs), elle devrait améliorer et refactoriser exactement les éléments de la base de code à modifier, pas moins, pas plus.
obtenir une copie du livre de Feather "Travailler efficacement avec le code hérité" pour l'équipe.
Alors, le moment venu, quand vous saurez avec certitude que vous devez changer et refactoriser les modules fragiles (soit à cause du développement de nouvelles fonctionnalités, soit parce que les tests que vous avez ajoutés à l'étape 1 révèlent des bugs), alors vous et votre équipe êtes prêts à refactoriser les modules et ignorer plus ou moins les commentaires d’avertissement.
En réponse à certains commentaires : pour être juste, si l’on suspecte un module d’un produit existant d’être la cause régulière de problèmes, en particulier un module portant la mention "ne touche pas", je suis d’accord avec tous les toi. Il devrait être revu, débogué et très probablement refactoré dans ce processus (avec l’appui des tests que j’ai mentionnés, et pas nécessairement dans cet ordre). Les bugs sont une justification solide du changement, souvent plus forte que les nouvelles fonctionnalités. Cependant, il s’agit d’une décision prise au cas par cas. Il faut vérifier très attentivement si cela vaut vraiment la peine de changer quelque chose dans un module qui a été marqué comme "ne touchez pas".
la source
Oui, vous devez refactoriser le code avant d'ajouter les autres fonctionnalités.
Le problème avec de tels commentaires est qu'ils dépendent de circonstances particulières de l'environnement dans lequel la base de code est exécutée. La temporisation programmée à un moment donné était peut-être vraiment nécessaire lors de la programmation.
Mais il y a un certain nombre de choses qui pourraient changer cette équation: modifications du matériel, modifications du système d'exploitation, modifications non liées à un autre composant du système, modifications des volumes de données types, vous l'appelez. Il n’existe aucune garantie que cela soit encore nécessaire aujourd’hui ou qu’il soit encore suffisant (l’intégrité de la chose qu’elle était supposée protéger aurait pu être brisée pendant longtemps - sans tests de régression appropriés, vous pourriez ne jamais le remarquer). C'est pourquoi la programmation d'un délai fixe afin de permettre à un autre composant de se terminer est presque toujours incorrecte et ne fonctionne que de manière accidentelle.
Dans votre cas, vous ne connaissez pas les circonstances initiales et vous ne pouvez pas non plus demander aux auteurs originaux. (Vous n'avez probablement pas non plus les tests de régression / d'intégration appropriés, ou vous pouvez simplement continuer et laisser vos tests vous dire si vous avez cassé quelque chose.)
Cela pourrait ressembler à un argument pour ne rien changer par prudence; mais vous dites qu'il faudra de toute façon des changements majeurs, de sorte que le danger de perturber le délicat équilibre qui existait auparavant à cet endroit existe déjà. Il est bien mieux de bouleverser le panier de pommes maintenant , lorsque la seule chose que vous faites est le refactoring, et assurez-vous que si les choses se cassent, c'est le refactoring qui l'a provoqué, plutôt que d'attendre que vous apportiez des modifications supplémentaires en même temps et jamais. etre sur.
la source
Non, ou du moins pas encore. Vous sous-entendez que le niveau de test automatisé est très faible. Vous avez besoin de tests avant de pouvoir refacturer en toute confiance.
À l’heure actuelle, vous devez vous concentrer sur l’augmentation de la stabilité et non sur la refactorisation. Vous pouvez effectuer une refactorisation dans le cadre d’une augmentation de la stabilité, mais c’est un outil pour atteindre votre véritable objectif: une base de code stable.
Il semble que cela soit devenu une base de code héritée, vous devez donc le traiter un peu différemment.
Commencez par ajouter des tests de caractérisation. Ne vous inquiétez pas des spécifications, ajoutez simplement un test qui affirme le comportement actuel. Cela aidera à éviter que de nouveaux travaux ne cassent des fonctionnalités existantes.
Chaque fois que vous corrigez un bogue, ajoutez un scénario de test prouvant qu'il est corrigé. Cela empêche les régressions directes.
Lorsque vous ajoutez une nouvelle fonctionnalité, ajoutez au moins quelques tests de base indiquant que la nouvelle fonctionnalité fonctionne comme prévu.
Peut-être obtenir une copie de "Travailler efficacement avec le code hérité"?
Commencez par obtenir une couverture de test. Commencez par les zones qui cassent le plus. Commencez par les domaines qui changent le plus. Ensuite, une fois que vous avez identifié les mauvais modèles, remplacez-les un à un.
Vous ne faites presque jamais un seul refactor, mais un flux constant de petits refactors chaque semaine. Un grand refactor présente un risque énorme de casser des choses et il est difficile de bien tester.
la source
Rappelez - vous la clôture du GK Chesterton : ne pas abattre une clôture obstruant une route avant d'avoir compris pourquoi elle a été construite.
Vous pouvez trouver le ou les auteurs du code et les commentaires en question et les consulter pour en comprendre le sens. Vous pouvez consulter les messages de validation, les fils de discussion ou les documents s'ils existent. Ensuite, vous pourrez soit refactoriser le fragment marqué, soit écrire vos connaissances dans les commentaires, de sorte que la prochaine personne chargée de la maintenance de ce code puisse prendre une décision plus éclairée.
Votre objectif est de comprendre ce que fait le code, pourquoi il a été marqué d’un avertissement à l’époque et ce qui se passerait si cet avertissement était ignoré.
Avant cela, je ne toucherais pas le code qui est marqué "ne touche pas".
la source
Code avec des commentaires tels que ceux que vous avez affichés serait en haut de ma liste de choses à refactoriser si, et seulement si j'ai une raison de le faire . Le fait est que le code pue tellement que vous le sentez même à travers les commentaires. Il est inutile d'essayer de mélanger toute nouvelle fonctionnalité dans ce code, ce code doit mourir dès qu'il doit changer.
Notez également que les commentaires ne sont d'aucune utilité: ils ne donnent que des avertissements et aucune raison. Oui, ce sont les signes avant-coureurs autour d'un réservoir de requins. Mais si vous voulez faire quelque chose dans les environs, essayez de nager avec les requins. A mon avis, vous devriez d'abord vous débarrasser de ces requins.
Cela dit, vous devez absolument disposer de bons cas de tests avant de pouvoir oser travailler sur un tel code. Une fois que vous avez ces cas de test, assurez-vous de comprendre chaque petit détail que vous modifiez pour vous assurer que vous ne modifiez pas vraiment le comportement. Faites-en votre priorité absolue de garder toutes les particularités comportementales du code jusqu'à ce que vous puissiez prouver qu'elles sont sans aucun effet. N'oubliez pas que vous avez affaire à des requins - vous devez faire très attention.
Donc, dans le cas du délai d'expiration: laissez-le jusqu'à ce que vous compreniez précisément ce que le code attend, puis corrigez la raison pour laquelle le délai d'expiration a été introduit avant de procéder à sa suppression.
Assurez-vous également que votre supérieur hiérarchique comprend les efforts que vous entreprenez et pourquoi vous en avez besoin. Et s'ils disent non, ne le fais pas. Vous avez absolument besoin de leur soutien à cet égard.
la source
Ce n'est probablement pas une bonne idée de refactoriser le code avec de tels avertissements, si les choses fonctionnent correctement telles quelles.
Mais si vous devez refacturer ...
Commencez par rédiger des tests unitaires et des tests d'intégration pour tester les conditions sur lesquelles les avertissements vous avertissent. Essayez de reproduire autant que possible les conditions de production . Cela implique de refléter la base de données de production sur un serveur de test, d'exécuter les mêmes services sur la machine, etc., quoi que vous puissiez faire. Ensuite, essayez votre refactoring (sur une branche isolée, bien sûr). Ensuite, lancez vos tests sur votre nouveau code pour voir si vous pouvez obtenir les mêmes résultats qu'avec l'original. Si vous le pouvez, vous pouvez probablement implémenter votre refactoring en production. Mais soyez prêt à annuler les changements si les choses tournent mal.
la source
Je dis allez-y et changez-le. Croyez-le ou non, tous les codeurs ne sont pas des génies et un tel avertissement signifie que cela pourrait être un point d'amélioration. Si vous trouvez que l'auteur avait raison, vous pouvez (haleter) DOCUMENTER ou EXPLIQUER le motif de l'avertissement.
la source
J'élargis mes commentaires en une réponse parce que je pense que certains aspects du problème spécifique sont soit négligés, soit utilisés pour tirer des conclusions erronées.
À ce stade, la question de savoir s'il faut ou non refactoriser est prématurée (même si une forme spécifique de «oui» y répondra probablement).
Le problème central ici est que (comme indiqué dans certaines réponses) les commentaires que vous citez indiquent fortement que le code a des conditions de concurrence ou d'autres problèmes de simultanéité / synchronisation, tels que discutés ici . Ce sont des problèmes particulièrement difficiles, pour plusieurs raisons. Premièrement, comme vous l'avez constaté, des modifications apparemment non liées peuvent déclencher le problème (d'autres bogues peuvent également avoir cet effet, mais les erreurs de concurrence le sont presque toujours.) Deuxièmement, elles sont très difficiles à diagnostiquer: le bogue se manifeste souvent à un endroit éloigné dans le temps ou le code de la cause, et tout ce que vous faites pour la diagnostiquer peut la faire disparaître ( Heisenbugs). Troisièmement, les bogues d'accès simultané sont très difficiles à trouver dans les tests. Cela est dû en partie à l'explosion combinatoire: c'est assez mauvais pour un code séquentiel, mais l'ajout des interconnexions possibles d'une exécution simultanée le fait exploser au point que le problème séquentiel devient insignifiant en comparaison. De plus, même un bon cas de test ne peut occasionnellement déclencher le problème - Nancy Leveson a calculé que l'un des insectes mortels du Therac 25s'est produite dans 1 des 350 passages environ, mais si vous ne savez pas quel est le bogue, ni même s'il en existe un, vous ne savez pas combien de répétitions font un test efficace. En outre, seuls les tests automatisés sont réalisables à cette échelle et il est possible que le pilote de test impose des contraintes de minutage subtiles de sorte qu'il ne déclenche jamais le bogue (Heisenbugs à nouveau).
Il existe certains outils de test de concurrence dans certains environnements, tels que Helgrind pour le code utilisant des pthreads POSIX, mais nous ne connaissons pas les détails ici. Les tests doivent être complétés par une analyse statique (ou est-ce l'inverse?), S'il existe des outils adaptés à votre environnement.
Pour ajouter à la difficulté, les compilateurs (et même les processeurs, au moment de l'exécution) sont souvent libres de réorganiser le code de manière à rendre le raisonnement sur la sécurité des threads très contre-intuitif (le cas le plus connu est peut-être le double contrôle). verrouiller l' idiome, bien que certains environnements (Java, C ++ ...) aient été modifiés pour l'améliorer.)
Ce code peut avoir un problème simple qui est la cause de tous les symptômes, mais il est plus probable que vous ayez un problème systémique susceptible d'empêcher vos projets d'ajouter de nouvelles fonctionnalités. J'espère vous avoir convaincu que vous pourriez avoir un problème sérieux, peut-être même une menace existentielle pour votre produit, et la première chose à faire est de savoir ce qui se passe. Si cela révèle des problèmes de simultanéité, je vous conseille vivement de les résoudre avant de vous poser la question de savoir si vous devez effectuer une refactorisation plus générale et avant d'essayer d'ajouter d'autres fonctionnalités.
la source
Oui, vous devriez refactoriser ces parties en particulier . Les précédents auteurs ont placé ces avertissements dans le sens suivant: "Ne modifiez pas inutilement ces informations, elles sont très complexes et entraînent de nombreuses interactions inattendues". Alors que votre entreprise envisage de développer davantage le logiciel et d'ajouter de nombreuses fonctionnalités, elle vous a spécifiquement confié le nettoyage de ces éléments. Donc, vous ne le modifiez pas inutilement , vous êtes délibérément chargé de le nettoyer.
Découvrez ce que font ces modules compliqués et divisez-le en problèmes plus petits (ce que les auteurs originaux auraient dû faire). Pour obtenir du code maintenable dans un gâchis, les bonnes parties doivent être refactorisées et les mauvaises parties doivent être réécrites .
la source
Cette question est une autre variante du débat sur quand / comment refactoriser et / ou nettoyer le code avec un tiret de "comment hériter du code". Nous avons tous des expériences différentes et travaillons dans différentes organisations avec des équipes et des cultures différentes. Il n'y a donc pas de bonne ou de mauvaise réponse à part "faites ce que vous pensez devoir faire, et faites-le de manière à ne pas vous faire virer" .
Je ne pense pas que de nombreuses organisations seraient bien disposées à laisser tomber un processus métier, car l'application associée aurait besoin d'un nettoyage ou d'une refactorisation du code.
Dans ce cas particulier, les commentaires de code soulignent le fait que la modification de ces sections de code ne doit pas être effectuée. Donc, si vous poursuivez, et que l’entreprise tombe de son côté, non seulement vous n’avez rien à montrer pour appuyer vos actions, mais en réalité un artefact va à l’encontre de votre décision.
Ainsi, comme toujours, vous devez procéder avec précaution et apporter des modifications uniquement après avoir compris tous les aspects de ce que vous êtes sur le point de modifier, et trouver le moyen de la tester, en accordant une attention particulière à la capacité, aux performances et au calendrier, en raison de: les commentaires dans le code.
Néanmoins, même dans ce cas, votre direction doit comprendre le risque inhérent à ce que vous faites et convenir que ce que vous faites a une valeur commerciale qui l’emporte sur le risque et que vous avez fait tout ce qui peut être fait pour le réduire.
Maintenant, revenons à nos propres TODO et les choses que nous savons peuvent être améliorées dans nos propres créations de code si seulement nous avions plus de temps.
la source
Oui absolument. Ce sont des indications claires que la personne qui a écrit ce code était insatisfaite de ce code et l’a probablement piquée jusqu’à ce qu’elle fonctionne. Il est possible qu'ils n'aient pas compris quels étaient les véritables problèmes ou, pire, les aient compris et étaient trop paresseux pour les résoudre.
Cependant, c'est un avertissement qu'il faudra beaucoup d'efforts pour les réparer et que de tels problèmes comporteront des risques.
Idéalement, vous serez en mesure de comprendre le problème et de le résoudre correctement. Par exemple:
Ceci suggère fortement que le module A n'indique pas correctement quand il est prêt à être utilisé ou quand il a fini de traiter. Peut-être que la personne qui a écrit ceci ne voulait pas se soucier de réparer le module A ou ne pouvait pas le faire pour une raison quelconque. Cela ressemble à une catastrophe imminente car il suggère une dépendance au timing traitée par la chance plutôt que par un séquencement approprié. Si je voyais cela, je voudrais vraiment le réparer.
Cela ne vous dit pas grand chose. Cela dépend de ce que le code faisait. Cela pourrait signifier qu'il comporte des optimisations qui semblent évidentes et qui, pour une raison ou une autre, vont réellement casser le code. Par exemple, une boucle peut laisser une variable à une valeur particulière dont dépend un autre morceau de code. Ou bien une variable peut être testée dans un autre thread et le fait de changer l'ordre des mises à jour de variable peut endommager cet autre code.
Cela ressemble à un facile. Vous devriez pouvoir voir ce qui
setTimeout
se passe et peut-être trouver un meilleur moyen de le faire.Cela dit, si ces types de correctifs sortent du cadre de votre refactor, ce sont des indications selon lesquelles essayer de le refactoriser à l'intérieur de ce code peut augmenter considérablement la portée de vos efforts.
Au minimum, examinez de près le code concerné et voyez si vous pouvez au moins améliorer le commentaire au point qu'il explique plus clairement le problème. Cela pourrait sauver la prochaine personne de faire face au même mystère que vous affrontez.
la source
L'auteur des commentaires n'a probablement pas bien compris le code eux-mêmes . S'ils savaient vraiment ce qu'ils faisaient, ils auraient écrit des commentaires réellement utiles (ou n'auraient pas introduit les conditions de course en premier lieu). Un commentaire du type " Fais-moi confiance, tu vas casser des choses ", indique-t-il que l'auteur tente de changer quelque chose qui a causé des erreurs inattendues qu'il n'a pas bien comprises.
Le code est probablement développé par devinettes et essais et erreurs sans une compréhension complète de ce qui se passe réellement.
Ça signifie:
Il est risqué de changer le code. Évidemment, il faudra du temps pour comprendre, et cela ne suit probablement pas de bons principes de conception et peut avoir des effets et des dépendances obscurs. Il est très probablement insuffisamment testé, et si personne ne comprend parfaitement ce que fait le code, il sera difficile d'écrire des tests pour s'assurer qu'aucune erreur ou changement de comportement n'est introduit. Les conditions de course (comme les commentaires auxquels il est fait allusion) sont particulièrement pénibles - c’est un endroit où les tests unitaires ne vous sauveront pas.
Il est risqué de ne pas changer le code. Le code a de fortes chances de contenir des bugs obscurs et des conditions de course. Si un bogue critique est découvert dans le code, ou si un changement d’exigence hautement prioritaire l’oblige à modifier ce code dans un bref délai, vous avez de graves problèmes. Maintenant, vous avez tous les problèmes décrits dans 1, mais sous la pression du temps! De plus, de telles "zones sombres" dans le code ont tendance à se propager et à infecter d'autres parties du code qu'il touche.
Une complication supplémentaire: les tests unitaires ne vous sauveront pas . Généralement, l’approche recommandée pour ce code hérité de correctif consiste à ajouter d’abord des tests unitaires ou d’intégration, puis à isoler et à refactoriser. Mais les conditions de course ne peuvent pas être capturées par des tests automatisés. La seule solution consiste à s'asseoir et à réfléchir au code jusqu'à ce que vous le compreniez, puis à le réécrire pour éviter les conditions de course.
Cela signifie que la tâche est beaucoup plus exigeante que la simple refactorisation de routine. Vous devrez le planifier comme une vraie tâche de développement.
Vous pourrez peut-être encapsuler le code affecté dans le cadre d'une refactorisation régulière afin qu'au moins le code dangereux soit isolé.
la source
La question que je voudrais poser est pourquoi quelqu'un a écrit, NE PAS ÉDITER en premier lieu.
J'ai travaillé sur beaucoup de code, dont certains sont laids et ont nécessité beaucoup de temps et d’efforts pour fonctionner, dans les limites des contraintes imposées à l’époque.
Je parie que dans ce cas, cela est arrivé et la personne qui a écrit le commentaire a découvert que quelqu'un le changeait et devait ensuite répéter tout l'exercice pour le remettre dans l'ordre, afin que tout fonctionne correctement. Après cela, par frustration de cisaillement, ils ont écrit ... NE PAS ÉDITER.
En d'autres termes, je ne veux pas avoir à régler ce problème car j'ai de meilleures choses à faire avec ma vie.
En disant NE PAS ÉDITER, c'est une façon de dire que nous savons tout ce que nous allons savoir aujourd'hui et que, par conséquent, nous n'apprendrons jamais rien de nouveau.
Il devrait y avoir au moins un commentaire sur les raisons de ne pas éditer. C'est comme dire "ne pas toucher" ou "ne pas entrer". Pourquoi ne pas toucher la clôture? Pourquoi ne pas entrer? Ne vaudrait-il pas mieux dire: "Clôture électrique, ne touchez pas!" ou "Mines terrestres! N'entrez pas!". Ensuite, il est évident pourquoi, et pourtant vous pouvez toujours entrer, mais au moins, vous devez connaître les conséquences avant de le faire.
Je parie également que le système n’a aucun test autour de ce code magique et que, par conséquent, personne ne peut confirmer que le code fonctionnera correctement après toute modification. Placer des tests de caractérisation autour du code du problème est toujours une première étape. Reportez-vous à «Utilisation du code hérité» de Michael Feathers pour obtenir des conseils sur la manière de supprimer les dépendances et d'obtenir le code sous test avant de commencer à le modifier.
Je pense qu’en fin de compte, il est difficile de restreindre la refactorisation et de laisser le produit évoluer de manière naturelle et biologique.
la source