Overblog
Editer l'article Suivre ce blog Administration + Créer mon blog
Laurent COCAULT

Let it RAIN on this DRY land

La semaine dernière j'achetais le dixième numéro du magazine "Coding" dont le dossier spécial nous promet de nous apprendre à "optimiser notre programmation". Sur ses "quarante pages pour progresser" deux sont consacrées aux "10 erreurs de programmation et de codage les plus courantes à éviter". Et la liste commence par un rappel du principe DRY: Don't Repeat Yourself. Ce principe est certainement le plus communément admis parmi les développeurs et est très bien assimilé même chez les plus jeunes d'entre nous. Il est si bien établi qu'on ne peut pas trouver un seul livre sérieux sur la pratique du développement qui ne le mentionne pas: chapitre 12 du "Clean Code" de Robert Martin, chapitre 3 du "Refactoring" de Martin Fowler, chapitre 21 du "Working effectively with legacy code" de Michael Feathers... et planche 29 de la formation interne "Clean Code" que je partage avec mes collègues de Capgemini.

Let it RAIN on this DRY land

La duplication de code est présentée comme une très mauvaise pratique pour des raisons évidentes de maintenabilité. Si un même comportement est implémenté à plusieurs endroits dans la base de code source, alors il est nécessaire de procéder à plusieurs modifications identiques pour adapter ce comportement de manière homogène. Par ailleurs, il existe un risque non négligeable que le mainteneur oublie de modifier l'un des blocs de code dupliqué, ouvrant ainsi la porte à des anomalies particulièrement difficiles à analyser.

Pourtant, l'oncle Bob lui-même semble ouvrir la porte à une duplication de code dans la présentation qu'il fait du principe de responsabilité unique dans son ouvrage "Clean Architecture". En effet, si un comportement est susceptible d'évoluer pour deux raisons différentes, alors il peut s'avérer utile de l'implémenter dans deux éléments distincts, initialement identiques mais susceptibles de diverger à moyen ou long terme. Une telle suggestion est de nature à heurter notre conception dogmatique du principe DRY et à considérer qu'il y a de bonnes et de mauvaises duplications de code.

L'application du Single Responsibility Principle (SRP) n'est pas le seul cas où la duplication de code peut avoir un intérêt. Des raisons techniques peuvent à mon sens être prises en considération: je suis en effet partisan d'une certaine méfiance par rapport aux modules "communs" que nous sommes nombreux à connaître sur nos projets. Si vous développez depuis plusieurs années, vous avez très probablement travaillé sur un projet qui comprend un module "common" ou "utility" qui propose aux modules applicatifs de partager une même définition ou une même implémentation de mécanismes de base, métiers ou techniques. Ces modules s'appellent d'ailleurs plus souvent "commons" ou "utilities" parce qu'ils agrègent de nombreux éléments communs qui, avec le temps, ont tendance à ne plus proposer d'autre argument de consistance que d'être partagés par les autres modules. Et chaque fois qu'une nouvelle fonctionnalité présente un intérêt pour deux modules applicatifs, il finit absorbé par le trou noir qui grandit au milieu du système. Jusqu'au jour où le système que l'on croyait modulaire n'est plus qu'un monolithique commun autour duquel gravitent quelques légers modules applicatifs. Le problème avec cette approche des "communs" est qu'elle conduit à une instabilité permanente d'un élément de base qui se traduit par des freins au développement liés à la gestion des dépendances versionnés. On passe ainsi un temps considérable à "attendre" que la nouvelle version des "communs" soit publiée pour faire avancer les modules applicatifs. Les éléments communs sont également la source d'une violation constante du SRP, où la vision initialement partagée qui a conduit à l'introduction d'un comportement dans le module commun, diverge finalement et conduit à des anomalies, voire des dissensions au sein de l'équipe de développement.

Let it RAIN on this DRY land

Ce constat a certainement été fait par les promoteurs des architectures micro-services qui prônent une organisation du code dans laquelle les services ne partagent pas d'éléments communs. Et même si ces architectures s'appuient sur un socle technique existant qui n'est pas nécessairement partagé, il est inévitable que les micro-services redéfinissent les mêmes structures de données pour pouvoir interopérer. Là encore, cette duplication est tout à fait légitime puisqu'elle sert un principe d'architecture qui vise à maitriser la complexité du système.

En lisant mon argumentaire contre les "commons", certains pourraient objecter que le commun unique n'est pas inéluctable et qu'on peut tout à fait jouer sur la modularité de ces communs. L'inconvénient avec une telle approche est qu'on multiplie alors les efforts de ginéluctable estion des dépendances versionnées et qu'on court le risque d'une incompatibilité du socle à force de le faire évoluer au gré des besoins des modules applicatifs. Des systèmes de gestion de telles dépendances existent dans les différents écosystèmes de programmation (Maven et OSGi pour Java, PIP pour Python ou encore npm pour JavaScript), mais la fragmentation extrême à laquelle elles exposent fragilise la cohérence de la structure logicielle; les chances d'avoir un problème avec l'une des dépendances deviennent en effet assez sensibles. Le monde JavaScript nous fournit plusieurs exemples des impacts d'une granularité poussée à l'extrême.

En mars 2016, de nombreuses équipes de maintenance de sites Web (inévitablement développés en JavaScript) se trouvèrent confrontés à un dysfonctionnement du module "left-pad" elle-même causée par l'indisponibilité du module "kik" que son auteur avait décidé de retirer des dépôts npm suite à un différend avec la plate-forme GitHub. Le plus déroutant dans cette histoire est que le code source en question ne faisait que 11 lignes de code.

Let it RAIN on this DRY land

Plutôt que de dupliquer ces 11 lignes de code, en respectant naturellement la mention à l'auteur initial et à la licence du code copié, les développeurs de "leaf-pad" ont tiré la dépendance vers ce commun à plusieurs modules JavaScript et se sont ainsi exposés à la rupture de disponibilité qui a suivi.

Cet exemple montre que, si l'application du principe DRY ne fait aucun doute au sein d'un module, il est tout à fait questionnable lorsque son application introduit des couplages entre modules ou une interdépendance vis-à-vis de modules qui échappent à notre contrôle. Dans certains cas, la taille du périmètre réutilisé justifie qu'on accepte la dépendance ou le couplage. Dans d'autres, le pragmatisme doit nous inviter à considérer une infraction au principe DRY.

Autrement dit, laissez une place dans vos réflexions à un principe RAIN - Repeat Anything If Needed.

Partager cet article
Repost0
Pour être informé des derniers articles, inscrivez vous :
Commenter cet article

Bastien Guende 03/06/2020 18:58

Salut Laurent, je découvre ton blog, et je ne peux qu'approuver, surtout la partie autour de la limite SRP/DRY: l'un est simple à appréhender, à mettre en place et apporte un bénéfice direct, et est même sorti comme une metrique dans certains outils comme Sonar, et un autre demande de prendre du recul et à réfléchir sur l'utilisation et le futur du design.
Définitivement un article que je vais partager :)