N.B. : Dans cet article je n’utiliserai que le terme masculin “développeur” pour des raisons de lisibilité. Évidemment ces bonnes pratiques s’appliquent tout autant aux développeuses et aux développeurs :)

Dans un précédent article nous avions vu comment Robert C. Martin alias Uncle Bob définit le professionnalisme concernant l’attitude et les engagements du développeur. Dans le reste du livre “The Clean Coder”, il présente sa vision des bonnes pratiques professionnelles lorsque l’on code. Je résume dans cet article les points qui me semblent pertinents (et qui ne sont pas toujours évidents). Nous allons parler des bonnes pratiques pour coder efficacement et comment bien tester son code. Nous verrons aussi la gestion du temps et de la pression ainsi que la communication au sein de l’équipe et avec les parties prenantes.

I) Coder efficacement

Dans ce premier chapitre, nous ne parlons pas du code en lui-même mais de l’attitude et l’état d’esprit à adopter, selon Uncle Bob, pour coder de façon précise et efficace.

a) Être clair dans ses intentions

Votre code doit être facilement lisible et compréhensible par les autres développeurs qui passeront après vous. Il ne s’agit pas simplement d’écrire de beaux commentaires mais bien d’organiser et d’écrire son code d’une façon qui révèle clairement votre intention.

Dans son livre “Clean Code”, Uncle Bob précise les règles qui selon lui permettent d’atteindre ce but. Un résumé est disponible sur cet article de Dzone.

C’est une tâche très compliquée et c’est probablement la chose la plus difficile qu’un développeur est amené à faire.

b) Savoir quand s’arrêter

Pour coder de façon claire et précise, il faut être plein d’énergie et très concentré. Uncle Bob conseille vivement d’arrêter de coder lorsqu’il est tard et qu’on se sent fatigué. Il vaut mieux s’arrêter et recommencer le lendemain plutôt que s’acharner sur un problème. Vous gagnerez du temps et de l’énergie. C’est souvent dans des moments de déconnexion que nous prenons du recul, sommes les plus créatifs et trouvons des solutions innovantes. Si un problème vous résiste, n’insistez pas. Faites un pas de côté et revenez-y plus tard.

De manière similaire, si vous êtes préoccupés par un sujet extérieur à votre travail, il vaut mieux le régler avant de se remettre à coder. Le but étant de réduire l’anxiété et les arrières pensées pour avoir l’esprit tranquille quand vous vous mettez à coder.

c) Être dans la Zone

Vous connaissez surement cette notion de flow Zone, un moment pendant lequel le développeur est ultra concentré et devient hyper-productif et efficace. De nombreux développeurs cherchent à atteindre cette Zone. Chez Deepki, c’est justement pour éviter de faire sortir un développeur de la Zone que nous avons mis en place le DRIS.

Contre toute attente, Uncle Bob déconseille d’entrer dans la Zone. Selon lui, lorsque l’on est dans la Zone, on est tellement absorbé par le code qu’on en oublie la vue d’ensemble du logiciel. Bien sûr vous écrirez plus de code et vous aurez l’impression d’être très productif mais vous prendrez de mauvaises décisions sur lesquelles vous devrez revenir. Le code écrit dans la Zone sera écrit rapidement mais vous devrez le retoucher plus tard.

C’est pour éviter d’entrer dans cet état d’euphorie où on code très (trop) rapidement que Uncle Bob conseille de faire du pair programming (c’est un des nombreux avantages du pair programming comme nous le verrons dans la suite de l’article). Selon lui, le seul moment où il est souhaitable d’entrer dans la Zone est lorsqu’on s’entraîne à coder.

Pour moi, la Zone est un état très intéressant quand on veut performer au même titre qu’un sportif le jour J. On va donc réserver cet état pour les moments où on connait exactement le problème initial et comment le résoudre. Le code écrit dans la Zone sera pertinent seulement si la situation est ultra connue et la solution répétée plusieurs fois et maîtrisée.

Il me semble que de telles situations arrivent rarement, d’autant plus lorsque la partie métier du logiciel est compliquée. Chez Deepki nous développons des fonctionnalités difficiles à appréhender et nous avons de multiples complexités à gérer (gestion des différentes sources de facturation, de relèves de consommations, agrégations à différents niveaux etc…). Ainsi, je suis plutôt aligné avec les propos d’Uncle Bob : il me semble important d’éviter la Zone sauf dans les rares cas où la solution est évidente pour tout le monde.

