C'est probablement quelque chose que tout le monde devra affronter tôt ou tard pendant le développement.
Vous avez un code existant écrit par quelqu'un d'autre et vous devez l'étendre pour qu'il fonctionne sous de nouvelles exigences.
Parfois, c'est simple, mais parfois les modules ont un couplage moyen à élevé et une cohésion moyenne à faible, donc au moment où vous commencez à toucher quoi que ce soit, tout se casse. Et vous ne sentez pas qu'il est corrigé correctement lorsque vous remettez en marche les nouveaux et anciens scénarios.
Une approche consisterait à écrire des tests, mais en réalité, dans tous les cas que j'ai vus, c'était à peu près impossible (dépendance à l'interface graphique, spécifications manquantes, threading, dépendances et hiérarchies complexes, délais, etc.).
Donc, tout revient en quelque sorte à une bonne approche de codage de cowboy. Mais je refuse de croire qu'il n'y a pas d'autre moyen systématique qui rendrait tout plus facile.
Quelqu'un connaît-il une meilleure approche ou le nom de la méthodologie qui devrait être utilisée dans de tels cas?
Tout d'abord, il devient un peu usant que tout le monde sur ce site pense que tout ce qui est écrit par quelqu'un d'autre est des ordures.
Comprendre le code est difficile, certes, certaines mauvaises pratiques de programmation le rendent plus difficile, mais, pour tout système raisonnablement complexe, la compréhension de la structure interne et des idiomes utilisés sera difficile, même si son code est bien écrit.
Les systèmes fonctionnent régulièrement depuis plus de vingt ans. Les méthodologies de programmation, les meilleures pratiques, les philosophies et les modes de conception changent tous les deux ans et les programmeurs choisissent les styles améliorés à des rythmes différents. Donc, ce qui aurait été considéré comme un état de l'art et un excellent exemple de code en 2007, semble aujourd'hui démodé et décalé. Comme exercice, je vous suggère de creuser le code que vous avez écrit il y a trois ans, je peux presque vous garantir que vous grincerez des dents.
Donc, vous devez d'abord supprimer la réponse WTF initiale. Dites-vous que le système a fonctionné assez bien et assez longtemps pour qu'il devienne votre problème, il doit donc y avoir quelque chose de bien.
Essayez de vous familiariser avec le style de codeurs d'origine, les idiomes utilisés, étudiez les bits de code les plus étranges et voyez s'ils tombent dans un modèle.
Si les modifications requises sont minimes, suivez le style de codage d'origine, de cette façon, quelqu'un récupérant le code après que vous n'ayez besoin de vous habituer qu'à un seul ensemble de particularités.
Si les modifications requises sont importantes et qu'elles sont concentrées dans quelques fonctions ou modules, profitez-en pour refactoriser ces modules et nettoyer le code.
Surtout ne refacturez pas le code de travail qui n'a rien à voir avec la demande de changement immédiate. Cela prend trop de temps, cela introduit des bogues et, par inadvertance, vous pouvez appliquer une règle métier qui a mis des années à se perfectionner. Votre patron vous détestera d'avoir été si lent à apporter de petits changements, et vos utilisateurs vous détesteront d'avoir fait planter un système qui a fonctionné pendant des années sans problème.
Fondamentalement, vous avez trois approches:
Ecrire à partir de zéro. Cela peut fonctionner pour les petites applications, mais vous ne pouvez pas l'utiliser pour une grande base de code.
Notez que pour une base de code de petite à moyenne taille, ce n'est pas le pire scénario à éviter à tout prix.
Il y a bonne raison de ne pas réécrire à partir de zéro: une ancienne base de code devrait généralement être testée, contenir des astuces qui font que le code fonctionne dans certaines circonstances, etc., et lorsque vous réécrivez tout, vous introduire un tas de bugs qui ont déjà été résolus.
Il s'agit d'un argument invalide dans les cas dont vous parlez, puisque vous avez mentionné que le code est de très faible qualité: non testé, avec des spécifications manquantes, jamais refactorisé, etc. Ne réécrivez pas à partir de zéro en revanche, la règle doit être limitée au code de haute qualité, testé par l'AQ.
tilisez l'approche de codage cowboy: changez tout, puis testez. Cela peut échouer ou réussir en fonction principalement du contexte.
Exemple 1: vous modifiez l'application critique pour l'entreprise. Vous savez que votre client serait en colère si vous cassiez quelque chose dans le code existant, et ce client est convaincu que le produit réel fonctionne non seulement bien, mais est également correctement écrit (ou que le client ne comprend tout simplement pas ce qu'est le code de spaghetti merdique vs code bien écrit).
Ici, vous ne pouvez pas utiliser le codage cowboy, car vous ne devez pas introduire de nouveaux bogues. Cela signifie que vous devez utiliser l'une des deux autres approches.
Exemple 2: votre client vous dit que son expérience avec le développeur précédent a été un désastre. Le produit ne fonctionne même pas la moitié du temps et est totalement inutilisable tel quel. Le client comprend que la modification du code source si mal écrit peut introduire encore plus de bugs.
Ici, en revanche, le codage cowboy peut être une bonne solution, si vous êtes sûr qu'il réduira le coût global, par rapport à deux autres solutions.
Commencez par refactoriser le code initial, en ajoutant des tests unitaires (et d'autres tests requis dans des cas spécifiques), en le documentant, etc., puis ajoutez vos modifications. Cela peut être une bonne approche lorsque le code n'est pas trop merdique et a une certaine valeur. Par exemple, si je sais que le code a été écrit par mon collègue plus compétent sous la pression du temps et des contraintes budgétaires, je vais certainement utiliser cette approche car:
Il y a des parties de code qui sont intelligemment faites,
Vous apprendrez beaucoup de choses grâce au code que vous refactorisez.
Même sous la pression du temps et des contraintes budgétaires faibles, un code d'un développeur plus compétent est toujours un code d'un certain niveau. Cela signifie que s'il y a une astuce pour faire fonctionner l'application dans certaines circonstances, l'homme du métier le développeur vous laissera toujours un commentaire expliquant pourquoi la ligne de code suivante a été ajoutée.
Je ne pense pas qu'il existe une solution parfaite: comparer les trois pour chaque projet ou tâche, évaluer leur coût respectif et sélectionner la meilleure en fonction des circonstances.
En général, suivez ces deux règles:
Règle 1: plus les développeurs sont compétents pour écrire le code, plus refactoring vs réécriture à partir de zéro, vous devez utiliser.
Règle 2: plus le projet est grand, plus refactoring vs réécriture à partir de zéro, vous devez utiliser.
Refactorisez une grande base de données écrite par quelqu'un de plus compétent que vous. Réécrivez à partir de zéro de petits morceaux de code ou du code écrit par des programmeurs non qualifiés.
C'est essentiellement mon quotidien. Il y a des cas où l'écriture de petits tests est tout simplement trop peu pratique pour les raisons que vous avez mentionnées. Vous devriez toujours garder de gros tests, mais couvrir toutes les bases est souvent impossible. La meilleure approche que je trouve est de spirale vers l'extérieur.
Au lieu de réécrire dès le début, obtenez une bonne compréhension des éléments. S'il y a des fonctions longues, essayez de les couper en morceaux logiques. Découplez/démêlez les éléments les plus petits, les dépendances non circulaires les moins compliquées. Démarrez une bibliothèque parallèle que vous comprenez bien et lancez la migration de petites fonctions en bits génériques.
Faites cela, et vous démêlerez lentement le système, et travaillerez vers des morceaux de code de plus en plus gros qui deviendront également plus modulaires.
Documentez comme si c'était une drogue et que vous en étiez accro. Je ne veux pas écrire un roman dans votre code, mais il est probable que le code ne soit pas trop documenté, alors faites-le style .NET ou Doxygen: ce que fait la fonction, entrée, sortie (et si besoin est quelles variables globales ou propriétés que vous utilisez ou modifiez).
Dernière astuce: définir le comportement par défaut. S'il y a des options ou des paramètres pour le programme dans son ensemble, définissez ce qui est par défaut, implémentez un moyen facile de déployer avec toutes les valeurs par défaut (je l'ai fait avec un simple INI. Très utile) Vous pouvez ensuite ajouter d'autres options et fonctionnalités en tant que nouvelles options liées au système d'origine.
Lorsque vous travaillez avec Big Ball Of Mud , la seule approche efficace pour moi consistait àobtenir (plus) de testeurs et organiser une professionnelle assurance qualité complète dans le projet.
Maintenant, revenons à ce qui a fonctionné pour moi. La première chose à faire est de ne pas laisser la gestion vous tromper avec vous êtes un bon développeur, vous pouvez gérer ça rhétorique. Pensez par vous-même, pensez de manière critique et vous découvrirez que malgré les écrans de fumée de bon développeur,le tout est fortement incliné vers assurance qualité côté.
D'accord, si vous avez lu jusqu'à présent, vous vous demandez peut-être comment cela fonctionne exactement pour moi? C'est vraiment simple.
La première chose est - chaque fois que je termine une fonctionnalité de correction de bogue, je réattribue simplement l'élément respectif dans issue tracker à QA guy pourvérifier mes modifications. De cette façon, je n'ai pas besoin de perdre du temps sur des tests fonctionnels lourds, il suffit de vérifier rapidement que code candidat est prêt pour le contrôle qualité.
Si j'ai vraiment de la chance (ça n'arrive pas souvent), je n'ai plus besoin de m'inquiéter. Quant au pire des cas, il n'est pas aussi mauvais non plus. Si j'ai fait quelque chose de mal, le testeur me répond juste un jour ou deux plus tard, avec une explication claire de ce qui s'est exactement passé et comment reproduire le bogue, avec leur suite de tests de régression étendu à attraper facilement ce genre de bugs s'ils se reproduisent. Pas mal vraiment, tu ne crois pas?
La prochaine grande chose qui vient avec QA est cycles de test de régression réguliers.
Les cycles de test réguliers (imNSho hebdomadaire sont les meilleurs, bien que mensuels aient également bien fonctionné pour moi) vous permettent de faire l'impossible -refactoring efficace.
Par efficace Je veux dire, si la chose a besoin de dire une semaine pour le codage et une semaine pour la correction des bogues de régression, vous pouvez vous attendre à passer juste cela - deux semaines comme vous l'aviez prévu - sans couler en un mois supplémentaire perdu en exécution de tests préjudiciables au cerveau et en analyse compliquée d'échecs - parce que les testeurs couvriront cela pour vous.
Outre les principaux avantages mentionnés ci-dessus, il existe d'autres bonus plus petits mais agréables en cours de route.
Avec les testeurs, vous avez quelqu'un avec qui discuter du design. Comme je l'ai écrit, les documents et le code ne sont pas utiles ici lorsque vous traitez avec Big Ball of Mud - créant une sorte de vide si vous travaillez seul. Un testeur qui exécute votre code et le regarde sous différents angles est un excellent partenaire pour faire rebondir des idées et explorer les changements possibles.
Avec les testeurs, vous obtenez quelqu'un pour sauvegarder votre compréhension des problèmes de conception. Lorsqu'il n'y a que des développeurs qui se plaignent de la qualité du code, cela ressemble souvent à WTF subjectifs de derrière la porte fermée .
Mais quand cela est repris par un gars de QA disant quelque chose comme component A
avait 100 bogues de régression pour 10 nouvelles fonctionnalités, par opposition à component B
qui avait 10 bogues de régression pour 20 nouvelles fonctionnalités, la communication se transforme soudainement en un tout autre jeu.
Le dernier mais non le moindre, l'assurance qualité professionnelle vous aide à mieux comprendre la quantité d'efforts qui méritent d'être investis dans des améliorations de conception. Comme je l'ai déjà mentionné, la gestion ne fonctionne pas très bien WTF de qualité de code.
Mais quand il y a un contrôle qualité professionnel, avec toutes les données qu'ils collectent habituellement, vous pouvez trouver des trucs qui ont tendance à monter en flèche à travers les cerveaux des managers directement dans cette cellule secrète avec un cachet magiqueJ'approuve
Au cours de l'année dernière, nous avons perdu environ 6 mois-homme uniquement pour corriger des bogues de régression dans le produit. Maintenant, qu'en est-il de donner à l'équipe de développement une semaine ou deux pour analyser s'il y a quelque chose que nous pouvons faire pour réduire ces déchets de moitié?
Une approche consisterait à écrire des tests,
Je pense que vous avez en quelque sorte répondu à votre propre question. Il y a un certain nombre de façons dont vous pouvez examiner cela, mais avec l'expérience, j'ai constaté qu'il semble toujours graviter vers l'application de certaines des pratiques Agiles standard, et tout faire de manière systématique de la même manière que vous aborderiez. tout problème technique difficile à résoudre.
C'est ainsi que j'aborde personnellement ce genre de situation:
C'est un processus qui, selon moi, fonctionne bien pour moi. Cela prend du temps et peut parfois être fastidieux, mais cela fonctionne et me donne toujours un bon résultat. Je n'ai jamais trouvé de code que je n'aurais pas pu améliorer étant donné le temps et les ressources pour le faire. Le fait est que vous constaterez peut-être qu'au cours de vos enquêtes initiales et de vos pics, vous identifiez des problèmes très difficiles, voire des bouchons, en fonction du temps et des efforts que vous estimez que tout prend, et estimer ce genre de chose peut être très difficile à faire avec certitude. Vous constaterez peut-être qu'en fin de compte, il sera préférable de recommencer, ou vous constaterez que le code est d'une grande valeur commerciale pour l'entreprise pour laquelle vous travaillez. Donnez votre meilleure estimation pour faire le travail, et si vous avez une compréhension complète du domaine problématique, donnez votre meilleure estimation pour recommencer. Présentez quelques cas et scénarios à votre patron, puis laissez la direction décider où dépenser l'argent. Cela vous enlève la chaleur et la ramène fermement avec eux.
Comme je l'ai dit, cette approche fonctionne pour moi, elle pourrait ne pas fonctionner exactement de la même manière pour vous. Vous pouvez modifier le processus ou envisager de résoudre le problème d'une autre manière. Le fait est que vous ne pouvez résoudre ces problèmes difficiles qu'en réfléchissant latéralement et en ne voyant pas un désordre dans le code comme une simple cause perdue. Considérez-le plutôt comme un problème difficile et un défi, et avec tout problème, il existe toujours un moyen systématique de le résoudre. Il vous suffit de trouver une approche qui vous convient. Personnellement, quand je relève un tel défi et une fois que j'ai quelques victoires à mon actif, je suis très fier de transformer une application mal conçue en un produit magnifiquement conçu.
Je travaille sur une base de code complexe depuis plus d'un an maintenant. Voyez si mes idées peuvent vous aider:
Vos idées sont exactes, au moment où vous atteignez une partie différente du code, vous oubliez la partie précédente. Ce peut être un cycle sans fin. La leçon importante à retenir ici est que le produit ne peut pas fonctionner sans que toutes les pièces fonctionnent correctement. Même si une partie échoue, le produit ne fonctionne pas. Voyez-le sous un autre angle: si vous améliorez une pièce de façon spectaculaire, cela NE POURRAIT PAS encore entraîner un meilleur fonctionnement du produit, ce qui est votre objectif principal ici.
Donc, au début: ne soyez pas développeur. Soyez testeur.
N'essayez pas de comprendre partie par partie. Comprendre l'ensemble du produit et son fonctionnement lorsque toutes les pièces sont réunies. Dans un environnement de production (c'est-à-dire un environnement sans développement - pas de points de débogage), testez le produit. Ensuite, comme tous les testeurs, enregistrez les problèmes que vous rencontrez dans un outil de suivi des bogues. Attribuez-lui la gravité et la priorité. Comme ce logiciel existe depuis un certain temps, voyez s'il y a déjà un tracker de bug créé. S'il y en a déjà un, vous avez de la chance. Ajoutez à ceux-ci et prenez le temps et vérifiez chacun des existants. À la fin de ce cycle, vous comprenez le produit d'un point de vue utilisateur (vous ne devriez certainement pas le manquer) et également d'un point de vue QA. En temps voulu, vous pourriez même vous rendre compte qu'une ligne de code corrigera le bogue, et ceux qui l'ont codé ne l'ont pas fait car il n'y avait pas vraiment de besoin à l'époque.
Deuxième étape: portez votre cape de créateur
Divisez le produit en plusieurs parties (pas littéralement ou selon votre convenance, mais selon la façon dont elles fonctionnent ensemble). Peut être votre travail jusqu'à maintenant ou les connaissances existantes pourraient entrer en jeu. Ensuite, essayez de comprendre comment ils fonctionnent les uns avec les autres ainsi qu'avec les 10 bibliothèques dépendantes. Ensuite, pour chaque bug suivi, écrivez vos notes identifiant les entités de code (par exemple: ce changement implique de modifier les classes X, Y, Z, etc.). Probablement, à la fin de cette étape, vous aurez QUELQUES conseils sur les problèmes de l'architecture actuelle et ce qui peut être amélioré.
Ensuite, vous pouvez décider si l'architecture/la conception actuelle est suffisante et vous pouvez continuer à améliorer le logiciel OR si le produit a besoin d'une meilleure conception ou de modifications dans la conception existante.
Château de cartes
De plus, comme les produits complexes sont livrés avec beaucoup de code, nous pourrions ne pas être en mesure de ramasser quelques éléments et de les modifier ou de les améliorer. C'est parce que tout le système peut être entrelacé de telle manière que le changement dans l'une des classes équivaut à changer la position d'une carte dans un château de cartes, vous ne savez jamais quelle extrémité pourrait casser. D'après mon expérience, cela a été vrai. J'ai choisi une partie, amélioré son code, ignorant les contrats qu'elle avait avec d'autres parties du code et j'ai fini par abandonner le code et réaliser mon erreur. Donc, au lieu d'essayer de comprendre les parties, essayez de comprendre que c'est un tout.
Priorisez vos préoccupations
Vous devez garder à l'esprit ce que vous essayez d'améliorer:
Voulez-vous que le produit soit plus rapide?
Bien sûr, vous le faites. Mais est-ce le premier des soucis? Est-ce lent? Si oui, créez des critères de performance, identifiez les goulots d'étranglement et améliorez ces pièces. Testez à nouveau.
Voulez-vous améliorer la convivialité?
Ensuite, c'est à peu près le côté API/UI.
Voulez-vous améliorer la sécurité?
Ensuite, ce sont les limites que vous devriez explorer.
Je n'ai fourni que 3 exemples, mais il y en a beaucoup plus à rechercher.
Dernière et meilleure documentation
J'ai lu ici dans l'un des articles que la documentation la plus récente et la meilleure est le code lui-même. Même si vous créez une bonne quantité de documentation aujourd'hui, c'est de l'histoire après un certain temps. Le code est donc votre dernière documentation. Donc, chaque fois que vous parcourez du code, écrivez votre compréhension dans les commentaires. Lors du passage de la base de code, avertissez-les de ne pas dépendre UNIQUEMENT des commentaires!
Écrivez autant de documentation que nécessaire pour expliquer le code actuel. Une fois que vous êtes sûr d'en comprendre la logique, vous pouvez être prêt à le changer. Documentez tout ce que vous allez ajouter et assurez-vous de couvrir tous les cas.
Écrivez des tests de régression. Peu importe si vous testez ou exécutez les tests manuellement, vous devez conserver la fonctionnalité d'origine et les tests de régression aident à cela.
S'il n'y a pas assez de spécifications, commencez à mettre les gens sur écoute pour plus d'informations et notez-les.
Rédigez un plan pour changer le code et divisez-le en différentes phases.