J'ai changé une signature de méthode et j'ai maintenant plus de 25 000 erreurs. Et maintenant?

166

J'ai récemment commencé un nouveau travail où je travaille sur une très grosse application (15M loc). Dans mon travail précédent, nous avions une application similaire, mais (pour le meilleur ou pour le pire), nous utilisions OSGi, ce qui signifiait que l'application était divisée en une multitude de microservices pouvant être modifiés, compilés et déployés de manière indépendante. La nouvelle application est juste une base de code volumineuse, avec peut-être quelques fichiers .dll.

J'ai donc besoin de changer l'interface de cette classe, car c'est ce que mon patron m'a demandé de faire. Au départ, ils l’ont écrit avec des hypothèses qui n’ont pas été généralisées, et ils évitent le problème de la refactorisation depuis un certain temps, car il est si étroitement couplé. J'ai changé l'interface et maintenant, il y a plus de 25 000 erreurs. Certaines erreurs sont dans des classes avec des noms de sondage importants comme « XYZPriceCalculator » qui reaaallyne devrait pas casser. Mais je ne peux pas démarrer l'application pour vérifier si elle fonctionne tant que toutes les erreurs ne sont pas résolues. Et bon nombre des tests unitaires font directement référence à cette interface ou sont couplés à des classes de base faisant référence à cette interface. La simple réparation de celles-ci est donc une tâche très lourde en soi. De plus, je ne sais pas vraiment comment toutes ces pièces vont bien ensemble, donc même si je pouvais le faire commencer, je ne sais pas vraiment à quoi ça ressemblerait si les choses étaient cassées.

Je n'ai jamais vraiment été confronté à un problème de ce type lors de mon dernier emploi. Que fais-je?

utilisateur788497
la source
7
Vous allez avoir besoin de nous donner plus d'informations sur les types d'erreurs et sur ce qu'est "cette classe", nous ne sommes pas des lecteurs d'esprit.
Whatsisname
137
"plus de 25 000 erreurs", ces chiffres indiqués dans le VS sont souvent faux. Il y a quelques erreurs, mais avec la construction d'une DLL souvent utilisée cassée, les autres versions échouent aussi, ce qui donne lieu à des nombres d'erreur astronomiques. Je commence toujours à corriger avec le premier message d'erreur trouvé dans la sortie de la construction, pas dans la liste des erreurs.
Bernhard Hiller
13
J'aimerais voir les signatures avant et après la méthode que vous avez modifiée. BTW - 25k erreurs ne semble pas vraiment trop à traiter. Fastidieux oui, intimidant, oui, ingérable, non.
Jmoreno
46
Une interface publique est un contrat. Ne rompez pas de tels contrats - créez-en de nouveaux.
Matthew Lu
6
25000 erreurs !? Houston, nous avons un problème. Annulez définitivement le changement et parlez à votre responsable, expliquez-lui que vous devrez peut-être créer une nouvelle interface.
Code Whisperer

Réponses:

349

25000 erreurs signifient essentiellement "ne touchez pas cela". Changer le retour. Créez une nouvelle classe dotée de l'interface souhaitée et déplacez lentement les utilisateurs de la classe vers la nouvelle. Selon la langue, vous pouvez marquer l'ancienne classe comme obsolète, ce qui peut provoquer toutes sortes d'avertissements du compilateur, mais ne casse pas réellement votre construction.

Malheureusement, ces choses se produisent dans les anciennes bases de code. Vous ne pouvez rien faire à ce sujet, si ce n’est que lentement, vous améliorez la situation. Lorsque vous créez les nouvelles classes, assurez-vous de les tester correctement et de les créer à l'aide des principes SOLID afin qu'elles soient plus faciles à modifier à l'avenir.

