Via Negativa : Revues, Binômage, Team Programming

(Dans cette série d'articles, nous nous inspirons de la Via Negativa pour partir à la recherche de pratiques robustes, basées sur des connaissances négatives, et dont le principe est d'identifier rapidement et avec certitude ce qui ne fonctionne pas, afin de construire un système plus solide.)

Un crible pour la robustesse aux erreurs

La réunion vient à peine de commencer. Le chef de service, qui siège au bout de la longue table, prend la parole pour faire un bref rappel du contexte. Il est interrompu par une musique trépidante émanant de la poche de l'un de ses collaborateurs. Celui-ci s'empresse de couper le son de son smartphone et le remet prestement dans sa poche. Pratiquement au même moment, de la manière la plus discrète et furtive qui soit, plusieurs participants passent également leur smartphones en mode silencieux. D'un point de vue systémique, la première interruption par sonnerie a vacciné la réunion contre les futures interruptions du même type.

À deux étages de distance, une équipe de développement commence une revue de code concernant une demande d'évolution un peu critique. L'auteur du code que l'on projette au mur (le code, pas l'auteur) explique le fonctionnement d'une méthode, lorsqu'un coéquipier l'interrompt pour faire remarquer une subtile erreur de type off-by-one. Une courte discussion s'ensuit, au terme de laquelle on inscrit une nouvelle règle au standard, laquelle concerne la manière d'écrire les boucles et permet d'éviter ce type d'erreur. Deux membres de l'équipe qui jusqu'à présent ignoraient cette règle en prennent note. L'un d'eux se demande in petto à quels endroits dans son code à lui cette règle pourrait s'appliquer, et se promet de faire la recherche une fois à son poste. Le code de l'application -- disons la prochaine version qui sera mise en production -- est maintenant un peu plus robuste aux erreurs de type off-by-one.

La revue agit comme un crible : elle produit (entre autres) un savoir négatif à propos du code, qui rend ce dernier un peu plus robuste aux futures erreurs, de la même manière que le crible d'Erathostène retient les nombres premiers en supprimant tous les multiples de chaque nombre premier trouvé.

Jerry Weinberg fait remarquer que la revue est un excellent moyen pour ses participants d'apprendre quelque chose sans avoir à poser de question. C'est un aspect dont l'importance est sous-estimée, qu'une revue de code (correctement menée) favorise la progression d'une équipe par le fait qu'elle réduit le coût de transaction associé aux demandes d'aides explicites. Dans une équipe de développement, les nouveaux arrivants, débutants ou non, sont souvent encouragés à demander de l'aide, et souvent cet encouragement est vain. Car demander de l'aide, c'est risquer d'exposer son ignorance et se rendre vulnérable, tout en mobilisant le temps (précieux) de ses coéquipiers. Vis à vis de ses participants, la revue de code fait plus que dispenser un savoir-faire : elle érige en norme de l'équipe de développement le fait de progresser dans son savoir-faire.

The objective is for everyone to find defects, including the author, not to prove the work product has no defects. People exchange work products to review, with the expectation that as authors, they will produce errors, and as reviewers, they will find errors. Everyone ends up learning from their own mistakes and other people’s mistakes. – Jerry Weinberg, The Psychology of Computer Programming, 1971

Asymétrie

On connaît sinon par l'expérience, au moins par l'intuition, la robustesse que procure une revue, quelle qu'elle soit : je demandais récemment à un responsable d'application si son équipe pratiquait une forme de revue de code. Sa réponse fut brève : - On devrait, mais ce n'est pas possible, on passe déjà beaucoup trop de temps en réunion. - Vous voulez dire que si j'arrive dans votre équipe en tant que débutant, je peux écrire du code qui sera mis en production, sans que personne ne l'ait relu ? - Bien sûr que non... Attendez. Maintenant que vous le dites, oui.

Lors d'un atelier à propos des revues de code, Etienne et moi demandions à une équipe dont le tech lead affirmait l'inutilité des revues : - Avez-vous des standards de code, de conception, une façon commune de faire les choses ? - Bien sûr ! - Est-ce que ces standards sont respectés ? - Bien sûr ! - Comment le savez-vous ? - On le sait, c'est tout.

Il n'y a aucune mystification dans cette réponse : le simple fait de modifier l'application à de nombreuses reprises et en de nombreux endroits du code permet de constater, par échantillonnage, une certaine homogénéité de celui-ci.

