Du code sain dans des produits sains

Dans l'industrie du logiciel, voir les équipes faire des heures indécentes pour livrer difficilement des features anecdotiques à la qualité plus que discutable est la norme, mais c'est tout sauf normal !

Si vous ne partagez pas ce constat (vous avez bien de la chance), vous pouvez arrêter là votre lecture.

Dans cet article, je ne me risquerai pas à essayer d'analyser les causes de ce constat amer : je pense qu'elles sont bien trop nombreuses, difficilement démontrables et que finalement, ça ne fera pas avancer le schmilblick. Ce qui m'intéresse ici, c’est de partager quelques clés pour sortir de cette situation et, enfin, livrer des features.

Des features ?

Commençons par nous mettre d'accord sur ce que j'entends par feature. Dans ma définition, une feature n'est pas toujours utilisable directement dans l'application : tant qu'elle est utilisable par du code et qu'elle fonctionne, c'est OK.

Ainsi, une feature peut être un élément de domain model ou un secondary adapter nécessaire pour la suite, mais pas encore invoqué. Le but est de pouvoir avancer vers l'objectif identifié et partagé. Il est important de s'autoriser ces petites étapes pour réduire les coûts d'une éventuelle adaptation.

Avec cette définition, je m'attends à une moyenne de 3 features / jours / développeur·euse. En fait, cette moyenne est très basse, on sera bien plus souvent à 10 dans une équipe rodée ! Le but de ce chiffre est surtout de casser cette tendance à ne rien intégrer pendant des jours.

Ces features étant fonctionnelles, même si pas toujours utilisées, il est tout à fait possible de les livrer en production. Ainsi, une équipe de 5 développeur·euse·s fera souvent une centaine de livraisons en production par semaine (et à peu près 3.5 fois plus de livraisons tous environnements confondus).

Des livraisons ?

Pour un tel rythme de livraisons, il est nécessaire de tout automatiser. Des environnements dédiés aux besoins (par exemple sur chaque branche) se créent et se détruisent à longueur de journée.

Bien sûr, une fois les environnements disponibles, les déploiements sont entièrement automatiques (il en va de même pour les schémas et les données).

Avec les outils à notre disposition aujourd'hui (merci les orchestrateurs de conteneurs et, surtout, merci GitLab), cette mise en place n'est pas vraiment un challenge (sauf si on veut que ça en soit un). La partie qui pose plus de questions est la testabilité, la qualité du code livré. Pour rendre cette approche viable il faut que toutes les validations soient automatisées !

Des tests ?

Dans une industrie où la majorité des acteurs sont sous l'emprise d'une approche waterfall dogmatique, il est très complexe d'arriver à convaincre que :

  • Des tests réalisés par des humains ne valident presque rien (mais coûtent quand même très cher) ;
  • Des tests automatisés peuvent apporter un niveau d'assurance vraiment élevé.

C'est une discussion que j'ai souvent, je ne veux pas la refaire ici et puis Cédric le fait mieux que moi. Sur ce sujet, j'aime aussi beaucoup cette citation de Kent Beck qui dit : "Test until Fear turns to Boredom" ("Testez jusqu'à ce que la peur devienne de l'ennui").

En réalité, arriver à construire des tests qui apportent immédiatement de la valeur tout en améliorant le niveau de confiance est tout sauf simple. Connaître et maîtriser les approches demande du temps (des années) et de l'implication !

Même si ce n'est pas leur principal apport, des tests bien écrits assurent une chose : le code fait ce que la personne l'ayant écrit pense qu'il doit faire. Il reste donc une inconnue : est-ce que c'est ce qu'il fallait faire ? Pour limiter les risques sur ce point une solution efficace est la co-construction.

Co-construction ?

N'en déplaise aux aficionados des spécifications, ce qui part en production, c'est le code qui, dans le meilleur des cas, traduit la compréhension de la personne l'ayant écrit.

Dès lors, assurer une bonne communication est essentiel. Un moyen redoutablement efficace pour partager une compréhension est d'imaginer ensemble la solution. De cette manière, on évite les rédactions interminables qui seront rarement lues et jamais comprises tout en assurant un bien meilleur partage.

Autre avantage de la co-construction : les contraintes de tout le monde sont prises en compte. Il est très courant qu'un échange entre un·e développeur·euse et un·e référent·e Métier fasse émerger en quelques minutes une solution qui n'aurait jamais été trouvée avec des réflexions isolées.

