web-dev-qa-db-fra.com

Que faire lorsque le code soumis pour examen du code semble trop compliqué?

Le code est difficile à suivre, mais il semble (surtout) bien fonctionner, au moins avec des tests superficiels. Il peut y avoir de petits bugs ici et là mais il est très difficile de dire en lisant le code s'ils sont symptomatiques de problèmes plus profonds ou de correctifs simples. La vérification manuelle de l'exactitude globale via la révision du code est cependant très difficile et longue, si elle est même possible.

Quelle est la meilleure ligne de conduite dans cette situation? Insister sur une refonte? Une reprise partielle? Re-factoriser d'abord? Corrigez les bugs uniquement et acceptez la dette technique ? Faites une évaluation des risques sur ces options, puis décidez? Autre chose?

116
Brad Thomas

Si elle ne peut pas être révisée, elle ne peut pas passer l'examen.

Vous devez comprendre que la révision du code n'est pas pour trouver des bogues. C'est à cela que sert l'AQ. La révision du code vise à garantir que la maintenance future du code est possible. Si vous ne pouvez même pas suivre le code maintenant, comment pouvez-vous en six mois lorsque vous êtes affecté à des améliorations de fonctionnalités et/ou à des corrections de bogues? Trouver des bogues en ce moment n'est qu'un avantage secondaire.

Si c'est trop complexe, cela viole une tonne de principes SOLIDES . Refactor, refactor, refactor. Décomposez-le en fonctions correctement nommées qui font beaucoup moins, plus simple. Vous pouvez le nettoyer et vos cas de test vous assureront qu'il continue de fonctionner correctement. Vous avez des cas de test, non? Sinon, vous devriez commencer à les ajouter.

247
Kevin Fee

Tout ce que vous mentionnez est parfaitement valable pour le souligner dans une revue de code.

Lorsque je reçois une revue de code, je passe en revue les tests. Si les tests ne fournissent pas une couverture suffisante, c'est quelque chose à souligner. Les tests doivent être utiles pour garantir que le code fonctionne comme prévu et continuera de fonctionner comme prévu dans les modifications. En fait, c'est l'une des premières choses que je recherche dans une revue de code. Si vous n'avez pas prouvé que votre code répond aux exigences, je ne veux pas investir mon temps à le regarder.

Une fois qu'il y a suffisamment de tests pour le code, si le code est complexe ou difficile à suivre, c'est aussi quelque chose que les humains devraient examiner. Les outils d'analyse statique peuvent mettre en évidence certaines mesures de complexité et signaler des méthodes trop complexes ainsi que la découverte de failles potentielles dans le code (et doivent être exécutées avant une révision humaine du code). Mais le code est lu et maintenu par les humains, et doit d'abord être écrit pour être maintenable. Ce n'est que s'il existe une raison d'utiliser un code moins facile à gérer qu'il doit être écrit de cette manière. Si vous avez besoin d'un code complexe ou peu intuitif, il devrait être documenté (de préférence dans le code) pourquoi le code est ainsi et avoir des commentaires utiles pour que les futurs développeurs comprennent pourquoi et ce que fait le code.

Idéalement, rejetez les revues de code qui n'ont pas de tests appropriés ou qui ont un code trop complexe sans raison valable. Il peut y avoir des raisons commerciales d'aller de l'avant, et pour cela, vous devez évaluer les risques. Si vous allez de l'avant avec une dette technique dans le code, mettez immédiatement des tickets dans votre système de suivi des bogues avec quelques détails sur ce qui doit changer et quelques suggestions pour le changer.

44
Thomas Owens

La vérification manuelle de l'exactitude globale via la révision du code est cependant très difficile et longue, si elle est même possible.

Ce n'est pas à distance le but d'une révision de code. La façon de penser à une révision de code est d'imaginer qu'il y a un bogue dans le code et que vous devez le corriger. Avec cet état d'esprit, parcourez le code (en particulier les commentaires) et demandez-vous "Est-il facile de comprendre la situation dans son ensemble afin que je puisse affiner le problème?" Si c'est le cas, c'est un laissez-passer. Sinon, c'est un échec. Plus de documentation est nécessaire au minimum, ou éventuellement une refactorisation est nécessaire pour rendre le code raisonnablement compréhensible.

