La dette technique
Maintenance sur son produit
Référence : “Unraveling the Secrets of Software Maintenance: A Strategic Guide for Product Managers” (Henrique from the Tech PM’s Newsletter)
Les actions de maintenance vont viser soit à la correction d’erreur (donc plus ou moins les tickets de bug du sprint), soit rendre possible la croissance du produit (typiquement, l’idée derrière la notion de dette technique). Le Product Manager doit être capable de comprendre les raisons des maintenances et la valeur que peuvent apporter le traitement des bugs autant que de la dette technique.
La correction de bug correspond spécifiquement aux erreurs qui affectent la version en production (ou sur le point d’être livré en production, si nous avons une phase d’intégration/qualification). Cela peut aller de petites erreurs dans le code, jusqu’à des problèmes subtiles nécessitant de l’investigation, et depuis une erreur mineur que nous planifierons dans un prochain sprint jusqu’au “hotfix”, bloquant pour les utilisateurs, et nécessitant une correction la plus rapide possible.
Maintenant, ça n’évite pas d’une part de chercher une solution plus robuste, plus “stratégique”, au problème (contrairement à la solution rapide, généralement “tactique”). Nous pourrons même chercher à anticiper les éventuels problèmes que pourrait rencontrer notre produit dans le futur, notamment en améliorant la qualité du code. L’amélioration de la qualité de code peut également passer par la refactorisation. Il peut également s’agir de mises à jour sur les dépendances, importantes pour bénéficier des corrections de failles ou bugs issues de nos dépendances, ou même pour apporter de l’innovation à la façon de répondre aux besoins.
Tout ces cas de maintenance font typiquement l’objet du traitement de la dette technique.
Les différents types de dette techniques
- Faille de sécurité : régulièrement des failles de sécurité sont identifiées sur les logiciels, les modules, etc. utlisés par notre solution. Il suffit parfois d’une “petite” mise à jour (une nouvelle version mineur), mais cette mise à jour a parfois un impact conséquent sur le code, et peut nécessiter beaucoup plus de travail.
- Évolution des techniques de programmation : il s’agit parfois de code qui avait été écrit en suivant des pratiques, depuis passées de mode. Cela peut paraître anodin, dit comme ça, mais les nouvelles pratiques peuvent améliorer la lisibilité, faciliter la maintenance du code, voir résulter d’évolutions du langages ou des applications tierces avec lesquelles nous travaillons. Il peut également s’agir d’une mise à jour du code pour une raison tout sauf anodine : attirer de nouvelles recrues en montrant du code moderne et à jour (dur d’attirer avec du code de 10 ou 20 ans d’age et peu maintenu…)
- Implémentation “à la va-vite” : c’est une chose très courante, car on souhaite rapidement mettre notre solution entre les mains des utilisateurs, pour bénéficier de retours rapides. Certaines optimisations sont mises de côté et restent quelque peu à l’abandon, car de nouvelles urgences arrivent sur la pile. Ce peut également être un problème de documentation manquante, rendant le code d’autant plus difficile à maintenir … Et lorsqu’une telle situation traîne, il devient parfois plus simple de recoder la portion concernée.
- La charge de la plateforme évolue : assez proche de la précédente, dans la mesure où on anticipe rarement la charge, lorsqu’on démarre un nouveau service, ou un nouveau produit. Ici, on parle de la croissance de l’acquisition de notre produit, qui entraîne d’autant plus d’activité sur l’application, et donc d’autant plus de données à traiter dans le même temps. Le premier réflexe serait de provisionner plus de ressources, pour supporter cette charge… Mais ces ressources ont un coût, alors qu’optimiser le code peut être une solution plus pérenne.
- Des besoins d’administration : cet aspect reprend un peu des deux précédents. On va souvent fournir à nos propres équipes une solution simplifiée pour “gagner du temps”, et lorsque le produit commence à amener beaucoup d’activité, nos premiers outils d’administration ont souvent d’autant plus de mal à suivre. Il va donc falloir se pencher sur les besoins des administrateurs afin de leur fournir un environnement de travail un “petit peu” plus agréable, quand même.
- La dette pas que technique : certains produits impliquent la collaboration de plusieurs équipes, avec des processus, des outils, etc. permettant l’interface entre ces équipes. Ces processus et outils sont souvent mis en place sans une vision claire au long terme. Ils nécessitent des évolutions, avec un coût d’autant plus élevé qu’il faut également changer des habitudes de travail … Mais avec un véritable bénéfice au long terme.
Remarque : sur le dernier point, dans mon approche du produit, j’estime que les équipes d’exploitations sont des utilisateurs à traiter avec autant d’égard que nos clients, et doivent donc bénéficier des mêmes efforts (cas d’usage, étude des besoins, etc.).
Les solutions à la dette technique : respecter les bonnes pratiques
Bien souvent, traiter la dette technique consiste à refactorer le code. Derrière cette expression se cache toutes les tâches que les développeurs entreprennent pour coller au maximum avec les normes et bonnes pratiques de développement logiciel. Typiquement, nous parlerons
- de renommage (variables, méthodes/fonctions, classes, etc.) et de documentation du code, permettant de rendre son fonctionnement le plus claire et compréhensible possible;
- de réorganisation du code (e.g. découpage de fonctions en plusieurs petites fonctions, redécoupage de classes en fonction de leur rôle, etc.), pour éviter la duplication, réutiliser des morceaux commun, et ainsi optimiser le programme, assurer un maximum de cohérence de l’ensemble et, là aussi, assurer la clarté du code.
Ces bonnes pratiques vont s’appuyer sur des paradigmes (ou “combinaison de paradigmes”) de programmations possédant des avantages et inconvénients suivant leur contexte d’utilisation et leur support pour le langage (ou les langages) de programmation utilisé pour notre produit.
Elles vont également s’appuyer sur les patrons de conception (ou “Design Pattern”, on parle en anglais, chez les devs) permettant de fournir des solutions spécifiques à des problèmes spécifiques dans la façon de structurer son code.
Ces bonnes pratiques sont également constitués des règles que les développeurs se fixent dans leur façon de coder. Cela peut aller depuis la façon de nommer les variables jusqu’aux paradigmes de programmation et les objectifs de couverture par les tests unitaires (par exemple).
Autrement dit, les bonnes pratiques donnent un objectif vers lequel tendre lorsque les développeurs vont traiter la dette technique accumulée sur leur produit, dans le cadre de sa maintenance.
Les tâches techniques
Laisser traîner la dette technique la rend bien vite indigeste à traiter. Plus le temps passe, plus la solution devient difficile à maintenir, les impacts sur la performance peuvent s’en faire ressentir, et donc l’impact sur les clients. L’approche la plus saine sera donc de la traiter régulièrement, et de la prendre en compte au fil de l’eau, en tant que Product Owner / Product Manager.
La première chose à faire, c’est de traquer les tâches identifiées avec des tickets, afin de les retrouver dans le backlog. Elles doivent ensuite faire l’objet du même travail d’estimation que n’importe qu’elle autre tâche du backlog.
Il sera d’ailleurs pertinent d’estimer la porté et l’impact sur les utilisateurs (quels utilisateurs, et dans quelle mesure cela leur facilitera la vie), ainsi que la charge de travail que cette tâche représente. Dans cette estimation, il devrait être bon d’intégrer l’ancienneté de la tâche : plus elle a traîné dans le backlog, plus sa priorité augmente, de sorte à ce qu’elle soit rapidement traitée (autrement dit, quand on dépile les tâches techniques, on devrait commencer par les plus anciennes).
Attention, on peut vite être tenté de laisser traîner une tâche de dette technique apportant peu de bénéfice mais représentant beaucoup de travail. Le travail en question doit être revu, justement pour réduire la charge de travail, quitte à découper la tâche en un ensemble plus digeste.
Après cela, il faut prévoir d’intégrer un minimum de ces tâches techniques dans les sprints, afin de maintenir le backlog a une dimension raisonnable.
Après tout ça, s’il traîne encore des tâches dans le backlog, dont on repousse constamment le traitement, il serait peut-être bon de se demander si elle sont toujours pertinentes, de la réestimer, de la réactualiser si possible, et sinon … de la retirer (i.e. “won’t do”).
Et pour finir, une question qui fâche : que faire lorsqu’on a déjà accumulé beaucoup de dette technique ? Honnêtement, nous en sommes encore à expérimenter des solutions.
La première à nous être venu à l’esprit (après “dépiler le backlog”) a été d’attribuer des labels à ces tâches. Nous avons ainsi identifié des thématiques,
- parfois très techniques,
- parfois rattachés à des besoins en interne (i.e. équipes d’exploitation),
- parfois à des sujets produits (donc sur des services à destination des clients.
Suivant les catégories, nous avons rattaché ces tickets à des sujets produits, ou bien nous avons estimé leur priorité, pour pouvoir ensuite les programmer dans nos sprint.