Au cours d'une discussion, un de mes collègues a dit qu'il avait des difficultés avec son projet actuel en essayant de résoudre des bugs. "Quand je résous un bug, quelque chose d'autre ne fonctionne plus ailleurs", a-t-il déclaré.
J'ai commencé à réfléchir à comment cela pouvait arriver, mais je ne peux pas le comprendre.
- J'ai parfois des problèmes similaires lorsque je suis trop fatigué / somnolent pour faire le travail correctement et pour avoir une vue d'ensemble de la partie du code sur laquelle je travaillais. Ici, le problème semble durer quelques jours ou quelques semaines et n'est pas lié à la concentration de mon collègue.
- Je peux également imaginer ce problème survenant dans un très grand projet, très mal géré , où les coéquipiers n'ont aucune idée de qui fait quoi, et quel effet sur le travail des autres peut avoir un changement qu'ils font. Ce n'est pas le cas ici non plus: c'est un projet assez petit avec un seul développeur.
- Cela peut également être un problème avec une base de code ancienne, mal entretenue et jamais documentée , où les seuls développeurs qui peuvent vraiment imaginer les conséquences d'un changement avaient quitté l'entreprise il y a des années. Ici, le projet vient de commencer et le développeur n'utilise la base de code de personne.
Alors, quelle peut être la cause d'un tel problème sur une nouvelle base de code de petite taille écrite par un développeur unique qui reste concentré sur son travail ?
Qu'est-ce qui peut aider?
- Tests unitaires (il n'y en a pas)?
- Architecture appropriée (je suis à peu près sûr que la base de code n'a pas d'architecture du tout et a été écrite sans réflexion préalable), nécessitant la refactorisation entière?
- Programmation en binôme?
- Autre chose?
project-management
refactoring
Arseni Mourzenko
la source
la source
Réponses:
Cela n'a pas grand-chose à voir avec la concentration, la taille du projet, la documentation ou d'autres problèmes de processus. De tels problèmes sont généralement le résultat d'un couplage excessif dans la conception, ce qui rend très difficile l'isolement des changements.
la source
L'une des causes peut être un couplage étroit entre les composants de votre logiciel: s'il n'y a pas d'interfaces simples et bien définies entre les composants, même un petit changement dans une partie du code peut introduire des effets secondaires inattendus dans d'autres parties du code.
Par exemple, dernièrement, je travaillais sur une classe qui implémente un composant GUI dans mon application. Pendant des semaines, de nouveaux bogues ont été signalés, je les ai corrigés et de nouveaux bogues sont apparus ailleurs. J'ai réalisé que cette classe était devenue trop grande, faisait trop de choses, et de nombreuses méthodes dépendaient d'autres méthodes étant appelées dans le bon ordre afin de fonctionner correctement.
Au lieu de corriger les trois derniers bugs, j'ai fait une refactorisation forte: diviser le composant en une classe principale plus des classes MVC (trois classes supplémentaires). De cette façon, j'ai dû diviser le code en morceaux plus petits et plus simples et définir des interfaces plus claires. Après la refactorisation, tous les bogues ont été résolus et aucun nouveau bogue n'a été signalé.
la source
Il est facile pour un bug d'en masquer un autre. Supposons que le bogue "A" entraîne l'appel d'une fonction incorrecte pour gérer l'entrée. Lorsque le bogue "A" est corrigé, la fonction correcte est soudainement appelée, qui n'a jamais été testée.
la source
Eh bien, la cause immédiate est que deux torts font un bien, ou du moins font un pas-évidemment-mal. Une partie du code compense le comportement incorrect de l'autre partie. Ou si la première partie n'est pas "fausse" en tant que telle, il y a un accord non écrit entre les deux parties qui est violé lorsque le code est modifié.
Par exemple, supposons que les fonctions A et B utilisent une convention de base zéro pour une certaine quantité, de sorte qu'elles fonctionnent correctement ensemble, mais C en utilise une, vous pouvez "corriger" A pour travailler avec C, puis découvrir un problème avec B.
Le problème le plus profond est le manque de vérification indépendante de l'exactitude des pièces individuelles. Les tests unitaires sont conçus pour résoudre ce problème. Ils agissent également comme une spécification des entrées appropriées. Par exemple, un bon ensemble de tests montrerait clairement que les fonctions A et B attendaient une entrée basée sur 0 et une base basée sur C 1.
Obtenir les bonnes spécifications peut également se faire par d'autres moyens, des documents officiels aux bons commentaires dans le code, selon les besoins du projet. La clé est de comprendre ce que chaque composant attend et ce qu'il promet, afin que vous puissiez trouver des incohérences.
Une bonne architecture permet de résoudre le problème de compréhension du code, ce qui facilite les choses. La programmation par paire est utile pour éviter les bogues en premier lieu ou pour les trouver plus rapidement.
J'espère que cela t'aides.
la source
Couplage serré, manque de tests, ce sont probablement les coupables les plus courants. Fondamentalement, le problème commun n'est que des normes et des procédures de mauvaise qualité. Un autre est juste un code incorrect qui parvient à avoir de la chance pendant un certain temps avec un comportement correct. Considérez les
memcpy
bogues de Linus Torvalds où la modification de son implémentation exposait (pas causait) des bogues dans les clients quimemcpy
étaient utilisés à des endroits où ils auraient dû être utilisésmemmove
avec des sources et des destinations qui se chevauchent.la source
Il semble que ces "nouveaux" bogues ne soient pas en fait de "nouveaux" bogues. Ce n'était tout simplement pas un problème, jusqu'à ce que l'autre code qui était cassé, soit réellement corrigé. En d'autres termes, votre collègue ne se rend pas compte qu'il a en fait eu deux bogues tout le temps. Si le code qui ne s'avère pas cassé n'a pas été cassé, il n'aurait pas échoué, une fois que l'autre morceau de code a été corrigé.
Dans les deux cas, un meilleur schéma de test automatisé pourrait être utile. Il semble que votre collègue doive tester à l'unité la base de code actuelle. À l'avenir, les tests de régression vérifieront que le code existant continue de fonctionner.
la source
Améliorez l' étendue de votre schéma de test automatisé. TOUJOURS exécuter l'ensemble complet de tests avant de valider les modifications de code. De cette façon, vous détecterez l'effet pernicieux de vos modifications.
la source
Je viens de rencontrer cela lorsqu'un test était incorrect. Le test a vérifié un état d'autorisation donné qui était! Correct. J'ai mis à jour le code et exécuté le test d'autorisation. Ça a marché. Ensuite, j'ai exécuté tous les tests. Tous les autres tests qui ont utilisé la ressource vérifiée ont échoué. J'ai corrigé le test et la vérification des autorisations, mais il y a eu un peu de panique au début.
Des spécifications incohérentes se produisent également. Ensuite, il est presque garanti que la correction d'un bogue en créera un autre (excitant lorsque cette partie particulière de la spécification n'est exercée que plus tard dans le projet).
la source
Imaginez que vous ayez un produit complet. Ensuite, vous ajoutez quelque chose de nouveau, tout semble bien, mais vous avez cassé autre chose, qui dépend du code que vous modifiez pour faire fonctionner la nouvelle fonctionnalité. Même si vous ne changez aucun code, ajoutez simplement des fonctionnalités à l'existant, cela pourrait casser quelque chose d'autre.
Donc, fondamentalement, vous vous êtes presque répondu:
Apprenez simplement à adapter le principe TDD (au moins pour les nouvelles fonctionnalités) et essayez de tester tous les états possibles qui peuvent se produire.
La programmation en binôme est géniale, mais pas toujours "disponible" (temps, argent, les deux ..). Mais les révisions de code (par vos collègues par exemple) une fois par jour / semaine / ensemble de validations seront également très utiles, surtout lorsque la révision inclut la suite de tests. (J'ai du mal à ne pas écrire de bugs dans les suites de tests ... parfois je dois tester le test en interne (sanity check) :)).
la source
Disons que le développeur A a écrit du code avec un bogue. Le code ne fait pas exactement ce qu'il est censé faire, mais quelque chose de légèrement différent. Le développeur B a écrit du code qui s'appuyait sur le code de A faisant exactement ce qu'il est censé faire, et le code de B ne fonctionne pas. B enquête, trouve le comportement incorrect dans le code de A et le corrige.
Pendant ce temps, le code du développeur C ne fonctionnait correctement que parce qu'il s'appuyait sur le comportement incorrect du code de A. Le code de A est maintenant correct. Et le code de C cesse de fonctionner. Ce qui signifie que lorsque vous corrigez du code, vous devez vérifier très attentivement qui utilise ce code et comment leur comportement changera avec le code fixe.
J'ai eu une autre situation: du code s'est mal comporté et a complètement empêché une fonctionnalité de fonctionner dans une situation X. J'ai donc changé le mauvais comportement et fait fonctionner la fonctionnalité. L'effet secondaire regrettable était que l'ensemble de la fonctionnalité avait des problèmes importants dans la situation X et avait échoué partout - cela n'était complètement inconnu de personne car la situation ne s'était jamais produite auparavant. Eh bien, c'est difficile.
la source