Nous developpons votre site vitrine SPA À PARTIR DE 300€. Oui, c'est dingue. Web SPA À PARTIR DE 300€ — Oui, c'est dingue. Parlons-en →

Comment donner du contexte à Claude Code pour qu'il n'invente rien

Comment donner du contexte à Claude Code pour qu'il n'invente rien

Quand un agent écrit du code qui ne compile pas, le diagnostic est simple. Le problème, ce sont les autres fois : quand il écrit du code qui compile, paraît raisonnable et repose sur une supposition fausse à propos de votre projet. Cela ne se corrige pas avec de meilleures instructions, mais en lui donnant le moyen de vérifier. Voyons d'où viennent réellement ces erreurs et quel contexte lui fournir pour qu'elles cessent. Les exemples portent sur un projet Laravel, l'un de nos terrains chez AndorraDev, mais l'idée de donner à l'agent le moyen de vérifier plutôt que de déduire vaut pour n'importe quel langage et n'importe quel agent.

Un agent invente dès qu'il doit déduire

Il est utile d'arrêter de parler d'"hallucinations" et de commencer à parler de déductions non vérifiées.

Quand vous demandez à un agent de travailler sur votre projet et qu'il n'a aucun moyen de vérifier quelque chose, il ne s'arrête pas : il comble le vide avec ce qui est le plus probable. Il lit une migration vieille de deux ans et conclut que la colonne existe. Il voit un modèle avec une relation déclarée et tient pour acquis que la clé étrangère est en place. Il trouve une méthode dans la documentation d'un paquet et l'appelle, même si la version que vous avez installée l'a renommée.

Aucune de ces trois choses n'est une erreur de raisonnement. Ce sont des inférences raisonnables à partir d'informations incomplètes, et chacune produit du code qui semble correct et qui ne l'est pas.

La façon de les réduire n'est pas de lui demander de ne rien inventer. C'est de lui donner les moyens de vérifier.

Il y a trois niveaux de certitude et il vaut mieux savoir où vous êtes

Toute la question s'ordonne assez bien si vous séparez d'où vient chaque donnée.

Niveau un : ce qu'un humain a écrit. Le fichier de contexte du projet, le README, les commentaires. C'est le moins coûteux à lire et le moins fiable, parce que la personne qui l'a écrit pouvait se tromper et parce que cela vieillit en silence. Personne ne reçoit d'alerte quand le code cesse de ressembler au document qui le décrit.

Niveau deux : ce que dit le code. Les migrations, les modèles, les routes déclarées. C'est vérifiable et c'est ce que le projet prétend être. Cela reste une inférence : une migration vous dit ce qu'on a voulu créer, pas ce qu'il y a dans la base de données aujourd'hui.

Niveau trois : ce qui existe vraiment. Le schéma réel interrogé, le nombre de lignes, les routes que le routeur a enregistrées, les dernières erreurs du journal. C'est le plus coûteux à obtenir et c'est la seule chose qui ne se discute pas.

La plupart des configurations restent au niveau un, qui est justement celui qui se dégrade le plus.

Le fichier de contexte écrit il y a six mois ment déjà

Presque tout le monde commence et s'arrête ici : un fichier d'instructions à la racine du dépôt avec ce que l'agent devrait savoir. C'est le bon endroit pour les conventions, et le pire possible pour tout ce que le code peut changer sans prévenir ce fichier.

Un fichier d'instructions du projet est commode : il est toujours présent, il n'y a rien à invoquer et il décrit des conventions que le code n'exprime pas. Pour cela, il est irremplaçable.

Le problème, c'est quand il devient la source de vérité sur le schéma ou l'architecture. Alors trois choses se produisent en même temps. Il se paie à chaque tour de chaque session, parce qu'il occupe du contexte en permanence. Il se périme sans prévenir, dès que quelqu'un renomme une colonne et ne touche pas au document. Et il affirme avec la même assurance ce qui est vrai et ce qui est obsolète, parce que rien ne vient le confronter.

Dans les grands projets, il est facile que ce fichier dépasse les deux cents kilo-octets. À cette taille, ce n'est plus une aide : personne ne le relit en entier, et une bonne partie décrit du code qui a changé il y a des mois.

La dérive de schéma est l'exemple parfait

S'il y a un cas qui illustre la différence entre déduire et vérifier, c'est celui-là.

Vous prenez le schéma réel de la base de données et vous reconstruisez le schéma attendu à partir des migrations. Ensuite vous les comparez, et les deux directions de l'écart signifient des choses différentes :

Une colonne présente dans la base de données et dans aucune migration est un avertissement. Quelqu'un l'a créée à la main, ou elle est restée d'une migration supprimée. Le code ignore qu'elle existe.

