Le basculement se produit toujours au même endroit : le moment où l'assistant cesse d'être un outil de rédaction pour devenir le seul dépositaire de la logique du projet. Tant que tu sais dire ce que fait chaque fichier et pourquoi il existe, la vitesse est un gain net. Dès que cette carte mentale se brouille, chaque nouvelle demande ajoute une couche par-dessus quelque chose que tu ne peux plus évaluer.
La génération n'est pas le maillon faible. La syntaxe passe dans la quasi-totalité des cas, les modèles produisent du code qui compile. Ce qui lâche, c'est la chaîne de décision autour du code : qui découpe, qui valide, qui refuse.
Cet article donne un cadre de travail utilisable dès aujourd'hui : comment découper une tâche, quoi relire, quand arrêter une boucle de correction, et quelle passe sécurité faire avant chaque fusion de branche. Si la définition même du terme reste floue, l'article vibe coding : effet de mode ou nouvelle pratique de dev moderne pose les bases et distingue les trois pratiques qu'on mélange en permanence.
Les quatre symptômes de la perte de contrôle
Avant de parler méthode, il faut savoir reconnaître le moment où le projet part. Ces quatre signaux apparaissent presque toujours dans cet ordre.
1. L'effet tunnel
Un bug apparaît. Tu le signales, l'assistant corrige, la correction casse autre chose. Tu signales de nouveau, il ajoute une garde, puis un try/except, puis un paramètre optionnel pour contourner le cas précédent. Au quinzième échange, le fichier contient des rustines pour des problèmes qui n'existaient pas au départ, et la conversation est devenue plus longue que ta compréhension du projet.
Le signe qui ne trompe pas : tu ne relis plus le code produit, tu relis le résumé que l'assistant en fait. À partir de là, tu débogues une description, pas un programme.
2. Le code orphelin
En revue, quelqu'un demande pourquoi il y a une classe abstraite pour un seul implémenteur. La réponse arrive : c'est ce que l'assistant a proposé. Ce code n'a plus d'auteur au sens où personne ne peut défendre les choix qu'il contient, ni les remettre en cause sans tout casser.
3. La duplication silencieuse
Un assistant qui n'a pas le projet entier en tête réécrit volontiers une fonction qui existe déjà trois dossiers plus loin. Trois fonctions de formatage de date, deux clients HTTP maison, quatre façons de valider un email. Chacune est correcte prise isolément. L'ensemble devient impossible à faire évoluer, parce qu'un changement de règle métier oblige à retrouver toutes les copies.
4. La faille qui ne fait pas de bruit
Une faille de sécurité ne provoque aucune erreur au lancement. L'application fonctionne parfaitement avec une requête SQL construite par concaténation, une clé API en dur, un endpoint sans contrôle d'accès. Rien dans ton usage quotidien ne te signale le problème, et c'est précisément ce qui rend cette catégorie de défaut différente des autres. Le blog LaPolaris détaille les cas les plus courants dans les 5 failles de sécurité qu'on retrouve dans presque tous les projets juniors.
Ce que disent les mesures sur le code généré
Il existe une étude longitudinale sérieuse sur le sujet, celle de Veracode. Plus de 100 modèles testés sur des tâches où la fonctionnalité demandée peut être implémentée de façon sûre ou non, le code produit étant ensuite passé dans un analyseur statique. Les résultats sont publiés depuis 2025 et mis à jour plusieurs fois par an.
Ce dernier point mérite qu'on s'y arrête. Attendre qu'un modèle plus puissant règle la question ne fonctionne pas, parce que la sécurité n'est pas une compétence qui émerge de la capacité brute : c'est une contrainte qu'il faut formuler. Le protocole de test de Veracode le montre en creux, puisque les tâches sont soumises sans consigne de sécurité explicite. Les mesures décrivent le comportement par défaut, pas un plafond.
Côté volume, les travaux d'Apiiro sur des dépôts d'entreprises du Fortune 50 pointent que les développeurs assistés produisent trois à quatre fois plus de commits que leurs collègues. Un taux d'erreur stable appliqué à un volume multiplié par trois donne un nombre absolu de failles multiplié par trois. C'est la mécanique qui rend le sujet urgent, plus que le taux lui-même.
Le principe : déléguer l'écriture, garder les décisions
Un projet logiciel se construit sur trois niveaux, et la confusion entre ces niveaux explique la majorité des dérives.
L'intention : ce que le logiciel doit faire, pour qui, avec quelles contraintes. Un utilisateur non authentifié ne doit jamais voir les factures d'un autre. Un import CSV doit rejeter un fichier de plus de 10 Mo. Ce niveau t'appartient, et aucun assistant ne peut le deviner.
La conception : le découpage en modules, les contrats entre eux, le choix des dépendances, la façon dont les données circulent. Ce niveau se discute avec l'assistant, mais la décision finale reste la tienne, parce que c'est celle qui coûte le plus cher à changer six mois plus tard.
L'implémentation : traduire tout ça en lignes de code. Ce niveau se délègue sans état d'âme, et c'est là que le gain de temps est massif.
La règle pratique tient en une phrase : tu peux déléguer le comment, jamais le quoi ni le pourquoi. Quand un prompt commence par « fais-moi une application de gestion de stock », les trois niveaux partent d'un coup, et le modèle invente à ta place des décisions structurantes que tu découvriras en production.
Le test de la revue
Un critère unique permet de trancher au moment de fusionner : es-tu capable d'expliquer chaque bloc de ce diff à un collègue, sans ouvrir la conversation avec l'assistant ? Si la réponse est non sur un bloc, ce bloc ne part pas. Soit tu le comprends en le lisant, soit tu demandes une explication et tu la vérifies, soit tu le réécris plus simplement.
Ce critère est plus exigeant que « ça marche », et c'est voulu. Le sujet du critère « ça marche » a d'ailleurs sa propre pathologie, décrite dans « Ça marche sur ma machine » : la phrase qui coûte une carrière.
Les principes de code deviennent du contenu de prompt
Un assistant applique les conventions dominantes de son corpus d'entraînement, pas les tiennes. Si ton projet suit une architecture en couches avec une règle stricte sur les dépendances entrantes, tu dois l'écrire quelque part, sinon le modèle appellera ton ORM depuis un contrôleur avec un naturel confondant.
Les principes que tu connais déjà servent ici de vocabulaire commun. Le moindre privilège s'applique aux tokens que le code génère autant qu'aux comptes de base de données, et l'article Least Privilege montre pourquoi ce seul principe divise la gravité de la plupart des incidents. Pour une vue d'ensemble, le hub les 20 principes de code qui font la différence entre un projet qu'on garde et un projet qu'on jette regroupe les quatre familles utiles au quotidien.
La méthode en sept étapes
Ce qui suit est une boucle, pas une procédure à dérouler une fois. Elle se répète à chaque tâche, et une tâche doit tenir dans une session de travail.
Étape 0 : le squelette vient de toi
Arborescence, choix du framework, liste des dépendances de départ, découpage des dossiers. Cette étape prend vingt minutes et détermine tout le reste. Un projet dont la structure a été improvisée par un modèle au premier prompt garde cette improvisation jusqu'à la fin.
Cette étape inclut le choix des bibliothèques. Une dépendance ajoutée par un assistant sans que tu l'aies validée est une dépendance que personne ne surveille, ni pour les mises à jour, ni pour les alertes de sécurité.
Étape 1 : un fichier de règles à la racine du dépôt
Tous les outils sérieux lisent un fichier de contexte au démarrage : CLAUDE.md pour Claude Code, AGENTS.md dans plusieurs environnements, les règles de projet pour Cursor. Ce fichier fait la différence entre un assistant qui devine et un assistant qui applique.
Il ne s'agit pas d'y écrire un manuel. Une trentaine de lignes qui couvrent la stack, les interdits absolus et les conventions de nommage suffisent, à condition d'être maintenues quand le projet évolue.
Étape 2 : une tâche, un commit, une intention
La granularité est l'outil de contrôle le plus efficace, et le plus négligé. Une session qui produit un diff de 900 lignes touchant onze fichiers ne sera pas relue, quelle que soit ta bonne volonté. Une session qui produit 60 lignes sur deux fichiers se relit en trois minutes.
Cette discipline suppose un usage de Git plus rigoureux que la moyenne : branche par tâche, commits atomiques, possibilité de revenir en arrière sur une seule décision. La formation Git et GitHub : gestion de versions pour débutants couvre ce socle, qui devient le filet de sécurité principal quand on travaille avec un assistant.
Étape 3 : lire le diff, pas la conversation
L'assistant te dit ce qu'il a fait. Le diff te dit ce qui a été fait. Ces deux choses divergent plus souvent qu'on ne l'imagine, en particulier sur les modifications collatérales : un import ajouté, une valeur par défaut changée, un fichier de configuration touché au passage.
Prends l'habitude de faire un git diff avant chaque validation, y compris quand tu as suivi la session en direct. C'est cinq minutes qui évitent des heures.
Étape 4 : les tests décrivent ton intention
Un test généré en même temps que le code teste ce que le code fait, ce qui ne prouve rien. Un test écrit à partir de l'intention teste ce que le code devrait faire, ce qui prouve quelque chose.
Une approche qui fonctionne bien : tu écris les cas de test en langage naturel, avec les cas limites et les cas d'échec attendus, puis tu demandes la traduction en code de test, puis tu demandes l'implémentation qui les fait passer. Le modèle travaille alors sous contrainte au lieu de définir lui-même sa cible.
Étape 5 : la passe sécurité avant merge
Elle prend la forme d'une checklist courte, détaillée plus bas, doublée d'outils automatiques dans la chaîne d'intégration. Un analyseur statique, un scanner de dépendances et un détecteur de secrets sur chaque poussée couvrent une grande partie des cas où un modèle se trompe par défaut.
La mise en place tient en un fichier de workflow. La formation CI/CD avec GitHub Actions montre comment brancher ces contrôles, et pour un contexte réglementé, Git, GitHub Actions et Cyber Resilience Act traite le pipeline sécurisé de bout en bout.
Étape 6 : le budget d'allers-retours
Fixe-toi une limite avant de commencer. Trois tentatives de correction sur le même bug, pas davantage. Au-delà, tu arrêtes la session, tu reviens au dernier commit propre, et tu reformules le problème.
Cette règle est celle qui a le plus d'effet sur l'effet tunnel, parce qu'elle transforme un enlisement progressif en décision consciente. Le coût d'un retour en arrière après trois échanges est faible. Après vingt, il devient assez élevé pour qu'on préfère continuer à empiler, ce qui est exactement le piège.
Exemples de code et de prompts
Un prompt vague et son résultat
Prenons une demande banale sur une API Python : permettre à un utilisateur d'envoyer un fichier.
Ajoute un endpoint pour uploader un fichier dans mon API FastAPI.
Le code renvoyé ressemble en général à ceci, à quelques variantes près.
@app.post("/upload")
async def upload(file: UploadFile = File(...)):
path = os.path.join("uploads", file.filename)
with open(path, "wb") as f:
f.write(await file.read())
return {"filename": file.filename}
Ce code fonctionne. Il passe la démo, il passe la recette fonctionnelle, et il contient quatre problèmes.
- Le nom de fichier vient du client et sert directement à construire un chemin. Un nom du type
../../etc/cron.d/backdoorécrit hors du dossier prévu. - Aucune limite de taille. Le
await file.read()charge le fichier entier en mémoire, ce qui suffit à faire tomber le service avec un envoi de quelques gigaoctets. - Aucun contrôle de type. Un fichier PHP ou un exécutable est accepté sans discussion.
- Aucune authentification, donc l'endpoint est ouvert à qui trouve l'URL.
Le modèle n'a rien fait de fautif : la demande portait sur un endpoint d'upload, il a livré un endpoint d'upload. Les contraintes absentes de la demande sont absentes du code.
Le même besoin, cadré
Ajoute un endpoint POST /documents sur mon API FastAPI.
Contraintes :
- accessible uniquement aux utilisateurs authentifies (dependance get_current_user existante)
- types acceptes : application/pdf et image/png, verifies sur le contenu et pas sur l'extension
- taille maximale 10 Mo, refusee en streaming sans charger le fichier en memoire
- le nom de fichier stocke est un uuid4, le nom d'origine va en base dans une colonne texte
- destination : le dossier defini par la variable d'environnement UPLOAD_DIR
- retourne 201 avec l'id du document, 413 si trop volumineux, 415 si type refuse
- ecris d'abord les tests pytest correspondants, y compris un test de traversee de chemin
La différence de longueur entre les deux prompts est d'environ quarante secondes de frappe. La différence sur le code produit se compte en heures de correction évitées, et le choix des codes de retour évite au passage la confusion habituelle sur les statuts HTTP, sujet traité dans codes HTTP : ce que les tutos t'expliquent mal.
Formuler ce genre de cadrage est une compétence à part entière, qui s'apprend et se travaille. La formation Prompt Engineering et API LLM traite ces techniques de cadrage, et Créer une API REST avec Python et FastAPI donne le socle technique qui permet de juger le résultat.
Un fichier de règles projet
Voici un exemple réel, raccourci, du genre de fichier à poser à la racine.
# Regles du projet
## Stack
Python 3.12, FastAPI, SQLAlchemy 2.x, PostgreSQL, pytest.
Aucune nouvelle dependance sans validation explicite dans le chat.
## Architecture
routers/ ne contient que du HTTP : validation d'entree, appel de service, reponse.
services/ contient la logique metier, sans import de fastapi.
repositories/ contient les acces base, seul endroit ou SQLAlchemy est importe.
Une couche n'importe jamais une couche superieure.
## Interdits
- pas de requete SQL construite par concatenation ou f-string
- pas de secret en dur, tout passe par les variables d'environnement
- pas de except nu, toujours un type d'exception
- pas de print, utiliser le logger configure dans core/logging.py
## Tests
Chaque service a ses tests unitaires. Les cas d'erreur sont testes,
pas seulement le chemin nominal.
## Style
Nommage en anglais, commentaires en francais.
Fonctions de moins de 40 lignes.
Ce fichier a un effet immédiat sur la qualité des propositions, et un effet secondaire utile : il oblige à formuler des règles qui étaient jusque-là implicites dans l'équipe.
L'injection SQL, cas d'école qui survit très bien à l'IA
Le motif suivant apparaît encore régulièrement quand on demande une recherche filtrée sans préciser la façon de faire.
# dangereux
query = f"SELECT * FROM users WHERE email = '{email}' AND active = {active}"
cursor.execute(query)
La version paramétrée fait exactement la même chose, en laissant au pilote de base le soin de séparer la commande des données.
# correct
cursor.execute(
"SELECT id, email FROM users WHERE email = %s AND active = %s",
(email, active),
)
Repérer ce motif dans un diff prend deux secondes quand on sait à quoi ressemble une requête paramétrée. Ce type de réflexe de lecture se construit en écrivant du SQL à la main, ce que couvre apprendre le SQL : les requêtes qui reviennent dans tous les projets.
La relecture adverse
Demander à l'assistant s'il a bien travaillé donne une réponse polie et peu informative. Une session neuve, sans l'historique de la génération, produit des résultats bien meilleurs.
Voici un diff. Tu es un attaquant, pas un relecteur bienveillant.
Pour chaque modification, indique :
1. l'entree utilisateur qui atteint ce code et par quel chemin
2. ce qui se passe si cette entree est hostile ou malformee
3. la CWE correspondante s'il y en a une
4. la ligne exacte a corriger
Ne propose pas de reecriture globale. Ne dis pas que le code est correct
sans avoir liste les entrees que tu as examinees.
Cette relecture ne remplace pas un analyseur statique, et elle a ses angles morts, notamment sur les problèmes de logique métier. Elle attrape en revanche une bonne partie des motifs classiques, à un coût proche de zéro.
La checklist sécurité avant merge
Dix points, applicables à n'importe quel projet web. La plupart se vérifient par une recherche dans le diff.
- Secrets : aucune clé, aucun mot de passe, aucun token dans le code ni dans un fichier suivi par Git. Vérifier aussi que le
.envest bien dans le.gitignoreavant le premier commit, pas après. - Entrées utilisateur : chaque donnée venant du client est validée en type, en taille et en format, côté serveur.
- Requêtes base : aucune construction par concaténation ou interpolation de chaîne.
- Autorisation : chaque route qui lit ou modifie une ressource vérifie que l'utilisateur courant a le droit d'y toucher, pas seulement qu'il est connecté.
- Affichage : toute donnée utilisateur rendue dans une page passe par l'échappement du framework. Les fonctions du type
dangerouslySetInnerHTMLouv-htmlfont l'objet d'une justification écrite. - Journalisation : les entrées utilisateur écrites dans les logs sont nettoyées des retours à la ligne, et aucun mot de passe ni jeton n'apparaît dans un log.
- Dépendances : chaque paquet ajouté existe réellement, est maintenu, et son nom a été vérifié caractère par caractère.
- Privilèges : le compte base de données, le conteneur et le token d'API utilisés n'ont que les droits nécessaires à ce qu'ils font.
- Erreurs : aucun message d'erreur technique renvoyé au client, aucune trace de pile exposée en production.
- Configuration de déploiement : mode debug désactivé, CORS restreint aux origines attendues, stockage objet non public par défaut.
Le dixième point mérite une attention particulière avec les plateformes de déploiement intégrées. Un projet peut avoir un code impeccable et rester ouvert à tous parce qu'un réglage par défaut n'a jamais été touché. Ce genre de configuration se maîtrise mieux quand on comprend ce qui tourne sous l'application, ce que couvre Docker pour développeurs web.
Sept pièges qui reviennent tout le temps
Faire valider le code par celui qui l'a écrit
Dans la même session, avec tout l'historique en contexte, l'assistant a déjà décidé que son approche était la bonne. Ouvre une session vierge, colle le diff seul, demande une analyse hostile.
Les dépendances qui n'existent pas
Les modèles inventent parfois des noms de paquets plausibles. Des attaquants publient ces noms sur npm ou PyPI en attendant que quelqu'un installe. La parade est courte : avant chaque installation, vérifie la page officielle du paquet, sa date de dernière publication et son nombre de téléchargements.
Le refactor massif en un seul prompt
« Refactorise ce module pour qu'il soit plus propre » produit un diff illisible où se mélangent des renommages, des changements de comportement et des suppressions. Un refactor se fait en passes séparées, chacune vérifiable, et les tests doivent exister avant.
Coller des données réelles dans un prompt
Un extrait de base de production pour illustrer un bug, un log complet avec des adresses email, une capture de configuration avec des identifiants. Selon l'outil et le contrat, ces données peuvent sortir de ton périmètre, et la question RGPD se pose immédiatement dès qu'il s'agit de données personnelles de clients. Anonymise avant, ou construis un jeu de test factice.
La documentation qui décrit le code rêvé
Une documentation générée à partir d'une intention plutôt que du code réel décrit un comportement qui n'existe pas. Elle est pourtant plus dangereuse qu'une absence de documentation, parce qu'on lui fait confiance. Génère la documentation à partir du code final, jamais à partir de la conversation qui a mené au code.
Accepter un code plus complexe que nécessaire
Un modèle produit volontiers une usine à gaz pour un besoin simple : une interface, deux implémentations, un registre, une factory, pour un cas qui demandait une fonction. La complexité inutile est un coût permanent, et elle est bien plus difficile à retirer une fois que du code s'appuie dessus.
Croire qu'un modèle plus récent règle la question
Les mesures de Veracode couvrent les modèles les plus récents de chaque fournisseur, et le taux de réussite en sécurité reste plat pendant que la correction syntaxique frôle les 100 %. La capacité brute augmente, la prise en compte spontanée des contraintes non exprimées, non.
Et quand on est en train d'apprendre à coder
Pour une personne en reconversion, un développeur junior ou un curieux non-informaticien, la question se pose différemment. La méthode décrite plus haut suppose de savoir lire un diff et de reconnaître un motif dangereux, deux compétences qui se construisent en écrivant du code soi-même.
Une règle simple permet de trancher : sur un sujet que tu apprends, écris d'abord, demande ensuite. L'assistant sert de relecteur et d'explicateur, pas de rédacteur. Sur un sujet que tu maîtrises déjà, l'ordre inverse fait gagner beaucoup de temps sans coût pédagogique.
Cette distinction a des conséquences sur les projets de portfolio. Un recruteur qui ouvre un dépôt et pose trois questions sur une décision d'architecture repère en une minute la différence entre un projet compris et un projet assemblé. Le sujet est développé dans premier projet pour un CV de développeur junior, et la question plus large du marché junior dans le vibecoding a-t-il tué le poste de dev junior.
Le catalogue complet des formations LaPolaris couvre les fondamentaux qui rendent cette lecture critique possible, du langage jusqu'au déploiement.
Questions fréquentes
Le vibe coding est-il dangereux en soi ?
La pratique n'est pas dangereuse, l'absence de cadre autour l'est. Un développeur qui découpe ses tâches, relit ses diffs et fait tourner des contrôles automatiques obtient du code au moins aussi sûr qu'en écrivant tout à la main, avec un gain de temps réel. Le risque apparaît quand la génération n'est suivie d'aucune étape de validation, ce qui correspond au comportement par défaut mesuré par les études : environ 44 % de code contenant une vulnérabilité connue quand aucune contrainte de sécurité n'est exprimée.
Comment sortir d'un effet tunnel une fois qu'on est dedans ?
Reviens au dernier commit qui fonctionnait, même si cela signifie perdre deux heures de travail. Reformule ensuite le problème en une phrase, seul, sans l'assistant, et découpe-le en sous-problèmes qui tiennent chacun en une vingtaine de lignes. Reprendre depuis une base saine coûte presque toujours moins cher que de corriger un empilement de rustines dont personne ne connaît plus la raison d'être.
Faut-il tout relire, même sur un projet personnel ?
Le niveau d'exigence dépend de l'exposition. Un script local qui traite tes propres fichiers ne mérite pas la même rigueur qu'une application accessible depuis internet et qui manipule des données de tiers. Trois questions permettent de trancher : est-ce accessible depuis l'extérieur, est-ce que ça touche à des données qui ne sont pas les miennes, est-ce que ça tourne sur une machine qui héberge autre chose. Une réponse positive à l'une des trois fait basculer le projet dans la catégorie où la checklist s'applique intégralement.
Quels outils automatiques mettre en place en priorité ?
Trois familles couvrent l'essentiel et se branchent dans une chaîne d'intégration en moins d'une heure : un détecteur de secrets qui bloque la poussée si une clé apparaît, un scanner de dépendances qui signale les paquets vulnérables, et un analyseur statique adapté au langage. Ajoute un linter avec une configuration stricte, qui attrape au passage une partie des motifs douteux. Ces contrôles doivent tourner avant la fusion, pas après le déploiement.
Un débutant peut-il utiliser un assistant de code sans se saboter ?
Oui, à condition d'inverser l'ordre habituel. Sur un sujet en cours d'apprentissage, écris d'abord ta version, puis demande une relecture commentée et compare. L'assistant devient un correcteur disponible en permanence, ce qui est une aide considérable. Le sabotage survient quand la génération remplace la tentative, parce que la compétence qui permet de juger le code produit ne se construit jamais.
Que mettre dans un fichier de règles projet pour qu'il serve réellement ?
Une trentaine de lignes maximum, sinon il sera ignoré ou deviendra obsolète. Les rubriques utiles sont la stack avec les versions, les règles de dépendance entre couches, la liste des interdits absolus, les conventions de nommage et les attentes en matière de tests. Formule les interdits de façon vérifiable dans un diff : « pas de requête SQL construite par f-string » se contrôle en un coup d'oeil, « écris du code propre » ne se contrôle pas.
Les plateformes de vibe coding tout-en-un posent-elles des problèmes spécifiques ?
Elles ajoutent une couche de configuration que l'utilisateur ne voit pas toujours : politique d'accès à la base, visibilité du projet déployé, gestion des variables d'environnement. Plusieurs incidents publics ont concerné des projets dont le code n'était pas en cause, seulement un réglage laissé par défaut. Avant toute mise en ligne, ouvre les paramètres de la plateforme et vérifie qui peut accéder à quoi, en te mettant dans la position d'un visiteur anonyme.