Stephen
la source
79
Il ne doit pas nécessairement s'agir d'une nouvelle classe . Selon la langue et les circonstances, il peut s'agir d'une fonction, d'une interface, d'un trait, etc.
Jan Hudec
33
Cela aiderait également si l'ancienne interface pouvait être remplacée par un wrapper de compatibilité autour de la nouvelle.
Jan Hudec
79
Parfois, 25 000 erreurs signifient en fait que nous n’avons jamais osé toucher à cela, mais maintenant qu’un nouveau venu est arrivé, confions-lui cette tâche de nettoyage, d’austérité et d’étable.
mouviciel
13
@theDmi: Je suis d'accord, 1 changement et 25k erreurs signifient très probablement 2,5k changements au maximum, et je ne serais pas surpris de constater qu'il se situait autour de 250. Beaucoup de travail dans les deux sens, mais faisable.
Jmoreno
33
Ces erreurs 25000 pourraient toutes être corrigées par un seul changement. Si je casse la classe de base d'une énorme hiérarchie, chacune des classes dérivées générera des erreurs sur une base non valide, chaque utilisation de ces classes générera des erreurs sur la classe inexistante, etc. Imaginons qu'il s'agisse de "getName", et j'ai ajouté une dispute. Le remplacement dans la classe d’implémentation "HasAName" ne fonctionne plus (erreur), et tout ce qui en hérite en génère maintenant des erreurs (toutes les 1000 classes), ainsi que chaque fois que je crée une instance d’elles (24x par classe sur moyenne). Le correctif est ... une ligne.
Yakk
80

Diviser et conquérir avec des refactorings

Découper le changement que vous devez faire en plusieurs étapes peut souvent s'avérer utile car vous pouvez alors exécuter la plupart des étapes plus petites d'une manière qui ne casse pas le logiciel. Les outils de refactoring aident beaucoup pour de telles tâches.

Diviser

Tout d’abord, identifiez les changements les plus minimes possibles (en termes de changements logiques et non en termes de LoC modifiée) qui se résument au changement que vous souhaitez obtenir. Essayez surtout d’isoler les étapes qui sont de simples refactorisations et qui peuvent être effectuées à l’aide d’outils.

Conquérir

Pour les cas compliqués comme le vôtre, il peut être judicieux de procéder à une refactorisation à la fois, puis de laisser le problème reposer, de sorte que tous les systèmes d'intégration continue puissent vérifier le changement. Cela valide les étapes que vous faites.

Pour effectuer une refactorisation particulière, vous avez absolument besoin de l’outil de support pour les cas où vous avez 25 000 sites d’appel de la méthode à modifier. Peut - être que chercher et remplacer fonctionne aussi, mais pour un cas aussi critique, cela me ferait peur.

Exemple

En C #, par exemple, il est possible d'utiliser Resharper pour modifier la signature d'une méthode. Si le changement est assez simple, par exemple en ajoutant un nouveau paramètre, vous pouvez alors spécifier la valeur à utiliser sur les sites d’appel qui autrement auraient une erreur de compilation.

Vous vous retrouvez immédiatement sur une base de code sans erreur et pouvez exécuter tous les tests unitaires, ce qui passera, car il s’agissait uniquement d’un refactoring.

Une fois que la signature de la méthode semble bonne, vous pouvez remplacer les valeurs ajoutées par Resharper en tant qu'arguments au paramètre nouvellement introduit. Ce n'est plus un refactoring , mais vous avez une base de code sans erreur et pouvez exécuter les tests après chaque ligne modifiée.

Parfois ça ne marche pas

C'est une approche très utile dans des cas comme le vôtre, où vous avez beaucoup de sites d'appels. Et vous avez maintenant un logiciel qui fonctionne, alors il est peut-être possible d'effectuer de petites étapes de refactoring pour modifier légèrement la signature, puis une autre.

Malheureusement, cela ne fonctionnera pas si le changement de signature est trop complexe et ne peut pas être décomposé en changements plus petits. Mais c'est rare. diviser le problème en petits problèmes montre généralement qu'il est possible.