d) Fausse livraison

Ce qui est très peu professionnel pour un développeur c’est de s’arranger avec la définition du Done (i.e le travail est fini).
Il peut arriver que nous considérions qu’un travail est assez fini pour l’annoncer aux autres équipes et passer à une autre tâche. Le problème est que toutes les parties prenantes entendent que le travail est bien fini et personne ne voit le danger du travail inachevé qui les guette (avec son lot de bugs qui arrivent à grands pas).

Pour éviter cela, il faut une définition du Done précise avec laquelle toutes les parties prenantes sont alignées. Pour arriver à un tel résultat on utilise entre autres des tests d’acceptance, co-écrits par l’équipe produit et les développeurs.

e) Pratiquer

Un développeur professionnel doit être capable de reconnaître un nombre important de schémas, situations, problèmes et savoir comment les traiter. Au même titre qu’un musicien révise ses gammes ou qu’un sportif s’entraîne de façon répétée, le développeur va s’exercer pour reconnaître ces situations typiques et savoir comment réagir. Ces sessions d’entraînement peuvent se faire à plusieurs dans une cérémonie qu’on appelle un Coding Dojo. De nombreux exercices existent, notamment :

  • le Kata, qui est une série de gestes (ici de code) longuement répétée. Par exemple The Bowling Game, Prime Factors ou encore Word Wrap.
  • le Randori, un exercice à plusieurs. Avec l’écran projeté, un premier développeur écrit un test unitaire. Le suivant fait passer le test et écrit le test suivant, et ainsi de suite.

II) Les différentes stratégies de tests

Dans une entreprise qui possède un pôle QA (Quality Assurance), le but de l’équipe des développeurs est de faire en sorte que l’équipe QA ne trouve aucun bug. La pyramide des tests ci-dessous présente les différents types de tests à mettre en place pour atteindre ce but. Les pourcentages à gauche de chaque étage sont le taux de couverture du code ciblé par chaque type de test.

a) Unit tests et TDD

Uncle Bob insiste sur le fait que le TDD est maintenant la norme et qu’il n’y a plus de débats sur sa pertinence.

Les trois règles du TDD sont :

  1. On ne doit pas écrire du code de production avant d’avoir écrit un test qui échoue
  2. On doit écrire le strict minimum pour que le test échoue
  3. On doit écrire le strict minimum de code de production pour que le test passe

Il est important de noter que le point 2 est encore plus restrictif que ce qu’on imagine. Dans un code qui compile, la non-compilation du test unitaire est déjà suffisant pour passer à l’étape 3. Ainsi, dès qu’une classe ou une fonction est nommée dans le test unitaire, celui-ci va échouer. Il faut alors définir cette classe ou cette fonction pour faire passer le test. Quand le test passera, on pourra alors l’enrichir.

Finalement cela fera des cycles très rapides (30 secondes en moyenne).

Le TDD est une approche professionnelle pour écrire des tests unitaires et les bénéfices sont nombreux.

En appliquant le TDD, vous écrirez des centaines et des centaines de tests. Ces tests apportent de la certitude sur le code et permettent d’être certain qu’on n’a rien cassé seulement en les faisant tourner. Cela permet de réduire considérablement le nombre de bugs lorsqu’on fait évoluer le code.

Le point le plus important selon Uncle Bob est la mise en confiance des autres développeurs quand ils travaillent sur un code bien testé. Au lieu de se dire que le code n’est pas propre et qu’y toucher est trop risqué, les tests permettent au développeur de refactorer sans s’inquiéter d’avoir cassé une fonctionnalité existante. Ainsi, n’ayant plus peur de modifier le code de production, les développeurs prennent l’habitude de nettoyer le code régulièrement.

Les tests unitaires sont aussi la documentation bas niveau la plus à jour. Ils décrivent précisément comment le code fonctionne. Enfin, le TDD incite à réaliser un design plus découplé pour que les tests soient simples à mettre en place.

Un autre article sur le TDD est également disponible sur notre blog.

b) Tests d’acceptance

Le développeur professionnel a tout autant un rôle de communicant que de développeur pur. Le fameux adage “garbage-in/garbage-out” s’applique aux développeurs. Il est donc crucial que le développeur soit exigeant sur sa compréhension du métier et dans sa communication avec les autres équipes.

