Les angles morts des LLM découverts pendant le codage avec l’IA. Basé sur Claude Sonnet
- Stop Digging → difficulté à changer de direction quand un problème survient
- Use Static Types → nécessité de mettre en place des types statiques
- Black Box Testing → dépendance excessive aux détails d’implémentation
- Use MCP Servers → problèmes de configuration et de sécurité des serveurs MCP
- Preparatory Refactoring → possibilité d’effectuer des refactorings inutiles
- Mise en Place → problèmes en cas d’échec de la configuration de l’environnement
- Stateless Tools → problèmes avec les outils dépendants de l’état
- Respect the Spec → forte probabilité de non-respect de la spécification
- Bulldozer Method → exécution excessive de tâches répétitives
- Memento → problèmes liés à une compréhension insuffisante du contexte
- Requirements, not Solutions → nécessité de clarifier les exigences
- Scientific Debugging → problèmes en cas de corrections fondées sur des suppositions
- Use Automatic Code Formatting → incohérences de style de code
- The Tail Wagging the Dog → focalisation sur des problèmes mineurs au détriment des tâches importantes
- Keep Files Small → problèmes lors de la modification de gros fichiers
- Know Your Limits → le modèle reconnaît mal ses propres limites
- Read the Docs → erreurs sur des informations en dehors des connaissances apprises
- Culture Eats Strategy → manque de cohérence dans le style de code
- Walking Skeleton → nécessité de faire d’abord fonctionner un système minimal
- Rule of Three → besoin de refactorer en cas de duplication de code
Ne pas s’enfermer dans un problème (Stop Digging)
- Les LLM actuels manquent de capacité à interrompre d’eux-mêmes une tâche et à changer de direction lorsqu’un problème survient
- Ex. : si, pendant l’implémentation de la fonctionnalité X, il apparaît qu’il faut d’abord implémenter la fonctionnalité Y, le LLM tente malgré tout de terminer la tâche initiale (X)
- C’est un avantage dans la mesure où le LLM exécute fidèlement les consignes, mais il lui est difficile de prendre conscience du problème et de se réorienter
- Stratégies pour éviter le problème
- Utiliser un modèle de raisonnement lors de la phase de planification pour déterminer les priorités et les tâches préalables
- Les LLM agentiques comme Sonnet lisent les fichiers et établissent un plan de travail → ils peuvent identifier les tâches nécessaires même sans instruction explicite de l’utilisateur
- Idéalement, le LLM devrait pouvoir détecter le problème et demander confirmation à l’utilisateur
- Cependant, cela consomme du contexte ; il peut donc être préférable qu’un LLM de supervision s’en charge
-
Example
- Après modification de la méthode d’échantillonnage aléatoire d’une simulation de Monte Carlo, demande à Claude Code de corriger les tests
- La nouvelle implémentation étant non déterministe, les tests réussissaient ou échouaient aléatoirement
- Claude Code ne l’a pas compris et a tenté de résoudre le problème en assouplissant les conditions des tests
- Il aurait plutôt fallu proposer un refactoring pour rendre la simulation déterministe
- Après modification de la méthode d’échantillonnage aléatoire d’une simulation de Monte Carlo, demande à Claude Code de corriger les tests
Utiliser des types statiques (Use Static Types)
- Le débat entre typage dynamique et typage statique est une question d’équilibre entre facilité de prototypage et maintenance à long terme
- Comme les LLM peuvent gérer le code boilerplate et les refactorings, la contrainte de choisir un langage adapté au prototypage diminue
- Il devient donc possible de choisir un langage plus favorable à la maintenance de long terme qu’au prototypage
- Stratégie de correction des erreurs de type
- Configurer l’agent pour que le LLM détecte les erreurs de type apparues après les modifications
- Cela permet aussi d’identifier facilement les autres fichiers à corriger
- Points d’attention
- Dans le cas de Python et JavaScript, les systèmes de typage sont progressifs → il faut configurer le type checker de façon stricte
- Rust convient en principe bien aux LLM, mais il est actuellement moins bien généré que Python/JavaScript
Tests en boîte noire (Black Box Testing)
- Les tests en boîte noire consistent à tester les fonctionnalités d’un composant sans connaître sa structure interne
- Comme les fichiers d’implémentation sont inclus dans le contexte, les LLM ont du mal à respecter les principes des tests en boîte noire
- Dans le cas de Sonnet 3.7 (avec Cursor), il existe une tendance à vouloir maintenir la cohérence du code → tentative de supprimer les duplications dans les fichiers de test
- Pourtant, dans les tests en boîte noire, conserver ces duplications est utile pour détecter les bugs
- Solution idéale
- Le LLM devrait pouvoir masquer ou résumer les détails d’implémentation des fichiers qu’il a chargés
- L’architecte devrait définir clairement les frontières d’encapsulation de l’information
-
Example
- Lors de la correction d’un test défaillant, Sonnet 3.7 a modifié une constante codée en dur pour la calculer à partir de l’algorithme d’origine
- Il aurait fallu conserver la constante telle quelle
- Lors de la correction d’un test défaillant, Sonnet 3.7 a modifié une constante codée en dur pour la calculer à partir de l’algorithme d’origine
Utiliser des serveurs MCP (Use MCP Servers)
- Les serveurs Model Context Protocol (MCP) fournissent une interface standard permettant au LLM d’interagir avec son environnement
- Le mode agent de Cursor et Claude Code utilisent largement les serveurs MCP
- Sans système RAG séparé, le LLM peut rechercher et modifier les fichiers nécessaires via des appels MCP
- Le modèle peut corriger immédiatement les problèmes après avoir exécuté les tests ou le build
- Points à considérer lors de la création de serveurs MCP personnalisés
- Dans Cursor, il est possible d’ajouter des commandes shell aux règles Cursor après avoir activé le mode YOLO
- C’est dangereux → des commandes shell arbitraires peuvent endommager l’environnement
- Alternative : écrire un serveur MCP personnalisé n’exposant que certaines commandes → sécurité renforcée
- Cependant, en mars 2025, la configuration de serveurs MCP par projet reste insuffisante dans Cursor
- Dans Cursor, il est possible d’ajouter des commandes shell aux règles Cursor après avoir activé le mode YOLO
-
Example
- Sonnet 3.7 a utilisé MCP pour vérifier les types d’un projet TypeScript et corriger les erreurs
- Le traitement est automatisé, sans avoir à copier-coller manuellement la sortie du terminal
- Cependant, le modèle peut inférer une mauvaise commande (
npm run typecheck)
- Sonnet 3.7 a utilisé MCP pour vérifier les types d’un projet TypeScript et corriger les erreurs
Refactoring préparatoire (Preparatory Refactoring)
- Le refactoring préparatoire consiste à refactorer d’abord afin de faciliter la modification à venir
- Comme le refactoring est une opération qui préserve le sens, il est plus facile à évaluer que la modification elle-même
- Effectuer d’abord le refactoring, puis la modification → facilite la revue et la correction des erreurs
- Problèmes actuels des LLM
- Tendance à vouloir tout traiter en une seule fois, sans refactoring préalable
- Exécution possible de travaux de nettoyage non nécessaires → risque de refactoring excessif
- Cursor Sonnet 3.7 est moins précis dans l’exécution des consignes → risque de refactorings sans rapport
- Pistes d’amélioration
- Il faut indiquer explicitement au LLM de ne modifier le code que pendant l’étape de refactoring précédant la modification
- Définir clairement la zone de code que le LLM peut éditer → évite les modifications inutiles
-
Example
- On a demandé au LLM de corriger une erreur d’import ; après correction, il a ajouté des annotations de type à une fonction lambda
- Certaines de ces annotations étaient incorrectes, ce qui a déclenché une boucle d’agent
- On a demandé au LLM de corriger une erreur d’import ; après correction, il a ajouté des annotations de type à une fonction lambda
Mise en place (Mise en Place)
- En cuisine, la mise en place consiste à préparer et organiser tous les ingrédients et outils avant de commencer
- Pour les LLM, la mise en place consiste à configurer complètement, avant le travail, les règles nécessaires, MCP et l’environnement de développement
- Sonnet 3.7 est fragile lorsqu’il s’agit de réparer un environnement cassé
- Il peut tenter de résoudre le problème en copiant-collant des commandes trouvées sur StackOverflow → risque d’endommager l’environnement
- Il faut donc configurer correctement l’environnement avant le travail pour éviter que Sonnet ne tombe dans une boucle de débogage
-
Example
- À cause d’un problème de
npm link, VSCode ne reconnaissait pas les imports d’un autre projet local- Cursor s’est obstiné à résoudre ce problème pendant la correction du lint et des tests, sans comprendre qu’il fallait exécuter
npm unlink
- Cursor s’est obstiné à résoudre ce problème pendant la correction du lint et des tests, sans comprendre qu’il fallait exécuter
- À cause d’un problème de
Utilisation d’outils sans état (Stateless Tools)
- Les outils doivent s’exécuter indépendamment à chaque fois, sans conserver d’état
- Le shell dépend de l’état du répertoire de travail courant → risque de confusion lié à la conservation d’état
- Sonnet 3.7 ne parvient pas à suivre correctement l’état du répertoire de travail courant
- Il faut configurer toutes les commandes pour qu’elles puissent être exécutées depuis le répertoire racine du projet
- Pistes d’amélioration
- Minimiser l’usage de commandes d’outils qui nécessitent des changements d’état
- Si un état est absolument nécessaire, fournir en continu l’état actuel au modèle afin de préserver la cohérence
-
Exemple
- Dans le cas d’un projet TypeScript composé de trois modules, common, backend et frontend
- Quand Cursor est lancé à la racine, il faut faire un
cdvers le bon répertoire → confusion sur les répertoires - Le problème a été résolu en ouvrant chaque module comme espace de travail distinct
- Quand Cursor est lancé à la racine, il faut faire un
- Dans le cas d’un projet TypeScript composé de trois modules, common, backend et frontend
Respect des spécifications (Respect the Spec)
- Lorsqu’on modifie un système, il faut distinguer clairement ce qui peut être modifié de ce qui ne peut pas l’être
- Si l’on modifie une API publique, il faut éviter de casser la rétrocompatibilité
- En cas d’intégration avec des systèmes externes, il faut s’adapter aux API réellement existantes → impossible de les modifier à sa convenance
- Si des tests échouent, il ne faut pas supprimer les tests → il faut en identifier la cause et la corriger
- Problèmes des LLM
- Forte probabilité de non-respect des spécifications → suppression de tests, modification d’API, etc. exécutées librement
- Respecter les spécifications relève du bon sens, mais il peut être nécessaire de l’indiquer explicitement dans le prompt
- Certaines limites ne peuvent être découvertes qu’au travers d’une code review
-
Exemple
- Après avoir échoué à corriger un test, Sonnet a remplacé son contenu par
assert True - Une fonction
publicrenvoyait un dict contenant la clépass→ Sonnet a tenté de la renommer enpass_(problème de mot réservé)
- Après avoir échoué à corriger un test, Sonnet a remplacé son contenu par
Méthode du bulldozer (Bulldozer Method)
- La méthode du bulldozer est une stratégie qui résout les problèmes par un travail itératif simple, avec un gain de vitesse dû à l’effet d’apprentissage
- Le codage avec l’IA est par nature efficace sur les tâches répétitives → avec suffisamment de tokens, de gros refactorings deviennent possibles
- Même des problèmes qu’un humain abandonnerait en se disant « il y a trop de travail » peuvent être résolus par un LLM
- En revanche, un LLM peut répéter la même tâche encore et encore, donc il faut vérifier ce qu’il est réellement en train de faire
-
Exemple
- En Haskell ou en Rust, modifier une fonction centrale peut exiger un refactoring étendu
- Le LLM peut automatiser la séquence lecture des erreurs de compilation → correction → recompilation
- Lorsqu’il faut modifier des valeurs de test codées en dur, le LLM peut relancer les tests puis effectuer automatiquement les corrections
- En Haskell ou en Rust, modifier une fonction centrale peut exiger un refactoring étendu
Memento
- Un LLM ne peut pas mémoriser l’état → à chaque tâche, il doit recommencer à comprendre la codebase depuis le début
- Il travaille uniquement à partir du prompt, du contexte explicite/implicite et des fichiers chargés par le modèle en mode agent
- Comme il doit reconstituer la compréhension de la codebase à chaque tâche, un échec dans la configuration initiale augmente fortement le risque de dysfonctionnement
- Stratégies pour éviter les problèmes
- Fournir clairement la documentation que le LLM peut consulter
- Configurer l’environnement pour que le modèle trouve facilement les informations nécessaires
- Fournir le contexte global du projet avant de demander des changements importants
-
Exemple
- On a demandé à Sonnet 3.7 d’établir un plan de tests end-to-end pour un projet existant
- Il a mal compris et a pensé que l’objectif global du projet était le test → il a modifié le README pour le centrer sur les tests
- On a demandé à Sonnet 3.7 d’établir un plan de tests end-to-end pour un projet existant
Clarifier les exigences (Requirements, not Solutions)
- En génie logiciel, une erreur fréquente consiste à proposer immédiatement une solution sans définir clairement les exigences
- Si l’espace du problème est suffisamment contraint, il suffit souvent de bien définir les exigences pour que la solution s’impose d’elle-même
- Si les exigences ne sont pas claires, des débats inutiles peuvent surgir à propos de la solution
- Problèmes des LLM
- Un LLM ne connaît pas les exigences → il génère la réponse la plus probable à partir des schémas appris
- Si on lui demande une tâche sans exigences claires, il peut produire un résultat à côté de la plaque
- On peut corriger une mauvaise interprétation en ajustant le prompt → mais si cette mauvaise interprétation reste dans le contexte, la correction devient difficile
- Pistes d’amélioration
- Si un mode de résolution précis est nécessaire, il faut l’indiquer explicitement
- Le LLM suit fidèlement les instructions, donc si on lui indique une mauvaise méthode, le résultat risque d’être inexact
-
Exemple
- Lorsqu’on demande à Sonnet de générer une visualisation, il produit par défaut un SVG
- Si l’on précise « interactif », il génère une application basée sur React → un seul mot-clé peut faire une grande différence
- Lorsqu’on demande à Sonnet de générer une visualisation, il produit par défaut un SVG
Débogage scientifique (Scientific Debugging)
- Il existe deux grandes approches pour corriger un bug
- Tenter des modifications au hasard puis compter sur la chance
- Analyser logiquement le fonctionnement du système pour identifier la cause de l’écart entre l’état réel et l’état attendu
- Le débogage scientifique (analyse logique) est une meilleure approche à long terme
- Problèmes des LLM
- Les LLM ont des capacités de raisonnement limitées, ce qui rend l’approche scientifique difficile
- Ils « devinent la bonne réponse » puis tentent immédiatement une correction → en cas d’échec, ils enchaînent des modifications aléatoires (boucle d’agent)
- Pour le débogage, des modèles de raisonnement comme Grok 3 ou DeepSeek-R1 sont plus adaptés
- Pistes d’amélioration
- Demander au modèle d’analyser la cause, ou fournir soi-même cette cause, améliore les chances de réussite de la correction
- Si on indique précisément la cause du problème, le modèle peut proposer une meilleure solution
-
Exemple
- Sonnet 3.7 a rencontré une erreur d’installation de paquet dans un environnement uv de base sans pip
- Faute d’avoir identifié la cause, il a répété des tentatives aléatoires → gaspillage de tokens et échec du débogage
- Sonnet 3.7 a rencontré une erreur d’installation de paquet dans un environnement uv de base sans pip
Utiliser le formatage automatique du code (Use Automatic Code Formatting)
- Les outils de formatage automatique du code (
gofmt,rustfmt,black, etc.) sont utiles pour maintenir un style de code cohérent- Les LLM ont du mal à respecter des règles mécaniques (par ex. pas d’espaces sur une ligne vide, limite de 78 caractères par ligne, etc.)
- Il faut confier le formatage aux outils et laisser le LLM se concentrer sur les tâches complexes
- Le même principe s’applique à la correction des lint
- Il est recommandé d’utiliser des lint avec correction automatique
- Il faut concentrer les ressources du LLM sur la résolution de problèmes complexes
La queue remue le chien (The Tail Wagging the Dog)
- Cela désigne une situation où un problème mineur finit par dicter un problème plus important
- On peut se focaliser à l’excès sur la résolution d’un problème de bas niveau et oublier l’objectif global de l’écriture du code
- Les LLM incluent toutes les informations dans le contexte d’une session de chat → difficulté à hiérarchiser ce qui est important
- Pistes d’amélioration
- Fournir dès le départ un prompt clair → orienter le LLM vers les tâches importantes
- Claude Code peut exécuter certaines tâches via des sous-agents afin d’éviter de polluer le contexte global
-
Exemple
- Lorsqu’on demande au LLM de réfléchir à une méthode pour exécuter une tâche donnée, il peut essayer d’exécuter la tâche elle-même au lieu de simplement y réfléchir
Garder des fichiers de petite taille (Keep Files Small)
- Le débat sur la taille des fichiers de code dure depuis longtemps
- Application du principe de responsabilité unique (une classe par fichier) vs acceptation de gros fichiers selon le contexte
- Si la taille des fichiers devient trop importante, cela peut poser problème lorsque les systèmes RAG chargent le contexte à l’échelle du fichier
- Dans des IDE comme Cursor, l’application des patchs peut échouer → et même lorsqu’elle réussit, elle peut prendre beaucoup de temps
- Exemple : dans Cursor 0.45.17, l’application de 55 modifications à un fichier de 64 KB a pris un temps considérable
- Sonnet 3.7 a du mal à modifier des fichiers de plus de 128 KB (limite de fenêtre de contexte de 200K tokens)
- Pistes d’amélioration
- Garder des fichiers de petite taille → le LLM peut alors gérer automatiquement les imports, etc.
-
Exemple
- Sonnet 3.7 a tenté de déplacer une petite classe de test dans un fichier Python de 471 KB
- La modification était minime, mais le patcher de Cursor n’a pas réussi à l’appliquer
- Sonnet 3.7 a tenté de déplacer une petite classe de test dans un fichier Python de 471 KB
Reconnaître ses limites (Know Your Limits)
- En cas de manque d’outils ou de limites de capacité, il faut reconnaître le problème et demander de l’aide
- Sonnet 3.7 est peu performant pour reconnaître ses propres limites
- Avec un prompt clair, il peut reconnaître ses limites → il faut configurer des avertissements sur les hallucinations pour certains sujets
- Problèmes
- Sonnet 3.7 croit à tort qu’il peut exécuter des commandes shell
- En l’absence de commande shell, il peut tenter de générer un script shell aléatoire → risque d’endommager l’environnement
- Il peut dire « je vais exécuter X », puis générer un appel concernant un Y totalement différent
- Sonnet 3.7 croit à tort qu’il peut exécuter des commandes shell
- Pistes d’amélioration
- Modifier le prompt ou fournir un outil dédié qui n’exécute que la tâche souhaitée
- En fournissant un outil spécifique, il est possible d’éviter des appels shell hors sujet
- Modifier le prompt ou fournir un outil dédié qui n’exécute que la tâche souhaitée
-
Exemple
- Sonnet 3.7 a tenté de générer un script shell inadapté pour accorder des droits d’exécution à un fichier
- Après une erreur de commande, il a répété des tentatives de correction erronées
- Sonnet 3.7 a tenté de générer un script shell inadapté pour accorder des droits d’exécution à un fichier
Lire la documentation (Read the Docs)
- Lorsqu’on apprend un nouveau framework ou une nouvelle bibliothèque, il est possible d’effectuer des tâches simples en modifiant le code du tutoriel
- Mais, à terme, il faut lire la documentation du début à la fin pour comprendre le fonctionnement global
- Atouts des LLM
- Les frameworks populaires sont souvent présents dans le pré-entraînement, donc ils se souviennent de la plupart des usages
- Mais avec des outils moins répandus ou sortis après la date de coupure des connaissances, des hallucinations peuvent apparaître
- Sonnet ne prend pas en charge la recherche web → il faut fournir la documentation manuellement
- Dans Cursor, fournir une URL permet de l’inclure automatiquement dans le contexte
-
Exemple
- Lorsqu’on a demandé au LLM de rédiger un YAML pour des appels de fonctions Python, il a généré une configuration incorrecte
- Après fourniture de la documentation, la correction a réussi et le format de sortie a été amélioré
- Lorsqu’on a demandé au LLM de rédiger un YAML pour des appels de fonctions Python, il a généré une configuration incorrecte
La culture l’emporte sur la stratégie (Culture Eats Strategy)
- La culture d’une équipe a un impact décisif sur sa capacité à exécuter une stratégie
- Les LLM génèrent du code en fonction de styles préalablement appris et de la fenêtre de contexte
- Ils privilégient les bibliothèques ou styles qui apparaissent souvent dans le contexte
- Si rien n’est précisé, ils appliquent leur style par défaut
- Stratégies pour modifier le style d’un LLM
- Modifier les règles de Cursor (changer le prompt)
- Refactorer le style du code existant vers la forme souhaitée → cela influence la prédiction du token suivant
- La taille du codebase a plus d’influence que le prompt → modifier le codebase est la solution de fond
-
Exemple
- Sonnet 3.7 privilégie le code synchrone en Python
- Pour obtenir du code asynchrone, la majeure partie du code existant a été portée en
async, avec succès
- Pour obtenir du code asynchrone, la majeure partie du code existant a été portée en
- Sonnet 3.7 privilégie le code synchrone en Python
Walking Skeleton
- Le walking skeleton est une stratégie d’implémentation minimale d’un système de bout en bout
- Même imparfait, on fait d’abord en sorte que l’ensemble du système fonctionne, puis on améliore les détails
- À l’ère du codage avec les LLM, il est devenu plus facile de construire rapidement l’ensemble du système
- Une fois le système opérationnel, l’étape suivante devient claire → il est important d’atteindre rapidement un état fonctionnel
- Comme les LLM ne peuvent pas utiliser directement le code qu’ils écrivent, il est important d’assurer un état de fonctionnement
La règle de trois (Rule of Three)
- La duplication du même code est tolérée jusqu’à deux fois, mais un refactoring est nécessaire à la troisième
- Une version améliorée du principe DRY (Don't Repeat Yourself)
- Cela clarifie le moment où supprimer les doublons → effectuer le refactoring à la troisième duplication
- Problèmes des LLM
- Les LLM ont tendance à générer du code dupliqué
- Si on demande une modification sans prompt précis, ils dupliquent tout le code puis appliquent la modification
- La suppression des doublons n’est effectuée que si le modèle décide spontanément de le faire → des consignes explicites sont nécessaires
- Pistes d’amélioration
- Il faut demander explicitement la suppression des doublons
- S’il existe déjà beaucoup de duplication dans le code, le modèle peut continuer à en générer
-
Exemple
- Lorsqu’on a demandé au LLM d’écrire du code de test, la même logique a été dupliquée dans plusieurs tests
- Le problème a été résolu après lui avoir explicitement demandé de créer une méthode auxiliaire
- mode agent
- Lorsqu’on a demandé au LLM d’écrire du code de test, la même logique a été dupliquée dans plusieurs tests
1 commentaires
Avis sur Hacker News
Les LLM commettent des erreurs différemment des humains, ce qui les rend difficiles à détecter
Quand un LLM ne connaît pas les exigences, il complète avec la réponse la plus probable issue de ses données d’entraînement
En génie logiciel, il est important de clarifier les exigences
Les LLM ont un niveau de codage de « programmeur débutant très intelligent »
Les LLM essaient de donner trop de réponses
À mesure que les billets du blog se multiplient, un travail d’organisation devient nécessaire
Conseils utiles pour coder avec des LLM
Les LLM sont faibles en calcul et en arithmétique
Points à prendre en compte aux côtés des développeurs humains
Cas où trois LLM ont identifié un « bug » inexistant