Il existe de nombreuses manières d'organiser cette co-construction, que ce soit avec des EventStorming, du BDD ou des échanges informels. Voici un exemple abrégé d'un échange que j'ai eu (en tant que dev) dans un contexte bancaire. Le besoin était de détecter les virements SEPA frauduleux pour permettre un contrôle manuel :

- Moi : "Pour la détection des fraudes, tu as décrit différents cas qui ne se combinent pas forcément très bien."
- Référente Métier : "Comment ça ? Tout est couvert !"
- Moi : "Qu'est-ce qui se passe si on est dans un cas qui rentre dans [régle 2] et [régle 5]"
- Référente Métier : "Ah oui... et puis il y a plein d'autres combinaisons... En fait je ne suis pas certaine de ces règles, je veux pouvoir tester des choses au lancement et voir ce qui se passe."
- Moi : "Du coup est-ce qu'on ne ferait pas un système de scoring ? Chaque règle correspond à un nombre de points et au-delà d'un certain seuil on passe en contrôle manuel"
- Référente Métier : "Ah oui, comme ça on couvre tout et on peut jouer sur les valeurs. Je reboucle avec mes collègues et je t'envoie les règles et les points pour commencer"

Ici, j'aurais vraiment été incapable de trouver les règles et les points ! De son côté, la référente Métier n'aurait pas pu imaginer cette mécanique de points. En partageant le besoin, j'ai aussi naturellement compris qu'il faudrait pouvoir lister les virements dans cet état et les reprendre ou les annuler (et nous avons pu creuser tout ça).

Dans un autre contexte, j'aurais reçu le ticket avec l'algorithme incomplet initial et un état à mettre en base et je me serais contenté de le coder. Au final :

  • L'implémentation aurait été plus compliquée, plus longue et plus hasardeuse pour moi (relire plusieurs fois le ticket, deviner les intentions, ...) ;
  • La solution aurait été incomplète et insatisfaisante pour les deux parties ;
  • Je n'aurais pas fait la suite pour traiter ces virements, je me serais contenté de mettre un état en base, sans même m'assurer que le virement ne partait pas (et oui, le ticket dit : si [...] alors mettre l'état TO_VALIDATE).

Une autre différence essentielle entre ces versions d'échange : la référente Métier ne décrit pas techniquement ce qui se passe, elle ne parle pas de base de données ou autre détail de ce type. En fait, ce qui se passe en base ou dans tout autre élément d'infrastructure n'est qu'un détail et ça ne doit pas apparaître dans ces premiers échanges. La solution la plus adaptée pour répondre au besoin apparaîtra pendant son implémentation !

Pour la petite histoire, le système qui a été codé ce jour-là, n'a eu sa première évolution qu'un an plus tard parce qu'il répondait au besoin.

Comment faire ?

Je pense qu'en fonction des contextes dans lesquels vous travaillez cet article peut être une description du quotidien ou une œuvre de fiction.

Si c'est votre quotidien, vous passez probablement des journées intéressantes et je n'ai pas grand chose à ajouter. Si c'est de la fiction, sachez que ça existe et qu'il est parfois possible de transformer les organisations pour tendre vers ce fonctionnement.

Maintenant que j'ai travaillé dans plusieurs contextes comme celui-ci, je ressens toujours une certaine frustration quand j'observe l'inefficacité des systèmes que j'appellerais command & control.

De ce que j'ai expérimenté, co-construire des solutions que l'on va implémenter autours de tests Métier fiables, dans des bases de code intégrées régulièrement est le meilleur moyen d'apporter des features et de la valeur en production.

Dans une organisation existante, il n'y a pas réellement d'ordre pour adresser ces points. Le plus complexe étant les tests, j'aime bien commencer ce sujet au début d'une transformation et en choisir un autre à faire en parallèle. Apprendre à écrire de bons tests prend de longs mois, temps pendant lequel on peut échanger avec les référents Métier, mettre en place une intégration continue ou découper différemment les tâches.

Dans le cas d'un nouveau projet où l'organisation est à créer, il est très sain de commencer en prenant tout ça en compte avec :

  • Des échanges réguliers (plusieurs fois par jour) entre tous les acteurs ;
  • Pas de cycles prévus à l'avance mais un flux priorisé de travail ;
  • Une qualité non négociable dans le code : le seul moyen d'aller vraiment vite c'est de faire vraiment bien ;
  • Mais surtout, surtout, le Métier au centre de tout. Les outils, les technologies sont là pour répondre à un besoin, ils ne sont pas le problème que l'on cherche à résoudre.