Dans les dernières années, j'ai vraiment réalisé qu’en développement logiciel, plusieurs équipes se concentrent souvent sur de trop gros incréments. Pourtant, ils auraient vraiment avantage à décomposer leur problèmes en plus petits morceaux afin de simplifier leurs hypothèses et de les valider plus rapidement. Pourquoi? Parce ce que ce qui coûte cher, c’est le “waste” i.e. le temps passé à faire des choses inutiles.
Ma devise s'est inspirée de Mike Hill, qui utilise l’expression MMMSS (many more much smaller steps). Ce que ça veut dire, c’est qu’on ne découpe jamais assez notre travail. Si tu veux créer de la valeur plus rapidement, fais beaucoup plus d’étapes, beaucoup plus petites et valide plus rapidement que tu fais la bonne chose. Et il y a plusieurs façons d’y arriver.
Dans cet article, j’explore différentes techniques, méthodologies et outils qui peuvent aider, voire inciter les développeurs à travailler en de plus petits incréments et à optimiser leur flux de livraison.
Dans mon rôle d'engineering manager chez Nexapp, je demande continuellement aux développeurs : est-ce qu’on peut découper davantage? Est-ce qu’on peut éliminer du “nice-to-have”? Qu’est-ce qui est absolument nécessaire pour démontrer un avancement? Je pose ce genre de questions parce qu’on a tendance à vouloir commencer rapidement à coder et on se dit tant qu’à y être, je vais faire cette tâche en même temps. Mais c’est comme dans la rénovation d’une maison, ça finit par coûter cher!
Alors quels sont les avantages de travailler en petits incréments? Tout d’abord, cela permet d’éliminer rapidement les incertitudes et de démontrer que la solution est viable. Cela permet aussi aux développeurs de se concentrer sur des éléments très ciblés et d’éliminer une surcharge cognitive. Le tout en résulte souvent en du code mieux divisé, plus cohésif, avec moins de couplage et plus facilement testable…
Découper le travail en petits morceaux facilite aussi la communication au sein de l’équipe et permet une meilleure compréhension des objectifs. On valide que le code fait ce qu’on veut qu’il fasse plus facilement et que le besoin du client est bien répondu, parce qu’on est capable de mettre un résultat dans ses mains plus rapidement.
Cette approche consiste à découper le travail en tranches verticales, permettant ainsi de créer un “walking skeleton” ou une première tranche de fonctionnalités qui montre qu’elles sont réalisables et pertinentes. Ainsi, on élimine beaucoup d'incertitudes rapidement. On démontre que notre solution est viable.
Ensuite, on peut construire sur cet incrément-là, sur cette base-là, en faisant des petits incréments qui viennent ajouter la plus-value au fur et à mesure. Ce que ça nous permet de faire en coupant le travail si petit, avec des éléments très petits et très ciblés, on peut arrêter au moment où le client considère que son besoin est comblé. C’est ce qu’on appelle le “good enough”. Un autre effet parfois apprécié par certaines équipes est qu’on peut mieux se partager le travail. Chacun travaille sur la petite fonctionnalité supplémentaire qui vient se construire sur la portion principale qu'on a développée et validée en groupe au préalable.
Dans l'ensemble, notre objectif est de simplifier le travail des développeurs en précisant et simplifiant nos hypothèses, permettant d’écrire des tests les confirmant et de mettre rapidement le résultat de cette expérimentation dans les mains des utilisateurs pour valider qu’on fait la bonne chose et le minimum nécessaire pour répondre à son besoin.
Une bonne façon de découper les stories est l’utilisation de la méthode SPIDR. Cette méthode nous suggère d’utiliser différents axes pour découper une tâche. Ainsi, on suggère d’essayer de voir si on peut diviser le travail selon les différents scénarios (Paths), selon les composants de l’interface utilisateur (Interface), selon le type ou l’échantillon de données (Datas) ou encore selon les règles d’affaires (Rules). On peut commencer par subdiviser selon un axe puis resubdiviser selon un autre et ainsi de suite. L’ordre dans lequel on subdivise n’a pas vraiment d’importance. Ce qui est important c’est de voir s’il est possible de découper davantage une tâche.
Par exemple, on pourrait considérer qu'ajouter le bouton pour passer à la prochaine page, c'est une fonctionnalité et que passer à la page précédente, c'est une autre fonctionnalité (Interface). Ou alors que le paiement par carte de crédit est différent du paiement par carte cadeau (Path). Ou que l’on va commencer par gérer seulement les clients adultes et plus tard les clients aînés, puis les adolescents, puis les enfants (Datas).
Voici une illustration qui résume cette méthode :
Le TDD est une technique de développement qui consiste à écrire des tests avant de développer le code. L’objectif est de guider le “design” ou l’architecture de la solution en utilisant les tests comme guide. Cette approche permet de raccourcir le cycle de feedback, de minimiser l’incrément qu'on ajoute et de se concentrer sur des fonctionnalités plus spécifiques. C’est très simple : on écrit un test, on le fait passer, puis on améliore la conception. La découpe en petits tests nous aide à obtenir une meilleure cohésion et moins de couplage dans le code en plus valider rapidement si notre solution valide notre hypothèse (le test). Au fur et à mesure on améliore notre code, notre design et on corrige notre trajectoire.
Une fois que notre code est écrit et que nos tests passent du rouge au vert, nous avons concrétisé notre solution. Ce n'est plus une hypothèse, mais un fait établi. Notre design et notre architecture ne s'appuient donc pas sur des hypothèses, mais sur des faits puisque c’est au moment de la phase de refactoring qu’on fait notre architecture. Construire sur des faits, c’est souvent plus efficace que de se baser uniquement sur des hypothèses.
Cependant, cela requiert une excellence technique. Le TDD (Test-Driven Development) en soi n’est pas de la magie; il demande de posséder d’excellents “skills” de refactoring et une habileté à construire une architecture évolutive. Les développeurs qui pratiquent le TDD adoptent une approche de conception évolutive. Chaque test apporte une contribution à la solution, et après chaque test, nous réévaluons l'architecture si nécessaire. Avec une maîtrise technique solide, les développeurs sont en mesure de s'adapter rapidement aux changements et d'améliorer continuellement leur code.
Le Continuous Delivery est un processus qui vise à livrer rapidement des petits incréments de code en production. Cette approche permet de valider rapidement si le code fonctionne dans un environnement réel et d’éviter les grandes phases d’intégration et de conflits entre les différents développeurs.
Au début de ma carrière, on travaillait en waterfall et on essayait de tout prévoir à l'avance. On estimait les projets sur une période de six mois à un an, en essayant de couvrir tous les requis et l'architecture dès le départ. Après plusieurs mois, voire une année, on découvrait les limitations de notre solution imaginaire et l’invalidité de certaines de nos hypothèses. Ainsi, on découvrait trop tard qu’il fallait réorienter notre solution, qu’il y aurait des dépassements de coûts et qu’on n’allait pas respecter les délais prévus. Finalement, dans les derniers moments avant l'échéancier, on essayait de mettre en commun tout le code qui avait été fait, parce qu'on avait supposément tout planifié correctement. Quand arrivait le moment de l'intégration, de nombreux bogues et conflits apparaissaient. Il y avait donc une phase de stabilisation pour justement permettre le fonctionnement de la solution globale. Malheureusement, encore beaucoup d'entreprises ont, encore aujourd’hui, cette mentalité waterfall, déguisée sous le masque de la fausse agilité.
La philosophie extrême programming, qui est arrivée pour corriger ça et qui est à l’origine de la VRAIE agilité, opte pour un développement plus expérimental. L’objectif est d'essayer d'aller plus rapidement (plus tôt et non plus vite) de manière plus incrémentale, de ne pas essayer de tout prévoir à l'avance. On expérimente avec des petites hypothèses qu’on transforme en faits au fur et à mesure. Donc, au lieu d'essayer de tout prévoir, allons y avec ce qu'on connaît puis évoluons avec ça, on va apprendre de nos erreurs, on va apprendre de ce qu'on a essayé de faire et on va s'ajuster en cours de route. On veut développer en continu, avoir du feedback en continu et s’ajuster en continu. En expérimentant le plus tôt possible, on peut aussi valider le résultat et mesurer son impact. Savoir si c’est vraiment la meilleure solution qu’on a développée.
Bien sûr, pour pouvoir faire du continuous delivery, ça nous demande d'avoir des techniques pour découper le travail, pour travailler en petits incréments. Le continuous delivery pour moi, c'est un mindset. Oui, c'est une technique, mais c'est d'abord et avant tout un mindset qu'il faut expérimenter continuellement.
Le conseil que je pourrais donner aux jeunes développeurs, puis même aux plus expérimentés, c'est de continuellement chercher à découper son travail en plus petits morceaux. Quand on pense avoir atteint le bon équilibre ou la bonne taille, posez-vous la question : est-ce qu'il y a encore moyen de découper davantage? Et la réponse est souvent oui. Vous allez le savoir quand il n’ y a plus rien à découper et que c'est le plus petit “valuable piece”. À la limite, on fera les deux morceaux en même temps. Mais de les séparer, d'avoir la vision que c'est deux choses différentes, ça nous donne le luxe de pouvoir les faire de manière asynchrone.
Malheureusement, on s'arrête souvent à une certaine taille en se disant : je ne veux pas retravailler le code deux fois, je vais être obligé de changer trop d’éléments. Par contre, en gardant les blocs de travail trop gros, on a justement plusieurs raisons d'échouer et plusieurs raisons de faire des trucs qui, au final, n’auraient pas dû être faits. On a plus de chances de faire des erreurs, ou bien de coder inutilement. Il n'est pas nécessaire de rechercher la perfection ou de chercher à couvrir tous les cas possibles, car certains cas ne se produiront jamais dans la réalité. On est mieux d’attendre et être certain que c’est la bonne chose à faire, de se limiter à l’essentiel, puis de peaufiner avec les nice to have.
Je pense qu'on a tout intérêt à ne pas viser la solution parfaite du premier coup, mais plutôt de découvrir la solution parfaite au fil du temps. On est mieux d’accepter les imperfections en échange de feedback. Parce qu'en réalité, la solution parfaite n'existera jamais en développement logiciel. Du software, ça le dit, c'est malléable, donc ça doit évoluer dans le temps. Le contexte dans lequel on développe un produit va continuellement évoluer, que ce soit en raison des avancées technologiques, de la complexité croissante du système, des nouveaux besoins ou des nouveaux marchés. Pour être en mesure de s’adapter à ces changements, il est essentiel de travailler en petits incréments et de rapprocher la phase d’analyse, de réalisation et de validation.
En résumé, il est crucial pour les développeurs de travailler en petits incréments pour optimiser leur processus de développement logiciel. L’utilisation de méthodologies et d’outils tels que le découpage de travail, le TDD et le Continuous Delivery peut aider à travailler de manière plus efficace et à valider rapidement les progrès. Les développeurs doivent également se concentrer sur l’excellence technique et être prêts à s’adapter à l’évolution du contexte dans lequel ils travaillent.
Je vous ai présenté quelques techniques, philosophies, outils pour aller vers le MMMSS (Many More Much Smaller Steps), mais il en existe encore beaucoup d’autres tels que les micro-commits, la méthode mikado et le refactoring. Ce seront des sujets pour un futur article! Voici quelques références pour approfondir les sujets :