Les tests d’acceptance sont écrits par le métier pour le métier, même quand c’est un développeur qui finit par les écrire en collaboration avec l’équipe produit. Le but des tests d’acceptance est la communication, la clarté et la précision entre les parties prenantes pour spécifier un logiciel. Atteindre une telle précision dans la spécification est la responsabilité de toutes les parties : l’équipe produit, l’équipe QA (si vous en avez une) et l’équipe des développeurs.

Les tests d’acceptance ne sont pas du travail en plus. Ils permettent d’éviter d’implémenter le mauvais système et donc d’économiser beaucoup d’efforts, de temps et d’argent. Ils sont aussi très utiles pour savoir quand on a fini d’implémenter une fonctionnalité et éviter les fausses livraisons.

Un principe très important est le principe de la “précision tardive”. Les développeurs vont être tentés de demander des détails précis au tout début du cycle de développement. À contrario, l’équipe produit veut savoir à quoi va ressembler la fonctionnalité et le logiciel avant de se lancer dans le chantier. On se rend compte que ces deux contraintes sont incompatibles. De plus, en pratique, l’équipe produit revient souvent sur les spécifications au fur et à mesure du développement. Pour éviter de tomber dans ces travers, on va privilégier le principe de la “précision tardive” : les tests d’acceptance seront écrits le plus tard possible, si possible juste avant que la tâche soit embarquée dans l’itération.

Bien que les tests unitaires et les tests d’acceptance testent souvent la même partie du code, ils ne sont pas redondants. En effet, ils empruntent différents chemins : les tests unitaires vont appeler directement les méthodes et les classes alors que les tests d’acceptance font des appels au niveau de l’API voir au niveau de l’UI.

Le point de vue d’Uncle Bob est original : la raison la plus importante qui prouve que ces tests ne sont pas redondants est le fait qu’ils servent à documenter différents aspects du code. Leur but premier est de spécifier le design, la structure et le comportement de l’application. Le fait qu’ils permettent de vérifier que ces spécifications sont bien respectées est très utile mais ce n’est pas leur première raison d’être.

En conclusion, la communication concernant les détails d’un logiciel est complexe et périlleuse. Il est très courant de croire qu’on s’est compris entre développeurs et équipe produit. C’est seulement à la fin du travail du développeur que la catastrophe apparait : on se rend compte que ce n’est pas du tout le cas. Les tests d’acceptance permettent donc d’écrire des spécifications précises et sans ambiguïté dans un langage commun avec l’équipe produit.

c) Les autres tests : tests d’intégration, tests système et tests manuels

Pour être sur que “l’équipe QA ne trouvera aucun bug”, d’autres types de tests doivent être implémentés.

Les tests d’intégration sont des tests de chorégraphie : ils ne testent pas les règles métier mais bien comment les composants du système s’articulent ensemble. Ils vérifient donc que la “plomberie” entre les composants est correctement branchée.

Les tests sytème sont écrits pour s’assurer de la bonne construction du système. C’est dans ces tests qu’on s’attend à voir des tests de performance et de charge.

Enfin les tests exploratoires sont des tests manuels réalisés par des humains. Ces tests ne sont PAS scriptés, contrairement à ce qui a longtemps été fait (dans le livre Uncle Bob partage l’image d’un manuscrit de plusieurs centaines de pages qui décrit les tests manuels à effectuer par l’équipe QA). La valeur de ces tests vient justement du fait que l’humain va cliquer partout où il est intrigué et on va utiliser sa créativité pour dénicher des bugs que les tests automatisés n’auraient pas révélés.

Ces cinq stratégies de tests permettent de s’approcher du but ultime : développer un logiciel sans aucun bug.

III) La gestion du temps, de la pression et la collaboration

a) La gestion du temps

Uncle bob met en garde contre un mal récurrent (très présent en France) : la réunionite. Les réunions sont à la fois nécessaires tout en étant une perte de temps énorme.

Un professionnel est conscient du coût très élevé des réunions et que son temps est précieux : il a du code à écrire et des deadlines à tenir. C’est pourquoi le développeur professionnel n’ira que dans des réunions à forte valeur ajoutée. Chez Deepki, pour être sûr que chaque réunion est pertinente, nous utilisons le système du ROTI (Return On Time Invested). Il ne faut pas hésiter à quitter une réunion si celle-ci ne s’avère pas pertinente pour notre travail (de façon polie évidemment).

