Le site est en chantier, l'accès est réservé pour l'instant.

Le mirage du vibe coding

Un an après la formule de Karpathy : le vibe coding marche chez l'un et explose chez l'autre pour une raison qui tient en un mot, le non-déterminisme. Ce que ça veut dire, et quatre questions à se poser avant de commencer.

17 min de lecture

Andrej Karpathy a popularisé le terme de vibe coding dans un tweet de 2025. Il le décrivait comme un nouveau type de codage qui consiste à se laisser « entièrement porter par le feeling, à miser sur la croissance exponentielle, et à oublier que le code existe », avant de terminer son tweet en précisant que cette approche n’était « pas mauvaise pour des projets jetables du week-end ».

Le problème, c’est que l’industrie n’a lu que la première partie de son tweet, et que cette variante de vibe coding a pris de l’ampleur jusqu’à devenir une sous-culture importante du développement par IA.

Il est vrai que la promesse est séduisante au premier abord : développer n’importe quel outil en langage naturel, sans aucune limite à la créativité. Elle a séduit beaucoup de personnes qui jusque-là dépendaient d’un ou de plusieurs développeurs.

Un an après, cette promesse a-t-elle porté ses fruits ?

Oui, sur le périmètre que Karpathy avait tracé. Non partout ailleurs.

Le vibe coding fonctionne très bien pour certains, très mal pour d’autres, pour une raison qui tient en une phrase : les modèles ne sont pas déterministes, et leur attention est limitée.

Dans cette note, je vous propose d’approfondir les problématiques liées au non-déterminisme des modèles sous deux angles : d’abord sous l’angle de la génération elle-même, puis sous celui de la mémoire de travail/attention du modèle, pour ensuite apporter une grille de lecture qui vous permettra d’avoir une vision claire des problématiques du vibe coding.

1. Le non-déterminisme

On qualifie de déterministe un phénomène qui, reproduit dans les mêmes conditions, donne toujours le même résultat. Un compilateur est déterministe. Un LLM ne l’est pas, par construction.

Pourquoi ?

Un LLM est une machine à générer du texte. Elle prend en entrée un texte de départ (le prompt) et produit un texte de sortie. Le texte de sortie n’est pas généré en une seule opération, mais en une série d’opérations appliquées à chaque token de réponse, qu’on appelle un pas de génération.

À chaque pas de génération, un LLM produit une distribution de probabilités sur le token suivant, parmi un vocabulaire de l’ordre de 100 000 à 200 000 entrées. Pour le prompt « écris une fonction qui parse une date en Python », la première production du LLM n’est pas def : c’est def à 62 %, function à 21 %, const à 9 % et une longue traîne de candidats à probabilité décroissante.

Un tirage choisit le prochain token parmi cette distribution. Le token choisi est ajouté à la réponse qui est en train d’être générée, et l’ensemble prompt + token choisi devient le nouveau prompt sur lequel le modèle va calculer une nouvelle distribution, afin de produire, après N pas de génération, la réponse finale.

L’opération de tirage, qui choisit le prochain token depuis la distribution des candidats potentiels, peut être influencée par des paramètres qu’on appelle « paramètres d’échantillonnage ».

Un exemple de paramètre est la température. Elle aplatit ou creuse la distribution ; à T = 0, le modèle prend systématiquement le token le plus probable, celui avec la plus forte probabilité dans la table de distribution des tokens probables. Un autre paramètre, le top-p, coupe la traîne. Ces réglages changent la largeur du tirage, pas sa nature.

Si le tirage donne parse_date au lieu de parseDate au pas 12, les 500 tokens qui suivent partent d’un autre point : convention de nommage, choix de la bibliothèque, structure des erreurs, gestion des fuseaux, tout dérive à partir de cette bifurcation.

Deux exécutions du même prompt sur un nombre de pas suffisant ne diffèrent pas d’un mot ici ou là, elles donnent deux programmes syntaxiquement différents.