Il demeure cependant une asymétrie essentielle entre ces deux connaissances, l'une globale, probable, pour ainsi dire optimiste :

Par sondage (ou plutôt par "fréquentation" habituelle) du code de l'application, il est permis de supposer que celui-ci est d'une qualité homogène,

et l'autre, négative, locale, et 100% certaine :

La revue a porté à notre connaissance tels défauts précis, qui jusqu'à maintenant étaient présents dans le code.

En fonction de l'impact estimé des défauts et des efforts requis pour effectuer des revues, une équipe (et son management) pourra opter pour l'un ou l'autre côté de l'asymétrie entre ces deux connaissances. On retrouve la même asymétrie lorsqu'on considère ce que l'on peut tirer de la connaissance d'un défaut selon qu'il a été détecté en test (voire en production) ou bien en revue :

Lorsqu'un défaut est détecté (via des tests ou suite à un incident en production) dans un code non revu, la probabilité que d'autres défauts du même type soient présents dans le code est non-nulle.

Lorsqu'un défaut est identifié en revue, la probabilité qu'un nouveau défaut de ce type se reproduise peut être ramenée à une valeur proche de zéro.

La raison de cette asymétrie tient presque entièrement dans le fait que lorsqu’elle effectue des tests, l’équipe applique une analyse du système en “boîte noire” c’est à dire sans pouvoir établir une relation directe entre la source (contenant éventuellement un défaut) et le symptôme. Tandis que lorsqu’elle effectue une revue, laquelle identifie un défaut, l’équipe est généralement en mesure d’inspecter d’une manière outillée le reste des sources du système à la recherche d’autres défauts du même type. Une équipe qui pratique une forme systématique de revue de code dispose donc de meilleures options qu'une équipe qui se contente de tester son produit : elle peut "tuer dans l'œuf" tous les incidents potentiels liés à un défaut identifié, et ce pour un coût marginal. Elle peut donc appliquer vis à vis des erreurs de programmation l’adage : fool me once, shame on you, fool me twice, shame on me. À l'inverse, l'équipe "zéro revue" devrait payer un effort de test considérable si elle voulait, à l’aide des seuls tests, éradiquer une catégorie de défauts suite à une anomalie constatée en test ou en production.

Cette asymétrie se rencontre dans tant de domaines, technologiques ou non, et depuis si longtemps qu'elle a pris forme de proverbe :

Mieux vaut prévenir que guérir.

Dans le cas qui nous intéresse, une stratégie de recherche des défauts basée sur la relecture de code est à la fois plus sûre et moins coûteuse que celle qui consiste à tenter de tester en totalité la base de code. Même si les deux approches sont évidemment parfaitement complémentaires, en faisant l'hypothèse que votre base de code contient (probablement) une dizaine de défauts du type erreur off-by-one, et qu'il soit requis de supprimer ces défauts du code destiné à aller en production, pour quelle méthode : revues ou tests, opteriez-vous en premier ?

Pour le dire proverbialement : la manière la plus simple de ne pas avoir à chercher une aiguille dans une botte de foin, c'est de n'avoir sur soi aucune aiguille au moment de travailler avec le foin.

Sagesse inversée

Cette asymétrie entre coût de prévention et coût de correction est si apparente qu’il est surprenant de constater que tant d’équipes de développement n’en tiennent pas compte. Ces équipes effectuent des estimations de charge étayées par des analyses d’impact; elles prennent en charge un développement dans son entier; elles vont jusqu’à s’engager sur la tenue des budgets et délais; mais personne dans cette équipe, ni au début ni au cours du projet, ne songe à exploiter l’asymétrie. La revue de code — l'activité qui consiste à relire du code ensemble d'une manière structurée afin d'en retirer les défauts — n'est pas une activité populaire auprès des équipes de développement (ni de leur managers). Il est facile de trouver de nombreux arguments contre :

  • L'équipe est là pour produire du code, et non pas pour passer du temps en réunion.
  • Quelle est l'efficacité lorsque une équipe entière assiste à un débat technique prolongé entre deux personnes ?
  • Le travail est déjà assez difficile sans qu'on ait en plus à recevoir des critiques.
  • Pour peu qu'un manager tienne à y participer, la revue risque de se transformer en évaluation de performances.
  • On ne va pas faire une revue pour si peu de code (ou pour un code si élémentaire).
  • Le code est déjà d'excellente qualité, on ne trouvera probablement rien.
  • On a déjà les outils d’analyse du code qui font cela automatiquement.
  • Les défauts, ce n'est pas vraiment notre problème : on nous demande de faire vite, pas de faire du beau code.
  • C'est le rôle du technical leader de relire le code des autres.
  • On peut parfaitement faire des retours sur le code à distance et de manière asynchrone.
  • Relever des défauts qu'on n'aura pas le temps d'améliorer de toutes façons, ça ne sert à rien, et ça génère du cynisme.
  • Il n'y a pas vraiment de standard ici, donc c'est inutile de faire des revues.
  • On n'a pas le temps pour la "sur-qualité".