Si un débat dure plus de 5 minutes, il ne sera pas résolu immédiatement. Effectivement, si la solution n’est pas évidente, c’est qu’on manque de données pour trancher d’un côté ou de l’autre. Si vous vous retrouvez dans ce genre de débats stériles, mettez-y fin en proposant de récolter de la donnée afin de trouver la solution la plus pertinente.

Une autre notion intéressante est le “mana de la concentration”. Un professionnel saura déterminer quand sa jauge de mana de concentration est élevée et donc que le moment est idéal pour coder. Cependant, cette jauge tend à diminuer avec la durée de travail dans la journée et il faudra faire des pauses pour la recharger : une promenade, une conversation avec des collègues ou tout simplement regarder les oiseaux chanter à l’extérieur.

Uncle Bob préconise la méthode Pomodoro pour optimiser l’alternance entre temps de travail efficace et pauses (voici un article sur medium pour en savoir plus).

b) La pression

La pression apparaît lorsqu’on s’est engagé sur des tâches dans des délais peu réalistes. Un développeur professionnel sait que s’il s’engage sur une deadline pour une tâche, cette annonce est très impliquante. Il fera tout ce qu’il peut pour tenir les délais. C’est pour ça qu’il faut s’engager sur un délai qu’on est sûr de tenir. Il sera souvent préférable d’estimer un temps de réalisation avec une fourchette basse et une fourchette haute afin que la partie métier qui doit prendre des décisions vis à vis des clients le fasse en connaissance des risques. S’engager sur une deadline que nous ne sommes pas sûr de tenir portera préjudice à toute l’équipe (vous trouverez plus de détails dans la partie sur les rôles contradictoires du premier article).

Si jamais vous vous retrouvez dans une situation de crise (un bug découvert juste avant une présentation importante, les délais ne sont pas respectés etc…), il est important de rester discipliné comme en dehors des situations de crise. La pratique du TDD et des tests d’acceptance seront donc à garder même en période de crise. Uncle Bob insiste sur le fait que si vous suivez ces pratiques seulement par temps calme, c’est que vous ne croyez pas vraiment à leurs vertus.

c) Collaboration

Certains développeurs sont des individus qui aiment bien coder seuls devant leur ordinateur pendant de longues heures sans qu’on vienne les embêter. Ainsi ces développeurs ont choisi ce métier pour éviter les interactions sociales. Cette description est très clichée mais un fond de vérité s’y cache.

Il peut alors arriver un problème terrible : certains développeurs se sentent propriétaires de leur code. Il existe alors des parties du code que seul un développeur comprend et le danger est grand : si ce développeur est malade ou part de l’entreprise, il est très compliqué alors de s’approprier son domaine. Uncle Bob insiste sur le fait qu’il est important que toute l’équipe s’approprie TOUT le code dont elle est responsable.

Pour favoriser cela et diffuser la connaissance du code, la meilleure pratique est le pair programming.

Le pair programming possède plusieurs avantages :

  • Deux têtes valent mieux qu’une : il est souvent plus simple de résoudre un problème à deux plutôt que seul
  • Le pair permet de diffuser efficacement la connaissance et les bonnes pratiques
  • Enfin, collaborer à l’écriture du code est le moyen le plus efficace de review du code

Certains développeurs pensent qu’ils travaillent mieux lorsqu’ils travaillent seuls. C’est peut-être vrai mais ça ne signifie pas que l’équipe travaille mieux.

Le pair programming n’est pas adapté à toutes les situations (par exemple si la tâche est tellement triviale qu’une tête suffit) mais il est très souvent à privilégier.

Certains développeurs n’ont peut-être pas fait ce métier pour travailler avec d’autres développeurs. Pas de chance, la programmation est principalement du travail d’équipe, que ça soit avec les autres développeurs ou l’équipe produit.

Fin du livre

À travers ces deux articles nous avons vu quelle est l’attitude d’un développeur professionnel ainsi que les bonnes pratiques. Ce qui m’a le plus marqué est l’insistance d’Uncle Bob sur l’importance de collaborer avec les différentes équipes pour éviter le travail inutile et la perte de temps.

Ce livre a été écrit il y a maintenant plus de 10 ans. Les parties concernant les outils, que je n’ai pas résumées, sont pour la plupart dépassées mais il est très marquant de se rendre compte que tous les conseils d’attitude et de bonnes pratiques n’ont pas pris une ride.