"Dette Technique" et procédés en conflit

Dans mon précédent article, je me proposais de bannir le terme "Dette Technique" de mon vocabulaire de travail lorsque celui-ci n'est pas utilisé dans son acception initiale :

[Contracter une Dette Technique] : procédé (heuristic) dans lequel on contrevient temporairement à l'état de l'art du projet afin de réaliser un objectif intermédiaire prioritaire.

mais dans cette acception, devenue générale dans notre industrie et au delà :

La Dette Technique : état général d'une solution jugée non conforme à l'état de l'art minimal communément admis dans l'industrie du logiciel.

Comme par exemple dans cet article qui contient l'absurdité suivante :

Collectively, companies today lose upward of $300 billion a year paying down "technical debt," as developers pour time into maintaining legacy systems or dealing with the ramifications of bad software.

Dans cet épisode je tenterai d'illustrer mon projet de remplacer l'expression

Notre solution comporte de la dette technique.

Par la phrase :

Notre solution s'appuie sur des procédés en conflit.

en présentant des exemples de solutions bâties (ou évoluant) sur la base de procédés en conflit, ce qui donne lieu à des situations que nous interprétons et qualifions (superficiellement) comme des cas de "dette technique".

Vocabulaire

Chaque membre d'une équipe de développement possède un état de l'art (sota) spécifique qui est la somme de tous les procédés (heuristics) que cette personne connaît à un instant t, et qu'elle partage autant que possible avec le reste de l'équipe. À travers toutes les interactions, propositions, discussions, et négociations au sein de l'équipe, se forme l'état de l'art de l'équipe, lequel est également dans une relation d'héritage, d'échange et d'intégration avec l'état de l'art de l'entreprise à laquelle l'équipe appartient (ou bien qu'elle sert).

Lorsqu'elle se voit confier la construction d'une solution nouvelle, c'est à dire lors du cadrage de projet, l'équipe (au sens très large) constitue, ou plutôt se bricole — le verbe n'est pas péjoratif ici — un état de l'art spécifique pour ce projet, qui servira de guide plus ou moins explicite pour l'ensemble des décisions prises au cours de son activité. L'état de l'art concerne autant que possible tous les aspects de cette activité. Il constitue à un instant t la somme de tous les procédés permettant à l'équipe de produire la meilleure solution à un problème défini imparfaitement dans un contexte particulier et une situation incertaine, et ce à l'aide de ressources limitées.

Cet état de l'art de l'équipe pour ce projet à l'instant t comporte donc un nombre considérable de procédés heuristiques (entendre : règles, recettes, patterns, principes, "lois", métriques, conventions, préférences, contraintes, etc.) Certains de ces procédés sont implicites, comme "ancrés" dans la culture de l'équipe, de l'entreprise ou même de l'industrie. Certains autres procédés doivent, au contraire, être explicités et parfois rééxaminés, voire remis en question à mesure que le contexte évolue.

Chaque discussion à propos de la solution que l’équipe est en train de créer est une occasion pour elle de consolider, rafraîchir, pondérer ou améliorer les procédés qui forment son état de l'art. Cette mise à jour se produit également dès que l'équipe, en conjonction avec l'ensemble des autres acteurs concernés par la solution, établit ou révise ses normes, ses principes d'architecture, ses règles de fonctionnement (qui sont aussi des procédés heuristiques), adaptant ainsi au fur et à mesure son état de l'art au contexte et au problème qu'elle cherche à résoudre.

Dans l'état de l'art de l'équipe, les procédés ont une portée, une visibilité et un impact variables. Par exemple, le choix de la couleur de fond sur mon poste de travail est un procédé qui m'appartient (je préfère un fond noir) et qui n’a aucune espèce d’impact sur le reste du travail de l'équipe. En revanche, les règles d'architecture choisies pour le projet ont une importance majeure, et font (généralement) l'objet d'un consensus continu.

Procédés en harmonie