Certaines de ces objections ne sont pas sans fondement. Beaucoup d'entre elles instancient une règle simple, non-écrite, insidieuse, et très courante à propos des dépenses en développement de logiciel : il est plus facile de justifier l'effort de correction d'un défaut avéré qu'un effort de prévention des défauts possibles. On est ici encore une fois en présence de cette asymétrie entre détection et prévention : Le constat (via les tests ou les incidents en production) de défauts dans une application constitue une information finale, indiscutable, qui conduit immédiatement à l'action ("le mal est fait, il faut réparer"). En revanche, la présence supposée de défauts dans un produit en cours de construction reste seulement une hypothèse douteuse, qui conduit à la défiance ("démontrez moi le retour sur investissement des revues avant de changer nos process") et en fin de compte à l'inaction.

Cette supériorité de l'action correctrice sur l'action préventive est bien connue. Elle est fondée à la fois sur une croyance et sur l'expérience, lesquelles sont parfaitement résumées dans cette maxime :

There is never enough time to do it right the first time, but there's always enough time to do it over.

La croyance est que le processus de revue n'apporte pas de valeur ajoutée (même s'il permettrait hypothétiquement d'éviter un gros effort de correction) par rapport à son coût.

L'expérience nous dit que le travail de correction (débogage après détection en test ou en production) est incontournable car il permet d'éviter une violation du contrat (tacite ou explicite) de qualité, même s'il n'apporte pas de valeur ajoutée, et nonobstant son coût.

L'étendue de cette croyance/expérience est si grande qu'elle nous autorise à (malheureusement) inverser la sagesse proverbiale, tout en la complétant :

En informatique, si l'on veut : - faire des bénéfices - conserver son poste - éviter les conflits - privilégier l'action - faire montre d'audace et d'héroïsme (rayer les mentions inutiles),

il vaut mieux guérir que prévenir.

J'ai la conviction que cette absurdité est une cause majeure du stress au travail qui marque notre industrie.

Pair Programming, Mob Programming

