Deux heures du matin. Vous fixez la ligne if is_premium and not is_trial and billing_cycle == 'annual' dans le diff, le curseur suspendu au-dessus du bouton « Request Changes », sans parvenir à appuyer. Le titre de la Pull Request est « Fusionner la logique de calcul de remise entre customer et broker ». Les deux blocs de code se ressemblent effectivement beaucoup — charger un enregistrement, mettre à jour un champ de pourcentage, écrire dans la base de données. Un ingénieur a repéré cette « duplication », extrait une méthode unifiée avec un paramètre entity_type. Cela semble propre, raisonnable, DRY.
Mais vous savez que la remise customer va passer à un calcul par paliers dès demain, alors que la logique de commission broker ne changera pas avant deux ans. En forçant la fusion maintenant, on élimine certes une duplication apparente, mais on soude ensemble deux concepts dont les trajectoires d’évolution sont radicalement différentes. C’est exactement le piège contre lequel Sandi Metz mettait en garde il y a dix ans. En juin 2026, son article est remonté en tête de HN avec 409 points et 272 commentaires — à une époque où une IA peut vous générer en une seule fois cinq cents lignes de code « apparemment correct », ce principe mérite plus que jamais d’être rediscuté.
Sandi Metz a dessiné une carte de la décomposition
C’est lors de sa conférence à RailsConf 2014 que Metz a prononcé pour la première fois la phrase « duplication is far cheaper than the wrong abstraction », avant de la coucher dans un article de blog en 2016. Son argumentation est d’une simplicité désarmante, sans recours à aucun cadre théorique — elle se contente de décrire un processus de dégradation que tout le monde a vécu mais que presque personne n’a nommé :
Le programmeur A découvre du code dupliqué. Il extrait une méthode ou une classe commune, remplace tous les points de duplication et s’en va satisfait. Le temps passe, de nouveaux besoins arrivent, l’abstraction existante est « presque » suffisante. Le programmeur B prend le relais et, par respect pour le code existant, ne repart pas de zéro : il ajoute un paramètre à la méthode, puis une branche conditionnelle à l’intérieur. Puis un troisième besoin, un quatrième paramètre, un cinquième if-else. À la huitième étape, vous arrivez, confronté à des milliers de lignes de logique conditionnelle enchevêtrée, essayant de comprendre quelles branches appartiennent à quel appelant.
La solution de Metz est tout aussi simple : ré-inliner l’abstraction, laisser chaque appelant ne conserver que le code dont il a réellement besoin, puis repartir de l’observation — quelles similarités sont réelles, lesquelles ne sont qu’apparentes.
Ce discours est percutant parce qu’il fracture une croyance quasi religieuse dans la communauté des programmeurs : que la duplication est le mal, et que l’éliminer est intrinsèquement vertueux.
Le bagage historique de DRY : un contresens de la base de données au code source
Le principe DRY a été formulé par Andy Hunt et Dave Thomas en 1999 dans The Pragmatic Programmer. La formulation originale est : « Every piece of knowledge must have a single, unambiguous, authoritative representation within a system. » L’accent porte sur la « connaissance », pas sur les « caractères ». Une requête SQL, une règle métier, une valeur de configuration — voilà ce qu’est une connaissance. Deux boucles for syntaxiquement similaires n’en sont peut-être pas une.
Mais l’industrie a progressivement compressé cette distinction au fil de sa diffusion. « Ne vous répétez pas » est devenu « ne faites pas apparaître de lignes de code dupliquées ». Un principe heuristique, élevé au rang de règle absolue, a engendré une quantité massive d’abstractions qui n’auraient jamais dû exister : des classes Repository génériques, des méthodes Processor universelles, des fonctions de service dont la table de paramètres est plus longue que la logique métier.
Ce que fait Metz, fondamentalement, c’est recalibrer DRY : elle s’oppose au « DRY prématuré ». Ce point a d’ailleurs été souligné à maintes reprises dans la discussion HN — « cet article ne dit pas qu’il ne faut pas abstraire, il dit qu’il ne faut pas forcer l’abstraction ».
Les signaux d’ingénierie d’une « mauvaise abstraction »
Dans les commentaires HN, de nombreux ingénieurs ont partagé leurs critères pour identifier une abstraction erronée. « Ce code fait-il la même chose, ou en a-t-il seulement l’apparence ? » — c’est le critère central, cité à maintes reprises. Les signaux suivants, lorsqu’ils s’accumulent, indiquent que l’abstraction est probablement mauvaise :
Des branches conditionnelles pilotées par des paramètres. Une méthode reçoit des paramètres booléens ou énumérés et distribue en interne vers des chemins de code presque sans chevauchement. Chaque nouveau paramètre multiplie l’espace d’état que l’appelant doit comprendre.
Modifier le comportement d’un appelant oblige à « rattraper » pour les autres. Cela signifie qu’il n’existe pas de véritable relation de co-variation entre les appelants. Ils se trouvent simplement à exécuter un code similaire aujourd’hui.
L’abstraction n’a pas de raison d’être auto-évidente. Une abstraction saine peut être comprise sans remonter à ses appelants. Si chaque lecture exige de retracer le contexte de trois sites d’appel pour comprendre ce que fait cette logique, l’abstraction a perdu sa plus grande valeur : réduire la charge cognitive.
Quand vous ajoutez une nouvelle fonctionnalité, votre premier réflexe est de contourner l’abstraction, pas de la réutiliser. C’est le signal le plus fiable. L’intuition humaine capture souvent un problème structurel bien avant que la rationalisation a posteriori ne le formule.
Le débat central de HN : vérité unique contre localité
Dans la discussion HN, deux commentaires très votés ont précisément délimité les contours de la controverse.
Le commentaire de l’utilisateur lg5689 représente l’argument central du camp « abstraction d’abord » : « Il faut toujours suivre le principe de la source unique de vérité. Si deux duplications de code, en divergeant, constituent un bug, il faut refactoriser. La duplication crée un couplage invisible à longue distance dans le code. » Cette logique procède d’une intuition d’ingénierie saine : quand la même règle métier est dispersée à deux endroits, un jour quelqu’un en modifie une et oublie l’autre, et le bug est semé.
La réponse de l’utilisateur jonahx pointe vers le scénario qui préoccupe vraiment Metz : « Fondamentalement, l’article traite précisément du cas où l’on ne sait pas encore combien il y a de sources de vérité. Ces deux emplacements utilisent-ils le même algorithme, ou des versions légèrement différentes ? Et surtout, vont-ils changer pour les mêmes raisons ? Le point crucial est qu’une abstraction erronée détruit la localité — qui est en réalité le seul attribut auquel vous tenez vraiment quand vous modifiez du code. Je veux faire cette unique modification, sans m’inquiéter des effets de bord sur des parties non reliées du système. »
Les deux arguments sont valables, mais leurs domaines d’application diffèrent. Si vous êtes certain que les deux emplacements représentent le même fait invariant — le même taux d’imposition, le même algorithme de chiffrement, la même règle de validation de données — alors l’abstraction est le bon choix, et le bénéfice de la source unique de vérité dépasse de loin le coût de l’abstraction elle-même.
Le problème est que, dans ce métier, nous surestimons notre capacité à « voir si deux blocs de code sont synonymes ». Les deux calculs de l’exemple de Metz se ressemblent beaucoup : charger un enregistrement customer, mettre à jour un pourcentage de remise ; charger un enregistrement broker, mettre à jour un pourcentage de commission. Aujourd’hui, ils suivent par hasard le même motif « charger une entité — mettre à jour un pourcentage ». Mais la logique métier de la remise customer peut basculer à tout moment vers un calcul par paliers, tandis que la commission broker reste un pourcentage unique — parce que ces deux champs sont de nature complètement différente sur les plans juridique, contractuel et comptable.
La frontière entre « un code qui se ressemble » et « un code qui représente la même vérité » est plus difficile à tracer que la plupart des ingénieurs ne veulent bien l’admettre.
Comment la génération de code par IA amplifie ce problème
C’est précisément la raison pour laquelle l’article de Metz est remonté au sommet à l’ère des outils de programmation IA à grande échelle.
Les LLM ont deux tendances structurelles dans la génération de code. La première : ils cherchent naturellement à « éliminer les duplications apparentes ». Quand vous générez deux modules fonctionnels similaires avec le même prompt, le modèle extrait de ses données d’entraînement la méthode de fusion la plus « standard » et produit une abstraction paramétrée. Il ne vous demande pas quelles sont les frontières métier entre customer et broker — il n’a pas participé aux discussions de spécification. Il a simplement trouvé, au sens statistique, la représentation partagée optimale.
La seconde, plus insidieuse et plus dangereuse : les abstractions générées par LLM sont anormalement lisses. Le nommage est pertinent, l’indentation est correcte, la disposition des paramètres dégage une logique. Une mauvaise abstraction écrite par un humain dégage souvent une odeur — nommage bancal, structure lâche, on sent qu’elle s’adapte de force. La mauvaise abstraction du LLM paraît professionnelle, assurée, inattaquable. Le relecteur est plus enclin à la laisser passer.
De nombreux commentateurs HN ont relevé cette tension. L’un d’eux a dit que « le LLM est une machine anti-abstraction naturelle », car il ne comprend pas la sémantique métier, seulement les motifs de surface. Un autre a dit que « le LLM fait chuter le coût de la copie, donc le seuil de justification de l’abstraction doit être bien plus élevé ». Une observation plus acérée encore : « Ce sur quoi je passe le plus de temps à réfléchir, c’est comment expliquer à un LLM comment une base de code existante fonctionne réellement, sans qu’il la déforme par incompréhension. »
Un phénomène d’ingénierie intéressant émerge : le code généré en masse par l’IA a tendance à copier plutôt qu’à abstraire. Non pas que le modèle comprenne le principe de Metz — il manque de mémoire persistante entre les fichiers d’une requête à l’autre. Il ne sait pas qu’il a écrit quelque chose de similaire dans la session précédente — sauf si vous injectez le code pertinent dans la fenêtre de contexte. Résultat : les productions d’IA contiennent à la fois de nombreux blocs de code « qui auraient dû être abstraits mais ne le sont pas », et des blocs « déjà abstraits mais dans une direction complètement fausse ». Les deux types d’erreurs cohabitent dans le même dépôt — c’est peut-être le nouveau quotidien que l’IA apporte aux mainteneurs de code.
Choisir entre deux erreurs
La position de Metz est souvent simplifiée en « la duplication vaut mieux que l’abstraction », ce qui n’est pas tout à fait juste. Ce qu’elle dit vraiment, c’est : si vous devez choisir entre la duplication et une mauvaise abstraction, choisissez la duplication. C’est un principe de second ordre — il ne vous dit pas ce qui est juste, il vous dit, quand vous n’êtes pas sûr de ce qui est juste, quelle direction d’erreur coûte le moins cher.
Un commentaire très voté sur HN propose une règle opérationnelle pratique — la « règle de trois » : première occurrence, écrivez-la. Deuxième occurrence, tolérez la duplication, mais commencez à observer. Troisième occurrence, envisagez l’abstraction — et uniquement le long de l’axe qui varie réellement. Cette règle implique un prérequis essentiel : il faut du temps pour que les vrais motifs émergent. Ce n’est qu’après que le code a tourné un certain temps dans le dépôt que l’on peut discerner quels sites d’appel évolueront ensemble et lesquels divergeront.
Un autre commentateur a résumé plus crûment : « Le contraire de DRY n’est pas la duplication, c’est WET — Write Everything Twice. Écrivez tout deux fois, puis observez. À la troisième fois, passez à l’action. »
Le jugement d’ingénierie au-delà des données
Les chiffres du vote HN — 409 points, 272 commentaires — indiquent que ce sujet touche une fracture encore ouverte dans la communauté des ingénieurs. Tout le monde sait que DRY peut être mal appliqué. Le problème est que, génération après génération, les nouveaux ingénieurs reçoivent encore, lors de leur intégration, une formation qui place « l’élimination de la duplication » comme une priorité non négociable dans la revue de code.
À une époque où une IA peut écrire du code conforme aux règles à votre place, la compétence vraiment rare n’est plus « comment abstraire », mais « quand abstraire ». Cette dernière ne demande pas de la technique, mais de la patience, un jugement formé par l’observation continue du domaine métier, et le sang-froid, face aux coûts irrécupérables, de défaire une abstraction. Les mots de Metz résonnent jusqu’à aujourd’hui : « Face à une abstraction erronée, le chemin le plus rapide vers l’avant est de reculer. »
Il n’y a pas de réponse ultime sur ce sujet. Je ne prends pas position de manière absolue dans ce débat. L’abstraction est l’un des rares concepts véritablement fondamentaux du génie logiciel, mais sa valeur dépend étroitement du moment et du contexte. Cet article ne milite pas pour remplacer l’abstraction par la duplication. Ce que je veux souligner, c’est un jugement plus étroit : dans ce nouveau régime où le code est produit alternativement par des humains et des machines, le coût d’« attendre avant d’abstraire » est peut-être bien plus bas que nous ne l’avons longtemps cru, tandis que le coût de « défaire une abstraction erronée » est peut-être plus élevé que nous ne l’imaginions.
Déclaration de l’auteur : cet article est basé sur l’analyse de l’article original de Sandi Metz, de la discussion HN de juin 2026 et de la littérature d’ingénierie associée. Il ne constitue pas un conseil technique absolu. Les décisions d’ingénierie doivent intégrer le contexte spécifique — taille de l’équipe, phase du cycle de vie du produit, ancienneté de la base de code, couverture de tests — n’importe laquelle de ces variables peut inverser la direction du jugement proposé ici.