Et si deux lois informatiques dans le domaine de l'architecture informatique pouvaient nous inspirer sur l'organisation des équipes de développement ?
Je suis parti d’une discussion autour de la certification Lightbend Reactive Architecte avec un collègue, et comme je lis en même temps le livre Team Topologies de M.Skeleton/M.Pais, mon esprit a touillé tout cela ces derniers jours. Alors cela donne cet article.
La loi d’Amdahl
Commençons par expliquer la loi d’Amdahls qui date de 1967. Elle permet de représenter l’accélération théorique de la latence d’exécution d’un programme en fonction du nombre de processeur. Et par ailleurs, elle montre que cette accélération est limitée par la portion du programme qui ne peut pas être parallélisé (exécutée sur un autre processeur).
Prenez par exemple une tâche qui demande 10 minutes à un seul processeur. Supposons ici que 3 minutes de cette tâche ne peuvent pas être exécutées en parallèle. Il reste donc 7 minutes que l’on peut exécuter en parallèle. Première conséquence : quelque soit le nombre de processeurs que vous rajouterez, le temps total ne pourra pas être inférieur à ces 3 minutes.
Ce petit graphe que j’ai tracé via Google Spreadsheet et un ratio de 0.7 montre que l’ajout de processeurs en parallèle n’a plus d’effet sur le temps de traitement passé un certain seuil. Vous pouvez ajouter autant d’ouvrier que vous voulez : cela n’ira pas plus vite. Notez que la courbe tend vers 3, alors que le nombre de processeurs double sur l’axe horizontal.
La page Wikipedia montre aussi l’influence de la portion parallélisable de la tâche sur l’accélération théorique de la latence :
Maintenant, si nous remplaçons la notion de processeur par la notion de Thread ou d’humain sur un projet, nous allons avoir un parallèle intéressant.
Soit une tâche de développement donnée à un ensemble de développeurs. Une partie de cette tâche est parallélisable, une autre partie ne l’est pas. Imaginons le développement d’un écran dans un logiciel, et supposons que ce développement demande 10 jours. Après discussion avec l’équipe, il faudra au moins 3 jours non parallélisable, à l’intégrateur Web pour monter la maquette, le reste du travail pouvant être effectué par plusieurs développeurs back en même temps. Nous n’avons malheureusement qu’un seul intégrateur Web (coucou Geoffroy) et nous ne pouvons pas aller plus vite. La fraction améliorable de mon projet est donc 0.7 et la fraction non-améliorable est à 0.3.
Si j’ai un seul développeur backend et un intégrateur web, la tâche prendra en théorie 10 jours. Si j’ajoute 2 développeurs, selon le calcul de la loi d’Amdahl, l’accélération sera de 1.538, je divise le temps de travail global. La tâche durera moins de temps mais ne prendra jamais moins de 3 jours (le travail de l’intégrateur). Avec 2 développeurs, la durée totale sera de 6.5 jours au lieu de 10. Notez que nous n’avons pas divisé par 2 le temps de travail. Une tâche de développement de 10 jours avec 70% de travail parallélisable, demande 6.5 jours avec 2 ressources. Nous n’avons pas divisé par 2 le temps nécessaire à compléter l’ensemble de la tâche.
Si j’ajoute (je sais je suis très naïf, soyez patient) deux autres développeurs backend, pour arriver à 4 personnes, une fois le calcul effectué, je trouve une accélération de 2.105. Cette fois-ci l’ensemble de la tâche (frontend de 3 jours + 1.75 jours de backend codé en parallèle) devrait demander 4.75 jours d’efforts.
L’ajout de ressources sur un projet déjà en retard, va encore plus retarder ce projet
Or toute personne ayant travaillé dans une équipe sait très bien que ces chiffres sont hypothétiques… voire fantaisiste. Il existe des tâches qui se parallélisent très bien, et il y a tout un ensemble d’activités qui vont réduire radicalement l’efficience de l’équipe.
Pourtant malheureusement vous entendrez le discours suivant : si tu ajoutes plus de développeurs, tu vas aller plus vite. Et si tu triple l’équipe, tu devrais aller trois fois plus vite.
Or c’est faux, comme expliqué avant (vous suivez un peu ? merci)
La Loi de Brooks est limpide sur ce sujet :
Adding manpower to a late software project makes it later
Frederick Brooks dans le livre « The Mythical Man-Month » publié en 1975
La question c’est : pourquoi ?
Reprenons notre discussion sur les lois liées à l’exécution en parallèle et qui datent des années 70 afin de voir si nous n’aurions pas d’autres pépites.
Du point de vue de l’organisation
Oublions quelques instants le développement d’un logiciel et regardons tout simplement l’organisation de plusieurs équipes dans une entreprise. La capacité à délivrer plus vite se heurte à la quantité de travail que l’on peut effectuer en parallèle.
J’ai vécu cela sur un projet récemment. Initialement, l’équipe de développement était submergée par les bugs à traiter. L’équipe consacrait presque chaque sprint à corriger des bugs. Et chaque release apportait un nouveau lot de bugs, comme si la fuite ne pouvait se résorber. Il a fallut revoir les indicateurs visuels, remonter la qualité, rendre plus stricte les revues de code, et la qualité est revenue en quelques semaines. L’équipe a parallélisé la correction de bugs et elle a pu reprendre le contrôle.
Quelques jours plus tard, cela met en évidence qu’une autre équipe n’arrive pas à travailler en parallèle : l’équipe QA. Et en effet : deux ingénieurs qualités ne pouvaient pas aller aussi vite que 7 ou 8 développeurs. Il a fallut alors intégrer l’équipe QA plus au cœur du Kanban de l’équipe, et ajouter des indicateurs visuels pour observer la contention. Cela a eu un effet bénéfique. Les développeurs ne livrant que ce que l’équipe QA était en mesure de tester. Les ingénieurs QA pouvaient enfin partager aussi et ils comprenaient ce qu’il fallait tester sur l’environnement de Dev, sans devoir attendre une livraison en Preprod.
Enfin quelques semaines plus tard, c’est finalement le client externe qui avait du mal à suivre le nouveau rythme. L’équipe globalement a rattrapé en 2 mois, au prix d’efforts humains importants. Il fallait se concentrer sur ce qui était parallélisable, et ne pas simplement ajouter des ressources.
La loi de Gunther
Nous sentons bien qu’instinctivement, l’ajout de développeurs sur un projet n’est pas gratuit. La loi de Brooks dit qu’un projet en retard sur lequel on ajoute d’autres développeurs va être encore plus en retard. Et c’est vrai. Temps de formation des nouveaux arrivants, difficulté à se coordonner, nombre d’échanges et de discussions pour expliquer le logiciel, coordination… tout ceci m’a fait penser à la loi de Gunther, que j’aimerai vous expliquer.
Tout d’abord Neil J. Gunther (70 ans en 2020) est un chercheur, auteur de livres, et qui s’est intéressé à pouvoir estimer le débit d’une plate-forme de calcul informatique en prenant en compte la contention, la cohérence et la concurrence. Or vous allez le voir, cher lecteur, que ça marche aussi avec les projets informatiques dans la vraie vie…
Commençons par un schéma, avant d’introduire les concepts. Ce qui va suivre est très pertinent lorsque vous vous plongerez dans la documentation de Kafka. Cela vous aidera aussi si vous êtes en train de construire un méga système partagé pour un grand site internet, et que l’on vous parle de cohérence et de contention.
Gunther explique ceci avec la « Universal Scalability Law » USL en 1993, en complément de la loi d’Amdahl. Il démontre que l’ajout de nouveaux processeurs dans une architecture tend à diminuer les performances globales du système lorsque les coûts de cohérence ainsi que la contention sont pris en compte.
On utilise ici le mot « performance » mais c’est plutôt la notion de débit (throughput) que l’on voit dans ses papiers de recherche. L’ajoute de processeurs augmente le coût de synchronisation et de recherche de cohérence. Pensez quelques instants aussi à 5 micro-services, puis 40 micro-services identiques. La scalabilité répond à certaines lois informatique et elle n’est certainement pas linéaire.
Je n’ai jamais posé d’équations sur le Touilleur Express en presque 20 ans mais je vais tenter le coup ici.
N représente le nombre de processeurs ou d’humains utilisant le logiciel
Les paramètres , et représentent respectivement les niveaux de contention (par exemple, mise en file d’attente pour les ressources partagées), le délai de cohérence (c’est-à-dire la latence pour que les données deviennent cohérentes) et la concurrence (ou parallélisme effectif) dans le système.
La contention Alpha peut être par exemple liée au réseau, à la taille des bus informatiques, aux bases de données ou au temps nécessaire pour que Jenkins valide votre PR…. c’est le temps d’attente pour l’utilisation de ressources partagées.
La cohérence Beta est le temps nécessaire pour obtenir un consensus et pour que de la donnée distribuée soit consistante. Si vous pensez architecture réactive et à de la distribution de messages sur des caches, c’est le temps nécessaire pour s’assurer que votre cluster a correctement répliqué la donné.
- L’utilisation simultanée de ressources uniques entraine de la contention
- Les données distribuées entrainent la nécessité d’un effort de cohérence
- tout ceci a forcément un impact sur le débit / la performance et donc sur la scalabilité de votre logiciel/votre équipe/votre plate-forme
Et donc pour un projet informatique ?
Voici quelques cas de contention, et qui donc, impactent fortement la capacité à produire de l’ensemble d’une équipe :
- un serveur Jenkins partagé entre plusieurs équipes
- un seul développeur a le droit de merger le code sur la branche principale
- l’équipe de production et d’exploitation travaille pour une dizaine d’équipe projets, et doit gérer des demandes / de l’attente / des priorités
- l’équipe client/product owner est aux USA alors que l’équipe de développement est en France : la fenêtre de communication est réduite
- seuls les internes ont accès aux fichiers de logs, et il faut passer par un ticket pour avoir les logs systèmes
Et quelques cas de situations qui entraînent des soucis de cohérence :
- les développeurs travaillent tous en remote
- il faut que chaque ticket de bug soit revu par le PO, un DEV et un QA car les informations saisies ne sont jamais suffisantes, ou les termes ne sont pas compris par le développeur
- la spécification utilisée par les testeurs en recette est mise à jour par le chef de projet pour le client, alors que les développeurs ont travaillé sur une version plus ancienne
- une Pull-request reste trop longtemps ouverte, et il est presque impossible de la merger
- un ticket spécifié il y a quelques mois passe en développement, alors que le besoin n’existe plus
Solutions théoriques sur un problème du monde réel
Après avoir exposé les 2 lois, et avoir j’espère sensibilisé votre intérêt pour le sujet, voyons un petit peu ce que l’on peut mettre en pratique.
Nous sommes donc d’accord que l’ajout de ressources n’augmentera pas linéairement la productivité de votre projet. Et par ailleurs, que l’ajout de ressources entraîne un coût de coordination qui dégradera la performance globale de l’équipe.
Tout ceci est un peu capilo-tracté car je mélange des lois informatiques pensées pour du traitement de tâches parallèles, avec de la gestion de projet informatique. J’assume. C’est plus un exercice pour vous permettre de voir differemment des sujets que vous vivez sur votre projet.
La contention en premier lieu peut se réduire en utilisant plus de services isolés. Si vous avez réellement un seul Jenkins pour 40 projets, prenez immédiatement une autre solution comme Github Actions par exemple. Chaque projet aura alors sa propre ligne d’intégration continue, et il n’y aura plus de partage de ressources. Sur un projet récent j’ai vu que l’intégration d’un expert Jira nous a aidé à refaire les tableaux de bord, et à améliorer radicalement la qualité de la communication en quelques semaines.
Eliminez aussi les transactions. S’il faut 5 validations pour avoir une machine en préprod pour réaliser des tests de charge, c’est 5 validations de trop. Allez sur des environnements sécurisés dans le Cloud, surtout s’il s’agit d’infrastructure temporaires. Cela nécessite la mise à disposition d’un accès et certainement un contrôle stricte des crédits, au risque sinon de se retrouver avec une facture douloureuse à payer en fin de mois. Mais c’est envisageable, en rappelant quelques bases de contrôle.
Essayez aussi de revoir en détail votre production. Comme avec mon exemple initial, certaines tâches ne peuvent pas être effectuées en parallèle. Dont acte ! Il faut se concentrer sur ce qui prend le plus de temps, et qui est le plus facile à exécuter en parallèle.
Concernant la cohérence il faut accepter que votre système soit éventuellement consistant. Cela veut dire qu’un testeur pourra trouver un bug et se référer à une spécification qui aura déjà évolué entre temps. C’est aussi le testeur qui trouve un bug déjà corrigé : ce n’est pas grave. Vous pouvez réduire ce souci en intégrant le QA dans l’équipe de DEV : le coût de la cohérence sera alors moins important à payer. Idem avec un client : s’il se cache derrière une spécification sans prendre attache avec les développeurs, il y aura forcément un coût de cohérence à payer. Priez pour que votre Product Owner soit compétent pour retranscrire ses intentions…
L’objectif je pense dans un projet doit être de viser l’amélioration permanente. Cela peut parfois destabiliser une équipe, et il est important que ce travail soit fait à plusieurs.
Je recommande de tester beaucoup d’idées et de ne pas craindre d’abandonner une pratique, lorsque celle-ci ne semble pas fonctionner. Cela veut aussi dire que les Tech Leads doivent embrasser cette mission. Ils doivent chercher à optimiser en permanence le travail de l’équipe. Leur rôle c’est de tester Github Action avec la CB d’un commercial. C’est de revoir les attributs d’un ticket pour faciliter le classement sur le Kanban. C’est de renforcer ou diminuer le nombre de personnes nécessaire pour approuver une PR. Je vous recommande d’essayer de changer rapidement et de conserver des notes de vos décisions. Un nouvel arrivant dans l’équipe pourra alors voir l’histoire du projet : « tiens on est passé sur des sprints de 3 semaines à 2 semaines il y a 2 mois… » ou encore « tiens ils ont déjà demandé des accès SSH sans succès il y a 4 semaines…«
La clé de tout ce travail c’est d’être bienveillant et d’avoir confiance dans l’équipe. Considérez que l’équipe donne son maximum. Si vous êtes ce tech-lead, il va falloir trouver des alliés et du courage. Et parfois il faudra bousculer et pousser des murs. Si vous êtes professionnel et pédagogue, tout passera. Soyez à l’écoute, et expliquez pourquoi.
Amdahl et Gunther n’ont maintenant plus de secrets pour vous.
Liens et ressources pour aller plus loin :
- Loi d’Amdahl en FR sur Wikipedia
- USL Scalability par N.Gunther sur son site
- Un article sur le blog de knóldus sur la transformation numérique
- Laws of Scalability dans le module Reactive Architecture / Building Scalable System. Vous devez vous inscrire sur academy.lightbend.com et les formations sont gratuites.
- Les photos de cet article viennent du site pxHere
Commentaires récents