La programmation en binôme (pair programming) et la programmation en équipe (mob programming, programmation en "meute", pourvu qu'on se rappelle que la meute en a seulement après les défauts et la dette technique) sont deux pratiques très similaires à la revue de code. Elles présentent l'avantage de mettre en place un retour d'information immédiat sur le code en train de s'écrire plutôt qu'ajourné à la date de la revue. Elles engagent les participants dans une interaction à la fois plus rapide, plus efficace, mais également plus exigeante en fluidité et en cohésion. Tout comme la revue, ces pratiques agissent à la fois comme crible, et comme terrain de progression du savoir-faire :

All of us have good and bad skills areas and moments. When you work by yourself, both your best and your worst make it into the code. When you have a team of people working together but separately, their best and their worst make it into the code. In the end it’s only what makes it into the code that actually matters.

When you work as a mob, everybody still has their highs and lows. However, this is not what makes it into the code. What makes it into the code is only the highest points.

This can be particularly empowering for team members whose programming skill might not be the best. We have found that on many teams, some of the best ideas come from members that have trouble turning those insights into production code. Left by themselves, those insights die. In the mob, they flourish.

-- Lewellyn Falco & Maaret Pyhäjärvi, The Mob Programming Guidebook.

La pratique du Mob Programming, issue de la (jeune) tradition des Dojos de Programmation, suscite -- sans doute pour encore un certain temps -- des objections encore plus véhémentes que celle des revues de code :

  • Comment toute une équipe peut-elle coder une seule tâche sur un écran sans mettre à mal sa productivité ?
  • Le potentiel de débats, distractions, interruptions, est encore plus élevé en groupe qu'en binôme.
  • S'il faut se mettre d'accord tous ensemble pour chaque petit détail du code, on ne s'en sortira pas.
  • Si le tech lead apporte l'essentiel des idées, n'est-ce pas une perte de temps pour les autres ?
  • Cette technique ne peut fonctionner qu'avec des équipes ayant une certaine maturité dans leur communication.
  • Il y a des personnes qui travaillent beaucoup moins facilement en groupe que toutes seules.

Là encore, certaines de ces objections sont fondées, selon le contexte. Quiconque a déjà participé à une réunion de travail possède une expérience des écueils qui menacent l'efficacité des prises de décisions collectives. Or le développement logiciel est une activité parsemée de décisions (sur différents plans, et dont la portée varie…). Un développeur me faisait part de cette difficulté :

- On a essayé la programmation en binôme, mais on n'était jamais d'accord sur le design. Par conséquent, on est revenu chacun sur ses tâches.

Et la question suivante, que je n'ai pas eu le loisir de poser, persiste : que sont devenus ces désaccords ? Ont-ils été "intégrés" d'une manière ou d'une autre dans le logiciel ? Se sont-ils évaporés à "l'assemblage" des différents composants ? Ou bien sont-ils encore présents comme autant de limitations, d'incompatibilités, de bugs potentiels ?

Dynamique d'équipe

Pour que la revue collective, ou la programmation en équipe, permettent pleinement de créer un code robuste aux erreurs et qui répond aux critères de qualité de l'équipe (et de l'entreprise), il faut que cette équipe, dans sa dynamique, allie autant que possible l'ouverture et l'efficacité.

Ouverture : la capacité à exposer le code (la conception, le standard, mais aussi les idées de chacun…) de manière à en extirper ce qui ne "marche" pas, mais aussi à apprécier et enrichir ce qui marche (le code, la conception, le standard, etc.).

Efficacité : vitesse à laquelle les décisions sont prises et mises à exécution, en cohérence avec les objectifs de l'équipe.

En observant des instances (correctement organisées) de revue collective, on peut rapporter le type des décisions prises sur un diagramme qui représenterait la relation entre ouverture et efficacité.

Par exemple, une décision à propos d'une violation du standard ("Il manque une clause default à cette construction switch case"), ne requiert pas un très grand niveau d'ouverture du groupe pour être prise de manière efficace. En revanche, un cas de conception nouveau et problématique ("ce design est trop compliqué, pas assez clair, il y a moyen de faire plus simple…") , nécessitera une plus grande ouverture (vulnérabilité, posture "egoless", pédagogie…), et prendra plus de temps.

Au fur et à mesure de sa pratique de la revue (ou de la programmation) en équipe, le groupe peut améliorer à la fois ses niveaux d'ouverture et d'efficacité. Certaines équipes qui fonctionnent en vision partagée, sont capables d'adapter et d'améliorer leur produit de manière rapide, fiable et sans mettre à mal leur relations interpersonnelles. A contrario, une équipe souffrant de problèmes de cohésion, ou qui se trouve en constant danger d'être remaniée, renoncera aux interactions).

De toute évidence, faire grandir la pratique de la revue de code dans l'entreprise suppose que certaines conditions soient remplies :

  • valeur accordée à l'apprentissage (techniques et posture),
  • affirmation et soutien d'une stratégie de prévention des défauts,
  • leadership et refus de la politique de l'urgence et du fait accompli,
  • responsabilité et symétrie (chacun est exposé aux conséquences de ses décisions),
  • valeur accordée au travail en équipe.

À l'instar des autres pratiques de développement, la revue de code ou le mob programming ne peuvent pas se mettre en place indépendamment de considérations sur la culture de l'entreprise qui souhaite encourager ces pratiques. Certaines cultures d'entreprise favorisent activement la formation d'équipes en cohésion (principalement en ne plaçant pas d'obstacles à leur formation). Dans d'autres cultures, la notion même d'équipe est dénuée de signification réelle. De la même manière qu'aucune pratique de développement ne saurait être "copiée/collée" d'une entreprise à l'autre sans considération du contexte, il est apparent que toutes les entreprises ne sont pas égales devant le défi que constitue le développement d'un logiciel de qualité. La présence ou non des revues de code dans leur corpus de pratiques est à mon sens une preuve de cette inégalité.