Il est important de ne pas être perfectionniste à ce sujet, sauf si vous êtes sûr que c'est ce que recherche votre employeur. La plupart du code aspire tellement qu'il pourrait facilement être refactorisé 10 fois de suite, devenant plus lisible à chaque fois. Mais votre employeur ne veut probablement pas payer pour avoir le code le plus lisible au monde.

30
DepressedDaniel

La vérification manuelle de l'exactitude globale via la révision du code est cependant très difficile et longue, si elle est même possible.

Il y a de nombreuses années, c'était en fait mon travail de faire exactement cela en notant les devoirs des élèves. Et bien que beaucoup aient fourni une qualité raisonnable avec un bug ici et là, il y en avait deux qui se sont démarqués. Les deux ont toujours soumis un code sans bogue. Un code soumis que je pouvais lire de haut en bas à grande vitesse et marquer comme 100% correct sans effort. L'autre code soumis qui était un WTF après l'autre, mais en quelque sorte réussi à éviter tout bogue. Une douleur absolue à marquer.

Aujourd'hui, le second verrait son code rejeté dans une revue de code. Si la vérification de l'exactitude est très difficile et prend du temps, c'est un problème avec le code. Un programmeur décent trouverait comment résoudre un problème (prend le temps X) et avant de le donner à un réviseur de code pour le refactoriser afin qu'il ne fasse pas seulement le travail, mais évidemment fait le travail. Cela prend beaucoup moins de temps que X et fait gagner beaucoup de temps à l'avenir. Souvent, en découvrant des bogues avant même qu'ils ne passent à l'étape de la révision du code. Ensuite, en rendant la révision du code beaucoup plus rapide. Et tout le temps à l'avenir en facilitant l'adaptation du code.

Une autre réponse a indiqué que le code de certaines personnes pourrait être refactorisé 10 fois, devenant plus lisible à chaque fois. C'est juste triste. C'est un développeur qui devrait chercher un travail différent.

15
gnasher729

Est-ce ancien code qui a été légèrement modifié? (100 lignes de code modifiées dans une base de code de 10000 lignes sont toujours un léger changement) Parfois, il y a des contraintes de temps et les développeurs sont obligés de rester dans un cadre ancien et peu pratique, simplement parce qu'une réécriture complète prendrait encore plus de temps et est bien en deçà du budget . + généralement, il existe un risque, qui peut coûter des millions de dollars en cas d'évaluation incorrecte. S'il s'agit d'un ancien code, dans la plupart des cas, vous devrez vivre avec. Si vous ne le comprenez pas par vous-même, parlez-leur et écoutez ce qu'ils disent, essayez de comprendre. N'oubliez pas qu'il peut être difficile à suivre pour vous, mais parfaitement bien pour les autres. Prenez leur côté, voyez-le de leur côté.

Est-ce nouveau code? Selon les contraintes de temps, vous devriez préconiser de refactoriser autant que possible. Est-il acceptable de passer plus de temps sur les révisions de code si nécessaire. Vous ne devriez pas vous chronométrer à 15 minutes, avoir l'idée et passer à autre chose. Si l'auteur a passé une semaine à écrire quelque chose, il est normal de passer de 4 à 8 heures pour le réviser. Votre objectif ici est de les aider à refaçonner. Vous ne renvoyez pas seulement le code disant "refactor. Now". Voyez quelles méthodes peuvent être décomposées, essayez de trouver des idées pour introduire de nouvelles classes, etc.

6
Neolisk

Souvent, les correctifs/listes de modifications "compliqués" sont ceux qui font beaucoup de choses différentes à la fois. Il y a du nouveau code, du code supprimé, du code refactorisé, du code déplacé, des tests étendus; il est difficile de voir la grande image.

Un indice commun est que le patch est énorme mais sa description est minuscule: "Implémenter $ FOO".

Un moyen raisonnable de gérer un tel patch consiste à demander qu'il soit divisé en une série de pièces plus petites et autonomes. Tout comme le principe de responsabilité unique stipule qu'une fonction ne doit faire qu'une seule chose, un patch doit également se concentrer sur une seule chose.

Par exemple, les premiers correctifs peuvent contenir des refactorisations purement mécaniques qui n'apportent aucun changement fonctionnel, puis le ou les correctifs finaux peuvent se concentrer sur la mise en œuvre et les tests réels de $ FOO avec moins de distractions et de harengs rouges.

