[{"data":1,"prerenderedAt":1155},["ShallowReactive",2],{"i-simple-icons:github":3,"i-simple-icons:linkedin":8,"blog-\u002Fblog\u002Ftester-ce-qui-compte":11,"blog-others-\u002Fblog\u002Ftester-ce-qui-compte":821},{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":7},0,24,false,"\u003Cpath fill=\"currentColor\" d=\"M12 .297c-6.63 0-12 5.373-12 12c0 5.303 3.438 9.8 8.205 11.385c.6.113.82-.258.82-.577c0-.285-.01-1.04-.015-2.04c-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729c1.205.084 1.838 1.236 1.838 1.236c1.07 1.835 2.809 1.305 3.495.998c.108-.776.417-1.305.76-1.605c-2.665-.3-5.466-1.332-5.466-5.93c0-1.31.465-2.38 1.235-3.22c-.135-.303-.54-1.523.105-3.176c0 0 1.005-.322 3.3 1.23c.96-.267 1.98-.399 3-.405c1.02.006 2.04.138 3 .405c2.28-1.552 3.285-1.23 3.285-1.23c.645 1.653.24 2.873.12 3.176c.765.84 1.23 1.91 1.23 3.22c0 4.61-2.805 5.625-5.475 5.92c.42.36.81 1.096.81 2.22c0 1.606-.015 2.896-.015 3.286c0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":9,"hidden":10},"\u003Cpath fill=\"currentColor\" d=\"M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037c-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85c3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.06 2.06 0 0 1-2.063-2.065a2.064 2.064 0 1 1 2.063 2.065m1.782 13.019H3.555V9h3.564zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0z\"\u002F>",true,{"id":12,"title":13,"body":14,"date":806,"description":807,"extension":808,"meta":809,"navigation":10,"path":810,"readingTime":131,"seo":811,"seoTitle":812,"stem":813,"tags":814,"tone":818,"updated":819,"__hash__":820},"blog\u002Fblog\u002Ftester-ce-qui-compte.md","Tester ce qui compte, avec Cypress",{"type":15,"value":16,"toc":798},"minimark",[17,21,26,29,48,51,55,326,329,344,355,518,522,525,535,541,642,645,649,652,680,699,703,706,761,764,768,771,791,794],[18,19,20],"p",{},"Un site peut avoir mille tests et casser le jour du lancement. J’ai vu des projets couverts à 90 % où personne n’avait vérifié que le bouton « Payer » envoyait quelque chose. La question n’est pas combien de tests, mais lesquels, et ce qu’ils vérifient vraiment.",[22,23,25],"h2",{"id":24},"les-parcours-clés","Les parcours clés",[18,27,28],{},"Je commence par lister ce qui doit marcher sans exception. La liste se fait avec le client, pas devant l’éditeur de code, et elle tient en général en moins de dix lignes :",[30,31,32,36,39,42,45],"ul",{},[33,34,35],"li",{},"Se connecter et se déconnecter.",[33,37,38],{},"Créer l’objet principal : un projet, une commande, une réservation.",[33,40,41],{},"Payer, ou envoyer le formulaire qui remplace le paiement.",[33,43,44],{},"Retrouver ce qu’on a créé après avoir fermé l’onglet.",[33,46,47],{},"Recevoir le message qui confirme que c’est fait.",[18,49,50],{},"Chacun devient un test de bout en bout, écrit comme un utilisateur le ferait, dans un vrai navigateur. Tout ce qui n’est pas dans la liste peut casser sans que le client perde un euro. Ce n’est pas une raison pour ne pas le tester, mais c’est une raison pour le tester autrement, et plus tard.",[22,52,54],{"id":53},"écrire-comme-on-utilise","Écrire comme on utilise",[56,57,63],"pre",{"className":58,"code":59,"filename":60,"language":61,"meta":62,"style":62},"language-ts shiki shiki-themes github-light github-dark","describe('Projets', () => {\n  beforeEach(() => {\n    cy.login('melanie@example.com')\n  })\n\n  it('crée un projet et le retrouve', () => {\n    cy.visit('\u002Fprojets')\n    cy.findByRole('button', { name: 'Nouveau projet' }).click()\n    cy.findByLabelText('Nom du projet').type('Maison Lacroix')\n    cy.findByRole('button', { name: 'Enregistrer' }).click()\n    cy.findByRole('status').should('contain', 'Projet enregistré')\n\n    cy.reload()\n    cy.findByRole('link', { name: 'Maison Lacroix' }).should('be.visible')\n  })\n})\n","cypress\u002Fe2e\u002Fprojets.cy.ts","ts","",[64,65,66,93,106,123,129,135,152,167,195,221,243,273,278,288,315,320],"code",{"__ignoreMap":62},[67,68,71,75,79,83,86,90],"span",{"class":69,"line":70},"line",1,[67,72,74],{"class":73},"sScJk","describe",[67,76,78],{"class":77},"sVt8B","(",[67,80,82],{"class":81},"sZZnC","'Projets'",[67,84,85],{"class":77},", () ",[67,87,89],{"class":88},"szBVR","=>",[67,91,92],{"class":77}," {\n",[67,94,96,99,102,104],{"class":69,"line":95},2,[67,97,98],{"class":73},"  beforeEach",[67,100,101],{"class":77},"(() ",[67,103,89],{"class":88},[67,105,92],{"class":77},[67,107,109,112,115,117,120],{"class":69,"line":108},3,[67,110,111],{"class":77},"    cy.",[67,113,114],{"class":73},"login",[67,116,78],{"class":77},[67,118,119],{"class":81},"'melanie@example.com'",[67,121,122],{"class":77},")\n",[67,124,126],{"class":69,"line":125},4,[67,127,128],{"class":77},"  })\n",[67,130,132],{"class":69,"line":131},5,[67,133,134],{"emptyLinePlaceholder":10},"\n",[67,136,138,141,143,146,148,150],{"class":69,"line":137},6,[67,139,140],{"class":73},"  it",[67,142,78],{"class":77},[67,144,145],{"class":81},"'crée un projet et le retrouve'",[67,147,85],{"class":77},[67,149,89],{"class":88},[67,151,92],{"class":77},[67,153,155,157,160,162,165],{"class":69,"line":154},7,[67,156,111],{"class":77},[67,158,159],{"class":73},"visit",[67,161,78],{"class":77},[67,163,164],{"class":81},"'\u002Fprojets'",[67,166,122],{"class":77},[67,168,170,172,175,177,180,183,186,189,192],{"class":69,"line":169},8,[67,171,111],{"class":77},[67,173,174],{"class":73},"findByRole",[67,176,78],{"class":77},[67,178,179],{"class":81},"'button'",[67,181,182],{"class":77},", { name: ",[67,184,185],{"class":81},"'Nouveau projet'",[67,187,188],{"class":77}," }).",[67,190,191],{"class":73},"click",[67,193,194],{"class":77},"()\n",[67,196,198,200,203,205,208,211,214,216,219],{"class":69,"line":197},9,[67,199,111],{"class":77},[67,201,202],{"class":73},"findByLabelText",[67,204,78],{"class":77},[67,206,207],{"class":81},"'Nom du projet'",[67,209,210],{"class":77},").",[67,212,213],{"class":73},"type",[67,215,78],{"class":77},[67,217,218],{"class":81},"'Maison Lacroix'",[67,220,122],{"class":77},[67,222,224,226,228,230,232,234,237,239,241],{"class":69,"line":223},10,[67,225,111],{"class":77},[67,227,174],{"class":73},[67,229,78],{"class":77},[67,231,179],{"class":81},[67,233,182],{"class":77},[67,235,236],{"class":81},"'Enregistrer'",[67,238,188],{"class":77},[67,240,191],{"class":73},[67,242,194],{"class":77},[67,244,246,248,250,252,255,257,260,262,265,268,271],{"class":69,"line":245},11,[67,247,111],{"class":77},[67,249,174],{"class":73},[67,251,78],{"class":77},[67,253,254],{"class":81},"'status'",[67,256,210],{"class":77},[67,258,259],{"class":73},"should",[67,261,78],{"class":77},[67,263,264],{"class":81},"'contain'",[67,266,267],{"class":77},", ",[67,269,270],{"class":81},"'Projet enregistré'",[67,272,122],{"class":77},[67,274,276],{"class":69,"line":275},12,[67,277,134],{"emptyLinePlaceholder":10},[67,279,281,283,286],{"class":69,"line":280},13,[67,282,111],{"class":77},[67,284,285],{"class":73},"reload",[67,287,194],{"class":77},[67,289,291,293,295,297,300,302,304,306,308,310,313],{"class":69,"line":290},14,[67,292,111],{"class":77},[67,294,174],{"class":73},[67,296,78],{"class":77},[67,298,299],{"class":81},"'link'",[67,301,182],{"class":77},[67,303,218],{"class":81},[67,305,188],{"class":77},[67,307,259],{"class":73},[67,309,78],{"class":77},[67,311,312],{"class":81},"'be.visible'",[67,314,122],{"class":77},[67,316,318],{"class":69,"line":317},15,[67,319,128],{"class":77},[67,321,323],{"class":69,"line":322},16,[67,324,325],{"class":77},"})\n",[18,327,328],{},"Les sélecteurs passent par les rôles et les libellés, avec Testing Library, jamais par des classes CSS ni des identifiants techniques. Deux avantages :",[30,330,331,338],{},[33,332,333,337],{},[334,335,336],"strong",{},"Le test survit à une refonte."," On peut changer les couleurs, la grille, le framework CSS : tant qu’il y a un bouton nommé « Enregistrer », le test passe.",[33,339,340,343],{},[334,341,342],{},"Il échoue si un bouton perd son nom."," Une icône sans libellé, un champ sans étiquette, et le test ne trouve plus rien. Le test d’accessibilité est gratuit.",[18,345,346,347,350,351,354],{},"Le ",[64,348,349],{},"cy.login"," est une commande maison qui passe par ",[64,352,353],{},"cy.session"," : la connexion se fait une fois, puis se rejoue depuis le cache. Sans ça, chaque test commence par trente secondes de formulaire.",[56,356,359],{"className":58,"code":357,"filename":358,"language":61,"meta":62,"style":62},"Cypress.Commands.add('login', (email: string) => {\n  cy.session(email, () => {\n    cy.visit('\u002Fconnexion')\n    cy.findByLabelText('E-mail').type(email)\n    cy.findByLabelText('Mot de passe').type(Cypress.env('PASSWORD'))\n    cy.findByRole('button', { name: 'Se connecter' }).click()\n    cy.findByRole('heading', { name: 'Tableau de bord' })\n  })\n})\n","cypress\u002Fsupport\u002Fcommands.ts",[64,360,361,395,410,423,441,470,491,510,514],{"__ignoreMap":62},[67,362,363,366,369,371,374,377,381,384,388,391,393],{"class":69,"line":70},[67,364,365],{"class":77},"Cypress.Commands.",[67,367,368],{"class":73},"add",[67,370,78],{"class":77},[67,372,373],{"class":81},"'login'",[67,375,376],{"class":77},", (",[67,378,380],{"class":379},"s4XuR","email",[67,382,383],{"class":88},":",[67,385,387],{"class":386},"sj4cs"," string",[67,389,390],{"class":77},") ",[67,392,89],{"class":88},[67,394,92],{"class":77},[67,396,397,400,403,406,408],{"class":69,"line":95},[67,398,399],{"class":77},"  cy.",[67,401,402],{"class":73},"session",[67,404,405],{"class":77},"(email, () ",[67,407,89],{"class":88},[67,409,92],{"class":77},[67,411,412,414,416,418,421],{"class":69,"line":108},[67,413,111],{"class":77},[67,415,159],{"class":73},[67,417,78],{"class":77},[67,419,420],{"class":81},"'\u002Fconnexion'",[67,422,122],{"class":77},[67,424,425,427,429,431,434,436,438],{"class":69,"line":125},[67,426,111],{"class":77},[67,428,202],{"class":73},[67,430,78],{"class":77},[67,432,433],{"class":81},"'E-mail'",[67,435,210],{"class":77},[67,437,213],{"class":73},[67,439,440],{"class":77},"(email)\n",[67,442,443,445,447,449,452,454,456,459,462,464,467],{"class":69,"line":131},[67,444,111],{"class":77},[67,446,202],{"class":73},[67,448,78],{"class":77},[67,450,451],{"class":81},"'Mot de passe'",[67,453,210],{"class":77},[67,455,213],{"class":73},[67,457,458],{"class":77},"(Cypress.",[67,460,461],{"class":73},"env",[67,463,78],{"class":77},[67,465,466],{"class":81},"'PASSWORD'",[67,468,469],{"class":77},"))\n",[67,471,472,474,476,478,480,482,485,487,489],{"class":69,"line":137},[67,473,111],{"class":77},[67,475,174],{"class":73},[67,477,78],{"class":77},[67,479,179],{"class":81},[67,481,182],{"class":77},[67,483,484],{"class":81},"'Se connecter'",[67,486,188],{"class":77},[67,488,191],{"class":73},[67,490,194],{"class":77},[67,492,493,495,497,499,502,504,507],{"class":69,"line":154},[67,494,111],{"class":77},[67,496,174],{"class":73},[67,498,78],{"class":77},[67,500,501],{"class":81},"'heading'",[67,503,182],{"class":77},[67,505,506],{"class":81},"'Tableau de bord'",[67,508,509],{"class":77}," })\n",[67,511,512],{"class":69,"line":169},[67,513,128],{"class":77},[67,515,516],{"class":69,"line":197},[67,517,325],{"class":77},[22,519,521],{"id":520},"des-données-quon-maîtrise","Des données qu’on maîtrise",[18,523,524],{},"Un test qui dépend de la base de données de la veille échoue dès que quelqu’un y touche. Deux règles :",[18,526,527,530,531,534],{},[334,528,529],{},"Chaque test crée ce dont il a besoin."," Le test « supprimer un projet » commence par en créer un, via l’API, pas via l’interface. Un appel ",[64,532,533],{},"cy.request"," de quelques millisecondes, et le test ne dépend plus de personne.",[18,536,537,540],{},[334,538,539],{},"Les services externes sont simulés."," Paiement, envoi d’e-mails, géolocalisation : je les intercepte et je renvoie une réponse connue.",[56,542,545],{"className":58,"code":543,"filename":544,"language":61,"meta":62,"style":62},"cy.intercept('POST', '\u002Fapi\u002Fpaiement', { statusCode: 200, body: { status: 'paid' } }).as('paiement')\ncy.findByRole('button', { name: 'Payer 49 €' }).click()\ncy.wait('@paiement')\ncy.findByRole('heading', { name: 'Merci pour votre commande' })\n","cypress\u002Fe2e\u002Fpaiement.cy.ts",[64,546,547,590,611,625],{"__ignoreMap":62},[67,548,549,552,555,557,560,562,565,568,571,574,577,580,583,585,588],{"class":69,"line":70},[67,550,551],{"class":77},"cy.",[67,553,554],{"class":73},"intercept",[67,556,78],{"class":77},[67,558,559],{"class":81},"'POST'",[67,561,267],{"class":77},[67,563,564],{"class":81},"'\u002Fapi\u002Fpaiement'",[67,566,567],{"class":77},", { statusCode: ",[67,569,570],{"class":386},"200",[67,572,573],{"class":77},", body: { status: ",[67,575,576],{"class":81},"'paid'",[67,578,579],{"class":77}," } }).",[67,581,582],{"class":73},"as",[67,584,78],{"class":77},[67,586,587],{"class":81},"'paiement'",[67,589,122],{"class":77},[67,591,592,594,596,598,600,602,605,607,609],{"class":69,"line":95},[67,593,551],{"class":77},[67,595,174],{"class":73},[67,597,78],{"class":77},[67,599,179],{"class":81},[67,601,182],{"class":77},[67,603,604],{"class":81},"'Payer 49 €'",[67,606,188],{"class":77},[67,608,191],{"class":73},[67,610,194],{"class":77},[67,612,613,615,618,620,623],{"class":69,"line":108},[67,614,551],{"class":77},[67,616,617],{"class":73},"wait",[67,619,78],{"class":77},[67,621,622],{"class":81},"'@paiement'",[67,624,122],{"class":77},[67,626,627,629,631,633,635,637,640],{"class":69,"line":125},[67,628,551],{"class":77},[67,630,174],{"class":73},[67,632,78],{"class":77},[67,634,501],{"class":81},[67,636,182],{"class":77},[67,638,639],{"class":81},"'Merci pour votre commande'",[67,641,509],{"class":77},[18,643,644],{},"Le vrai paiement se teste à part, une fois, à la main, avec les cartes de test du prestataire. Pas à chaque mise en ligne.",[22,646,648],{"id":647},"les-tests-qui-échouent-au-hasard","Les tests qui échouent au hasard",[18,650,651],{},"Un test instable finit ignoré, et un test ignoré ne protège plus rien. Je traque ces cas tout de suite, et ils ont presque toujours la même cause :",[30,653,654,668,674],{},[33,655,656,659,660,663,664,667],{},[334,657,658],{},"Une attente sur une animation."," Un ",[64,661,662],{},"cy.wait(500)"," qui « laisse le temps » au panneau de s’ouvrir. Il faut attendre l’élément, pas le temps : ",[64,665,666],{},"cy.findByRole('dialog')"," réessaie seul jusqu’à ce qu’il apparaisse.",[33,669,670,673],{},[334,671,672],{},"Un appel réseau pas simulé."," Le test passe en local et échoue en intégration continue parce que le serveur de test est plus lent. L’interception règle le problème.",[33,675,676,679],{},[334,677,678],{},"Une donnée partagée."," Deux tests qui créent « Maison Lacroix » et vérifient qu’il n’y en a qu’une. Un suffixe unique par test, et c’est fini.",[681,682,685],"callout",{"title":683,"type":684},"Le piège du retry","warning",[18,686,687,688,691,692,694,695,698],{},"Cypress réessaie une commande jusqu’à ce qu’elle réussisse, mais seulement la dernière de la chaîne. ",[64,689,690],{},"cy.get('li').first().should('contain', 'Lacroix')"," réessaie le ",[64,693,259],{},", pas le ",[64,696,697],{},"first()",". Si la liste se remplit après coup, le premier élément est resté vide. Quand une assertion dépend d’un contenu qui arrive plus tard, je sélectionne par le texte, pas par la position.",[22,700,702],{"id":701},"quand-ils-tournent","Quand ils tournent",[18,704,705],{},"Avant chaque mise en ligne. Pas après, pas « ce week-end ». Le déploiement est bloqué tant que les parcours clés ne passent pas, et c’est la seule règle que je ne négocie pas.",[56,707,712],{"className":708,"code":709,"filename":710,"language":711,"meta":62,"style":62},"language-yaml shiki shiki-themes github-light github-dark","- run: npm run build\n- run: npx cypress run --browser chrome\n- run: npm run deploy\n  if: success()\n",".github\u002Fworkflows\u002Fdeploy.yml","yaml",[64,713,714,729,740,751],{"__ignoreMap":62},[67,715,716,719,723,726],{"class":69,"line":70},[67,717,718],{"class":77},"- ",[67,720,722],{"class":721},"s9eBZ","run",[67,724,725],{"class":77},": ",[67,727,728],{"class":81},"npm run build\n",[67,730,731,733,735,737],{"class":69,"line":95},[67,732,718],{"class":77},[67,734,722],{"class":721},[67,736,725],{"class":77},[67,738,739],{"class":81},"npx cypress run --browser chrome\n",[67,741,742,744,746,748],{"class":69,"line":108},[67,743,718],{"class":77},[67,745,722],{"class":721},[67,747,725],{"class":77},[67,749,750],{"class":81},"npm run deploy\n",[67,752,753,756,758],{"class":69,"line":125},[67,754,755],{"class":721},"  if",[67,757,725],{"class":77},[67,759,760],{"class":81},"success()\n",[18,762,763],{},"Une série de cinq parcours tourne en deux ou trois minutes. C’est le prix d’une mise en ligne sereine, et il est bas.",[22,765,767],{"id":766},"ce-que-je-ne-teste-pas-en-bout-en-bout","Ce que je ne teste pas en bout en bout",[18,769,770],{},"Les tests Cypress sont lents et chers à maintenir : j’en garde peu. Le reste se teste au niveau où c’est le moins coûteux :",[30,772,773,779,785],{},[33,774,775,778],{},[334,776,777],{},"La logique pure"," (un calcul de prix, un formatage de date) : un test unitaire avec Vitest, qui tourne en millisecondes.",[33,780,781,784],{},[334,782,783],{},"Un composant isolé"," (un champ avec sa validation) : un test de composant, sans navigateur complet.",[33,786,787,790],{},[334,788,789],{},"Le visuel"," : pas de capture d’écran comparée pixel par pixel. Ça casse à chaque police mise à jour et personne ne regarde les différences.",[18,792,793],{},"Mieux vaut cinq tests de bout en bout fiables que cinquante qu’on relance en croisant les doigts. Les cinq disent, à chaque mise en ligne, que ce qui compte marche encore. C’est exactement ce qu’on leur demande.",[795,796,797],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":62,"searchDepth":95,"depth":95,"links":799},[800,801,802,803,804,805],{"id":24,"depth":95,"text":25},{"id":53,"depth":95,"text":54},{"id":520,"depth":95,"text":521},{"id":647,"depth":95,"text":648},{"id":701,"depth":95,"text":702},{"id":766,"depth":95,"text":767},"2026-05-20","Pas tout tester. Tester les parcours qui font gagner ou perdre un client. Ma façon de les choisir, de les écrire, et de les garder fiables assez longtemps pour qu’on leur fasse confiance.","md",{},"\u002Fblog\u002Ftester-ce-qui-compte",{"title":13,"description":807},"Tests Cypress : tester les parcours qui comptent (Vue, Nuxt)","blog\u002Ftester-ce-qui-compte",[815,816,817],"Tests","Cypress","Qualité","slate",null,"kQNuTaKAS6ASKzRQdJaqTaTmMeOzYJZrIUBLZv_NNaU",[822,982],{"id":823,"title":824,"body":825,"date":969,"description":970,"extension":808,"meta":971,"navigation":10,"path":972,"readingTime":125,"seo":973,"seoTitle":974,"stem":975,"tags":976,"tone":980,"updated":819,"__hash__":981},"blog\u002Fblog\u002Fcoder-avec-une-ia-sans-perdre-la-main.md","Coder avec une IA sans perdre la main",{"type":15,"value":826,"toc":962},[827,830,833,837,840,843,846,850,856,862,868,874,878,884,890,896,908,912,945,952,956,959],[18,828,829],{},"Il y a deux façons de rater le virage des assistants de code. La première est de les ignorer et de taper à la main ce qu’une machine écrit mieux et plus vite. La seconde est de tout leur laisser et de ne plus savoir ce qui tourne en production. Entre les deux, il y a une pratique, et elle se construit.",[18,831,832],{},"Voici la mienne, telle qu’elle est aujourd’hui, après un an de projets livrés avec Claude Code dans l’éditeur.",[22,834,836],{"id":835},"une-boucle-pas-une-baguette","Une boucle, pas une baguette",[18,838,839],{},"L’erreur la plus courante est de voir l’IA comme un générateur : on décrit, on reçoit, on colle. Ce n’est pas comme ça que ça marche bien. Ce qui marche, c’est une boucle courte où chaque étape a un responsable.",[841,842],"demo-loop",{},[18,844,845],{},"L’IA n’intervient qu’à une étape. Tout le reste, c’est du travail d’ingénierie classique, et c’est lui qui fait la qualité du résultat. Si le brief est flou, la génération est floue. Si la relecture est bâclée, les bugs passent. Si les tests n’existent pas, personne ne sait si la troisième itération a cassé la première.",[22,847,849],{"id":848},"ce-que-je-lui-confie","Ce que je lui confie",[18,851,852,855],{},[334,853,854],{},"Le code qui a déjà été écrit mille fois."," Un formulaire avec validation, un composant de liste avec ses états, une fonction qui formate des dates en français, la configuration d’un module Nuxt. L’IA connaît ces motifs par cœur et les écrit sans faute de frappe. Je gagne vingt minutes par occurrence, et il y en a beaucoup dans une journée.",[18,857,858,861],{},[334,859,860],{},"Les tâches larges et peu risquées."," Renommer un composant dans tout le projet, extraire un composable, ajouter un type manquant, écrire les premiers tests d’un fichier. Ce sont des tâches où l’erreur se voit tout de suite.",[18,863,864,867],{},[334,865,866],{},"La première version de quelque chose."," Quand je ne sais pas encore à quoi va ressembler un écran, je lui demande trois propositions. Je n’en garde jamais une telle quelle, mais je choisis plus vite.",[18,869,870,873],{},[334,871,872],{},"Les explications."," Un code hérité que personne ne commente, une erreur de build incompréhensible, une API que je n’ai jamais utilisée. Demander « explique-moi ce fichier » avant de le modifier m’a évité pas mal de casse.",[22,875,877],{"id":876},"ce-que-je-garde","Ce que je garde",[18,879,880,883],{},[334,881,882],{},"Les décisions."," L’architecture, le découpage en composants, le choix d’une bibliothèque, ce qui va dans le state global et ce qui n’y va pas. L’IA peut argumenter, elle ne tranche pas. C’est moi qui porterai la décision dans six mois.",[18,885,886,889],{},[334,887,888],{},"La relecture, ligne par ligne."," Je ne fusionne rien que je n’ai pas lu. Pas survolé : lu. Si une ligne me paraît magique, je demande pourquoi elle est là, et si la réponse ne me convainc pas, elle saute. Un code que je ne comprends pas est un code que je ne pourrai pas corriger à deux heures du matin.",[18,891,892,895],{},[334,893,894],{},"Tout ce qui touche à la sécurité et à l’argent."," Authentification, paiement, droits d’accès, données personnelles. L’IA écrit ce code aussi bien qu’un autre, mais là, je vérifie deux fois plus, et j’écris les tests moi-même.",[18,897,898,901,902,907],{},[334,899,900],{},"L’interface finale."," Les détails qu’un humain remarque sans savoir les nommer, j’en parle dans ",[903,904,906],"a",{"href":905},"\u002Fblog\u002Fce-que-l-ia-ne-voit-pas-dans-une-interface","un autre article",". Ils ne viennent pas de la génération, ils viennent de la relecture avec les yeux de l’utilisateur.",[22,909,911],{"id":910},"les-règles-que-je-me-suis-fixées","Les règles que je me suis fixées",[913,914,915,921,927,933,939],"ol",{},[33,916,917,920],{},[334,918,919],{},"Un fichier de conventions à la racine du projet."," Nom des composants, structure des dossiers, langue des commentaires, format des commits. L’assistant le lit à chaque session, et le code qu’il produit ressemble au mien. Sans ce fichier, chaque génération part dans une direction différente.",[33,922,923,926],{},[334,924,925],{},"Une tâche à la fois."," « Ajoute le champ téléphone au formulaire de contact, avec la validation et le test » est une tâche. « Améliore le formulaire » n’en est pas une. Plus la demande est bornée, plus la réponse est juste, et plus la relecture est rapide.",[33,928,929,932],{},[334,930,931],{},"Les tests tournent avant que je regarde."," Si la suite Cypress est rouge, je ne lis même pas le diff. Ça m’oblige à garder des tests fiables, et ça m’évite de perdre du temps sur un code qui ne marche pas.",[33,934,935,938],{},[334,936,937],{},"Je relis dans l’éditeur, pas dans la conversation."," Le diff dans Git, avec la coloration, le contexte autour, et la possibilité de lancer le projet. La conversation sert à discuter, pas à valider.",[33,940,941,944],{},[334,942,943],{},"Je garde une trace de ce qui a été généré."," Pas pour m’en excuser, mais parce que si un bug apparaît dans trois semaines, savoir qu’un bloc est sorti d’une génération rapide oriente la recherche.",[681,946,949],{"title":947,"type":948},"Le test du lendemain","tip",[18,950,951],{},"Avant de fusionner un gros morceau généré, je me pose une question simple : si cette fonction casse demain et que je n’ai plus accès à l’assistant, est-ce que je sais la réparer ? Si la réponse est non, ce n’est pas prêt.",[22,953,955],{"id":954},"ce-que-ça-change-concrètement","Ce que ça change, concrètement",[18,957,958],{},"Je livre plus vite, c’est vrai, mais ce n’est pas le plus intéressant. Ce qui a changé, c’est où va mon attention. Moins de temps sur la syntaxe et la plomberie, plus de temps sur ce que l’utilisateur voit et ressent. Plus de temps aussi sur les tests, parce qu’ils sont devenus la vraie garantie, celle qui ne dépend pas de qui a écrit le code.",[18,960,961],{},"Et je comprends toujours ce que je livre. C’était la condition, et elle tient.",{"title":62,"searchDepth":95,"depth":95,"links":963},[964,965,966,967,968],{"id":835,"depth":95,"text":836},{"id":848,"depth":95,"text":849},{"id":876,"depth":95,"text":877},{"id":910,"depth":95,"text":911},{"id":954,"depth":95,"text":955},"2026-10-21","J’utilise Claude Code tous les jours. Voici ce que je lui confie, ce que je garde, et les règles qui m’évitent de livrer du code que je ne comprends pas.",{},"\u002Fblog\u002Fcoder-avec-une-ia-sans-perdre-la-main",{"title":824,"description":970},"Coder avec Claude Code sans perdre la main : ma méthode","blog\u002Fcoder-avec-une-ia-sans-perdre-la-main",[977,978,979],"IA","Méthode","Claude Code","lavender","v2_5veRBSuqiUYQJU5moplrHaQM26kKEITUIO4IdjOU",{"id":983,"title":984,"body":985,"date":1145,"description":1146,"extension":808,"meta":1147,"navigation":10,"path":905,"readingTime":125,"seo":1148,"seoTitle":1149,"stem":1150,"tags":1151,"tone":818,"updated":819,"__hash__":1154},"blog\u002Fblog\u002Fce-que-l-ia-ne-voit-pas-dans-une-interface.md","Ce qu’une IA ne voit pas dans une interface",{"type":15,"value":986,"toc":1136},[987,990,993,997,1000,1003,1006,1032,1036,1039,1047,1051,1062,1073,1077,1080,1083,1087,1090,1093,1097,1100,1106,1110,1113,1133],[18,988,989],{},"Demandez à un assistant de code « une page de liste de projets avec un bouton pour en créer un ». Vous aurez une page propre, des cartes alignées, un bouton bleu. Elle marchera. Et elle sera incomplète d’une façon que personne ne remarque dans la conversation, mais que tout le monde remarque en l’utilisant.",[18,991,992],{},"Ce n’est pas une critique des outils. C’est la nature du problème : une interface se juge dans la main, pas sur une capture d’écran. Voici ce que je regarde systématiquement sur un écran généré, et que je trouve presque toujours à corriger.",[22,994,996],{"id":995},"les-états-que-personne-na-demandés","Les états que personne n’a demandés",[18,998,999],{},"Un écran n’existe pas dans un seul état. Il se charge, il peut être vide, il peut échouer, et seulement ensuite il montre quelque chose. Une génération produit l’état « succès » parce que c’est celui du brief. Les trois autres restent à écrire.",[1001,1002],"demo-states",{},[18,1004,1005],{},"Pour chacun, la question est la même : qu’est-ce que la personne comprend, et qu’est-ce qu’elle peut faire ?",[30,1007,1008,1014,1020,1026],{},[33,1009,1010,1013],{},[334,1011,1012],{},"Chargement"," : montrer la forme de ce qui arrive, pas un spinner au milieu du vide. Un squelette garde la page stable et dit « ça vient ».",[33,1015,1016,1019],{},[334,1017,1018],{},"Vide"," : ce n’est pas une erreur. Dire qu’il n’y a rien encore, et proposer la première action.",[33,1021,1022,1025],{},[334,1023,1024],{},"Erreur"," : dire ce qui s’est passé sans jargon, rassurer sur ce qui n’a pas été perdu, et offrir une sortie. « Une erreur est survenue » n’aide personne.",[33,1027,1028,1031],{},[334,1029,1030],{},"Succès"," : c’est le seul qu’on obtient gratuitement.",[22,1033,1035],{"id":1034},"le-retour-à-laction","Le retour à l’action",[18,1037,1038],{},"Un bouton généré change de couleur au survol, et c’est tout. Il ne réagit pas à l’appui, il ne confirme rien, il ne dit pas si l’action est en cours. Sur mobile, où il n’y a pas de survol, il ne donne aucun signe de vie.",[18,1040,1041,1042,1046],{},"J’ai écrit ",[903,1043,1045],{"href":1044},"\u002Fblog\u002Fun-bouton-qui-repond","un article entier"," sur ce seul point, parce qu’il change la sensation de toute l’interface. Le résumé : le retour visuel part à l’appui, l’action part au relâchement, et la confirmation arrive après.",[22,1048,1050],{"id":1049},"le-clavier","Le clavier",[18,1052,1053,1054,1057,1058,1061],{},"Essayez de parcourir l’écran avec la touche Tab. Souvent, l’ordre de parcours ne suit pas l’ordre visuel, le focus n’est pas visible, et la carte cliquable n’est pas atteignable parce que c’est un ",[64,1055,1056],{},"div"," avec un ",[64,1059,1060],{},"@click",". Rien de tout ça ne se voit sur une capture.",[18,1063,1064,1065,1068,1069,1072],{},"Les corrections sont petites : un vrai ",[64,1066,1067],{},"button"," ou un vrai lien, un ",[64,1070,1071],{},":focus-visible"," dessiné, l’ordre du DOM aligné sur l’ordre de lecture. Mais il faut les faire, et il faut y penser.",[22,1074,1076],{"id":1075},"le-texte","Le texte",[18,1078,1079],{},"Les textes générés sont corrects et interchangeables. « Gérer vos projets », « Commencer maintenant », « Une erreur est survenue ». Ils remplissent la case. Un texte qui aide est spécifique : il nomme ce qui va se passer, dans les mots de la personne qui lit.",[18,1081,1082],{},"Je réécris presque tous les libellés d’un écran généré. C’est le poste qui prend le plus de temps après les états, et c’est celui qui se voit le plus.",[22,1084,1086],{"id":1085},"les-écrans-intermédiaires","Les écrans intermédiaires",[18,1088,1089],{},"Entre 375 et 1440 pixels, il y a des largeurs où tout casse : une grille à trois colonnes qui devient illisible à 800, un titre qui déborde à 390, un tableau qui force le défilement horizontal. Les générations sont testées sur un écran, le vôtre, et c’est souvent le seul qui marche.",[18,1091,1092],{},"Je redimensionne la fenêtre lentement, de la plus étroite à la plus large, et je note chaque endroit où quelque chose saute. Ça prend deux minutes et ça évite les retours clients.",[22,1094,1096],{"id":1095},"le-mode-sombre","Le mode sombre",[18,1098,1099],{},"Si le projet a un thème sombre, le code généré oublie une couleur sur trois. Un fond blanc codé en dur dans une carte, une ombre invisible, un texte gris sur gris. La solution est en amont : ne jamais écrire une couleur dans un composant, toujours passer par les variables du thème. Une fois la règle dans le fichier de conventions, l’assistant la suit.",[681,1101,1103],{"type":1102},"note",[18,1104,1105],{},"Tout ce qui précède tient en une idée : l’IA produit ce qu’on lui décrit, et personne ne décrit les états vides, le focus clavier ou le texte d’erreur dans un brief. Ce sont les angles morts de la description, pas de l’outil.",[22,1107,1109],{"id":1108},"ma-liste","Ma liste",[18,1111,1112],{},"Avant qu’un écran généré n’aille plus loin, je passe ces points dans l’ordre :",[913,1114,1115,1118,1121,1124,1127,1130],{},[33,1116,1117],{},"Les quatre états existent et disent quoi faire.",[33,1119,1120],{},"Chaque action a un retour à l’appui et une confirmation.",[33,1122,1123],{},"Tout est atteignable au clavier, dans le bon ordre, avec un focus visible.",[33,1125,1126],{},"Chaque libellé nomme ce qui va se passer.",[33,1128,1129],{},"L’écran tient à 390, 768, 1024 et 1440 pixels.",[33,1131,1132],{},"Aucune couleur n’est écrite en dur.",[18,1134,1135],{},"Six points, dix minutes. C’est la différence entre un écran qui fonctionne et un écran qu’on a envie d’utiliser.",{"title":62,"searchDepth":95,"depth":95,"links":1137},[1138,1139,1140,1141,1142,1143,1144],{"id":995,"depth":95,"text":996},{"id":1034,"depth":95,"text":1035},{"id":1049,"depth":95,"text":1050},{"id":1075,"depth":95,"text":1076},{"id":1085,"depth":95,"text":1086},{"id":1095,"depth":95,"text":1096},{"id":1108,"depth":95,"text":1109},"2026-10-07","Une IA génère un écran correct en quelques secondes. Les détails qui le rendent agréable, ou simplement utilisable, viennent après. Liste de ce que je vérifie à la main.",{},{"title":984,"description":1146},"Interface générée par IA : 6 points à vérifier à la main","blog\u002Fce-que-l-ia-ne-voit-pas-dans-une-interface",[977,1152,1153],"Interface","Accessibilité","h6PnK0QzuO576IsiV6s7VYyIjKFmKVXSJ2Lu4TYMc0M",1793214703033]