OK, donc beaucoup de révision de code est assez routinière. Mais parfois, il y a des changements qui affectent largement le code complexe et fragile existant. Dans cette situation, le temps qu'il faudrait pour vérifier la sécurité des changements, l'absence de régression, etc. est excessif. Peut-être même dépasser le temps qu'il a fallu pour faire le développement lui-même.
Que-faire dans cette situation? Fusionner et espérer que rien ne vous échappe? (Ne le préconisez pas!) Faites de votre mieux et essayez seulement de repérer les défauts évidents (peut-être est-ce le plus de révision de code devrait viser de toute façon?) Fusionner et tester de manière approfondie comme meilleure alternative que la révision de code?
Il ne s'agit pas spécifiquement de savoir si les tests doivent être effectués dans le cadre d'une révision de code. Il s'agit de demander quelles sont les meilleures options dans la situation décrite, en particulier avec un délai pressant, aucune suite complète de tests unitaires disponibles ou des tests unitaires non viables pour le code fragmenté qui a été modifié.
EDIT: J'ai l'impression que quelques-unes des réponses/commentaires jusqu'à présent ont repris mon expression "à large impact", et ont peut-être compris que le changement impliquait un grand nombre de lignes de code. Je peux comprendre que c'est l'interprétation, mais ce n'était pas vraiment mon intention. Par "impact général", je veux dire par exemple que le potentiel de régression est élevé en raison de l'interconnectivité de la base de code ou de l'ampleur des effets d'entraînement, pas nécessairement que le changement lui-même est important. Par exemple, un développeur peut trouver un moyen de corriger un bogue avec une seule ligne en appelant une routine de haut niveau existante qui cascade les appels vers de nombreuses routines de niveau inférieur. Il est facile de tester et de vérifier que la correction de bogue a fonctionné. La validation manuelle (via la révision du code) de l'impact de tous les effets indirects est beaucoup plus difficile.
La prémisse de la question est, franchement, étonnante. Nous supposons qu'il y a un grand changement dans le code fragile et complexe, et qu'il n'y a tout simplement pas pas assez de temps pour l'examiner correctement . C'est le tout dernier code que vous devriez passer moins de temps à réviser ! Cette question indique que vous avez des problèmes structurels non seulement dans votre code lui-même, mais dans votre méthodologie de gestion du changement.
Alors, comment faire face à cette situation? Commencez par ne pas y entrer en premier lieu:
Identifiez les sources de complexité et appliquez des refactorisations correctes, soigneusement examinées et correctes pour augmenter le niveau d'abstraction. Le code doit être compréhensible par un nouvel employé fraîchement sorti du collège qui sait quelque chose sur votre domaine d'activité.
Identifier les sources de fragilité; cela pourrait être en examinant le code lui-même, en examinant l'historique des corrections de bogues du code, etc. Déterminez quels sous-systèmes sont fragiles et les rendent plus robustes . Ajoutez une logique de débogage. Ajoutez des assertions. Créez une implémentation lente mais évidemment correcte du même algorithme et dans votre build de débogage, exécutez les deux et vérifiez qu'ils sont d'accord. Dans votre version de débogage, des situations rares se produisent plus fréquemment. (Par exemple, créez un allocateur de mémoire qui déplace toujours un bloc lors d'une réallocation, ou alloue toujours un bloc à la fin d'une page, etc.) Rendez le code robuste face aux changements à son contexte. Maintenant, vous n'avez plus de code fragile; vous avez maintenant un code qui trouve les bogues, plutôt que de provoquer les bogues.
Écrivez une suite de tests automatisés. Évidemment.
Ne faites pas de grands changements. Effectuez une série de petits changements ciblés, dont chacun peut être considéré comme correct.
Mais fondamentalement, votre scénario est "nous nous sommes plongés dans un trou de dette technique et chaque changement complexe et non révisé nous creuse plus profondément; que devons-nous faire?". Que faites-vous lorsque vous vous trouvez dans ce trou? Arrêtez de creuser. Si vous êtes tellement endetté que vous ne pouvez pas effectuer des tâches de base comme examiner le code de l'autre, vous devez cesser de vous endetter davantage et passer du temps à le rembourser.
L'un des principaux objectifs d'une révision de code est de augmenter la qualité et fournir un code robuste . Robuste, car 4 yeux détectent généralement plus de problèmes que 2. Et le réviseur qui n'a pas écrit le code supplémentaire est plus susceptible de contester les hypothèses (potentiellement erronées).
Éviter les évaluations par les pairs ne contribuerait dans votre cas qu'à accroître la fragilité de votre code. Bien sûr, renforcer les tests avec une suite de tests solide et reproductible pourrait certainement améliorer la qualité. Mais il devrait être complémentaire à l'examen par les pairs, pas un remplacement .
Je pense que la complexité doit être comprise et maîtrisée, et le l'examen complet par les pairs est l'occasion de partager les connaissances et d'y parvenir. L'investissement que vous faites pour que davantage de personnes comprennent la force et la faiblesse du code fragile, contribuera à l'améliorer au fil du temps.
Une citation pour conclure:
"Si vous voulez aller vite, allez seul. Si vous voulez aller loin, allez ensemble"
Bienvenue dans le monde du développement de logiciels hérités.
Vous avez des centaines de milliers, des millions, des dizaines de millions de lignes de code.
Ces lignes de code sont précieuses, car elles génèrent une source de revenus et leur remplacement est impossible.
Votre modèle d'entreprise est basé sur l'exploitation de cette base de code. Donc, votre équipe est petite, la base de code est grande. Il est nécessaire d'ajouter des fonctionnalités à pour que les utilisateurs achètent une nouvelle version de votre code ou pour satisfaire les clients existants.
Dans un monde parfait, votre énorme base de code est testée à l'unité dans le wazoo. Vous ne vivez pas dans un monde parfait.
Dans un monde moins parfait, vous avez le budget pour régler votre dette technique - décomposez votre code en morceaux testables unitaires, faites des tests d'intégration approfondis et répétez.
Cependant, cela réduit la dette sans produire de nouvelles fonctionnalités. Ce qui ne correspond pas à l'analyse de rentabilisation de "récolter les bénéfices du code existant, tout en le modifiant afin de générer une incitation à la mise à niveau".
Vous pouvez prendre d'énormes morceaux de code et le réécrire en utilisant des techniques plus modernes. Mais partout où vous interagissez avec le code existant, vous exposerez des points d'arrêt possibles. Ce piratage du système dont vous vous êtes débarrassé a en fait compensé une bizarrerie dans un sous-système que vous n'avez pas réécrit. Toujours.
Ce que vous pouvez faire, c'est agir avec prudence. Vous pouvez trouver une partie du code que vous comprenez réellement et dont le comportement et l'interaction avec le reste du système sont bien compris. Vous pouvez moderniser cela, en ajoutant des tests unitaires et en rendant son comportement encore plus clair.
Trouvez ensuite les parties du reste de l'application qui interagissent principalement avec elle et attaquez-les une à la fois.
Ce faisant, vous pouvez améliorer le sous-système, en ajoutant des fonctionnalités que les clients sont prêts à payer.
En bref, c'est l'art du possible - apporter des changements sans casser des choses qui fournissent une analyse de rentabilisation.
Mais ce n'est pas votre question. Votre question est: "Je fais quelque chose qui est énorme et qui risque de casser des trucs, et comment suivre les meilleures pratiques?"
Lorsque vous faites quelque chose d'énorme, il est vrai que si vous voulez le faire de manière fiable, vous finissez par consacrer plus d'efforts à rechercher et à corriger les bogues que de les écrire. C'est la règle générale du développement logiciel: écrire des trucs est facile, le faire fonctionner sans problème est difficile.
Vous avez probablement une analyse de rentabilité suspendue au-dessus de votre tête, où vous avez promis à une partie prenante que ce changement massif se produirait. J'aime ça".
Si vous avez le pouvoir et le budget, passez réellement l'effort à générer la confiance que le changement fonctionne, ou rejetez simplement le changement. Ce sera une question de degré, pas de nature.
Si vous n'avez pas beaucoup de puissance, mais que vous en avez encore, essayez d'insister pour que le nouveau système soit testable à l'unité. Si vous réécrivez un sous-système, insistez pour que le nouveau sous-système soit composé de petites pièces avec un comportement bien spécifié et des tests unitaires autour d'eux.
Ensuite, il y a le pire des cas. Vous allez plus profondément dans la dette. Vous empruntez contre l'avenir du programme en ayant plus de code fragile et plus de bugs pour sortir la fonctionnalité maintenant, et sacrément les conséquences. Vous effectuez un contrôle qualité basé sur le balayage pour trouver les pires problèmes et ignorer le reste. C'est en fait parfois la bonne réponse du point de vue de l'entreprise, car c'est moins cher maintenant. S'endetter pour générer des profits est une stratégie commerciale valable, surtout si l'apurement de la dette via la faillite (abandon du code) est sur la table.
Un grand problème est que les incitations des propriétaires d'entreprise sont rarement alignées sur les décideurs et les programmeurs. Il y a généralement beaucoup de pression pour `` livrer '', et le faire en générant une dette technique presque invisible (pour vos supérieurs) est une excellente stratégie à court et parfois à moyen terme. Même si vos supérieurs/parties prenantes seraient mieux servis par pas en créant toute cette dette.
Résolvez les problèmes plus importants qui rendent la révision du code trop difficile.
Ceux que j'ai repérés jusqu'à présent:
Vous pouvez renvoyer la révision de code et demander au développeur de la diviser en ensembles de modifications plus petits et plus incrémentiels, et soumettre une révision de code plus petite.
Vous pouvez toujours vérifier les odeurs de code, les modèles et les anti-modèles, les normes de formatage du code, les principes SOLID, etc.) sans nécessairement passer par chaque détail du code.
Vous pouvez toujours effectuer des inspections de code tactique pour une validation d'entrée correcte, une gestion de verrouillage/thread, d'éventuelles exceptions non gérées, etc. à un niveau détaillé, sans nécessairement comprendre l'intention générale de l'ensemble des modifications.
Vous pouvez fournir une évaluation des zones de risque globales qui, selon vous, peuvent être affectées par le code, et demander au développeur de confirmer que ces zones de risque ont été testées unitaire (ou lui demander de rédiger des tests unitaires automatisés et de les soumettre également pour examen). ).
Dans cette situation, le temps qu'il faudrait pour vérifier la sécurité des changements, l'absence de régression, etc. est excessif.
Les révisions de code ne devraient pas viser principalement à l'exactitude. Ils sont là pour améliorer la lisibilité du code, la maintenabilité et le respect des normes d'équipe.
Trouver des bogues d'exactitude lors d'une révision de code est un bon sous-produit de les faire, mais un développeur doit s'assurer leur code fonctionne parfaitement (y compris la non régression) avant de le soumettre pour révision.
La correction doit être intégrée dès le départ. Si un développeur n'est pas en mesure de l'atteindre, demandez-lui de jumeler le programme ou d'élaborer un plan avec toute l'équipe, mais ne le traitez pas comme quelque chose que vous pouvez ajouter après coup.
Si vous pensez que la révision du code est trop difficile, car elle a changé du code fragile qui est presque impossible à changer sans le casser, alors vous avez un problème. Mais le problème ne vient pas de la révision du code. Le problème n'est pas non plus lié aux tests unitaires, car le code fragile ne peut pas être testé à l'unité! Si votre code était testable à l'unité, il aurait été divisé en petites unités indépendantes, que chacune peut être testée, et qui fonctionnent bien ensemble, et c'est exactement ce que vous n'avez pas!
Vous avez donc un tas de code poubelle (aka "dette technique"). La pire chose que vous puissiez faire est de commencer à réparer ce tas de code de détritus et de ne pas terminer le travail parce que de cette façon, vous obtiendrez un tas encore plus grand de code de détritus. Donc, la première chose à faire est de convaincre votre direction de le réparer et pour terminer le travail. Ou non. Dans ce cas, vous ne le touchez tout simplement pas.
Lorsque vous le corrigez, vous extrayez une unité du code, en faites quelque chose qui a un comportement bien défini et bien documenté, écrivez des tests unitaires pour cette unité, revoyez le code et priez pour que rien ne se casse. Et puis vous faites de même avec l'unité suivante, et ainsi de suite.
La partie délicate survient lorsque vous rencontrez des bogues. Votre nid de code de rats fera les mauvaises choses dans certains cas parce que les choses sont si fragiles et compliquées, les choses iront mal. Lorsque vous extrayez des unités, le code restant devient plus clair. (J'ai eu un cas où après une refactorisation, une fonction a commencé avec "if (condition1 && condition2 && condition3) crash ();" qui était exactement le comportement avant refactoring, seulement plus clair. J'ai ensuite supprimé cette ligne :-) Vous verrez comportement étrange et indésirable clairement, vous pouvez donc y remédier. D'un autre côté, c'est là que vous devez changer le comportement du code existant, donc cela doit être fait avec soin).
Malheureusement, vous ne pouvez pas faire grand-chose à ce sujet au moment de la révision du code, à part prendre une autre tasse de café. La véritable solution à ce problème est de faire face à la dette technique que vous avez accumulée: design fragile, manque de tests. J'espère que vous avez au moins une sorte d'AQ fonctionnelle. Si vous n'en avez pas, il y a toujours des prières sur des os de poulet.
Si vous n'êtes pas content de vous livrer avec un logiciel buggy/non fonctionnel et de le corriger plus tard, alors l'effort V&V DEVRAIT être plus long que l'effort de développement!
Si le code existant est fragile, alors une première question est "devriez-vous même le changer?" La direction doit faire un appel pour savoir si le coût/risque de refonte et de réimplémentation de ce code est supérieur au coût/risque de réparer le tas de déchets indésirables. S'il s'agit d'un élément unique, il peut être plus facile de simplement le corriger. S'il y aura probablement plus de changements nécessaires à l'avenir, prendre le coup maintenant pour éviter plus de douleur à l'avenir peut être une meilleure décision. Vous devez en parler à votre direction, car donner à vos managers de bonnes informations fait partie de votre travail. Ils doivent prendre cette décision, car c'est une décision stratégique qui est au-dessus de votre niveau de responsabilité.
Je ne sais pas pourquoi cela n'a pas encore été mentionné, mais ces 2 sont les pièces les plus importantes:
* Exemple: vous remplacez la bibliothèque A par la bibliothèque B.Une liste de modifications introduit la bibliothèque B, différentes listes de modifications remplacent l'utilisation de A par B pièce par pièce (par exemple, une liste de modifications par module), et la dernière liste de modifications supprime la bibliothèque A.
Faites-vous le meilleur possible et essayez seulement de repérer les défauts évidents (c'est peut-être le plus de révision de code devrait viser de toute façon)?
Ne sous-estimez pas la valeur potentielle des revues de code. Ils peuvent être efficaces pour détecter les bogues:
Ils sont également utiles pour d'autres raisons:
Que-faire dans cette situation?
Dans le meilleur cas/idéal, passer l'inspection du code ne signifie pas seulement "pas de bugs évidents": cela signifie "évidemment pas de bugs" (bien que vous souhaitiez bien sûr le tester également).
Si vous ne pouvez pas vérifier la nouvelle base de code via l'inspection du code, elle aura besoin de tests plus approfondis dans la "boîte noire". Vous pouvez être habitué à un cycle de développement où vous mettez du code en production après son inspection réussie, mais s'il ne peut pas "passer l'inspection", vous ne pouvez pas le "mettre en production" et il a besoin d'un cycle plus long: par exemple tests d'intégration, tests système, tests alpha, tests d'acceptation, tests bêta, etc.
aucune suite complète de tests unitaires disponibles ou tests unitaires non viables pour le code fragmenté qui a été modifié
Qu'en est-il des tests d'intégration, de système et d'acceptation?
Quoi qu'il en soit, vous devriez probablement dire au chef de projet et au chef de produit que le code est presque certainement bogué, avec un nombre inconnu de bogues; et qu'ils "obtiendront ce qu'ils inspectent" au lieu d'obtenir simplement "ce qu'ils attendent" - c'est-à-dire que la qualité du code n'est pas meilleure que leurs tests (parce que la qualité du code n'a pas été et ne peut pas être garantie par l'inspection du code) .
Ils devraient éventuellement relayer ce message au client ou aux utilisateurs, afin de faire des tests bêta (s'ils sont prêts à être des adopteurs précoces), ou d'utiliser l'ancienne version jusqu'à ce que la nouvelle version soit sortie de la version bêta (si ce n'est pas le cas).
D'après mon expérience, je vous recommande fortement de couvrir votre code avec une bonne quantité de tests, à la fois unitaires et d'intégration, AVANT que des modifications ne soient apportées au système en question. Il est important de se rappeler qu'il existe aujourd'hui un très bon nombre d'outils à cet effet, peu importe le langage avec lequel vous développez.
De plus, il existe L'outil de tous les outils pour créer vos tests d'intégration. Oui, je parle de conteneurs et spécialement de Docker et Docker Compose . Il nous offre magnifiquement un moyen de mettre en place rapidement un environnement d'application complexe, avec des infrastructures (base de données, mongodb, serveurs de file d'attente, etc.) et des applications.
Les outils sont disponibles, utilisez-les! :)
Plus de réponses concernent comment vous en êtes arrivé à ce point. Beaucoup d'entre eux donnent quelques suggestions pour remédier à la situation, mais je voudrais apporter ma réponse pour donner la réponse courte.
Vos développeurs étaient super! Chats de retour pour tout le monde!
(ou pour ceux qui n'ont pas grandi en regardant " The Simpsons " à la télévision américaine: si les tests réussissent, ne tentez pas de regarder les différences et demandez au développeur de vous guider dans une visite du changements)
Continuez à refactoriser et à ajouter une couverture de test jusqu'à ce que les tests réussissent.
Comme @EricLippert le souligne dans son excellente réponse, ce type de changement nécessite plus attention, pas less. Si vous réalisez qu'un changement sur lequel vous travaillez va devenir un tel changement, il existe quelques stratégies qui pourraient vous aider:
Beaucoup de code est écrit et fusionné sans examen approprié du code. Ça peut marcher. Il y a une raison pour laquelle on l'appelle code odeur pas "code cassé" ou quelque chose à cet effet. L'absence de révision du code est un signe d'avertissement, pas un signe avant-coureur de Doom.
La solution à ce problème est qu'il n'y a pas de solution unique pour s'adapter à tous les cas que nous pouvons regrouper dans une réponse de style StackExchange. C'est le fort consensus de la communauté du développement logiciel que la révision du code est une "meilleure pratique" cruciale, et dans ce cas, elle est ignorée. Votre développement n'est plus dans ce canal étroit de "suivre toutes les meilleures pratiques". Vous devrez trouver votre propre chemin.
Qu'est-ce qu'une "meilleure pratique" de toute façon? En fait, c'est un ensemble de pratiques que les gens pensent généralement améliorer le code. Font-ils du bon code? Heck non! L'Internet est jonché d'histoires d'entreprises qui ont suivi les "meilleures pratiques" et se sont brouillées. Peut-être qu'un meilleur point de vue sur les "meilleures pratiques" est qu'elles sont les solutions "feu et oublie" du monde du logiciel. Je ne peux rien savoir de votre entreprise, de votre projet, de votre équipe et être capable de éliminer les "meilleures pratiques" comme des choses qui vous aideront. Ce sont les conseils généraux "ne pas nuire".
Vous vous êtes clairement écarté de ce plan. Heureusement, vous le reconnaissez. Bon travail! Ils disent que la connaissance représente la moitié de la bataille; si c'est le cas, la sensibilisation en représente bien plus de la moitié! Maintenant, une solution est nécessaire. D'après votre description, il est clair que l'environnement commercial dans lequel vous vous trouvez a évolué au point que les conseils ennuyeux de "allez faire la révision du code, c'est la meilleure pratique" ne vont pas le couper. Pour cela, je recommande une règle clé que j'utilise en ce qui concerne les meilleures pratiques logicielles:
Aucune bonne pratique de développement logiciel ne l'emporte sur un besoin commercial.
Franchement, ils paient votre salaire, et la survie de l'entreprise est généralement beaucoup plus importante que la qualité du logiciel. Nous n'aimons pas l'admettre, mais un logiciel parfaitement écrit est inutile s'il est piégé dans le corps d'une entreprise mourant de ses efforts pour maintenir ce logiciel parfaitement écrit.
Alors où allez-vous? Suivez la piste de la force. Vous avez souligné que, pour une raison non déclarée, il est déraisonnable de subir une révision du code pour une tâche. D'après mon expérience, cette raison est toujours temporelle. C'est toujours "pas assez de temps" ou "pas assez d'argent pour maintenir les salaires pendant que vous passez le temps". C'est des affaires; ça va. Si c'était facile, tout le monde le ferait. Suivez la piste de la force vers le haut et trouvez la direction qui est en mesure de vous aider à comprendre pourquoi une révision de code n'est pas une option. La langue est difficile, et bien souvent, un décret se répercutera sur la haute direction et sera déformé. La solution à votre problème peut être cachée dans cette distorsion.
La réponse à cela est nécessairement un scénario de cas spécifique. Cela revient à essayer de prédire si un tirage au sort sera une tête ou une queue. Les meilleures pratiques disent de le retourner 100 fois et l'attente sera d'environ 50 têtes et 50 queues, mais vous n'avez pas le temps de le retourner 1 fois. C'est là que les détails de votre situation comptent. Saviez-vous qu'une pièce atterrira généralement dans la même orientation qu'elle a été lancée dans environ 51% des cas? Avez-vous pris le temps d'observer le sens de la pièce avant de la lancer? Cela pourrait faire une différence.
Une solution générale qui peut être à votre disposition est d'essayer de trouver un moyen de tirer le processus de révision du code et d'en faire un effort à très faible coût. Une grande partie du coût d'un processus de révision de code est que tout le monde est entièrement dédié à la révision de code pendant que vous le faites. Cela doit être le cas car, une fois la révision du code terminée, le code est béni. Vous pouvez peut-être mettre le code dans une branche différente et effectuer la révision du code en parallèle avec le développement sur le tronc principal. Ou peut-être pouvez-vous même le configurer pour que le logiciel effectue les tests pour vous. Peut-être que vous êtes dans un environnement commercial où vos clients peuvent exécuter le "nouveau" code en parallèle avec l'ancien, et leur faire comparer les résultats. Cela transforme les clients en un tas de dispositifs de création de cas d'utilisation.
Une clé de tous ces "maybes" en cours d'exécution est que vous devez vous efforcer de diviser votre code facilement en morceaux. Vous pourriez être en mesure de "prouver" des parties du code sans vous fier à une révision formelle du code en les utilisant dans des projets moins critiques. Il est plus facile de le faire si les changements sont en plus petits morceaux, même si leur somme totale est trop importante pour être examinée par les pairs.
En général, recherchez des solutions spécifiques à votre projet, votre entreprise, votre équipe. La réponse d'ordre général était "les meilleures pratiques". Vous ne les utilisez pas, vous devez donc rechercher des solutions plus personnalisées à ce problème, cette fois. C'est des affaires. Si tout se passait comme nous l'espérions tout le temps, les introductions en bourse seraient beaucoup plus faciles à attribuer des valeurs, n'est-ce pas!
Si le remplacement d'une révision de code est une tâche difficile, n'oubliez pas qu'il n'y a jamais eu un seul morceau de code qui s'est avéré efficace dans une révision de code. * Tout ce qu'une révision de code fait, c'est vous donner confiance dans le code et la possibilité de faire des corrections avant qu'ils ne deviennent un problème. Ces deux produits précieux d'une revue de code peuvent être acquis par d'autres moyens. L'examen du code a juste une valeur reconnue pour être particulièrement bon dans ce domaine.
* Eh bien, presque: le micro-noyau L4 a reçu un examen du code il y a quelque temps par un système de preuve automatisé qui prouve que son code, s'il est compilé par un compilateur C++ conforme, fera exactement ce que dit la documentation.