L’objection habituelle : il suffit de mettre la température à 0, et le top-k à 1. En pratique, non, pour plusieurs raisons qui ne dépendent pas de l’utilisateur du modèle. En voici quelques-unes.

Les noyaux de calcul

Un serveur d’inférence ne traite pas votre requête seule : il la regroupe avec celles qui arrivent au même moment et calcule le lot d’un bloc. La taille du lot change l’ordre des additions, et en virgule flottante, additionner les mêmes nombres dans un autre ordre donne un résultat légèrement différent. Les probabilités calculées pour votre prompt ne sont donc pas les mêmes à 3 h du matin, seul sur le serveur, et à 14 h, noyé dans le trafic. Quand deux tokens candidats sont proches, cet écart minuscule suffit à inverser leur ordre, même à température 0, et la bifurcation décrite plus haut fait le reste. L’équipe de Thinking Machines (Horace He) a documenté ce mécanisme dans « Defeating Nondeterminism in LLM Inference », en septembre 2025.

Les routages MoE

Les modèles à mélange d’experts (MoE) routent chaque token vers un sous-ensemble du réseau, et ce routage dépend lui aussi de ce qui est traité dans le même batch.

Les mises à jour du fournisseur

Le fournisseur met à jour les poids, la quantification ou le moteur d’inférence derrière un alias qui ne change pas. Le modèle que vous appelez en mars ne pointe pas forcément sur les mêmes poids en juin.

Pour du code, ça veut dire que le modèle tire un programme dans la distribution des programmes plausibles pour ce prompt, et qu’un programme plausible passe le test manuel avant de se tromper sur le cas limite. Rien dans la boucle de génération n’est rejouable. Ce qui l’est vit hors du modèle : un type qui compile ou non, un test qui passe ou non.

2. La mémoire de travail (context)

Les modèles disposent de deux mémoires principales.

  • La mémoire d’entraînement

    Elle contient tout ce que le LLM a appris pendant son entraînement. Elle est en lecture seule : la modifier demande de repasser par une phase d’entraînement dans un labo, et elle est datée, figée au jour où le corpus a été gelé. Lorsque vous téléchargez un modèle open source pour l’exécuter en local, vous téléchargez les poids du modèle qui représentent cette mémoire d’entraînement.

  • La mémoire de contexte

    C’est la mémoire de travail. C’est la seule des deux sur laquelle vous agissez. Elle contient votre tâche et tout ce qui s’est dit depuis le début de la session, mais pas seulement. Elle contient en plus :

    • le prompt système de l’outil (Claude Code, Cursor, Codex), plusieurs milliers de tokens ;
    • vos fichiers d’instructions (CLAUDE.md, AGENTS.md, .cursorrules) ;
    • la déclaration de chaque outil et de chaque serveur MCP, avec son schéma, souvent plusieurs centaines de tokens par outil ;
    • chaque fichier que le modèle a lu, en entier ;
    • chaque sortie de commande, de test, de linter ;
    • les propres réponses du modèle, qui redeviennent son entrée au tour suivant.

Vous ne voyez, dans l’interface de votre agent, qu’une fraction de ce que le modèle lit. La fenêtre grossit à chaque tour et ne maigrit jamais à l’initiative du modèle.

La nature problématique de la fenêtre de contexte

La fenêtre de contexte fait émerger cinq pathologies. Elles se cumulent au fil d’une session, et chacune a sa parade dans la section suivante.

Context rot. La qualité de génération baisse à mesure que le contexte se remplit. Une consigne donnée au message 3 est respectée aux messages 4 et 5. La même consigne, toujours là, mot pour mot dans le contexte, n’est plus respectée au message 60. C’est le premier symptôme, et celui qu’on remarque le plus souvent.