Pour les fonctionnalités qui nécessitent beaucoup de nouveau code, le nouveau code peut souvent être introduit en morceaux testables qui ne modifient pas le comportement du produit jusqu'à ce que le dernier patch de la série appelle réellement le nouveau code (un flip indicateur).

Quant à faire cela avec tact, je le résume généralement comme mon problème, puis je demande l'aide de l'auteur: "J'ai du mal à suivre tout ce qui se passe ici. Pourriez-vous diviser ce correctif en étapes plus petites pour m'aider à comprendre comment tout cela convient ensemble?" Il est parfois nécessaire de faire des suggestions spécifiques pour les étapes plus petites.

Un gros patch comme "Implement $ FOO" se transforme en une série de patchs comme:

  1. Introduisez une nouvelle version de Frobnicate qui prend une paire d'itérateurs car je vais devoir l'appeler avec des séquences autres que vector pour implémenter $ FOO.
  2. Changez tous les appelants existants de Frobnicate pour utiliser la nouvelle version.
  3. Supprimez l'ancien Frobnicate.
  4. Frobnicate en faisait trop. Factorisez l'étape de réfraction dans sa propre méthode et ajoutez des tests pour cela.
  5. Présentez Zerzify, avec des tests. Pas encore utilisé, mais j'en aurai besoin pour $ FOO.
  6. Implémentez $ FOO en termes de Zerzify et du nouveau Frobnicate.

Notez que les étapes 1 à 5 n'apportent aucune modification fonctionnelle au produit. Ils sont triviaux à examiner, notamment pour s'assurer que vous disposez de tous les bons tests. Même si l'étape 6 est toujours "compliquée", au moins, elle se concentre sur $ FOO. Et le journal vous donne naturellement une bien meilleure idée de la façon dont $ FOO a été implémenté (et pourquoi Frobnicate a été modifié).

2
Adrian McCarthy

Comme d'autres l'ont souligné, la révision du code n'est pas vraiment conçue pour trouver des bogues. Si vous trouvez des bogues lors de la révision du code, cela signifie probablement que vous n'avez pas suffisamment de couverture de test automatisée (par exemple, tests unitaires/d'intégration). S'il y a pas assez de couverture pour me convaincre que le code fait ce qu'il était censé faire, je demande généralement plus de tests et je signale le type de cas de test que je recherche et je n'autorise généralement pas le code dans le base de code qui n'a pas une couverture adéquate.

Si l'architecture de haut niveau est trop complexe ou n'a pas de sens, j'appelle généralement une réunion avec quelques membres de l'équipe pour en parler. Il est parfois difficile de répéter sur une mauvaise architecture. Si le développeur était un novice, je m'assure généralement que nous passons par ce qu'il pense à l'avance de temps au lieu de réagir à une mauvaise demande de pull. Cela est généralement vrai même avec des développeurs plus expérimentés si le problème n'a pas de solution évidente qui sera plus que probablement choisie.

Si la complexité est isolée au niveau de la méthode, elle peut généralement être corrigée de manière itérative et avec de bons tests automatisés.

Un dernier point. En tant que réviseur, vous devez décider si la complexité du code est due à essentiel ou accidentelcomplexité. La complexité essentielle concerne les parties du logiciel qui sont légitimement difficiles à résoudre. La complexité accidentelle fait référence à toutes les autres parties du code que nous écrivons qui sont trop complexes sans raison et pourraient être facilement simplifiées.

Je m'assure généralement que le code avec la complexité essentielle est vraiment cela et ne peut pas être simplifié davantage. Je vise également une plus grande couverture des tests et une bonne documentation pour ces pièces. La complexité accidentelle devrait presque toujours être nettoyée pendant le processus de demande d'extraction, car ce sont la majeure partie du code que nous traitons et peuvent facilement provoquer un cauchemar de maintenance, même à court terme.

1
c_maker

À quoi ressemblent les tests? Ils doivent être clairs, simples et faciles à lire avec idéalement une seule assertion. Les tests doivent clairement documenter le comportement prévu et cas d'utilisation du code.

Si ce n'est pas bien testé, c'est un bon endroit pour commencer à réviser.

0
Tom Squires