Tous les procédés, quelles que soient leur importance et leur visibilité, doivent opérer en relative harmonie pour conduire l'équipe à une solution qui soit la meilleure possible étant donnés le contexte, sa compréhension imparfaite du problème à résoudre, et la limite des ressources dont elle dispose.

Voici quelques exemples de procédés résidant probablement harmonieusement dans l'état de l'art de votre équipe :

[Indentez votre code] afin d'en clarifier la structure.

Ce procédé est pratiquement invisible, car il est enfoui dans les règles de présentation du code implémentées par les IDEs. Ce n'est même pas un sujet. Les développeurs l'ont acquis il y a longtemps, lors de leurs premières heures de programmation. Le PO s'en fiche. Le client n'en a cure. Personne dans l'équipe ne songerait à le mettre en évidence (il n'est même pas utile de le documenter, il suffit de lire une portion du code du projet), et encore moins à le remettre en question.

[Critères de recette] pour chaque scénario utilisateur, clarifiant à quelles conditions ce scénario sera considéré comme réalisé.

Celui-ci est tout à fait visible, des développeurs comme des autres acteurs du projet, et ne fait l'objet d'aucun conflit. Appliqué correctement (i.e. s'il n'est pas abandonné, ou bien poussé ad nauseam par exemple), il fonctionnera très probablement en harmonie avec le reste de votre état de l'art.

Procédés en conflit

Lorsque deux procédés (ou plus) appartenant à l'état de l'art d'une équipe sont en conflit concernant un aspect de la solution, alors cette solution présente ce que l'on appelle communément et superficiellement une "dette technique". C'est à dire qu'elle ne constitue plus, dans le contexte de ce projet, la meilleure solution que pourrait donner cet état de l'art s'il était plus cohérent.

Je présente ici trois exemples d'états de l'art comportant des procédés en conflit.

Un remplacement implicite

Un procédé entre "silencieusement" en conflit avec un autre procédé ayant la même portée et le même niveau de visibilité.

Le procédé