L’étude Context Rot de Chroma (juillet 2025), sur 18 modèles, montre que la baisse de qualité de génération est continue, sans seuil en dessous duquel tout va bien, y compris sur des tâches triviales comme recopier une liste de mots répétés.

Le benchmark NoLiMa (2025) en donne l’ampleur : il retire les correspondances littérales entre la question et le passage à retrouver, pour forcer le modèle à raisonner au lieu de chercher un mot ; à 32k tokens, dix des douze modèles testés sont sous la moitié de leur score à contexte court, alors que tous annoncent 128k et plus. Preuve que la taille de fenêtre annoncée et la taille de la fenêtre utile sont deux nombres différents.

Enfin, la position compte : Liu et al. (« Lost in the Middle », 2023) montrent que les modèles récupèrent mieux ce qui est au début et à la fin du contexte que ce qui est au milieu. Au fur et à mesure que la session s’allonge, un nombre croissant d’instructions vont se retrouver dans ce milieu.

Context distraction et confusion. Trop de matière, dont trop de matière inutile. Quarante outils MCP déclarés dont trois servent, trente fichiers lus pendant la phase d’exploration de votre agent de code. Le modèle choisit le mauvais outil, ou mélange deux conventions architecturales vues dans deux fichiers.

Le rapport technique de Gemini 2.5 en donne un exemple : l’agent qui joue à Pokémon, passé 100k tokens de contexte, se met à répéter des actions de son historique au lieu d’en planifier de nouvelles. Sur le Berkeley Function-Calling Leaderboard, Breunig relève que les modèles perdent en précision à mesure que le nombre d’outils proposés augmente.

Context poisoning. Une erreur entre dans le contexte : une API inventée, un test lu comme réussi alors qu’il échoue. Le modèle construit dessus. Chaque tour suivant la renforce, parce qu’elle fait désormais partie des faits de la session. Le modèle ne remet pas systématiquement en cause ce qu’il lit dans son propre historique.

Un cas type : demander à un modèle d’implémenter une API dont la version a été publiée après la date de cutoff des données d’entraînement du modèle.

Context clash. Deux décisions contradictoires cohabitent. « On utilise des UUID » au message 10, « on passe aux entiers auto-incrémentés » au message 40. Les deux sont dans la fenêtre. Le modèle pioche l’une ou l’autre selon le tour, et le schéma de base de données finit avec les deux.

Context overflow. La limite est atteinte. L’outil tronque le début de l’historique ou le compacte en un résumé. Les contraintes posées au départ, les fichiers lus, les décisions prises disparaissent. Le modèle ne le dit pas. Pour contourner cette contrainte, les agents de code fournissent souvent des outils de compaction, qui réduisent la taille du contexte. Mais ces outils ont un coût dans la mesure où ils peuvent compacter, de manière destructrice, des informations nécessaires pour la validité finale de la tâche (exemple : compaction de règles métier critiques).

Le concept de fenêtre de contexte optimale

Le corollaire de ces cinq pathologies tient en une définition, adaptée de « Effective context engineering for AI agents » d’Anthropic (septembre 2025).

Une fenêtre de contexte optimale, c'est le plus petit ensemble de tokens à fort signal qui suffit à la tâche en cours, placé loin sous la limite annoncée, avec les consignes structurantes en tête et les données de travail en queue.

L’attention est un budget : chaque token ajouté dilue les autres. Il convient donc de fournir au modèle exactement ce dont il a besoin pour mener à bien une tâche.

Les deux mécanismes se multiplient

Comme vu en début d’article, à chaque tour, le modèle tire un token dans une distribution calculée sur tout le contexte : prompts, échanges précédents, outils, fichiers lus. Ce qu’il génère rejoint ce contexte et sert de base au tour suivant. Un tirage un peu à côté produit un fichier un peu à côté ; au tour suivant, le modèle relit ce fichier comme un fait acquis et construit dessus. Chaque écart de tirage pollue le contexte, et le contexte pollué élargit l’écart suivant.

