Comment vous arrêtez-vous de refactoriser du code qui fonctionne si terriblement?

J’ai ce problème. Je ne peux pas m’empêcher de refactoriser le code existant qui fonctionne mais, à mon avis (et peut-être objectivement), il est mal conçu ou contient d’autres «odeurs de code». Cela peut avoir un effet négatif significatif sur ma productivité immédiate. Mais en fin de compte, ce sera une bonne affaire de maintenance.

Si vous souffrez également de cette “affliction”, comment vous retenez-vous? Ou du moins gérer le refactoring pour éviter d’avoir à modifier de gros morceaux de code existant afin de le rendre maintenable à long terme.

    Ce qui marchait pour moi était de me faire virer d’un emploi pour ça. : \

    Cela dit, mon problème de base était double:

    1. Je refaisais le code que personne ne m’avait demandé de travailler.

    2. J’ai trouvé trop difficile de mettre les tests en place, alors je l’ai fait sans eux. Ce qui, naturellement, a cassé certaines choses.

    Ce que j’ai appris était:

    1. Ne travaillez pas sur du code sur lequel vous n’avez pas besoin de travailler et sur lequel personne ne vous a demandé de travailler. Il est facile de s’en souvenir maintenant, compte tenu des conséquences du passé.

    2. Lorsque vous avez besoin de refactoriser du code, vous êtes censé travailler sur, avoir des tests en place, mais il n’est pas nécessaire de les automatiser.

    Après avoir lu trop de choses sur TDD, j’avais tendance à penser aux tests automatisés comme le seul type de test. En réalité, même un Debug.Print instructions Debug.Print peut être un test décent pour déterminer si la fonctionnalité rest cohérente.

    Si vous devez refactoriser et que vous ne pouvez pas effectuer de tests automatisés, vous devez effectuer une sorte de test, que ce soit l’impression de texte, un script d’interface utilisateur, etc. Tout test efficace vaut mieux qu’aucun test.

    La réponse est que vous ne le faites pas. Ce n’est pas parce que le code fonctionne que ça marchera toujours. Le code incorrect est un code incorrect.

    Nous refactorisons pour rendre le code plus lisible, plus facile à maintenir et pour assurer la fiabilité.

    Tant que vous conservez la logique de l’ancien code, cela devrait toujours fonctionner, en plus d’avoir l’avantage d’être globalement meilleur.

    J’essaie toujours de laisser le code mieux que je l’ai trouvé.

    Décidez en fonction du “bénéfice final” au lieu d’écouter votre “voix intérieure”. Si ce code est réutilisé très souvent, il peut être judicieux de le restructurer. Cependant, j’essaie généralement d’ignorer ma haine pour “odeur de code” et de me concentrer sur le rapport bénéfice / perte de temps du refactoring en question.

    Je ne dirai pas que je souffre de cette affliction mais … je vous entends frère!

    Je pense que nous devons laisser le code dans un meilleur état que lorsque nous l’avons quitté. Donc, votre poursuite est noble. Appelons cela «refactoritis».

    Je suppose que nous sums tous d’accord sur les avantages de la refactorisation et que cela dépend en quelque sorte de la nécessité du code … Au coeur de la question alors …

    Une des manières dont je me retiens, c’est que j’essaie de me sentir en sécurité en sachant que c’est mûr pour la réparation. Et je sais comment le réparer. Et plutôt que de me retenir complètement, je fais juste une étape, comme «Extraire la méthode». Et laissez le prochain «tour» de fixation pour plus tard (peut-être devrions-nous appeler cela «deuxième aide» ou «dessert» si vous êtes sûr que c’est la dernière étape). Ensuite, collez un gros TODO dessus pour pouvoir le retrouver. Et clarifier ce qu’il rest à faire.

    Merci pour la question intéressante. Je me demande si nous avons besoin d’un groupe «Refactorors Anonymous» où nous pouvons nous asseoir en cercle et partager nos problèmes et nos histoires de guerre.

    Je ne me retiens généralement pas. Si je trouve un mauvais morceau dans le code sur lequel je travaille, je le corrige directement.

    Je travaille sur un logiciel qui est maintenu depuis environ 10 ans et qui devra travailler au moins dix ans. Dans une telle situation, plus une odeur de code rest dans le code, plus les autres développeurs et moi-même allons la trouver et perdre du temps à essayer de trouver ou d’inventer des solutions de contournement. Cela coûte plus cher que de simplement faire le travail maintenant.

    Une exception concerne les gros problèmes de conception qui nécessitent des jours de refactorisation. Si ceux-ci ne font pas obstacle de manière significative à mon projet actuel, je l’ajoute à notre liste de tâches de maintenance pour travailler sur le refactoring en tant que tâche planifiée dans un avenir proche.

    Martin Fowler aborde la question:

    Son essence est d’appliquer une série de petites transformations préservant le comportement, chacune d’entre elles «trop petite pour en valoir la peine». Cependant, l’effet cumulatif de chacune de ces transformations est assez significatif. En les faisant par petites étapes, vous réduisez le risque d’introduire des erreurs. Vous évitez également que le système ne soit brisé pendant que vous effectuez la restructuration, ce qui vous permet de restructurer progressivement un système sur une période prolongée. – Martin Fowler

    Donc, quand vous changez de gros morceaux de code à la fois, je n’appellerais pas vraiment ça un véritable refactoring. Plus comme réécrire totalement des segments de code particuliers. Les changements doivent être progressifs et “presque pas la peine de faire”. Après le temps, vous remarquerez leur effet cumulatif.

    J’ai entendu une définition du “code hérité” qui est du code sans tests automatisés. Si vous travaillez sur du code sans tests, je vous suggérerais d’écrire des tests avant d’apporter des modifications significatives. Si vous avez des tests en place que vous pouvez refactoriser sans crainte de casser ce code terrible parce que vous avez vos tests pour vous assurer que tout est encore fonctionnel.

    La réponse la mieux notée qui vous invite à aller de l’avant et à refondre est utile dans de nombreux cas, mais elle est quelque peu simpliste. (Je ferais probablement des commentaires à ce sujet, mais je n’ai pas le privilège de le faire – j’espère que cela fonctionnera aussi de façon autonome.)

    Si vous travaillez avec un grand système (hérité) en développement depuis des années, il y a toujours trop de choses à restructurer en même temps (sauf si vous avez été exceptionnellement rigoureux pendant toutes ces années, ce que je ne crois pas :). Donc, vous ne pouvez tout simplement pas obtenir toutes les tangentielles que vous souhaitez; c’est une réalité que vous devez accepter. Sinon, vous perdriez toujours des jours à tout nettoyer, lorsque le changement d’origine (correction ou amélioration) aurait pu être fait en moins de temps (tests et quelques remaniements inclus!).

    Donc, en général, vous devez tracer une ligne quelque part; refactor uniquement le code qui concerne directement la tâche en cours, et seulement si cela ne prend pas un temps disproportionné .

    En ce qui concerne les modifications majeures de l’architecture – que vous ne pouvez certainement pas éviter lorsque vous traitez les grands codes de base susmentionnés. Vous devrez sélectionner ceux qui sont jugés les plus critiques et les hiérarchiser suffisamment dans votre processus pour qu’ils soient réellement réalisés, même si ces modifications n’ajoutent pas de valeur externe (par exemple, seule la valeur immédiate pour les développeurs). code plus gérable). (Maintenant, si cela n’était pas possible – si les décideurs ne sont pas assez intelligents pour voir qu’il est nécessaire d’utiliser du temps sur de telles améliorations, eh bien, votre base de code est tout simplement condamnée à long terme. :))

    Si vous n’avez aucune contrainte en matière de développement de logiciels commerciaux, votre kilométrage peut varier. 😉

    Au fait, bonne question – moi aussi, je me demande où tracer la ligne assez souvent.

    En règle générale, je ne touche à aucun code hérité à moins que l’on me demande de le développer de manière approfondie. Si le code est tellement mauvais que l’ajout d’une fonctionnalité se répercute dans les bogues partout, vous avez une vraie raison de le modifier.

    En outre, il existe un risque très réel que vous introduisiez des bogues corrigés dans le désordre hérité. Cela vous rendra pas professionnel.

    La seule bonne façon, IMO, est de refactoriser lentement l’ancien code en dehors de la production. Ensuite, lorsque vous pensez que la fonctionnalité est la même, parcourez les anciens bogues résolus et assurez-vous qu’aucun ancien bogue ne réapparaisse.

    Déployez-le dans QA et vantez-le à la direction à ce sujet, de préférence avec de nouvelles fonctionnalités intéressantes. Si le nouveau code fonctionne beaucoup plus rapidement, c’est un argument de vente certain.

    En général, vous rencontrerez deux problèmes: un manque de temps total et une gestion qui n’a pas le temps d’atsortingbuer le temps nécessaire à la refactorisation. Si aucun de ces problèmes ne vous pose problème, considérez-vous très chanceux.

    Edit: Joel Spolsky a la meilleure réponse , je crois.

    Question parfaitement valide.

    J’ai l’habitude de me retrouver à refactoriser le code automatiquement lorsque je l’ai rencontré. Quand j’ai environ 5 minutes dans l’opération (vérifier les choses, etc.), j’ai soudainement l’impression que ce que je fais va prendre plus de temps que prévu. À ce stade, je me demande si cela vaut la peine de descendre dans ce trou de lapin? Pour être honnête, c’est parfois le cas, mais la plupart du temps ce n’est pas le cas, et après un certain temps, vous réalisez que vous voulez réécrire tout le système, juste au moment où vous y êtes!

    Cela m’a amené à me demander continuellement: est-ce que le code que j’écris va maintenant m’aider à accomplir cette tâche dans un délai raisonnable? Est-ce que je «gaspille» le temps de l’entreprise en procédant à cette refactorisation alors qu’il y a des éléments en suspens qui sont beaucoup plus élevés sur la liste de priorités?

    J’ai vu des programmeurs qui ne réalisent même pas qu’ils le font. Ils travailleront pendant des jours pour obtenir le code dans un état où ils peuvent désormais commencer à implémenter de nouvelles fonctionnalités ou à corriger des bogues. Ils ne peuvent pas faire la distinction entre refactoring et passer du temps sur le problème assigné.

    Ce que je fais est juste de commenter ce que je veux faire plus tard, et de travailler simplement avec le système existant, peu importe à quel point il est moche! Cela me permet de briser moins de choses et de passer à travers ma liste de choses à faire beaucoup plus rapidement. (Nous ne faisons pas encore TDD)

    Une fois que vous avez trouvé le temps, revenez et refactorez.

    Je suis d’accord, c’est tentant mais si vous vous concentrez dessus, vous ne pourrez peut-être pas accomplir votre véritable travail!

    Deux suggestions:

    1. Marquez le code pour pouvoir y revenir plus tard pour le nettoyer (utilisez le commentaire TODO ou quelque chose de similaire)
    2. Ajoutez un bogue à votre système de suivi des bogues en indiquant un code malodorant. Vous pouvez peut-être alors avoir le temps prévu pour en réparer un groupe.

    Avez-vous l’habitude de refactoriser le code écrit par d’autres, ou le code que vous avez écrit il y a six mois alors que vous étiez dans un autre type de «zone»? Si votre réponse est la dernière, c’est très bien que vous ne peigniez pas et que vous ne faites pas de sculptures pour gagner votre vie… vous seriez arrêté pour avoir pénétré par effraction dans les maisons pour «finir» vos œuvres longtemps après leur achat.

    Je suis un grand fan de Doxygen , ce qui me permet de coller un simple:

     /** * Function to rule the world * @todo make this actually work */ 

    Ensuite, je dis à Doxygen de générer des listes de tâches basées sur mes commentaires. Cela aide à garantir que les fonctions qui sentent les pieds ne sont pas oubliées.

    Je repense aux choses que j’ai faites il y a deux mois et je dis “Ick, à quoi je pensais … Je devrais le faire de cette façon …”, ce qui fait que jamais un produit consommable ne sort par la porte.

    Si le code de ses autres personnes que vous recomposez systématiquement, essayez de vous demander si cela améliore réellement le code. Si vous êtes en mesure de re-factoriser une fonction, il est évident que vous comprenez la fonction .. donc à moins que vous obteniez certains avantages (plus que sortingviaux) de la réécrire, giflez un commentaire au-dessus et revenez plus tard.

    Une fois que vous êtes habitué à tout le code et que vous avez réfléchi à la manière de rendre les choses plus cohérentes… puis plongez dans le jeu. Sinon, vous finirez par ré-factoriser votre refactorisation précédente (quel cercle vicieux a pris des années perfectionner!).

    Donc, à moins que ce soit en ligne, ou utilisé partout ailleurs, essayez d’éviter de jouer avec, sauf si vous en avez vraiment besoin. Plus tard, revenez et frappez votre liste TODO d’un seul coup.

    “Refactoring Code existant qui fonctionne” – Ceci est évidemment une opinion minoritaire ici, mais c’est une perte de temps totale. Il est bon que vous essayiez de vous retenir.

    Si vous pensez que le code est si mauvais, trouvez un bogue non sortingvial avant de le refactoriser . Ajoutez ensuite un test automatisé pour ce bogue. Seulement alors, permettez-vous de refactoriser.

    L’attitude selon laquelle, après avoir refait le code de travail “J’ai mieux défini le code”, c’est souvent l’ hubris du programmeur. Dans de nombreux cas, vous ne savez pas vraiment que vous avez laissé le code mieux, vous croyez que vous l’avez fait. Si vous n’avez même pas corrigé un bug ou ajouté des fonctionnalités, pourquoi prendre le risque de vous tromper?

    La règle suivante de Boy Scout applique très bien au (mauvais) code et à la conception:
    Laissez le camping plus propre que vous l’avez trouvé!

    Tout d’abord, vous devez comprendre et accepter que la seule raison pour laquelle le code est important est de savoir s’il doit être lu et / ou modifié. Il suffit de le lire et / ou de le modifier si vous recherchez un bogue ou des fonctionnalités supplémentaires. Donc, ne touchez le code que si vous corrigez ou améliorez les bogues. Donc, si vous rencontrez des instructions if-then-else massivement nestedes, ou une variable ou une méthode mal nommée, laissez-la seule à moins que vous ayez besoin de comprendre ou de modifier ce code pour terminer la tâche sur laquelle vous travaillez. Si vous devez changer le code, refactorez-le suffisamment pour rendre le code compréhensible et le changement plus facile à faire, mais pas plus.

    Whoa, beaucoup d’entre vous sont un peu OCD si vous savez ce que je veux dire. Je pense que pour CYA, si vous ne travaillez que sur la base du code à court terme, ne vous souciez pas de la refactorisation. Mais si vous possédez ce code à long terme, alors allez-y! Mais vous devez sauvegarder votre nouveau code avec des tests unitaires et append des tests unitaires au code hérité que vous allez affecter si les tests n’existent pas déjà.

    J’avais l’habitude de refactoriser le code chaque fois que je rencontrais quelque chose que je n’aimais pas, mais plus maintenant. J’ai trouvé par expérience que ça ne valait tout simplement pas la peine. Il y a de meilleures choses sur lesquelles vous pourriez passer votre temps.

    La principale raison pour laquelle je ne refactore plus est le risque d’introduire un nouveau bogue. Plus l’ancien code est mauvais, plus vous risquez de vous tromper. Par exemple, l’ancien code peut avoir un effet secondaire involontaire. Cela pourrait être considéré comme une erreur de codage et vous le supprimeriez dans votre réécriture. Cependant, le code qui appelle dans votre fonction réécrite peut être basé sur cet effet secondaire.

    Je travaille sur beaucoup de code hérité vraiment moche, pour lequel il n’ya pas de spécifications. Le code ne fait que ce qu’il fait, et personne ne peut vraiment dire pourquoi. Ils savent juste que cela semble fonctionner. Aussi horrible soit-il, ce code fait son travail, il est donc préférable de le laisser tranquille.

    Cela dit, je pense que le mauvais code devrait être réécrit. Je pense simplement que les programmeurs ne devraient pas faire ce genre de choses par eux-mêmes. Il doit être traité comme un projet, avec une scope, des échéanciers, des plans de test, etc. définis, tout comme l’implémentation d’une nouvelle fonctionnalité.

    Arrêtez de vous soucier.

    Je veux dire, cmon, nous sums tous des ingénieurs ici, et les ingénieurs adorent sortir LE CODE PARFAIT LE PLUS ABSOLU. Et nous allons continuer à essayer. Et nous ne serons pas heureux de notre travail, à moins que nous sachions que tout ce que nous avons touché est TMAPCE. D’ailleurs, on sait juste que cette voie est tellement meilleure …

    Donc, le seul moyen réel de ne pas essayer de refaire un petit peu à la fois est de ne pas se soucier de votre travail.
    (Comme je le note, cela m’est arrivé une fois. C’était très sortingste… jusqu’à ce que je change d’emploi.)

    Je priorise.

    Lorsque j’ai quelques cycles disponibles (ha),

    J’essaie généralement de recomposer le code qui:

    • serait sensible pour quiconque de travailler sur,
    • aurait besoin d’être travaillé , ce qui signifie qu’il est touché régulièrement
    • qui est en mauvais état pour des raisons de performance ou de documentation, dans cet ordre.

    Là où la ré-factorisation ajoute de la valeur, je le fais. Tenter de mettre en correspondance les nouvelles fonctionnalités futures et les rendre plus réalistes facilite le travail sous le capot.

    Raisons de ne rien toucher, on a envie de toucher?

    • Souvent, l’ancien code a été renforcé pour les entrées / sorties, aussi maladroit que cela puisse paraître, cela fonctionne.
    • Si ce n’est pas cassé, ne le faites pas comme ça.
    • Il devrait toujours y avoir quelque chose de nouveau et de soigné sur lequel travailler, et coder la bonne manière. Les choses anciennes seront ré-établies au fil du temps au fur et à mesure de leur extension.

    Je voulais juste append que si le projet en question est soutenu par des tests unitaires, le refactoring aura considérablement moins de risques et aura un coût de productivité moindre (parce que vous allez savoir exactement quand faux).

    Il y a toujours un coût / bénéfice à faire, mais en principe, je serais d’accord avec Longhorn que vous ne devriez pas vous arrêter.

    J’essaie de refactoriser au coup par coup. En d’autres termes, lorsque j’ai besoin d’append de nouvelles fonctionnalités, je les restructure de manière à faciliter l’ajout de cette fonctionnalité en premier. Cela rend le refactoring plus lent, mais cela facilite l’ajout de modifications.

    Avant de commencer le refactoring, vous devez avoir des tests unitaires complets , et personne n’aime écrire des tests unitaires 🙂

    Si je travaille sur une tâche, je ne laisse rien y faire – il est trop facile de s’emballer. Tout ce qui semble avoir besoin de travail est probable, mais peut-être pas tout de suite – faites une note / ajoutez-la à votre outil de suivi des bogues.

    Je crée un problème dans notre outil de suivi des problèmes (ou vous pouvez utiliser une note de post-it) pour corriger ce code, puis vous oubliez le problème.

    Cela permet à tout le monde de savoir qu’il existe un code qui pourrait causer des problèmes, mais qui me permet également de continuer à travailler sans trop de distractions.

    J’ai du mal à digérer le code malodorant, mais je retarde généralement le nettoyage et le refactoring jusqu’à ce que je doive réellement apporter des modifications ou si j’ai un temps d’arrêt.

    Jusque-là, je noterai simplement les idées de changements dans les commentaires.

    J’ai lu la vieille histoire d’oncle Remus sur le Tarbaby . Ou je fais ressortir un de ces signes dans les magasins où il est dit “Vous le touchez, vous en êtes propriétaire”.

    J’en ai souffert dans le passé, et je fais de la microgestion de ma «mauvaise» bonne habitude.

    Fondamentalement, je fais ce qui suit:

    • J’ai deux instances distinctes de notre arbre source à un moment donné.
    • Si j’ai un refactor massif qui n’est pas vraiment prioritaire, je le pirate dans la deuxième twig.
    • Je continuerai alors à travailler dans ma première twig, et je maintiendrai la deuxième twig, en faisant de petits tests en y réfléchissant pour m’assurer que je n’ai rien cassé.
    • Je le vérifierai ensuite une fois que je serai confiant et que je pourrai ralentir notre cycle de production (c.-à-d. Ne pas enregistrer juste avant le jalon!).

    En toute honnêteté, ce remaniement «majeur» ne se produit jamais vraiment – généralement, si je dois le faire, cela a déjà été fait de toute façon. Pourtant, les quelques fois que j’ai fait cela, c’est utile. Idéal si vous pouvez faire une twig pour cela, et continuez à intégrer les changements.

    Pour les petites choses, je garde souvent ces changements en local dans ma twig principale et je les utilise localement pendant un certain temps. Au moment où je travaille sur ces fichiers, je vérifie tout le changement. Notre processus inclut actuellement un système de vérification des contacts avant les check-ins, de sorte que ce sont généralement des éléments qui permettent une évaluation par les pairs.

    En tout cas Pourrait être plus un vidage du cerveau que vous avez pris soin, mais j’espère que cela aide.

    Souvent, le refactoring peut réduire ou éliminer le besoin de faire le vrai travail assigné. Si vous corrigez un bug, le refactoring est un bon endroit, en supposant que des tests soient en place. Dans une moindre mesure, l’ajout de nouvelles fonctionnalités peut être réalisé avec une simple refactorisation du code pour le rendre un peu plus propre ou plus efficace, puis vous constatez que la fonctionnalité est déjà présente et que l’utilisateur peut l’activer.

    La réponse courte, cependant, est que vous ne refactorez pas le code qui fonctionne à moins qu’il y ait un besoin pressant. Une bonne liste de contrôle est:

    • Est-ce que je vais travailler avec ce code quotidiennement pendant une période prolongée ET suis-je la SEULE personne qui le sera?
    • Est-ce que le code est si mauvais qu’il ne peut pas être compris correctement? D’habitude je trouve que je souffre juste d’un syndrome de non-inventé ici (la chose où la solution parfaite de quelqu’un d’autre n’est pas ce que j’aurais fait alors je veux le refaire sans réel gain sauf la fierté).
    • L’architecture n’est-elle pas capable de supporter ce nouveau travail (fonctionnalité, correction de bogue, etc.).
    • Est-ce que le code est tellement incestueux que changer quelques petites choses a un effet d’entraînement considérable sur les bogues ultérieurs?

    Si vous répondez oui à l’une de ces questions, il serait peut-être temps de refactoriser.

    C’est facile vraiment.

    Tout ce que nous modifions, nous devons élaborer un plan de test écrit qui soit compris et examiné par un ingénieur QA. Ne pas modifier les choses de manière drastique simplifie réellement la diffusion du code. Par conséquent, à moins que vous ne touchiez ce code pour une autre raison, tout type de refactoring est quasiment un non-non.

    EDIT: Même si votre entreprise ne dispose pas d’une telle politique, n’oubliez pas que vous devez tester ce que vous modifiez . Estimez le temps nécessaire pour tester vos modifications.

    Répondre à la question “Quelle est la valeur ajoutée?” aide aussi.

    Le vieil adage sur les hypothèses vient à l’esprit chaque fois que la tentation est de réparer le code qui n’est pas rompu. Travailler sur du code laid qui a beaucoup d’utilisateurs peut affecter la productivité de nombreuses personnes, surtout si un correctif fonctionne dans un environnement, mais pas dans un autre. Quiconque a travaillé sur des sites intranet sait bien comment la conversion de JScript en JavaScript peut avoir de nombreux effets secondaires imprévus.

    Je souffre aussi de refactoritis et je le traite en le dirigeant vers le code que je possède et que d’autres personnes ont maintenu ou que je dois maintenir. Et par la même occasion, je veux dire “vérifier le contrôle de la version toutes les heures sur l’heure pour voir si quelqu’un a touché mon code et, s’il le faisait, faire immédiatement une différence”.