[garbage in, garbage out] un programme ne peut pas produire un résultat qui transcenderait les défauts de ses données en entrée (ce qui permet de limiter les efforts de spécification et d'implémentation à déployer pour ce programme).

entre en contradiction avec le procédé

[graceful exit] un programme devrait s'efforcer de sortir d'une manière contrôlable et documentée lorsqu'il rencontre des données ou conditions inattendues.

quand une application en production rencontre une combinaison de données en entrée que les développeurs, peut être lassés d'attendre des réponses à leurs questions "et si…?", ou bien pressés par les délais, avaient décidé (unilatéralement) de négliger dans le code de la solution. Il est notable que les deux procédés ne sont pas, en soi, contradictoires. Le premier s'applique souvent dans des contextes où l'on souhaite une solution rapide à un problème temporaire. Le second, dans des contextes où la robustesse de l'application dans son environnement d'exécution est primordiale. Ici la substitution a lieu parce que le contexte du projet n'a pas permis, pour ce cas précis de données en entrée, de préciser la règle à appliquer. Il n'existe pas de règle universelle, et il n'est pas possible (ni souhaitable) de préciser toutes les règles. Il existe probablement dans l'état de l'art de l'équipe, un procédé qui aurait permis d'approfondir le cas en question. Quoi qu'il en soit, il devient évident à l'analyse, que ce nouveau problème avec la solution choisie n'a rien de purement "technique".

Remplacement explicite

Un procédé est mis en œuvre pendant un temps puis déclaré caduc au profit d'un procédé contraire.

Dans un projet d'innovation, la stratégie initiale :

[Proof of concept] : construire une solution simplifiée et dégradée en vue de démontrer la faisabilité d'une conception pour un futur projet industriel complet.

est supplantée par la stratégie :

[Move fast and break things] assumer des risques en production en privilégiant la réactivité plutôt que de longues études préalables d'impact.

lorsque le client qui a mandaté la réalisation du POC exige que le programme réalisé soit installé en production sans autre forme d'amélioration, créant ainsi une solution de facto "endettée". Là encore, les deux procédés ne sont pas en soi contradictoires au sein d'un même projet. On pourrait imaginer de mener les deux stratégies en parallèle, ou de les combiner. La contradiction vient du changement abrupt de stratégie : un programme écrit pour un environnement "safe to fail" est transporté du jour au lendemain dans un environnement où son fonctionnement attendu doit respecter des conditions de robustesse et de fiabilité plus élevées. Cette situation ne présente pas de dette technique, à moins que nous décidions d'appeler "endettement technique" le choix de résoudre un problème à l'aide d'une non-solution. De fait, cette situation présente l'histoire d'un choix entièrement non-technique.

Des objectifs contradictoires

L'état de l'art d'un projet contient des procédés visant des objectifs contradictoires.

Lors du cadrage de projet, l'équipe met en exergue un ensemble de pratiques reconnues et référencées par une grande partie de l'industrie, pratiques qu'elle convient d'appliquer pour ce projet.

[Référentiel de pratiques] à l'état de l'art et adapté au contexte donné, choisi par un groupe lors du cadrage d'un projet.

Ce procédé contredit une autre disposition du projet, répondant à un autre objectif :

[Onboarding des débutants] en vue de les former sur un projet de développement.

Les limites de ressources et les contraintes de délai conduisant l'équipe à revoir à la baisse cet objectif de formation, les débutants, alors livrés à eux mêmes, adoptent une stratégie

[Quick & Dirty] : réalisation via une série d'expédients dans l'objectif prioritaire de livrer une solution dans des délais remarquablement courts.

qui les écarte résolument du référentiel de pratiques, et conduit à une solution dégradée par rapport à ses objectifs initiaux. Cinq ans plus tard, l'équipe en passe de se voir confier la reprise de cet asset, dont le train de vie en production est de plus en plus coûteux, le qualifie de : "criblé de dette technique". Un jugement non seulement superficiel, mais de plus préjudiciable au succès de la reprise en question, puisqu'il escamote les contraintes du contexte et les choix de stratégies qui ont mené à la situation présente. Or ces choix et contraintes ne manqueront pas de se manifester à nouveau durant le projet de reprise (d'autant que l'équipe repreneuse multiplie d'emblée le coût prévisionnel de chaque évolution par un facteur de "dette technique"). Ces choix et contraintes ne sont nullement techniques. Ils manifestent plutôt la tendance de l'organisation à assurer la persistance d'une solution inadaptée à ses objectifs sous prétexte que l'investissement passé ne saurait être assimilé à une perte. Dans une organisation où l'échec n'est pas une option, l'échec est transféré, ajourné, et démultiplié. Il prend alors le nom de "dette technique".

La fin de la "Dette Technique"

En examinant les contraintes, les objectifs, le contexte et l'histoire d'un projet ayant mené à une certaine solution, on se rend capable de retracer les conflits de procédés qui ont contribué à diminuer ou altérer l'état de l'art initialement choisi pour cette solution.

Le terme de "dette technique" est particulièrement inapte à décrire cette détérioration de l'état de l'art en conflits de procédés, dans la mesure où ces choix conflictuels qui ont été pris ne relèvent pratiquement jamais de la technique prise isolément. Le terme n'apporte en fait aucune information nouvelle ou importante concernant la solution considérée, et je choisis de le bannir de mon vocabulaire, comme j'ai choisi de bannir de mon vocabulaire les termes :

bug : symptôme d'un problème encore non identifié dans une solution impliquant du logiciel, dont on présume qu'il a un rapport direct avec une erreur ou omission faite par ceux qui en ont écrit le code.

ressource humaine : personne (identifiable) considérée sous l'angle du temps de travail qu'elle peut contribuer à une activité donnée dans un plan de charge donné.

puisque je considère qu'ils empêchent au lieu de permettre les changements nécessaires pour créer efficacement des logiciels fiables et pérennes (bien qu'ils me permettent parfois de naviguer dans des environnements où ces termes sont utilisés par défaut).

Remettre en question son jugement, analyser

Étant donnée une solution produite à un instant t par une équipe, une personne extérieure au projet initial peut évidemment toujours évaluer cette solution, et juger que l'état de l'art mis en œuvre par l'équipe à l'instant t n'était pas adapté pour résoudre le problème en question, c'est à dire qu'une solution encore meilleure était possible à l'instant t. Un tel jugement n'est valable, évidemment, qu'à condition de disposer :

  • d'un meilleur état de l'art à l'instant t,
  • d'un procédé adéquat permettant de comparer les deux états de l'art,
  • et d'une connaissance approfondie des objectifs, du contexte et des contraintes auxquels était initialement assujettie l'équipe.

Dans les jugements (hâtifs) que nous formulons parfois en face d'une solution, nous manquons souvent d'au moins un de ces trois éléments, et alors nous nous comportons dans notre analyse comme si nous étions sur Twitter ou autre conversation en ligne : nous formulons un raccourci, lequel référence une doctrine qui restera inexpliquée, parce que le raccourci nous semble plus "efficace". Au mieux nous formulons nos préférences. Au pire nous devenons dogmatique.

Il ne viendrait à personne l'idée de critiquer le choix d'écrire en 1982 un logiciel en C alors qu'aujourd'hui on pourrait écrire, en beaucoup moins de temps et pour une plus grande robustesse, une solution fonctionnellement équivalente en Ruby. Par exemple Ruby dispose de manière native des tableaux associatifs. En C, il fallait implémenter une telle construction (par exemple en écrivant le code permettant de gérer une liste chaînée et un algorithme de hachage, ou bien en important des librairies extérieures).

Il serait tout aussi absurde de comparer d'un côté une solution écrite à l'instant t par une personne en stage de fin d'études et avec un budget d'un mois, de l'autre côté une solution écrite à l'instant t par une équipe de développeurs chevronnés avec un budget de 24 mois.

Si l'on considère ces deux solutions : le programme écrit en C, le programme écrit en stage de fin d'études, aucune n’est, en soi objectivement endettée techniquement.

On parlera — superficiellement — de "dette technique" seulement à partir du moment où l'on cherche à réutiliser dans le contexte présent, la solution écrite en C, alors qu'on n'ignore pas l'existence d'un langage comme Ruby, dans lequel des constructions comme le tableau associatif constituent une "commodité" et non plus un aspect crucial de la solution.

On parlera — superficiellement — de "dette technique" seulement à partir du moment où l'on cherche à donner à ce code écrit par un stagiaire les mêmes objectifs et les mêmes garanties qu'un programme de qualité dite "industrielle".

Dans ces deux cas, ce sont nos intentions à propos des solutions considérées qui font que ces solutions sont "endettées".

Si nous n'avons ni le besoin ni l'intention de détourner une solution du contexte, des objectifs, et de l'état de l'art dans lesquels cette solution existe, alors cette solution ne contient pas de "dette technique" à proprement parler.

De la même manière, le simple fait qu'une solution donnée existe dans un contexte où sa maintenance évolutive nous semble plus difficile, plus risquée ou plus onéreuse que dans d'autres contextes, ne nous permet pas d'employer le terme de dette technique à propos de cette solution. La seule chose que nous affirmons (implicitement) à propos d'une solution que nous qualifions de "techniquement endettée", c'est que cette solution ne correspond pas à notre état de l'art personnel, un peu comme ceux qui, se promenant dans un musée, décrètent en face de chaque œuvre exposée : "ça c'est de l'art, ça ce n'est pas de l'art, etc.".

La sagesse populaire admet que la beauté (et par conséquent aussi la laideur) se trouve dans l'œil de celui qui regarde,

beauty is in the eye of the beholder

La sagesse informatique devrait faire sienne un nouveau proverbe adapté à nos temps de "crise du logiciel" :

technical debt is in the eye of the stakeholder

La dette technique tient entièrement dans le regard des parties intéressées.