theDmi
la source
12
Cette. Refacturez si vous le pouvez (en toute sécurité), sinon vous aurez besoin d'une migration appropriée.
Sleske
3
Et pour l'amour de n'importe quoi, veuillez utiliser les outils de refactoring intégrés de votre IDE, plutôt que de simplement changer la signature de la méthode (par exemple) localement et ensuite contourner le problème en corrigeant les erreurs qui en découlent.
KlaymenDK
36

Clarifiez votre tâche avec votre patron pour l’aider à comprendre le problème et vos besoins en tant que développeur logiciel professionnel.

Si vous faites partie d'une équipe, recherchez le développeur principal et demandez-lui conseil.

Bonne chance.

Mmehl
la source
15
C’est le premier pas que je ferais: "Je suis un junior et mon patron m’a dit de faire quelque chose et maintenant je reçois un très gros message d’erreur, mais mon patron ne m’a pas dit à ce sujet." Étape 1: "Hé patron, j'ai fait la chose, mais j'ai 25 000 erreurs maintenant. Est-ce censé se produire, ou ..."
Pimgd
28

N'y touche pas. Ne commets rien.

Au lieu de cela, asseyez-vous sur votre chaise et criez "Heeeeelp !!!!!" aussi fort que vous pouvez.

Eh bien, pas exactement comme ça, mais demandez conseil à l’un de vos principaux collègues. Si vous avez 25 000 erreurs, vous ne corrigez pas les erreurs, vous corrigez la cause des erreurs. Et un collègue plus expérimenté devrait être en mesure de vous conseiller comment faire le changement votre patron veut sans que les 25.000 erreurs impliquées. Il y a différentes façons de le faire, mais ce qui est bon dépend de votre situation.

Et c’est peut-être le patron qui a demandé à vos collègues de faire le même changement, et ils ont dit "non". Parce qu'ils savaient ce qui allait arriver. C'est pourquoi on vous a donné le travail.

gnasher729
la source
2
C'est certainement une chose importante à garder à l'esprit. Si un nouvel employé est vraiment ambitieux dans une large mesure, il pourrait être suffisamment travailleur (crédule) pour s’acquitter de la très grosse tâche qui finit par obtenir un avantage mémoire de 5 octets ou être un peu plus logique pour le recodage et la maintenance ultérieurs.
Le grand canard
@TheGreatDuck: Dites-moi que vous n'avez jamais vu une interface utilisée partout et mal partout. J'ai bien sûr.
Josué
@ Josué, ce n'est même pas pertinent pour ce que je viens de dire. Il y a eu des moments où corriger un bogue mineur n'est pas toujours l'idée la plus intelligente qui soit, cela signifie réécrire plus de 10 000 lignes de code. Malheureusement, un nouvel employé pourrait être assez crédule pour tirer 10 nuits blanches en dehors du travail en réécrivant ce code pour impressionner son patron et le faire. Bien sûr, c'est une bonne chose à faire, mais parfois, c'est assez. Vous devez juste accepter l'existence du bogue.
Le grand canard
1
@Joshua btw, je n'ai rejoint cette communauté que parce que cette question a été posée dans le fil de mes questions populaires. Je n'ai aucune expérience avec la conception à grande échelle comme celle-ci. Je viens d'accord avec le point de vue de cette personne.
Le grand canard
22

Les API enracinées ne peuvent pas être simplement modifiées. Si vous avez vraiment besoin de les changer, annotez-les et / ou documentez-les comme obsolètes (quel que soit le moyen utilisé par le langage) et indiquez quelle API doit être utilisée à la place. L'ancienne API peut alors être supprimée progressivement ... peut-être très lentement, en fonction de votre budget temps pour la refactorisation.

Kevin Krumwiede
la source
10

Évaluer

Déterminez si cette modification est nécessaire ou si vous pouvez ajouter une nouvelle méthode et déconseiller l'autre.