Une session de vibe coding est une boucle autorégressive à l’échelle du projet, avec les mêmes propriétés que la génération d’un token : une bifurcation tôt dans la boucle, et tout ce qui suit part d’un autre point. Un projet de week-end fait dix tours de cette boucle. Un projet de six mois en fait des milliers.

3. Les typologies de projets et d’équipes

Ces mécanismes sont les mêmes pour tout le monde. Mais leurs conséquences ne s’appliquent pas de la même façon en fonction des projets et des équipes.

La complexité métier

La complexité métier est le nombre de règles et d’invariants que la codebase implémente ou doit implémenter.

  • Une landing page : proche de zéro.
  • Un outil de facturation avec TVA par pays, prorata, avoirs et relances : des centaines, voire des milliers.

Le modèle est excellent sur le générique. Formulaires, CRUD, authentification, appels d’API : il en a vu des millions à l’entraînement, ils sont dans la mémoire d’entraînement. Les règles de votre métier n’y sont pas. Pour qu’il les applique, elles doivent entrer dans la fenêtre de contexte. Or, comme vu plus tôt, une tâche de génération qui doit comprendre des dizaines de règles et d’invariants métier qui ne sont pas présents dans ses données d’entraînement va avoir une tolérance très faible à la dégradation du contexte qui contient ces règles et invariants.

Passé un certain volume, le rot et la confusion s’installent : une règle donnée au message 8 n’est plus appliquée au message 50 ; deux règles voisines (le prorata du premier mois, le prorata à la résiliation) sont fusionnées en une seule ; une exception (l’autoliquidation de TVA intra-UE) est appliquée au cas général. Le résultat est bancal d’une façon précise : la facture est juste pour la France, fausse pour l’Allemagne, et le vibe coding consiste justement à ne pas relire le code. Sur un domaine générique, ça passe : le modèle fait ce que tout le monde fait. Sur un domaine spécifique, ça ne passe plus.

Les codebases brownfield

Un projet brownfield s’insère dans une base de code qui a déjà ses habitudes, ses zones mortes et son savoir implicite. Une codebase greenfield est une codebase qui part de zéro, sans code existant.

En brownfield, les conventions et les invariants sont dispersés dans des centaines de fichiers et ne tiennent pas dans la fenêtre. Le modèle en lit un échantillon, celui que la recherche du moment lui a remonté, et cet échantillon change d’une session à l’autre. Il produit du code qui marche mais ne s’intègre pas : un helper qui existe déjà sous un autre nom trois dossiers plus loin, un invariant cassé que personne ne lui avait signalé parce qu’il n’était écrit nulle part. Le poisoning y prend une forme particulière : le modèle lit du code mort comme du code vivant, étend un module abandonné depuis deux ans, ou applique des modifications sans avoir la capacité de voir que ces modifications vont casser une autre partie de l’application.

Le greenfield générique marche, puis devient brownfield

Le greenfield est celui qui offre les meilleurs résultats. Le domaine est dans la mémoire d’entraînement. La fenêtre reste petite : pas de base existante à lire, pas de règle métier à décrire. La boucle fait dix tours, et dix tours ne suffisent pas à ce que le rot ou le poisoning se voient. Le périmètre de Karpathy, c’est exactement ça.

La limite arrive avec la taille. Chaque fichier généré doit être relu à la session suivante pour que le modèle reste cohérent avec lui-même. Au trentième fichier, le projet ne tient plus dans une fenêtre optimale, et les choix initiaux (une structure de dossiers, une convention d’erreurs, que le modèle avait pris sans les écrire nulle part) se diluent. C’est la transition vers le brownfield : elle est silencieuse et se matérialise par du code incohérent, des oublis de règles, des bugs.