Une colonne présente dans une migration et absente de la base de données est critique. Cela veut dire que cette migration n'a pas été exécutée là où vous regardez, et tout code qui l'utilise va échouer.

Un agent qui lit seulement les migrations ne peut pas distinguer ces deux cas, parce qu'il n'en voit qu'un seul côté. Et c'est exactement le genre de chose sur laquelle il va écrire du code avec la plus grande assurance.

Andie recommande

Il en va de même pour les clés étrangères orphelines. Une migration vous dit que la relation est déclarée ; seul un comptage contre les données réelles vous dit qu'il y a neuf lignes pointant vers un enregistrement qui n'existe plus. La requête tient sur une ligne et transforme un soupçon en fait, ce qui est justement la différence entre un rapport utile et un rapport qu'il faut vérifier à la main.

Dérivez l'API du code installé, pas de la documentation

La documentation d'un paquet décrit une version, et pas forcément la vôtre. Un agent qui la consulte lit un projet ressemblant au vôtre, alors que la réponse exacte se trouve à un ls de là, dans vendor.

Quand l'agent va utiliser un paquet, la question n'est pas "que dit la documentation de ce paquet", c'est "quelle version ai-je installée et qu'expose-t-elle". Et la réponse est sur le disque :

Le fichier de verrouillage prime sur tout le reste. Le composer.json du paquet lui-même peut ne pas porter de version, et une branche de développement ne vous dit rien. La version résolue dans le projet qui le consomme fait autorité.

Et le code installé prime sur le fichier de verrouillage. Un alias de branche peut mentir sur la version ; la présence ou l'absence d'un fichier, non. Vérifier que la classe que vous allez utiliser existe est plus fiable que vérifier le numéro de version.

Poussé à l'extrême, le motif que je préfère consiste à dériver les noms disponibles en appliquant la même règle que le framework. Si vous savez qu'un outil est nommé en convertissant le nom de sa classe en minuscules avec des tirets, lister le répertoire des classes vous donne l'ensemble exact de la version installée, quoi qu'en dise n'importe quelle documentation.

Détecter l'environnement au lieu de le supposer

Parmi les commandes qu'un agent propose et qui ne marchent pas, la plupart n'échouent pas parce qu'elles sont mal écrites : elles échouent parce qu'elles supposent où vit l'application. Et cela change entre votre portable, le conteneur et le serveur sans que rien dans le code ne le dise.

"Exécute cette requête" ne veut pas dire la même chose selon l'endroit où vit l'application. Cela peut être un docker exec vers un conteneur dont il faut deviner le nom, un appel direct en local avec Herd ou Valet, un chemin Windows natif où l'échappement du shell n'est pas celui de bash, ou une session WSL qui ressemble à Linux sans toujours l'être.

La règle qui fonctionne, c'est de vérifier avant d'exécuter et, surtout, de dire dans le rapport quel environnement a été utilisé. Un résultat sans cette ligne est un résultat que vous ne pouvez pas reproduire.

Séparer la lecture de l'écriture, toujours

La différence entre un outil qu'on utilise et un autre qu'on désinstalle au bout d'une semaine tient rarement à sa justesse. Elle tient à la possibilité de le lancer sans crainte la première fois, quand vous ne savez pas encore s'il va toucher à ce qu'il ne devait pas.

Sans argument, l'outil se contente de rapporter et ne touche à rien. Avec l'ordre explicite de corriger, il demande confirmation pour chaque changement. Et il existe un mode intermédiaire qui montre le diff sans l'appliquer.

Cela paraît évident et presque aucune configuration ne le respecte. La conséquence de ne pas l'avoir, c'est que les gens cessent de lancer des analyses au cas où, et alors l'outil ne sert plus à rien.

Il y a un cas où c'est particulièrement important : toute opération qui supprime des fichiers. Consolider cent migrations en une seule est extrêmement utile et c'est aussi l'opération qui peut vous laisser sans historique si elle s'exécute sur le mauvais environnement.

Étiqueter la provenance à l'intérieur du rapport

Un rapport automatique arrive comme un bloc homogène : vingt constats de même allure et de même ton. Rien ne distingue celui qui a été vérifié contre la base de celui qui a été déduit d'une migration d'il y a deux ans, et c'est précisément cette distinction qui décide de ce que vous pouvez croire.

Quand un rapport mélange des constats déduits du code et des constats vérifiés contre les données, la liste entière vaut ce que vaut son pire élément, parce que vous ne savez pas lequel est lequel.

La solution, c'est que chaque ligne dise d'où elle vient :

Orphaned Foreign Keys
  operations.client_seller_id -> clients.id
    [WARNING] 9 orphaned rows (verified against live data)

Schema Drift
  table: invoices
    [WARNING] column `legacy_ref` exists in DB but not in any migration