Vers l'avant

Si le changement est nécessaire; alors un plan de migration est nécessaire.

La première étape consiste à introduire la nouvelle méthode et à faire en sorte que l'ancienne méthode répartisse ses arguments de manière à pouvoir appeler la nouvelle. Cela peut nécessiter de coder en dur quelques choses; C'est très bien.

Ceci est un point de validation: vérifiez que tous les tests réussissent, validez, envoyez.

Émigrer

Le travail occupé migre tous les appelants de l'ancienne méthode vers la nouvelle. Heureusement, cela peut être fait progressivement grâce au transitaire.

Donc vas-y; n'hésitez pas à utiliser des outils pour vous aider ( sedétant le plus fondamental, il y en a d'autres).

Marquez l'ancienne méthode comme étant déconseillée (avec un soupçon de passage à la nouvelle méthode); cela vous aidera à savoir si vous avez oublié quelque chose et si un collègue introduit un appel à l'ancienne méthode pendant que vous travaillez sur cette question.

Ceci est un point de validation (ou peut-être plusieurs), vérifiez que tous les tests réussissent, validez, envoyez.

Retirer

Après un certain temps (peut-être aussi peu qu'une journée), supprimez simplement l'ancienne méthode.

Matthieu M.
la source
4
sedest probablement une mauvaise idée ... Quelque chose qui "comprend" le langage et ne fera pas de changements radicaux inattendus est préférable.
wizzwizz4
1
@ wizzwizz4: Malheureusement, j'ai trouvé très peu d'outils qui comprennent suffisamment bien le langage pour le C ++. la plupart des outils semblent prendre en compte le changement de nom et c'est tout. Certes, le fait de renommer uniquement les appels de méthode exacts (et non les surcharges ni les appels de méthode non liés mais portant le même nom) est déjà impressionnant, mais il est insuffisant pour des tâches plus compliquées. Au minimum, vous aurez besoin de la capacité de (1) mélanger les arguments, (2) d'appliquer une transformation à un argument donné (appel .c_str()par exemple) et d'introduire de nouveaux arguments. sedun peu fonctionne, le compilateur attrape ses problèmes par la suite.
Matthieu M.
1
sed(ou ed) peut convenir à ce genre de chose - tant que vous examinez correctement le diff avant de vous engager.
Toby Speight
C'est le TCRR du HTML, ouais? :)
Daniel Springer
8

Si votre modification de la signature de méthode est simplement un changement de nom, la solution simple consiste à utiliser des outils pour automatiser le changement dans les 25 000 classes faisant référence à la méthode en question.

Je suppose que vous avez simplement édité le code manuellement, ce qui a donné lieu à toutes les erreurs. Je suppose également que vous connaissez bien Java (voir votre référence à OSGi). Par exemple, dans Eclipse (je ne sais pas quel environnement de programmation vous utilisez, mais d'autres environnements ont des outils de refactoring similaires), vous pouvez utiliser "Refactoring -> Renommer". pour mettre à jour toutes les références à la méthode, ce qui ne devrait pas vous laisser d’erreur.

Si vous apportez des modifications à la signature de la méthode autres que le simple changement de nom (modification du nombre ou des types de paramètres), vous pouvez utiliser "Refactoring -> Modifier la signature de la méthode". Cependant, il est fort probable que vous deviez faire plus attention, comme le suggèrent les autres réponses. En outre, quel que soit le type de modification, il peut rester une tâche ardue de valider toutes ces modifications dans une base de code occupée.

MikkelRJ
la source
2
OP a spécifiquement déclaré qu'OSGi occupait son poste précédent. Ce n'est donc pas vraiment pertinent ici.
un CVn
3
@ MichaelKjörling Si l'OP était un programmeur Java dans son travail précédent, il y a une chance, bien au contraire, qu'il soit aussi un programmeur Java.
Rich
@ MichaelKjörling Je souhaitais faire une recommandation concrète en utilisant Eclipse pour le refactoring car OP est familier avec Java. Il est moins important de savoir si OP utilise réellement Java pour le projet actuel, mais je devrais préciser ma réponse. Merci.
MikkelRJ
6

Voici ma contribution.

J'ai récemment commencé un nouveau travail où je travaille sur une très grosse application (15 millions de lignes de code).

Vous n'êtes probablement pas familier avec le projet et ses "fonctionnalités". Avant de saisir une seule ligne de code, il est important de connaître le projet. Alors annulez vos modifications et commencez par analyser le code . (Au moins celui qui est affecté)

Comprendre la solution existante vous donne une meilleure idée de l'endroit où vous vous dirigez. Contextualiser la solution et son importance.

Comme @Greg l'a souligné, vous devriez pouvoir tester le code existant pour disposer d'une référence valide à comparer (tests de régression). Votre solution doit être capable de générer les mêmes résultats que la solution existante. À ce stade, ne vous souciez pas de savoir si les résultats sont corrects . Le premier objectif est de refactoriser, pas de corriger les bugs. Si la solution existante indique "2 + 2 = 42", votre solution le devrait également. S'il ne jette pas d'exceptions, le vôtre ne le devrait pas non plus. S'il renvoie des valeurs NULL, le vôtre doit également renvoyer des valeurs NULL. Etc. Sinon, vous compromettez 25 000 lignes de code.

C'est pour la rétro-compatibilité.

Pourquoi? Parce qu’à l’heure actuelle, c’est votre garantie unique d’un refactor réussi.

Et beaucoup de tests unitaires référencent directement cette interface ou sont couplés à des classes de base référençant cette interface.

Un moyen de garantir la rétro-compatibilité est urgent pour vous. Voici votre premier défi. Isolez le composant pour les tests unitaires.

Gardez à l'esprit que ces 25 000 lignes de code ont été créées en supposant les résultats possibles du code existant. Si vous ne rompez pas cette partie du contrat, vous êtes à mi-chemin de la solution finale. Si vous le faites bien: que la force soit avec vous

Une fois que vous avez conçu et mis en œuvre le nouveau "contrat", remplacez l'ancien. Déconseillez ou supprimez-le.

J'ai suggéré de laisser les bogues seuls, car la refactorisation et la correction des bogues sont des tâches différentes. Si vous essayez de les faire avancer ensemble, vous pouvez échouer sur les deux. Vous pouvez penser que vous avez trouvé des bugs, cependant, ils pourraient être des "fonctionnalités". Alors laissez-les tranquilles (une minute).

25k lignes de code me semblent assez de problèmes pour me concentrer sur une seule tâche.

Une fois que votre première tâche est terminée. Exposez ces bugs / fonctionnalités à votre patron.

Enfin, comme @Stephen l’a dit:

Vous ne pouvez rien faire à ce sujet, si ce n’est que lentement, vous améliorez la situation. Lorsque vous créez les nouvelles classes, assurez-vous de les tester correctement et de les créer à l'aide des principes SOLID afin qu'elles soient plus faciles à modifier à l'avenir.

Laiv
la source
5

Essaye-le.

Tout le monde recommande comment procéder à la refactorisation, de sorte qu'il y ait peu d'impact. Mais avec autant d'erreurs, même si vous avez réussi à refactoriser avec seulement 10 lignes de code (vous le pouvez probablement), vous avez impacté 25 000 flux de code , même si vous n'avez pas eu besoin de les réécrire.

Vous devez donc vous assurer que votre suite de tests de régression réussit avec brio. Et si vous n'en avez pas, faites-en un qui le fera. Ajouter une suite complète de tests de régression à votre projet monolithique semble ennuyeux, mais constitue un bon moyen d'accroître la confiance dans les candidats à la publication, ainsi que de le publier plus rapidement si la suite est bien automatisée.

Greg
la source