- Ces derniers temps, les développeurs passent davantage de temps à corriger et compléter le code généré par les LLM (grands modèles de langage)
- Comme pour du code legacy, modifier le code en toute sécurité exige d’abord de comprendre ce qui a été implémenté et pourquoi ; or, le code produit par un LLM rend ce processus plus difficile
- Certaines équipes ralentissent parce qu’elles relisent et retravaillent suffisamment le code, mais beaucoup intègrent dans le dépôt du code à peine lisible et sommairement testé
- Cela crée une « dette de compréhension (Comprehension Debt) », qui se paie ensuite par un coût en temps bien plus élevé lorsqu’il faut modifier le code
- Les LLM peuvent généralement aider à résoudre un problème jusqu’à environ 70 %, mais ils n’évitent pas la « Doom Loop », ces échecs répétitifs ; au final, une situation où un humain doit comprendre et corriger lui-même le code finit inévitablement par se produire
Le code généré par les LLM et le problème de la dette de compréhension
- Dans le développement logiciel, l’usage d’outils de génération automatique de code basés sur des LLM, comme ChatGPT et Copilot, est en hausse
- Ces outils génèrent rapidement du code complexe, même sans connaissance approfondie ni compréhension directe de la part du développeur
- Mais, dans ce processus, l’intention, les contraintes et le mode de fonctionnement du code ne sont pas clairement compris, ce qui entraîne l’accumulation d’une dette de compréhension
Qu’est-ce que la dette de compréhension (Comprehension Debt) ?
- La dette de compréhension désigne une situation où les membres de l’équipe ne comprennent pas suffisamment la qualité, la structure et l’intention du code
- À court terme, elle peut accélérer le développement, mais à long terme elle risque d’entraîner divers effets indésirables, comme une hausse des coûts de maintenance, l’apparition de bugs ou des limites à l’évolution fonctionnelle
Risques supplémentaires du code généré par les LLM
- Le code généré par les LLM produit rapidement un résultat sans fournir de commentaires clairs ni de contexte
- Il expose plus facilement des problèmes de partage de connaissances insuffisant au sein de l’équipe et de manque de compatibilité avec le système existant
- Une dépendance répétée au code produit par les LLM peut finir par affaiblir la fiabilité du code de l’ensemble du projet
Comparaison avec la dette technique
- La dette technique classique résulte d’un compromis conscient des développeurs, alors que la dette de compréhension liée aux LLM peut s’accumuler inconsciemment, ce qui la rend plus dangereuse
- Le problème est plus difficile à identifier, et le suivi de ses causes comme sa résolution deviennent plus complexes
- Lorsqu’il faut corriger le problème, il est fréquent de tomber dans la « Doom Loop » (un cercle vicieux où faire tourner le LLM à répétition échoue toujours)
Conclusion et implications
- Au final, il faut qu’un humain lise et corrige lui-même le code
- Lorsqu’on utilise un LLM, il est essentiel de documenter et de partager le contexte et l’intention du code afin que toute l’équipe les comprenne
- Des mécanismes à l’échelle de l’organisation sont nécessaires : renforcement de la revue de code, amélioration des commentaires et de la documentation, sessions de partage de connaissances
- Sans gestion de la dette de compréhension, les avantages des outils d’automatisation peuvent au contraire se transformer en risque à long terme
- L’ensemble du secteur est aujourd’hui assis sur une montagne de dette de compréhension qui grossit rapidement
8 commentaires
Moi aussi, je considère que l’IA ne peut pas tout décider entièrement, donc je prends mes propres décisions pour tout le code et je vérifie moi-même si cela a bien été fait.
Pour ma part, je m’en sers simplement pour écrire des snippets de moins de 10 lignes (par ex. parsing JSON, implémentation d’un tri). Même en l’utilisant seulement comme ça, j’ai l’impression de gagner un temps phénoménal.
Construire l’ossature avec l’aide de l’IA, ça va, mais peaufiner les détails jusqu’au bout semble vraiment très difficile.
Personnellement, il m’est souvent arrivé de ressentir les effets secondaires quand je codais avec un LLM de 0 à 90. Ces temps-ci, j’essaie donc autant que possible de faire moi-même la partie de 0 à 90, et j’utilise surtout le LLM dans la phase qui va de 90 à 100, ce qui me satisfait bien.
Avis Hacker News
J’ai constaté que la dépendance croissante aux LLM aggravait des problèmes qui existaient déjà.
En présentant le concept de « construction de théorie » de Naur, l’auteur souligne que lorsqu’une équipe de développement se dissout, le programme concerné est en pratique mort.
Il mentionne aussi l’idée de Lamport selon laquelle « programmer ≠ coder », en insistant sur le fait que l’essence de la programmation est de construire une théorie sur ce qu’il faut accomplir et comment y parvenir.
J’ai l’impression que plus un programmeur ne construit pas les modèles ou théories nécessaires, plus il a tendance à sentir que les LLM l’aident rapidement.
En pratique, j’ai aussi fait l’expérience qu’on peut créer davantage de valeur sur un projet logiciel lorsqu’on s’éloigne complètement de l’ordinateur et de la technique.
Quand on sait exactement ce qu’on veut, la vitesse de développement augmente de façon spectaculaire, et c’est là que les LLM deviennent très utiles.
Avec un objectif clair, on distingue immédiatement les hallucinations du LLM.
Je ne pense pas qu’une approche consistant à fixer une toile blanche sans direction soit efficace.
Les LLM peuvent aider à démarrer, mais ils m’ont souvent au contraire entraîné dans une mauvaise direction.
Les problèmes les plus difficiles, je les ai parfois résolus en y réfléchissant en cuisinant dans la cuisine.
L’une des raisons pour lesquelles j’ai mieux réussi que mes collègues à utiliser le codage avec l’IA, c’est qu’avant même l’arrivée des LLM, j’avais déjà l’habitude de prototyper rapidement, puis de restructurer complètement ou d’abandonner ce que j’avais fait, encore et encore.
Cette approche — prototypage rapide, refactoring continu, etc. — est assez connue, mais beaucoup d’ingénieurs ont tendance soit à vouloir tout concevoir parfaitement dès le départ, soit à continuer à patcher un prototype sans jamais vraiment l’améliorer.
Avec l’IA, on peut se permettre à faible coût plusieurs implémentations en parallèle, tester et comparer différentes versions, et ainsi construire plus vite et plus efficacement une théorie du programme ou une conception plus solide.
L’IA raccourcit cette boucle et permet de concentrer davantage l’effort sur la revue des livrables, ce qui rend tout le processus de développement bien plus efficace.
J’ai trouvé frappant le lien entre l’idée de « construction de théorie » et la « dette de compréhension » laissée par le code généré par les LLM.
Je pense que cela touche profondément non seulement à la programmation, mais aussi à la façon dont les humains pensent et comprennent.
Le code, les textes ou les images produits par un LLM ne sont que des résultats de surface ; si l’on ne construit pas soi-même, et si l’on ne vit pas directement, la théorie qui se trouve derrière, on ne peut finir qu’avec une compréhension superficielle.
Même si un jour les LLM deviennent capables de faire eux-mêmes cette construction de théorie, j’ai le sentiment qu’une compréhension artificielle à laquelle l’humain n’a pas directement pris part manque de sens.
Je crains que plus les machines penseront à notre place de manière pratique, plus la capacité humaine de <compréhension> s’atrophiera lentement.
Je suis d’accord avec Lamport et, en même temps, je pense que l’IA peut aussi aider en partie dans ce processus de « construction de théorie » — c’est-à-dire la compréhension de la codebase, des algorithmes et du système dans son ensemble — aussi bien pour l’équipe existante que pour l’équipe qui reprend le projet.
Même si elle ne peut pas remplacer totalement toutes les connaissances, j’espère qu’elle pourra combler une partie de l’écart.
Il m’est déjà arrivé qu’une nouvelle équipe reprenne un projet après la disparition complète de tous les développeurs d’origine, et ce fut extrêmement difficile car tout le savoir de l’équipe précédente s’était évaporé.
En essayant de comprendre la conception d’origine, nous avons multiplié les bugs, et nous avons énormément souffert, jusqu’à finir par réécrire et étendre une grande partie du code.
Mais, même dans cette difficulté, nous avons fini par rejouer nous-mêmes beaucoup de problèmes de conception.
Les LLM produisent souvent des solutions qui fonctionnent, mais ils génèrent aussi fréquemment un code bien plus complexe que nécessaire.
Au moment de l’écriture initiale, on comprend le mieux quel est le problème, donc on peut éliminer assez facilement cette complexité. Mais plus tard, il devient plus difficile de simplifier fortement ce code complexe, car on finit par croire que toute cette complexité est indispensable.
Les mainteneurs du code manquent souvent de contexte ou de connaissances de fond, et ne se rendent pas compte qu’une alternative plus simple était possible.
D’abord, il faut formuler un prompt clair afin de guider le LLM pour qu’il ne produise pas inutilement quelque chose de complexe.
Ensuite, il faut toujours lui transmettre des règles, une formation ou du contexte pour résoudre le problème de la manière la plus simple possible.
Enfin, si la solution produite est encore trop complexe, il suffit de lui demander via un prompt supplémentaire de réduire cette complexité.
Le problème des LLM qui produisent d’énormes quantités de code difficile à déboguer existe réellement.
Il y a bien ce principe selon lequel « une équipe qui se soucie de la qualité doit faire suffisamment de revue et comprendre le code », mais la génération est si rapide que la revue n’arrive pas à suivre, ce qui crée un goulot d’étranglement ou mène à des validations purement formelles.
Autour de moi, on ajoute sans cesse des couches au processus de revue, mais cette approche n’est efficace que pour les développeurs juniors, et comme l’IA n’apprend pas, les mêmes problèmes reviennent.
Les LLM ont besoin d’énormément de contexte, mais la plupart des gens les utilisent en disant à peine plus que « fais-moi une fonction qui résout ce problème ».
Au final, tout le monde accumule une montagne de code incompris, donc de dette technique.
Ce qu’il faut fondamentalement, ce sont des « primitives » qui permettent de transmettre plus efficacement au LLM pourquoi il doit produire ceci, avec quel contexte, quelle intention et quels précédents.
Si la revue de code devient le goulot d’étranglement, je pense quand même qu’il vaut mieux que seul le code review soit le goulot, plutôt que d’avoir à la fois le codage et la revue comme goulots.
Les outils pour compléter ce processus existent déjà : lint, fuzzing, tests, etc.
Pour les personnes qui ont du mal à architecturer un projet entier ou à lire et analyser rapidement du code, l’ère des LLM peut être difficile, mais ce sont des compétences qui se développent, et tout le monde finira par s’adapter avec le temps.
J’aime ce domaine, donc j’aborde positivement ce nouveau défi.
J’ai eu l’expérience de créer différents fichiers d’instructions
.md, en y mettant les règles de codage que les agents doivent respecter, les pièges à absolument éviter, ainsi que des liens vers des documents de standards de code, puis en les maintenant à jour.Des modèles comme Gemini et Claude suivent assez bien ce type d’instructions documentaires, mais ils répètent parfois certaines erreurs (par ex., en C++, ils ignorent parfois l’instruction d’éviter
auto).J’espère que lorsque les modèles s’amélioreront, leur prise en compte de ce feedback progressera aussi.
Au final, j’ai eu l’impression que sortir du « vibe coding », garder pour les humains la responsabilité de la structure du code et des interactions entre unités, et laisser l’IA se concentrer sur la syntaxe et la frappe constitue le compromis idéal, à la fois pour la productivité et pour permettre au développeur de garder la main sur la direction générale.
Depuis que j’ai changé ma manière de penser la construction des prompts et du contexte, mon intuition sur l’usage des LLM s’est nettement améliorée.
J’aborde désormais cela sous l’angle d’une réduction de l’espace probabiliste des résultats via le prompt et le contexte.
Ce processus est aussi très efficace pour injecter de manière réutilisable la théorie du code.
Mais cette préparation du contexte demande encore beaucoup d’efforts et de réflexion, et il reste difficile de trouver la frontière entre ce qu’il vaut mieux implémenter soi-même et ce qu’il vaut mieux déléguer au LLM.
Je suis d’accord avec l’idée que « les primitives sont différentes », et je pense que la vraie unité importante, c’est le test.
Je fais en sorte que le modèle écrive aussi les tests quand il produit du code, et je vérifie toujours qu’ils sont lisibles.
Le code ne doit être fusionné que lorsque l’ensemble des tests passe.
Il faut améliorer en continu l’infrastructure de test pour éviter que toute la suite devienne trop lente.
Le vieux code legacy se caractérisait souvent par l’absence de tests, et la même chose vaut à l’ère des LLM.
À propos de l’idée que « cela devient un goulot », même quand je colle du code StackOverflow, je le lis et je le relis toujours avant de l’utiliser ; de toute façon, l’humain reste le goulot d’étranglement.
En fin de compte, sans ce processus, c’est comme si on ne pouvait pas utiliser le code du tout.
Récemment, dans le podcast de Dwarkesh Patel, un invité a recommandé le roman <A Deepness In The Sky>, et en le lisant, j’ai été frappé par l’importance qu’y prennent des systèmes informatiques vieux de plusieurs millénaires et d’anciennes connaissances legacy.
C’est un excellent roman qui traite de manière très intéressante la valeur du code legacy et du savoir organisationnel, donc je le recommande.
Podcast : https://www.youtube.com/watch?v=3BBNG0TlVwM
Infos sur le livre : https://amzn.to/42Fki8n
Je trouve triste que l’auteur Vernor Vinge soit décédé, et j’ai l’impression que ses idées deviennent avec le temps de plus en plus inspirantes et réalistes.
Le passage qui décrit comment on devient programmeur dans un futur lointain était vraiment fascinant.
À cause de l’immense quantité de bibliothèques et de packages, la compétence principale n’était plus d’écrire du nouveau code, mais de trouver et d’assembler des modules existants.
La véritable maîtrise était décrite comme la capacité à saisir toutes les nuances et interprétations détaillées du code existant.
« A Deepness In The Sky » semble être le deuxième livre de la série ; je me demande si ce n’est pas gênant de le lire sans avoir commencé par le premier.
La personne demande s’il est possible de le lire directement.
La plupart des développeurs ne comprennent pas l’assembleur ni le langage machine jusque dans leurs détails.
Les langages de haut niveau sont devenus la couche centrale de communication et de collaboration entre humains.
Avec l’arrivée des LLM, cette couche est en train d’être remplacée par le développement à base de langage naturel et de spécifications.
Je pense qu’au final, après des décennies d’évolution, les langages de haut niveau transmettent déjà sous une forme presque optimale la spécification du comportement d’un programme.
Si l’on tente davantage d’abstraction en langage naturel, on perd de l’information ; et si l’on descend vers les langages de bas niveau, tout devient trop verbeux.
Le saut vers le langage naturel n’est pas simplement un changement de couche d’abstraction, c’est fondamentalement une manière entièrement nouvelle de faire.
Les outils précédents — assembleurs, compilateurs, frameworks, etc. — reposaient tous sur une logique codée en dur et mathématiquement vérifiable, alors qu’avec les LLM, on entre dans un monde mêlé d’incertitude, de conjecture et même d’hallucination.
Beaucoup de développeurs JavaScript ne maîtrisent déjà pas vraiment en profondeur des concepts pourtant de haut niveau, comme les structures de données appropriées, le DOM ou l’API Node.
Il en résulte une dépendance excessive aux couches d’abstraction et un état où l’on comprend mal les mécanismes internes.
Un observateur extérieur ne peut alors que se demander : « qu’est-ce qui se passe réellement ici ? »
Ce phénomène est déjà accepté comme quelque chose de quotidien.
L’idée est qu’au fond, puisque plus personne ne connaît vraiment l’intérieur du code, le fait qu’un LLM l’écrive ne change pas tant la nature du problème.
Je pense qu’une spécification en langage naturel a bien un rôle à jouer, mais qu’il faut absolument une couche intermédiaire dotée d’une sémantique stricte.
Par exemple, avec Rust et un LLM, le système de types fort élimine les états invalides, et même si la compilation prend du temps, le résultat final est le plus souvent correct.
Il existe dans la communauté Rust une culture du « si ça compile, ça marche généralement » ; bien sûr, des bugs logiques restent possibles, mais l’espace réel des erreurs se réduit.
Dans l’idéal, on aurait un langage de programmation strict définissant rigoureusement le sens logique, avec des spécifications construites à l’aide de préconditions, postconditions, etc., et les LLM joueraient le rôle de traducteurs du langage naturel vers une spécification formelle.
Le langage naturel est, par essence, un langage ambigu.
Si une ambiguïté de parsing apparaît dans un langage de programmation, on y voit un bug grave ; dans le langage naturel, l’ambiguïté fait partie intégrante de la poésie, de la nuance, de l’implicite et de nombreuses fonctions de communication.
La nature déterministe des langages de haut niveau (
import, par exemple) n’est pas la seule différence.En programmation, le déterminisme ne fait pas tout, et un code qui n’a pas un sens clair pour un humain peut tout à fait rester déterministe.
Autrement dit, les axes « abstraction haute/basse » et « déterministe/probabiliste » sont deux questions complètement distinctes.
Pour moi et mon équipe, cela ressemble à une énorme vague de travail à venir.
Cela fait près de huit ans que nous sauvons du code legacy pour faire tourner des entreprises, mais récemment la demande a baissé parce que les sociétés s’appuient sur les LLM pour coder.
Mais si nous tenons encore environ 18 mois, je prévois qu’une énorme opportunité va apparaître, avec une avalanche de demandes pour résoudre la dette technique liée aux LLM accumulée en peu de temps.
Les dégâts de la dette accumulée par Claude avec ses « votre code est maintenant au niveau production » vont bientôt apparaître.
J’ai entendu l’anecdote vécue par une connaissance en relisant une PR générée par un LLM.
À première vue, c’était une PR dont la fonctionnalité semblait parfaitement marcher, mais en regardant de plus près, il s’est avéré qu’au lieu de mettre à jour le backend, elle manipulait seulement le cache par une astuce.
Il a fallu beaucoup d’efforts pour convaincre le manager qu’il ne fallait pas merger ce code.
Au final, cela m’amène à soupçonner qu’il existe déjà dans le monde beaucoup de logiciels « vibe coded » qui ne donnent que l’apparence de fonctionner.
Dans ce genre de cas, je pense qu’il vaut mieux simplement merger et laisser les gens subir eux-mêmes les conséquences.
C’est en faisant l’expérience directe des résultats d’une mauvaise décision qu’on en tire une leçon.
Sans feedback, il n’y a pas d’apprentissage, et au contraire ce type de manager finit par accroître ses attentes irréalistes et sa pression sur l’équipe de développement, jusqu’à épuiser les employés et provoquer un cycle de remplacement.
Je me demande comment un engineering manager non spécialiste a pu rester aussi longtemps dans l’industrie.
En quinze ans, j’ai rarement vu de véritables managers non techniques.
Si un tel manager a créé ce problème, je pense qu’il faut le signaler au CTO, au CEO, au propriétaire, aux investisseurs, etc.
Avec le code de LLM comme avec le code écrit par d’autres, on finit toujours par payer une « dette de compréhension ».
Même dans de grandes entreprises comme Google, il faut des mois de compréhension avant qu’un nouvel ingénieur puisse modifier rapidement un algorithme de manière significative.
Mais un code bien conçu par des humains reste nettement plus facile à comprendre qu’une production de LLM.
On peut demander directement des explications aux personnes, et même si un LLM peut fournir des réponses plausibles, il existe une vraie différence de contexte.
J’ai moi aussi beaucoup fait de « vibe coding », mais comme on ne construit pas vraiment de modèle mental du code, on gagne peut-être du temps au départ pour en perdre beaucoup plus au moment du débogage.
Il est de toute façon impossible, dans le monde réel, de tout concevoir parfaitement dès le début.
Et je ne trouve pas très fiable le fait de prendre les « lignes de code approuvées » comme mesure de productivité ou de gain de temps.
En revanche, utiliser du code produit par un LLM en l’examinant attentivement et en le façonnant progressivement dans la bonne forme est clairement efficace.
Ce processus ressemble plutôt à du pair programming.
Cela souligne qu’utiliser directement la sortie d’un LLM sans aucune revue — c’est-à-dire le « vibe coding » — ne peut pas être vraiment efficace.
En citant la loi de Kernighan, quelqu’un rappelle que « déboguer est deux fois plus difficile qu’écrire le code ».
Donc si un LLM génère le code, il se pourrait qu’il faille un LLM deux fois plus intelligent pour le déboguer.
Mon expérience ne va pas forcément dans ce sens : je dois toujours rester moi-même au volant.
J’utilise surtout Claude Code, et chaque fois qu’il se trompe, je dois le lui signaler clairement pour qu’il corrige, ou lui indiquer précisément la zone problématique.
Comme le LLM ne sait pas lui-même quelles erreurs il a commises, travailler avec lui ressemble à travailler avec un développeur junior.
Autrement dit, le débogage peut se faire au même niveau d’intelligence, mais le LLM, lui, n’identifie pas seul le problème.
À propos de l’idée qu’« il faut quelqu’un deux fois plus intelligent pour déboguer », je pense que cela peut aussi être lié aux différences entre les divers modes de « profondeur de réflexion » des LLM.
Pour une fonction complexe, j’utilise une méthode qui consiste d’abord à faire écrire au LLM des commentaires de code explicitant clairement l’intention et l’approche, puis à les joindre au rapport du LLM.
Certains commentaires disent que même lorsqu’on réutilise du code écrit par un autre développeur, une revue est nécessaire, donc que ce n’est pas différent du cas des LLM, mais je ne suis pas d’accord. Dans le cas des humains, il y a des mécanismes de réputation, de notoriété, de récompense et de sanction. À mon avis, si quelqu’un travaillait aujourd’hui comme un LLM, il serait licencié sur-le-champ.
J’ai toujours pensé que « l’IA ne prend pas la responsabilité à votre place. »
Je pense que le jour où l’usage de l’IA pour écrire du code sera interdit n’est plus très loin. Ça peut sembler absurde, mais je pense que cela deviendra une réalité.