Ce (verified against live data) fait la différence entre un constat sur lequel vous agissez et un constat que vous devez aller vérifier. Et l'en-tête du rapport devrait dire dans quel mode il a été exécuté, pour que vous sachiez d'avance quel degré de certitude vous êtes en train de lire.

Ce qui existe pour la même chose et ce qui les distingue

Personne ne règle cela avec une seule pièce, et celles qui existent ne se concurrencent pas : chacune couvre un tronçon différent du trajet qui va de ce que l'agent suppose à ce qu'il peut vérifier. Autant les voir ensemble avant de choisir.

Le fichier de contexte écrit à la main est le point de départ de toutes. Coût nul, toujours présent, et c'est le seul endroit où tiennent les conventions que le code n'exprime pas. Son compromis, on l'a déjà vu : il se paie en jetons en permanence et il se dégrade sans prévenir.

Laravel Boost résout très bien le niveau trois : un serveur qui expose le schéma réel, les requêtes, les dernières erreurs et les informations de l'application, et qui écrit en plus le contexte sur le disque pour qu'il soit disponible sans rien invoquer. Le compromis, c'est que c'est une dépendance de développement dans votre projet, avec son installation et son serveur qui tourne. Et ce qu'il donne, c'est de l'état, pas des procédures : il sait dans quel état est votre projet, pas comment consolider cent migrations.

Un serveur générique de base de données vous donne le schéma réel et rien d'autre. Il renvoie des tables et des colonnes sans les conventions de l'ORM, sans savoir ce qu'est une relation polymorphe ni où vit la carte des types.

L'analyse statique classique, du type Larastan ou les inspections de l'IDE, est déterministe et n'hallucine pas par construction. Ce qu'elle ne fait pas, c'est parler à l'agent : le résultat vit dans l'outil, pas dans le contexte du modèle.

Ce qu'apporte un ensemble de procédures empaquetées, c'est l'autre axe : comment bien faire une tâche précise, avec les étapes, les sources à consulter et le format du rapport. Cela ne concurrence pas ce qui précède, cela s'y appuie. Son approche consiste à recalculer à chaque invocation : rien n'est laissé dans votre projet et rien n'occupe de contexte quand vous ne l'utilisez pas, en échange de quoi le coût de lecture se paie à chaque fois. Et comme ce sont des instructions en texte et non des appels de fonction, elles orientent le modèle au lieu de le forcer, ce qui les rend portables d'un agent à l'autre.

Et si vous n'utilisez pas Claude Code

Les principes ne dépendent pas de l'outil et valent pour n'importe quel agent qui travaille sur votre code :

  • Classez vos sources par degré de certitude et sachez à quel niveau se situe chaque affirmation.
  • Préférez vérifier à déduire chaque fois que le coût le permet. Une requête de comptage coûte des millisecondes et transforme un soupçon en fait.
  • Dérivez l'API du code installé, pas de la documentation publiée.
  • Détectez l'environnement et dites-le dans le résultat.
  • Séparez le mode lecture du mode écriture avec un ordre explicite.
  • Et étiquetez la provenance de chaque constat. Un rapport sans provenance doit être vérifié en entier, et du coup il n'a rien fait gagner.

Où le trouver

C'est un plugin de Claude Code, pas un paquet Composer, il ne s'installe donc pas avec le gestionnaire de dépendances et n'entre pas dans votre projet. Il s'ajoute depuis le terminal lui-même :

/plugin marketplace add edulazaro/laraclaude
/plugin install laraclaude@edulazaro

Le code est sur GitHub et il est sous licence MIT. Ce sont trente-deux procédures écrites en Markdown, environ sept mille lignes, sans une seule ligne de PHP : analyser un modèle, auditer la sécurité, consolider des migrations, générer des composants, détecter du code mort et vérifier l'état de l'application contre la base de données réelle.

Si vous intégrez des agents dans votre flux de développement, le vrai travail n'est pas dans le prompt, il est dans le choix de la source de chaque donnée. Nous en parlons dans intelligence artificielle et développement Laravel, et si vous nous racontez comment vous travaillez aujourd'hui nous vous dirons ce qui vaut la peine d'être automatisé.

Écrit par
Edu Lazaro
Edu Lazaro
Founder & Lead Developer en AndorraDev

Développeur full-stack avec plus de 15 ans d'expérience en Laravel, React, Node.js et architectures cloud. J'aide les entreprises en Andorre à construire leur présence digitale.

Partner de diseño · ionospace.
Besoin d'aide? ×
Andie by AndorraDev
Assistant IA + équipe humaine
Assistant IA d'AndorraDev
Andie
Bonjour! Je suis Andie, l'assistant IA d'AndorraDev. Comment puis-je vous aider? Si vous souhaitez parler avec Edu, il suffit de le demander.
17:26