L’opérateur non technique qui vibe code, c’est la cible de la promesse initiale : personne ne lit le code, il n’existe que par ses effets. Le poisoning y est invisible jusqu’en production, parce que le seul lecteur du contexte est le modèle qui l’a empoisonné. Le fondateur technique sait lire, mais n’a pas le temps, et la relecture saute dès que le rythme de production de l’agent monte.

À plusieurs, la boucle se multiplie. Plusieurs développeurs, c’est plusieurs sessions, donc plusieurs contextes, donc plusieurs tirages sur la même base.

Les guardrails, une alternative au vibe coding pur

Les guardrails, c’est tout ce qui contraint le modèle sans passer par un humain.

On en observe deux types :

  • Les guardrails non déterministes, comme les skills, CLAUDE.md/AGENTS.md, les instructions. Ces guardrails ont une utilité, par exemple pour influencer des comportements (comme le respect d’une architecture hexagonale), mais ne peuvent pas être utilisés pour garantir un comportement, pour la simple raison qu’ils ne sont ni plus ni moins que de simples prompts injectés dans le contexte du modèle, qui vont être soumis aux mêmes phénomènes de dégradation décrits dans les sections précédentes.

  • Les guardrails déterministes

    Comme les types, le linter, les tests, la CI, la validation de schémas, les règles d’architecture, les hooks de pre-commit, les cliquets.

    Ces guardrails sont les seules garanties de la boucle de génération de code. Sans eux, la variance du tirage et les pathologies du contexte atteignent tôt ou tard la production en silence, et l’erreur remonte trois semaines plus tard, par un utilisateur. Avec eux, le code passe ou ne passe pas, peu importe ce que le modèle pense avoir fait, et l’erreur remonte au tour suivant, de façon à fournir un feedback de correction à l’agent.

Les guardrails ont un second effet, moins discuté : ils compressent le contexte. Une erreur de type, c’est trois lignes à fort signal qui disent exactement ce qui cloche. Sans elle, le modèle relit dix fichiers pour comprendre, et ces dix fichiers restent dans la fenêtre jusqu’à la fin de la session. Un test rouge coûte cinquante tokens ; l’enquête qui le remplace en coûte vingt mille.

Enfermé dans des contraintes déterministes, un modèle non déterministe produit du code contraint. Le degré de contrainte du code produit sera alors fonction de la sophistication des guardrails mis en place autour du modèle.

C’est le moment de passer à notre conclusion.

Vibe coding or not ?

Question Exposition faible Exposition moyenne Exposition forte
Combien de règles métier ne sont écrites nulle part ? Aucune, le domaine est générique Une dizaine, listables sur une page Des centaines, dans la tête de deux personnes
Y a-t-il du code existant ? Non Oui, petit et conventionnel Oui, ancien, avec des zones mortes
Qu’est-ce qui bloque un commit sans humain ? Types, tests, linter, CI Types et linter Rien
Combien de personnes lisent le code ? Plusieurs, avec revue systématique et conventions écrites Une, qui sait lire Aucune, ou plusieurs sans conventions écrites

Quatre réponses dans la colonne de gauche : c’est le périmètre de Karpathy, vibe-codez. Une réponse dans la colonne de droite : les problèmes s’annoncent à l’horizon. Deux réponses à droite ou plus : anticipez les problèmes en production, la seule inconnue est la date.

Pour conclure

Le vibe coding n’est pas mauvais en soi. Sur le périmètre de Karpathy, c’est un excellent outil de prototypage.

Sur la durée, le vibe coding pur tend toujours vers du code instable, et la discipline de l’utilisateur, les skills glanés ici et là, etc., n’y changent rien.

Ce qui repousse l’échéance, y compris pour quelqu’un qui ne lit pas le code : des guardrails déterministes dès le premier commit, une architecture décidée avant le premier prompt, un fichier d’instructions court que chaque session relit, des sessions courtes à une tâche. Aucune de ces mesures ne rend le modèle déterministe. Elles construisent du déterminisme autour de lui.

Références