[{"data":1,"prerenderedAt":19685},["ShallowReactive",2],{"search-api":-1,"listing-tag-ia-page-1":3},[4,836,1856,2763,5015,10174,15939,18324],{"_path":5,"_dir":6,"_draft":7,"_partial":7,"_locale":8,"title":9,"description":10,"id":11,"date":12,"listed":13,"nocomments":7,"hidden":13,"categories":14,"tags":15,"cover":21,"readingTime":22,"body":27,"_type":830,"_id":831,"_source":832,"_file":833,"_stem":834,"_extension":835},"\u002Ffr\u002Fintelligence-artificielle\u002Frecap-5-pieges-claude-semaine-1","intelligence-artificielle",false,"","5 pièges Claude que j'ai vus tuer la qualité du code en 2026 : le récap","Synthèse des 5 pièges Claude que je vois revenir dans crmcoaching et dans les accompagnements que je mène depuis 2024, et les contre-patterns craft pour les neutraliser.",68,"2026-05-22",true,[6],[16,17,18,19,20],"claude-code","recap","software-craftsmanship","ia","best-practices","covers\u002Farticles\u002Frecap-5-pieges-claude-semaine-1.jpg",{"text":23,"minutes":24,"time":25,"words":26},"13 min read",12.755,765300,2551,{"type":28,"children":29,"toc":816},"root",[30,42,47,51,58,63,77,91,119,122,128,141,154,157,163,177,190,193,199,204,225,228,241,244,250,255,268,271,277,282,303,306,312,322,343,346,352,357,378,383,386,392,397,627,632,635,641,646,651,656,671,683,686,692,715,728,741,754,767,801,804],{"type":31,"tag":32,"props":33,"children":34},"element","p",{},[35],{"type":31,"tag":36,"props":37,"children":38},"strong",{},[39],{"type":40,"value":41},"text","Pendant une semaine, j'ai publié sur ce blog 6 articles consacrés à un seul sujet : les pièges silencieux que Claude installe dans nos codebases en 2026, et les contre-patterns craft pour les neutraliser. L'audience a réagi en messages, en commentaires, en partages internes. Synthèse en une page, avec les pointeurs pour creuser chaque piège et la ligne directrice pour la suite.",{"type":31,"tag":32,"props":43,"children":44},{},[45],{"type":40,"value":46},"À lire d'une traite, même sans avoir vu aucun des 6 articles précédents. Vous repartez avec la carte complète, les contre-patterns nommés, et les pointeurs vers la profondeur de chaque sujet.",{"type":31,"tag":48,"props":49,"children":50},"hr",{},[],{"type":31,"tag":52,"props":53,"children":55},"h2",{"id":54},"pourquoi-cette-série",[56],{"type":40,"value":57},"Pourquoi cette série",{"type":31,"tag":32,"props":59,"children":60},{},[61],{"type":40,"value":62},"J'ai commencé cette série en construisant crmcoaching seul avec Claude, et en accompagnant des indépendants et des équipes sur des projets similaires. Au fil des sessions, les mêmes patterns revenaient. Les mêmes phrases en post-mortem. Les mêmes métriques trompeuses sur les tableaux de bord.",{"type":31,"tag":32,"props":64,"children":65},{},[66,68,75],{"type":40,"value":67},"Le constat de fond est documenté par le DORA Accelerate Report 2024 et le GitClear AI Copilot Code Quality Report 2024. Les équipes qui utilisent intensivement Claude livrent plus de code, mais accumulent silencieusement de la dette de qualité et de la dette éthique. Les chiffres sont là : throughput +35%, mais change failure rate +19% et lead time fix +27%. Le problème n'est pas l'outil, mais une discipline humaine restée à l'ère pré-IA. C'est le fond de mon ",{"type":31,"tag":69,"props":70,"children":72},"a",{"href":71},"\u002Ffr\u002Fintelligence-artificielle\u002Fia-code-review-retour-experience",[73],{"type":40,"value":74},"retour d'expérience sur la code review IA",{"type":40,"value":76},".",{"type":31,"tag":78,"props":79,"children":80},"blockquote",{},[81],{"type":31,"tag":32,"props":82,"children":83},{},[84,89],{"type":31,"tag":36,"props":85,"children":86},{},[87],{"type":40,"value":88},"Ce que j'ai voulu transmettre",{"type":40,"value":90}," : ni un procès de Claude, ni une nostalgie du code 100% humain, ni du dogme craft hors-sol. Plutôt une boîte à outils concrète : 5 pièges nommés, 5 contre-patterns testables, et une éthique professionnelle en 4 engagements. L'objectif : que vous arriviez lundi matin avec une grille de review actualisée, pas juste une frustration de fin de semaine.",{"type":31,"tag":32,"props":92,"children":93},{},[94,96,102,104,109,111,117],{"type":40,"value":95},"Robert C. Martin a écrit dans ",{"type":31,"tag":97,"props":98,"children":99},"em",{},[100],{"type":40,"value":101},"Clean Code",{"type":40,"value":103}," (2008) que ",{"type":31,"tag":97,"props":105,"children":106},{},[107],{"type":40,"value":108},"\"the only valid measurement of code quality is WTFs per minute.\"",{"type":40,"value":110}," Le craft que je défends sur ce blog s'appuie largement sur cet héritage, comme je le détaille dans l'article fondateur sur les ",{"type":31,"tag":69,"props":112,"children":114},{"href":113},"\u002Ffr\u002Fdette-technique\u002Fclean-code-software-craftsmanship-principes-java",[115],{"type":40,"value":116},"principes Clean Code et software craftsmanship",{"type":40,"value":118},". Cette série visait à diminuer les WTF par minute dans les revues de PR Claude. Voici la synthèse, jour par jour.",{"type":31,"tag":48,"props":120,"children":121},{},[],{"type":31,"tag":52,"props":123,"children":125},{"id":124},"lundi-le-bug-invisible",[126],{"type":40,"value":127},"Lundi - le bug invisible",{"type":31,"tag":32,"props":129,"children":130},{},[131,133,139],{"type":40,"value":132},"Le piège : Claude écrit régulièrement des race conditions silencieuses dans les fonctions qui font lecture\u002Fécriture sur de l'état partagé (base de données, cache, queue de jobs). L'exemple concret que je détaille dans l'article sur ",{"type":31,"tag":69,"props":134,"children":136},{"href":135},"\u002Ffr\u002Fintelligence-artificielle\u002Fbug-claude-vendredi-dernier",[137],{"type":40,"value":138},"le bug que Claude vous a fait coder vendredi dernier",{"type":40,"value":140},", c'est la double-réservation de créneau dans le bounded context slot-hold de crmcoaching : Claude génère une fonction qui vérifie la disponibilité, puis écrit la réservation en deux étapes séparées. Tests verts. Prod cassée dès la première exécution concurrente à l'ouverture des inscriptions.",{"type":31,"tag":32,"props":142,"children":143},{},[144,146,152],{"type":40,"value":145},"Le contre-pattern craft tient en 4 garde-fous appliqués systématiquement en review : nommer explicitement la concurrence, utiliser une transaction atomique, typer les exceptions, et écrire un test de concurrence explicite. Cette grille prend 3 minutes par PR. Elle attrape 90% des bugs de concurrence que j'ai vus passer en prod sur des codebases IA-heavy. Elle s'inscrit dans la discipline plus large d'une ",{"type":31,"tag":69,"props":147,"children":149},{"href":148},"\u002Ffr\u002Fdette-technique\u002Frevue-de-code-java-guide-exemples",[150],{"type":40,"value":151},"revue de code structurée",{"type":40,"value":153}," qui doit s'adapter à l'ère IA, et elle complète directement les patterns de résilience type circuit breaker et retry que j'ai détaillés ailleurs sur le blog.",{"type":31,"tag":48,"props":155,"children":156},{},[],{"type":31,"tag":52,"props":158,"children":160},{"id":159},"mardi-les-5-anti-patterns-silencieux",[161],{"type":40,"value":162},"Mardi - les 5 anti-patterns silencieux",{"type":31,"tag":32,"props":164,"children":165},{},[166,168,175],{"type":40,"value":167},"Le deuxième piège est plus large : Claude reproduit en silence 5 anti-patterns issus de son training data. La God Function de 80 lignes. Le couplage métier directement à l'ORM, qui rend les tests unitaires impossibles. Les tests qui ne testent rien (du ",{"type":31,"tag":169,"props":170,"children":172},"code",{"className":171},[],[173],{"type":40,"value":174},"expect(...).toHaveBeenCalled()",{"type":40,"value":176}," sans vérification de comportement métier). Le catch-all silencieux qui masque les bugs au lieu de les remonter. Et les dépendances non auditées, qui ouvrent la porte aux vulnérabilités de sécurité dans le code généré par LLM.",{"type":31,"tag":32,"props":178,"children":179},{},[180,182,188],{"type":40,"value":181},"L'analyse complète, avec les signaux d'alerte et les contre-patterns nommés (Single Responsibility Principle, architecture hexagonale, Acceptance Criteria, exceptions typées, Dependabot configuré craft), est dans l'article sur les ",{"type":31,"tag":69,"props":183,"children":185},{"href":184},"\u002Ffr\u002Fintelligence-artificielle\u002F5-patterns-dangereux-claude",[186],{"type":40,"value":187},"5 anti-patterns que Claude reproduit en silence",{"type":40,"value":189},". Cette grille en 5 lignes se colle directement sur le PR template GitHub, et elle attrape 80% de ce que la review rapide laisse passer.",{"type":31,"tag":48,"props":191,"children":192},{},[],{"type":31,"tag":52,"props":194,"children":196},{"id":195},"mercredi-tests-verts-prod-rouge",[197],{"type":40,"value":198},"Mercredi - tests verts, prod rouge",{"type":31,"tag":32,"props":200,"children":201},{},[202],{"type":40,"value":203},"Le troisième piège est celui qui fait perdre le plus de nuits : les tests Claude couvrent le happy path, passent la CI, et ratent systématiquement les 4 modes d'échec qui font vraiment planter la prod. La concurrence à 2h du matin. Le rate limit Brevo qui renvoie un 429 lors d'un envoi d'emails en masse depuis le bounded context notification. Le timeout réseau silencieux. Le partial failure où la moitié d'une opération est exécutée et l'autre pas. Aucun de ces 4 tests n'est généré spontanément par Claude.",{"type":31,"tag":32,"props":205,"children":206},{},[207,209,215,217,223],{"type":40,"value":208},"Le contre-pattern craft consiste à exiger explicitement ces 4 tests sur toute fonction qui touche à une dépendance externe (réseau, base de données, queue, file system). J'ai détaillé le scénario complet, le pipeline de tests à 3 étages et le ROI mesuré dans l'article sur ",{"type":31,"tag":69,"props":210,"children":212},{"href":211},"\u002Ffr\u002Fintelligence-artificielle\u002Fcode-claude-tests-passent-plante-prod",[213],{"type":40,"value":214},"le code Claude dont les tests passent mais qui plante en prod",{"type":40,"value":216},". Cette discipline s'inscrit dans une Definition of Done qui pose les bons critères de qualité, et complète la ",{"type":31,"tag":69,"props":218,"children":220},{"href":219},"\u002Ffr\u002Fintelligence-artificielle\u002Ftester-code-genere-ia-checklist",[221],{"type":40,"value":222},"checklist pour tester du code généré par IA",{"type":40,"value":224}," que j'avais publiée en 2025.",{"type":31,"tag":48,"props":226,"children":227},{},[],{"type":31,"tag":229,"props":230,"children":235},"cta",{"cta":231,"href":232,"title":233,"type":234},"Coder comme un senior →","https:\u002F\u002Fapp.kamanga.fr\u002Fforms\u002Fmentoring","Vous voulez acquérir le réflexe qui repère ces pièges Claude avant le merge ?","call",[236],{"type":31,"tag":32,"props":237,"children":238},{},[239],{"type":40,"value":240},"Repérer une race condition ou un test qui ne teste rien dans une PR générée par Claude, ça ne s'apprend pas en lisant une grille : ça se travaille sur du vrai code. En mentoring 1:1, je relis vos PR avec vous, on applique les contre-patterns ensemble, et vous installez ces garde-fous comme un réflexe durable. Vous commencez à voir ce que le modèle ne voit pas.",{"type":31,"tag":48,"props":242,"children":243},{},[],{"type":31,"tag":52,"props":245,"children":247},{"id":246},"jeudi-le-faux-ami",[248],{"type":40,"value":249},"Jeudi - le faux ami",{"type":31,"tag":32,"props":251,"children":252},{},[253],{"type":40,"value":254},"Le quatrième piège est plus insidieux parce qu'il ne fait rien planter. Le code Claude \"qui marche\" cache 3 faux amis qui coûtent cher à 3 et 6 mois. Le copier-coller invisible, où Claude reproduit une fonction au lieu de réutiliser celle qui existe déjà ailleurs dans le repo. Par exemple dans crmcoaching, la logique de validation d'une offre se retrouve dupliquée entre le bounded context lead et le bounded context client, deux copies qui divergent silencieusement. Les abstractions accidentelles, avec une interface et une factory pour une seule implémentation. La sur-ingénierie déguisée, avec handler, bus et ports hexagonaux pour ce qui devrait être un SELECT trivial. Ensemble, ces 3 faux amis gonflent les codebases sans ajouter de valeur métier.",{"type":31,"tag":32,"props":256,"children":257},{},[258,260,266],{"type":40,"value":259},"Le contre-pattern, c'est la règle YAGNI étendue à l'ère IA, déclinée en 4 points dans l'article sur ",{"type":31,"tag":69,"props":261,"children":263},{"href":262},"\u002Ffr\u002Fintelligence-artificielle\u002Ffaux-ami-code-claude-coute-cher",[264],{"type":40,"value":265},"le code Claude qui marche mais qui coûte cher",{"type":40,"value":267},". Pas d'abstraction sans 3 implémentations réelles (la règle des 3 de Martin Fowler). Prompter Claude pour \"cherche les doublons avant de finaliser\". Proportionnalité entre la complexité du code et la complexité du domaine. Cette discipline rejoint directement les fondamentaux du clean code appliqués au code IA-généré, et c'est aussi un angle business : le craft n'est pas une dépense, c'est un levier d'économie composée.",{"type":31,"tag":48,"props":269,"children":270},{},[],{"type":31,"tag":52,"props":272,"children":274},{"id":273},"vendredi-lillusion-de-productivité",[275],{"type":40,"value":276},"Vendredi - l'illusion de productivité",{"type":31,"tag":32,"props":278,"children":279},{},[280],{"type":40,"value":281},"Le cinquième piège est celui que les COMEX adorent et que les PO redoutent : la productivité affichée. Le throughput IA grimpe de 35% à 89% en moyenne, c'est mesurable et visible. Pendant ce temps, les vraies features mettent plus de temps à arriver en prod et les incidents augmentent. Les deux observations sont vraies. Ce qui ment, c'est la métrique unique \"PR mergées par sprint\", qui compte l'activité au lieu de mesurer le résultat.",{"type":31,"tag":32,"props":283,"children":284},{},[285,287,293,295,301],{"type":40,"value":286},"Le contre-pattern craft consiste à abandonner les 4 métriques qui mentent (lignes de code, PR mergées, story points, tickets fermés) et à adopter les 4 métriques DORA (lead time, deployment frequency, change failure rate, MTTR) plus une 5ème métrique méta : le ratio ADR pour 100 commits. C'est ce que je détaille dans l'article sur ",{"type":31,"tag":69,"props":288,"children":290},{"href":289},"\u002Ffr\u002Fintelligence-artificielle\u002Fillusion-productivite-10x-pr",[291],{"type":40,"value":292},"l'illusion des 10x plus de PR",{"type":40,"value":294},", avec le tableau comparatif \"sans Claude \u002F avec Claude non piloté \u002F avec Claude piloté\". Cette grille à 5 indicateurs est aussi le socle d'une ",{"type":31,"tag":69,"props":296,"children":298},{"href":297},"\u002Ffr\u002Fmanagement\u002Fmetriques-management-developpeurs-motivation",[299],{"type":40,"value":300},"bonne pratique des métriques de management des développeurs",{"type":40,"value":302},", et c'est exactement la lecture qui permet à un CTO de décider sereinement comment évaluer Claude dans son équipe au-delà du buzz.",{"type":31,"tag":48,"props":304,"children":305},{},[],{"type":31,"tag":52,"props":307,"children":309},{"id":308},"samedi-la-question-éthique-de-fond",[310],{"type":40,"value":311},"Samedi - la question éthique de fond",{"type":31,"tag":32,"props":313,"children":314},{},[315,317],{"type":40,"value":316},"Au-delà des 5 pièges techniques, la semaine a posé une question qu'on évite trop souvent en stand-up : ",{"type":31,"tag":97,"props":318,"children":319},{},[320],{"type":40,"value":321},"à qui appartient le bug en prod, vous ou Claude ?",{"type":31,"tag":32,"props":323,"children":324},{},[325,327,333,335,341],{"type":40,"value":326},"La réponse craft, sourcée juridiquement (Thaler v. Perlmutter 2023, confirmé en appel 2025) : l'IA n'a pas la personnalité juridique. Le code mergé est attribué à celui qui l'a mergé. Si vous mergez, vous devenez l'auteur. J'ai détaillé la position complète, le contrat moral du dev 2026 en 4 engagements (lire, tester, tracer, assumer) et le squelette d'",{"type":31,"tag":69,"props":328,"children":330},{"href":329},"\u002Ffr\u002Farchitecture-craft\u002Fadr-architecture-decision-record",[331],{"type":40,"value":332},"ADR de gouvernance IA",{"type":40,"value":334}," dans l'article sur ",{"type":31,"tag":69,"props":336,"children":338},{"href":337},"\u002Ffr\u002Fintelligence-artificielle\u002Fa-qui-appartient-le-bug-ia",[339],{"type":40,"value":340},"à qui appartient le bug en prod",{"type":40,"value":342},". Ce sujet rejoint en profondeur la peur de l'automatisation pour les développeurs : ce n'est pas Claude qu'il faut craindre, c'est la déresponsabilisation silencieuse qui s'installe sans dispositif éthique explicite.",{"type":31,"tag":48,"props":344,"children":345},{},[],{"type":31,"tag":52,"props":347,"children":349},{"id":348},"ce-quon-regarde-la-semaine-suivante-la-dette-ia-induite",[350],{"type":40,"value":351},"Ce qu'on regarde la semaine suivante : la dette IA-induite",{"type":31,"tag":32,"props":353,"children":354},{},[355],{"type":40,"value":356},"Si la première semaine portait sur les pièges techniques individuels qu'un développeur peut identifier sur sa propre PR, la suivante monte d'un cran : c'est la dette technique systémique qui se construit dans les codebases IA-heavy non disciplinées. Spoiler : une part croissante de la dette technique observée en 2026 dans les projets que j'accompagne est directement attribuable à du code IA-généré non audité, un chiffre que je consolide en cross-référence avec le DORA 2024 et mes propres observations. Cette dette se chiffre comme le risque legacy classique, et elle se rembourse avec les mêmes outils, en commençant par un programme de refactoring approuvé business.",{"type":31,"tag":32,"props":358,"children":359},{},[360,362,368,370,376],{"type":40,"value":361},"Deux articles supplémentaires complètent la boucle. Le premier, sur ",{"type":31,"tag":69,"props":363,"children":365},{"href":364},"\u002Ffr\u002Fdette-technique\u002Fdependabot-craft-gestion-dependances",[366],{"type":40,"value":367},"Dependabot comme coéquipier silencieux contre la dette de dépendances",{"type":40,"value":369},", montre comment configurer l'outil pour qu'il devienne un filet rétroactif efficace sans inonder le repo de PR inutiles. Le deuxième, sur ",{"type":31,"tag":69,"props":371,"children":373},{"href":372},"\u002Ffr\u002Fdette-technique\u002F5-raisons-app-meurt-18-mois",[374],{"type":40,"value":375},"les 5 raisons pour lesquelles votre app meurt à 18 mois",{"type":40,"value":377},", élargit le propos au-delà de Claude et identifie les 5 piliers du craft durable qui font vivre une codebase 10 ans au lieu de 18 mois.",{"type":31,"tag":32,"props":379,"children":380},{},[381],{"type":40,"value":382},"Ces deux articles ferment la boucle : du tactique (pièges Claude individuels) au stratégique (dette IA-induite et durabilité des applications), du code à la culture d'équipe.",{"type":31,"tag":48,"props":384,"children":385},{},[],{"type":31,"tag":52,"props":387,"children":389},{"id":388},"la-grille-consolidée-à-imprimer",[390],{"type":40,"value":391},"La grille consolidée à imprimer",{"type":31,"tag":32,"props":393,"children":394},{},[395],{"type":40,"value":396},"Si vous ne devez retenir qu'une seule chose de cette série, c'est cette grille. À copier dans votre PR template GitHub, à afficher en sprint review, à utiliser en post-mortem.",{"type":31,"tag":398,"props":399,"children":400},"table",{},[401,425],{"type":31,"tag":402,"props":403,"children":404},"thead",{},[405],{"type":31,"tag":406,"props":407,"children":408},"tr",{},[409,415,420],{"type":31,"tag":410,"props":411,"children":412},"th",{},[413],{"type":40,"value":414},"Piège",{"type":31,"tag":410,"props":416,"children":417},{},[418],{"type":40,"value":419},"Signal d'alerte",{"type":31,"tag":410,"props":421,"children":422},{},[423],{"type":40,"value":424},"Contre-pattern craft",{"type":31,"tag":426,"props":427,"children":428},"tbody",{},[429,448,466,490,513,537,555,573,591,609],{"type":31,"tag":406,"props":430,"children":431},{},[432,438,443],{"type":31,"tag":433,"props":434,"children":435},"td",{},[436],{"type":40,"value":437},"Race condition",{"type":31,"tag":433,"props":439,"children":440},{},[441],{"type":40,"value":442},"Lecture + écriture sur état partagé sans transaction",{"type":31,"tag":433,"props":444,"children":445},{},[446],{"type":40,"value":447},"Transaction atomique + test de concurrence",{"type":31,"tag":406,"props":449,"children":450},{},[451,456,461],{"type":31,"tag":433,"props":452,"children":453},{},[454],{"type":40,"value":455},"God Function",{"type":31,"tag":433,"props":457,"children":458},{},[459],{"type":40,"value":460},"Fonction de plus de 40 lignes",{"type":31,"tag":433,"props":462,"children":463},{},[464],{"type":40,"value":465},"Découpe SRP en sous-fonctions nommées",{"type":31,"tag":406,"props":467,"children":468},{},[469,474,485],{"type":31,"tag":433,"props":470,"children":471},{},[472],{"type":40,"value":473},"Couplage ORM",{"type":31,"tag":433,"props":475,"children":476},{},[477,479],{"type":40,"value":478},"Import Prisma ou TypeORM dans le dossier ",{"type":31,"tag":169,"props":480,"children":482},{"className":481},[],[483],{"type":40,"value":484},"domain\u002F",{"type":31,"tag":433,"props":486,"children":487},{},[488],{"type":40,"value":489},"Repository interface + adapter",{"type":31,"tag":406,"props":491,"children":492},{},[493,498,508],{"type":31,"tag":433,"props":494,"children":495},{},[496],{"type":40,"value":497},"Tests vides",{"type":31,"tag":433,"props":499,"children":500},{},[501,506],{"type":31,"tag":169,"props":502,"children":504},{"className":503},[],[505],{"type":40,"value":174},{"type":40,"value":507}," sans assertion métier",{"type":31,"tag":433,"props":509,"children":510},{},[511],{"type":40,"value":512},"Given\u002FWhen\u002FThen sur comportement observable",{"type":31,"tag":406,"props":514,"children":515},{},[516,521,532],{"type":31,"tag":433,"props":517,"children":518},{},[519],{"type":40,"value":520},"Catch-all silencieux",{"type":31,"tag":433,"props":522,"children":523},{},[524,530],{"type":31,"tag":169,"props":525,"children":527},{"className":526},[],[528],{"type":40,"value":529},"catch (err) { console.error(err) }",{"type":40,"value":531}," sans politique",{"type":31,"tag":433,"props":533,"children":534},{},[535],{"type":40,"value":536},"Re-throw, ou métrique d'observabilité",{"type":31,"tag":406,"props":538,"children":539},{},[540,545,550],{"type":31,"tag":433,"props":541,"children":542},{},[543],{"type":40,"value":544},"Copier-coller",{"type":31,"tag":433,"props":546,"children":547},{},[548],{"type":40,"value":549},"Fonction sémantiquement proche d'une existante",{"type":31,"tag":433,"props":551,"children":552},{},[553],{"type":40,"value":554},"Prompter Claude pour mutualiser",{"type":31,"tag":406,"props":556,"children":557},{},[558,563,568],{"type":31,"tag":433,"props":559,"children":560},{},[561],{"type":40,"value":562},"Abstraction accidentelle",{"type":31,"tag":433,"props":564,"children":565},{},[566],{"type":40,"value":567},"Interface devant une seule implémentation",{"type":31,"tag":433,"props":569,"children":570},{},[571],{"type":40,"value":572},"Règle des 3 cas réels (Fowler)",{"type":31,"tag":406,"props":574,"children":575},{},[576,581,586],{"type":31,"tag":433,"props":577,"children":578},{},[579],{"type":40,"value":580},"Tests modes d'échec",{"type":31,"tag":433,"props":582,"children":583},{},[584],{"type":40,"value":585},"Pas de test 429, timeout ou concurrence",{"type":31,"tag":433,"props":587,"children":588},{},[589],{"type":40,"value":590},"4 tests anti-fragiles obligatoires",{"type":31,"tag":406,"props":592,"children":593},{},[594,599,604],{"type":31,"tag":433,"props":595,"children":596},{},[597],{"type":40,"value":598},"Métriques d'activité",{"type":31,"tag":433,"props":600,"children":601},{},[602],{"type":40,"value":603},"LOC, PR mergées, story points seuls",{"type":31,"tag":433,"props":605,"children":606},{},[607],{"type":40,"value":608},"DORA + ratio ADR pour 100 commits",{"type":31,"tag":406,"props":610,"children":611},{},[612,617,622],{"type":31,"tag":433,"props":613,"children":614},{},[615],{"type":40,"value":616},"Responsabilité diluée",{"type":31,"tag":433,"props":618,"children":619},{},[620],{"type":40,"value":621},"\"C'est Claude qui l'a écrit\" en post-mortem",{"type":31,"tag":433,"props":623,"children":624},{},[625],{"type":40,"value":626},"ADR de gouvernance IA + 4 engagements",{"type":31,"tag":32,"props":628,"children":629},{},[630],{"type":40,"value":631},"10 lignes. 5 minutes de revue par PR. Le retour sur investissement s'observe en 3 mois, ce qui s'inscrit dans une démarche plus large de maturité engineering progressive et qui transforme chaque PR en occasion de gain de qualité, selon la philosophie Boy Scout Rule.",{"type":31,"tag":48,"props":633,"children":634},{},[],{"type":31,"tag":52,"props":636,"children":638},{"id":637},"conclusion",[639],{"type":40,"value":640},"Conclusion",{"type":31,"tag":32,"props":642,"children":643},{},[644],{"type":40,"value":645},"Ce que je veux que vous reteniez de cette série : Claude n'est ni un magicien, ni un saboteur. C'est un collègue junior très rapide, qui produit beaucoup, apprend mal de la prod, et qui a besoin d'un dispositif humain de relecture, de test et de discipline pour devenir utile à long terme. Sans ce dispositif, vous accumulez de la dette à vitesse industrielle. Avec, Claude devient un accélérateur de qualité.",{"type":31,"tag":32,"props":647,"children":648},{},[649],{"type":40,"value":650},"La première semaine vous a donné la grille opérationnelle : 5 pièges, 5 contre-patterns, 4 engagements éthiques. La suivante vous donnera la lecture systémique : les 5 raisons pour lesquelles une application meurt à 18 mois, et les 5 pratiques craft pour bâtir des codes qui tiennent 10 ans au lieu d'un cycle court.",{"type":31,"tag":32,"props":652,"children":653},{},[654],{"type":40,"value":655},"Si vous vous reconnaissez dans ce tableau, l'arbitrage de lundi matin est simple : retourner sur votre repo en supposant que tout va bien, ou bloquer 30 minutes pour installer la grille consolidée, lancer un premier audit, démarrer la transformation. Coût ridicule, impact mesurable en 6 semaines.",{"type":31,"tag":32,"props":657,"children":658},{},[659,661,669],{"type":40,"value":660},"Pour la suite des contenus craft + IA en format court, vous pouvez me retrouver sur ",{"type":31,"tag":69,"props":662,"children":666},{"href":663,"rel":664},"https:\u002F\u002Fwww.instagram.com\u002Fkamangacode\u002F",[665],"nofollow",[667],{"type":40,"value":668},"mon profil Instagram kamangacode",{"type":40,"value":670},", où je publie chaque semaine les patterns que je vois revenir en mission et les contre-patterns que j'applique.",{"type":31,"tag":229,"props":672,"children":677},{"cta":673,"href":674,"title":675,"type":676},"Les 100 pratiques que l'IA n'enseigne pas →","https:\u002F\u002Fkamanga.fr\u002Freferentiel-craft","Cette grille à 10 lignes n'est qu'un extrait : il existe 100 pratiques craft","product",[678],{"type":31,"tag":32,"props":679,"children":680},{},[681],{"type":40,"value":682},"La grille consolidée de cette série fait partie d'un référentiel bien plus large : le Craft Bundle, les 100 pratiques craft que j'applique pour coder propre. Race conditions, découpe SRP, exceptions typées, tests anti-fragiles : tout ce que cette synthèse effleure y est détaillé, avec les centaines d'autres pratiques que l'IA ne vous apprendra jamais parce qu'elle ne les a jamais vues tourner en prod.",{"type":31,"tag":48,"props":684,"children":685},{},[],{"type":31,"tag":52,"props":687,"children":689},{"id":688},"faq-sur-la-série-craft-et-ia",[690],{"type":40,"value":691},"FAQ sur la série craft et IA",{"type":31,"tag":693,"props":694,"children":695},"details",{},[696,702],{"type":31,"tag":697,"props":698,"children":699},"summary",{},[700],{"type":40,"value":701},"1. La série fonctionne-t-elle pour des stacks autres que TypeScript ou Node ?",{"type":31,"tag":32,"props":703,"children":704},{},[705,707,713],{"type":40,"value":706},"Oui, à 95%. Les pièges sont indépendants du langage. Les exemples de code sont en TypeScript, mais les patterns existent en Java, Python, Go, C# avec les mêmes signatures. Pour les contre-patterns, les outils diffèrent (",{"type":31,"tag":169,"props":708,"children":710},{"className":709},[],[711],{"type":40,"value":712},"p-retry",{"type":40,"value":714}," devient Resilience4j en Java, ou Tenacity en Python), mais la philosophie est identique. Si vous êtes sur Java avec Maven, échangez juste les snippets, gardez la grille.",{"type":31,"tag":693,"props":716,"children":717},{},[718,723],{"type":31,"tag":697,"props":719,"children":720},{},[721],{"type":40,"value":722},"2. Combien de temps pour installer cette discipline dans une équipe de 10 développeurs ?",{"type":31,"tag":32,"props":724,"children":725},{},[726],{"type":40,"value":727},"3 à 6 mois pour que la grille devienne un réflexe partagé. La première semaine, on installe le PR template et l'ADR de gouvernance. Le premier mois, les seniors démontrent par l'exemple. Au deuxième mois, les juniors commencent à porter la grille spontanément. Au troisième mois, c'est dans la culture. À 6 mois, on mesure les chiffres avant\u002Faprès dans les articles de la série, et l'équipe est prête à passer à un sujet plus avancé comme l'évaluation collective d'un outil IA en équipe.",{"type":31,"tag":693,"props":729,"children":730},{},[731,736],{"type":31,"tag":697,"props":732,"children":733},{},[734],{"type":40,"value":735},"3. Faut-il refuser Claude tant que la grille n'est pas en place ?",{"type":31,"tag":32,"props":737,"children":738},{},[739],{"type":40,"value":740},"Non. La pire approche serait d'attendre la perfection avant d'utiliser l'outil. La meilleure approche : commencer par 2 garde-fous sur le piège qui vous touche le plus (souvent la race condition ou les tests vides), mesurer l'impact sur 6 semaines, puis ajouter le suivant. C'est de la transformation incrémentale, exactement ce que recommandent les principes d'un code évolutif.",{"type":31,"tag":693,"props":742,"children":743},{},[744,749],{"type":31,"tag":697,"props":745,"children":746},{},[747],{"type":40,"value":748},"4. Comment partager cette série avec mon équipe sans paraître donneur de leçons ?",{"type":31,"tag":32,"props":750,"children":751},{},[752],{"type":40,"value":753},"Le mieux est de proposer une session de 30 minutes pendant laquelle vous présentez un seul piège, celui qui vous parle le plus, avec un exemple tiré de votre propre repo et une discussion sur le contre-pattern. Pas une présentation magistrale, une discussion d'égal à égal. Sur les missions où j'ai vu cette approche, l'adhésion est à 80% en 3 sessions.",{"type":31,"tag":693,"props":755,"children":756},{},[757,762],{"type":31,"tag":697,"props":758,"children":759},{},[760],{"type":40,"value":761},"5. Les pièges vont-ils évoluer avec les prochaines versions de Claude ?",{"type":31,"tag":32,"props":763,"children":764},{},[765],{"type":40,"value":766},"Certains s'atténueront probablement (les modèles deviennent meilleurs sur la concurrence et l'observabilité), d'autres s'amplifieront (la facilité de génération augmente la sur-ingénierie). Ma règle : la grille doit être révisée tous les 12 mois en équipe, avec un audit des pièges présents et l'ajout éventuel de nouveaux. C'est un système vivant, pas un dogme figé.",{"type":31,"tag":693,"props":768,"children":769},{},[770,775],{"type":31,"tag":697,"props":771,"children":772},{},[773],{"type":40,"value":774},"6. Où trouver des ressources complémentaires pour aller plus loin ?",{"type":31,"tag":32,"props":776,"children":777},{},[778,780,784,786,791,793,799],{"type":40,"value":779},"Trois sources que je recommande systématiquement en mission : ",{"type":31,"tag":97,"props":781,"children":782},{},[783],{"type":40,"value":101},{"type":40,"value":785}," de Robert C. Martin (les fondamentaux du craft), ",{"type":31,"tag":97,"props":787,"children":788},{},[789],{"type":40,"value":790},"Release It!",{"type":40,"value":792}," de Michael Nygard (les patterns de résilience prod), et le DORA Accelerate Report annuel (les métriques de référence). En format court, vous me retrouvez sur ",{"type":31,"tag":69,"props":794,"children":796},{"href":663,"rel":795},[665],[797],{"type":40,"value":798},"Instagram @kamangacode",{"type":40,"value":800}," avec un pattern craft par semaine.",{"type":31,"tag":48,"props":802,"children":803},{},[],{"type":31,"tag":229,"props":805,"children":810},{"cta":806,"href":807,"title":808,"type":809},"Évaluer la maturité de mon équipe →","\u002Fmes-ressources","Ressource gratuite : Engineering Maturity Assessment","resource",[811],{"type":31,"tag":32,"props":812,"children":813},{},[814],{"type":40,"value":815},"L'EMA est l'outil que je propose au début de chaque mission. Il mesure la maturité de votre équipe sur 8 axes engineering, dont la gouvernance IA, la qualité du code, la dette technique et l'observabilité prod. Quelques minutes pour identifier lesquels des pièges Claude s'installent le plus vite chez vous, et où concentrer vos efforts en priorité.",{"title":8,"searchDepth":817,"depth":817,"links":818},2,[819,820,821,822,823,824,825,826,827,828,829],{"id":54,"depth":817,"text":57},{"id":124,"depth":817,"text":127},{"id":159,"depth":817,"text":162},{"id":195,"depth":817,"text":198},{"id":246,"depth":817,"text":249},{"id":273,"depth":817,"text":276},{"id":308,"depth":817,"text":311},{"id":348,"depth":817,"text":351},{"id":388,"depth":817,"text":391},{"id":637,"depth":817,"text":640},{"id":688,"depth":817,"text":691},"markdown","content:fr:intelligence-artificielle:recap-5-pieges-claude-semaine-1.md","content","fr\u002Fintelligence-artificielle\u002Frecap-5-pieges-claude-semaine-1.md","fr\u002Fintelligence-artificielle\u002Frecap-5-pieges-claude-semaine-1","md",{"_path":337,"_dir":6,"_draft":7,"_partial":7,"_locale":8,"title":837,"description":838,"id":839,"date":840,"listed":13,"nocomments":7,"hidden":13,"categories":841,"tags":842,"cover":847,"readingTime":848,"body":852,"_type":830,"_id":1853,"_source":832,"_file":1854,"_stem":1855,"_extension":835},"À qui appartient le bug en prod : toi ou Claude ?","Le débat éthique que personne n'ose avoir en solo. Une position claire, sourcée juridiquement, applicable dès lundi matin dans ton projet.",67,"2026-05-19",[6],[843,844,845,846,19],"ethique-ia","responsabilite","gouvernance-ia","post-mortem","covers\u002Farticles\u002Fa-qui-appartient-le-bug-ia.jpg",{"text":23,"minutes":849,"time":850,"words":851},12.75,765000,2550,{"type":28,"children":853,"toc":1842},[854,862,867,870,876,888,900,913,932,935,941,958,963,994,1018,1038,1041,1047,1057,1062,1067,1087,1106,1109,1118,1121,1127,1132,1142,1160,1170,1180,1185,1188,1194,1205,1501,1514,1517,1523,1528,1538,1548,1560,1563,1569,1574,1691,1696,1699,1703,1708,1713,1718,1729,1738,1741,1747,1760,1773,1786,1799,1812,1825,1828,1836],{"type":31,"tag":32,"props":855,"children":856},{},[857],{"type":31,"tag":36,"props":858,"children":859},{},[860],{"type":40,"value":861},"Début 2026. Je mergе une PR que Claude m'avait écrite pour le module slot-hold de crmcoaching. Je l'ai parcourue vite, la logique semblait cohérente, les types passaient. Deux jours plus tard, un bug de concurrence sur la réservation de créneaux : des doublons pouvaient se créer silencieusement sous charge. En cherchant la cause racine, je me suis posé une question que je n'avais pas vraiment formulée jusque-là : est-ce que la responsabilité appartient à Claude, ou à moi qui ai mergé sans vraiment lire ?",{"type":31,"tag":32,"props":863,"children":864},{},[865],{"type":40,"value":866},"La réponse, je la connaissais au fond. Mais la formuler clairement a changé quelque chose dans ma façon de travailler. Voici ma position, ce que dit le droit en 2024-2025, et le contrat moral que j'ai formalisé en ADR dans crmcoaching.",{"type":31,"tag":48,"props":868,"children":869},{},[],{"type":31,"tag":52,"props":871,"children":873},{"id":872},"cest-claude-qui-la-écrit-pourquoi-cette-excuse-ne-tient-pas",[874],{"type":40,"value":875},"\"C'est Claude qui l'a écrit\" : pourquoi cette excuse ne tient pas",{"type":31,"tag":32,"props":877,"children":878},{},[879,881,886],{"type":40,"value":880},"D'abord, une observation issue de mes accompagnements de développeurs solos et d'équipes en 2025 : à la question ",{"type":31,"tag":97,"props":882,"children":883},{},[884],{"type":40,"value":885},"\"Quand un bug en prod vient de code que tu as généré avec Claude, à qui en revient la responsabilité ?\"",{"type":40,"value":887},", une part significative attribue au moins une partie de la responsabilité à l'outil lui-même.",{"type":31,"tag":32,"props":889,"children":890},{},[891,893,898],{"type":40,"value":892},"Cette répartition révèle un glissement éthique réel. Le code généré par Claude ressemble à du code humain, donc on le traite comme s'il avait une intention, une responsabilité, un statut. Mais ce n'est pas du tout ce que dit le droit, ni ce qu'exige le craft, ni ce que documentent ",{"type":31,"tag":69,"props":894,"children":895},{"href":113},[896],{"type":40,"value":897},"les principes Clean Code et software craftsmanship",{"type":40,"value":899}," sur la responsabilité du développeur vis-à-vis de son code.",{"type":31,"tag":78,"props":901,"children":902},{},[903],{"type":31,"tag":32,"props":904,"children":905},{},[906,911],{"type":31,"tag":36,"props":907,"children":908},{},[909],{"type":40,"value":910},"Ce que j'ai vécu",{"type":40,"value":912}," : au moment où j'ai mergé la PR du module slot-hold, je n'avais pas pris le temps de dérouler mentalement les scénarios de concurrence. Claude avait produit du code structurellement correct pour le cas nominal. Mais je n'avais pas posé les bonnes questions, et je ne m'étais pas approprié le code au moment du merge. C'est ce passage manquant que j'identifie comme le vrai problème de fond.",{"type":31,"tag":32,"props":914,"children":915},{},[916,918,923,925,930],{"type":40,"value":917},"Robert C. Martin avait proposé en 2015 un ",{"type":31,"tag":97,"props":919,"children":920},{},[921],{"type":40,"value":922},"\"Programmer's Oath\"",{"type":40,"value":924}," qui inclut cette phrase : ",{"type":31,"tag":97,"props":926,"children":927},{},[928],{"type":40,"value":929},"\"I will produce, with every release, a quick, sure, and repeatable proof that every element of the code works as it should.\"",{"type":40,"value":931}," Le serment n'a pas de clause \"sauf si l'IA l'a écrit\". Le craft n'a pas d'exception sur l'origine du code. Si tu merges, tu attestes. Si tu attestes, tu portes.",{"type":31,"tag":48,"props":933,"children":934},{},[],{"type":31,"tag":52,"props":936,"children":938},{"id":937},"le-droit-2023-2024-lia-na-pas-la-personnalité-juridique",[939],{"type":40,"value":940},"Le droit 2023-2024 : l'IA n'a pas la personnalité juridique",{"type":31,"tag":32,"props":942,"children":943},{},[944,946,951,953],{"type":40,"value":945},"Le cas le plus structurant est l'affaire ",{"type":31,"tag":97,"props":947,"children":948},{},[949],{"type":40,"value":950},"Thaler v. Perlmutter",{"type":40,"value":952},", jugée par la D.D.C. en août 2023, confirmée en appel en mars 2025. Stephen Thaler avait tenté d'enregistrer un droit d'auteur sur une œuvre générée par son IA \"Creativity Machine\". L'US Copyright Office a refusé. Thaler a porté l'affaire en justice. La décision est claire : ",{"type":31,"tag":97,"props":954,"children":955},{},[956],{"type":40,"value":957},"\"Human authorship is a bedrock requirement of copyright. An AI cannot be the author.\"",{"type":31,"tag":32,"props":959,"children":960},{},[961],{"type":40,"value":962},"Cette décision a deux conséquences directes pour tes PR Claude :",{"type":31,"tag":32,"props":964,"children":965},{},[966,971,973,978,980,985,987,992],{"type":31,"tag":36,"props":967,"children":968},{},[969],{"type":40,"value":970},"Conséquence 1 : le code généré par Claude n'a pas d'auteur autonome.",{"type":40,"value":972}," Il a un ",{"type":31,"tag":97,"props":974,"children":975},{},[976],{"type":40,"value":977},"prompter",{"type":40,"value":979}," (la personne qui a écrit l'instruction), un ",{"type":31,"tag":97,"props":981,"children":982},{},[983],{"type":40,"value":984},"éditeur",{"type":40,"value":986}," (la personne qui a accepté ou modifié la suggestion), et un ",{"type":31,"tag":97,"props":988,"children":989},{},[990],{"type":40,"value":991},"intégrateur",{"type":40,"value":993}," (la personne qui a mergé). L'auteur juridique du code dans ton repo, c'est celui qui l'a fait entrer. Pas l'outil qui l'a suggéré.",{"type":31,"tag":32,"props":995,"children":996},{},[997,1002,1004,1009,1011,1016],{"type":31,"tag":36,"props":998,"children":999},{},[1000],{"type":40,"value":1001},"Conséquence 2 : la responsabilité civile suit la même logique.",{"type":40,"value":1003}," Le US Copyright Office, dans son guidance ",{"type":31,"tag":97,"props":1005,"children":1006},{},[1007],{"type":40,"value":1008},"\"Copyright and Artificial Intelligence\"",{"type":40,"value":1010}," publié en deux parties (2024 et début 2025), précise que la contribution humaine doit être ",{"type":31,"tag":97,"props":1012,"children":1013},{},[1014],{"type":40,"value":1015},"\"significative et identifiable\"",{"type":40,"value":1017}," pour qu'il y ait propriété intellectuelle. Symétriquement, c'est cette même contribution humaine qui porte la responsabilité civile en cas de dommage causé par le code. Pas le fournisseur de l'IA, sauf cas extrême de défaut produit.",{"type":31,"tag":32,"props":1019,"children":1020},{},[1021,1023,1029,1031,1036],{"type":40,"value":1022},"Le droit français suit une logique convergente. Le Conseil d'État, dans son étude de 2024 sur l'IA générative, rappelle que la responsabilité incombe à l'opérateur qui met le code en production, pas à l'éditeur de l'IA. Anthropic ne sera pas appelé à l'audience quand ta fonction ",{"type":31,"tag":169,"props":1024,"children":1026},{"className":1025},[],[1027],{"type":40,"value":1028},"transferFunds",{"type":40,"value":1030}," aura double-débité des clients (le scénario exact décrit dans ",{"type":31,"tag":69,"props":1032,"children":1033},{"href":135},[1034],{"type":40,"value":1035},"le bug Claude du vendredi",{"type":40,"value":1037},"). Toi, oui.",{"type":31,"tag":48,"props":1039,"children":1040},{},[],{"type":31,"tag":52,"props":1042,"children":1044},{"id":1043},"la-position-craft-si-tu-merges-tu-deviens-lauteur",[1045],{"type":40,"value":1046},"La position craft : si tu merges, tu deviens l'auteur",{"type":31,"tag":32,"props":1048,"children":1049},{},[1050,1052],{"type":40,"value":1051},"Voici la position que j'affiche dans le premier ADR de gouvernance IA que j'ai écrit pour crmcoaching : ",{"type":31,"tag":97,"props":1053,"children":1054},{},[1055],{"type":40,"value":1056},"\"Tout code mergé dans le repo principal est considéré comme rédigé par le développeur qui a mergé la PR. Aucune décharge d'origine IA n'est recevable en post-mortem.\"",{"type":31,"tag":32,"props":1058,"children":1059},{},[1060],{"type":40,"value":1061},"Cette phrase, à elle seule, change l'éthique de travail. Voici pourquoi.",{"type":31,"tag":32,"props":1063,"children":1064},{},[1065],{"type":40,"value":1066},"Sans cette règle, l'incitation est de cliquer \"Accept\" sur la suggestion Claude, de regarder vite fait, de merger. Si ça plante, ce n'est \"pas vraiment toi\". Le travail mental d'appropriation est court-circuité par l'origine IA.",{"type":31,"tag":32,"props":1068,"children":1069},{},[1070,1072,1078,1080,1085],{"type":40,"value":1071},"Avec cette règle, l'incitation se renverse. Si je merge, je suis l'auteur. Donc je lis. Donc je comprends. Donc je teste. Si je n'ai pas le temps de comprendre, je ne merge pas, ou je demande à Claude de simplifier jusqu'à ce que je comprenne. Cette discipline est exactement ce que ",{"type":31,"tag":69,"props":1073,"children":1075},{"href":1074},"\u002Ffr\u002Fintelligence-artificielle\u002Fia-developpeurs-peur-automatisation",[1076],{"type":40,"value":1077},"la peur de l'automatisation pour les devs",{"type":40,"value":1079}," doit transformer en réflexe sain, pas en panique. C'est aussi ce qui se joue dans une ",{"type":31,"tag":69,"props":1081,"children":1082},{"href":148},[1083],{"type":40,"value":1084},"revue de code structurée et appliquée",{"type":40,"value":1086}," : on n'approve pas ce qu'on n'a pas compris.",{"type":31,"tag":32,"props":1088,"children":1089},{},[1090,1092,1097,1099,1104],{"type":40,"value":1091},"C'est aussi ce que Bruce Schneier appelait ",{"type":31,"tag":97,"props":1093,"children":1094},{},[1095],{"type":40,"value":1096},"\"taking ownership\"",{"type":40,"value":1098}," dans son ouvrage ",{"type":31,"tag":97,"props":1100,"children":1101},{},[1102],{"type":40,"value":1103},"Beyond Fear",{"type":40,"value":1105}," (2003). La sécurité, la fiabilité, la qualité ne sont pas des propriétés du code. Ce sont des engagements de personnes. Le code lui-même est neutre. Sa qualité dépend du dispositif humain qui l'entoure.",{"type":31,"tag":48,"props":1107,"children":1108},{},[],{"type":31,"tag":229,"props":1110,"children":1112},{"cta":231,"href":232,"title":1111,"type":234},"Tu veux apprendre à t'approprier le code que Claude écrit pour toi ?",[1113],{"type":31,"tag":32,"props":1114,"children":1115},{},[1116],{"type":40,"value":1117},"Relire une PR Claude et dérouler les scénarios de concurrence avant de merger, ça ne se lit pas dans un article : ça se travaille. En mentoring 1:1, je relis ton code avec toi, je te montre les bonnes questions à poser avant chaque merge, et tu installes le réflexe d'assumer ton code au lieu de te défausser sur l'outil. Tu montes en niveau là où l'IA te laisse seul.",{"type":31,"tag":48,"props":1119,"children":1120},{},[],{"type":31,"tag":52,"props":1122,"children":1124},{"id":1123},"le-contrat-moral-du-dev-2026-4-engagements",[1125],{"type":40,"value":1126},"Le contrat moral du dev 2026 : 4 engagements",{"type":31,"tag":32,"props":1128,"children":1129},{},[1130],{"type":40,"value":1131},"Voici les 4 engagements que j'ai formalisés dans l'ADR de gouvernance IA de crmcoaching.",{"type":31,"tag":32,"props":1133,"children":1134},{},[1135,1140],{"type":31,"tag":36,"props":1136,"children":1137},{},[1138],{"type":40,"value":1139},"Engagement 1 : je lis avant de merger.",{"type":40,"value":1141}," Aucune PR n'est mergée sans que j'aie lu chaque ligne et puisse en expliquer le fonctionnement. Si je ne peux pas expliquer une partie du code à voix haute en 60 secondes, c'est que je ne l'ai pas vraiment lue. Je retourne en draft.",{"type":31,"tag":32,"props":1143,"children":1144},{},[1145,1150,1152,1158],{"type":31,"tag":36,"props":1146,"children":1147},{},[1148],{"type":40,"value":1149},"Engagement 2 : je teste avant de croire.",{"type":40,"value":1151}," Tests verts sur le happy path ne valent pas attestation. Je vérifie les modes d'échec critiques (concurrence, timeout, partial failure) comme détaillé dans cet article sur les tests Claude. Si je n'ai pas testé, je n'ai pas validé. La ",{"type":31,"tag":69,"props":1153,"children":1155},{"href":1154},"\u002Ffr\u002Fdette-technique\u002Fdefinition-of-done-qualite",[1156],{"type":40,"value":1157},"Definition of Done sur la qualité",{"type":40,"value":1159}," doit explicitement intégrer cet engagement.",{"type":31,"tag":32,"props":1161,"children":1162},{},[1163,1168],{"type":31,"tag":36,"props":1164,"children":1165},{},[1166],{"type":40,"value":1167},"Engagement 3 : je trace les décisions architecturales.",{"type":40,"value":1169}," Si une PR introduit un nouveau pattern, une nouvelle dépendance, un trade-off, j'ouvre un ADR. Le code peut venir de Claude. La décision d'intégrer le code, et le trade-off associé, viennent de moi et restent tracés. Dans crmcoaching, plus de 50 ADRs documentent ces décisions.",{"type":31,"tag":32,"props":1171,"children":1172},{},[1173,1178],{"type":31,"tag":36,"props":1174,"children":1175},{},[1176],{"type":40,"value":1177},"Engagement 4 : j'assume en post-mortem.",{"type":40,"value":1179}," Si une PR que j'ai mergée cause un incident, je dis \"j'ai mergé ce code\" et pas \"Claude l'a écrit\". Cette discipline du langage n'est pas une simple posture, c'est le fondement d'un travail rigoureux.",{"type":31,"tag":32,"props":1181,"children":1182},{},[1183],{"type":40,"value":1184},"Ces 4 engagements tiennent en une page. Ils transforment l'éthique de travail en quelques semaines. Et ils protègent juridiquement et professionnellement, parce qu'ils décrivent un dispositif de diligence raisonnable qu'un tribunal et un comité d'éthique peuvent reconnaître.",{"type":31,"tag":48,"props":1186,"children":1187},{},[],{"type":31,"tag":52,"props":1189,"children":1191},{"id":1190},"comment-écrire-cet-engagement-en-adr-de-gouvernance",[1192],{"type":40,"value":1193},"Comment écrire cet engagement en ADR de gouvernance",{"type":31,"tag":32,"props":1195,"children":1196},{},[1197,1199,1204],{"type":40,"value":1198},"Voici le squelette de l'ADR que j'ai formalisé dans crmcoaching. Format Michael Nygard simplifié, en suivant le formalisme détaillé dans ",{"type":31,"tag":69,"props":1200,"children":1201},{"href":329},[1202],{"type":40,"value":1203},"la pratique des ADR pour les décisions structurantes",{"type":40,"value":76},{"type":31,"tag":1206,"props":1207,"children":1210},"pre",{"className":1208,"code":1209,"language":830,"meta":8,"style":8},"language-markdown shiki shiki-themes catppuccin-frappe github-dark","# ADR-001 : Gouvernance des contributions Claude dans le repo\n\n## Statut\nActé - 2026-XX-XX\n\n## Contexte\nJ'utilise Claude pour générer du code dans crmcoaching. La question\nde la responsabilité du code IA-généré n'avait pas été tranchée\nformellement. Elle apparaissait en post-mortem et créait une ambiguïté\nsur la chaîne de responsabilité.\n\n## Décision\n1. Tout code mergé est attribué à l'auteur de la PR. Pas de mention \"généré par Claude\".\n2. Chaque PR assistée par Claude doit s'accompagner d'une auto-déclaration :\n   \"j'ai lu, testé et compris ce code, je l'assume comme si je l'avais écrit.\"\n3. Les 4 engagements du contrat moral (lecture, test, ADR, post-mortem)\n   sont rappelés dans le PR template GitHub.\n4. En post-mortem, l'origine Claude d'un bout de code n'est pas une circonstance\n   atténuante. Elle peut être mentionnée pour information process, mais ne\n   modifie pas l'attribution de responsabilité.\n\n## Conséquences\n+ Renforcement de la discipline de review et d'appropriation\n+ Clarté juridique en cas d'incident\n+ Alignement avec la jurisprudence (Thaler v. Perlmutter, 2023)\n- Légère charge cognitive supplémentaire à chaque PR\n- Tentation initiale de se défausser sur Claude en cas d'incident\n",[1211],{"type":31,"tag":169,"props":1212,"children":1213},{"__ignoreMap":8},[1214,1226,1234,1244,1254,1262,1271,1280,1289,1298,1307,1315,1324,1339,1353,1362,1376,1385,1399,1408,1417,1425,1434,1448,1461,1474,1488],{"type":31,"tag":1215,"props":1216,"children":1219},"span",{"class":1217,"line":1218},"line",1,[1220],{"type":31,"tag":1215,"props":1221,"children":1223},{"style":1222},"--shiki-default:#E78284;--shiki-default-font-weight:inherit;--shiki-dark:#79B8FF;--shiki-dark-font-weight:bold",[1224],{"type":40,"value":1225},"# ADR-001 : Gouvernance des contributions Claude dans le repo\n",{"type":31,"tag":1215,"props":1227,"children":1228},{"class":1217,"line":817},[1229],{"type":31,"tag":1215,"props":1230,"children":1231},{"emptyLinePlaceholder":13},[1232],{"type":40,"value":1233},"\n",{"type":31,"tag":1215,"props":1235,"children":1237},{"class":1217,"line":1236},3,[1238],{"type":31,"tag":1215,"props":1239,"children":1241},{"style":1240},"--shiki-default:#EF9F76;--shiki-default-font-weight:inherit;--shiki-dark:#79B8FF;--shiki-dark-font-weight:bold",[1242],{"type":40,"value":1243},"## Statut\n",{"type":31,"tag":1215,"props":1245,"children":1247},{"class":1217,"line":1246},4,[1248],{"type":31,"tag":1215,"props":1249,"children":1251},{"style":1250},"--shiki-default:#C6D0F5;--shiki-dark:#E1E4E8",[1252],{"type":40,"value":1253},"Acté - 2026-XX-XX\n",{"type":31,"tag":1215,"props":1255,"children":1257},{"class":1217,"line":1256},5,[1258],{"type":31,"tag":1215,"props":1259,"children":1260},{"emptyLinePlaceholder":13},[1261],{"type":40,"value":1233},{"type":31,"tag":1215,"props":1263,"children":1265},{"class":1217,"line":1264},6,[1266],{"type":31,"tag":1215,"props":1267,"children":1268},{"style":1240},[1269],{"type":40,"value":1270},"## Contexte\n",{"type":31,"tag":1215,"props":1272,"children":1274},{"class":1217,"line":1273},7,[1275],{"type":31,"tag":1215,"props":1276,"children":1277},{"style":1250},[1278],{"type":40,"value":1279},"J'utilise Claude pour générer du code dans crmcoaching. La question\n",{"type":31,"tag":1215,"props":1281,"children":1283},{"class":1217,"line":1282},8,[1284],{"type":31,"tag":1215,"props":1285,"children":1286},{"style":1250},[1287],{"type":40,"value":1288},"de la responsabilité du code IA-généré n'avait pas été tranchée\n",{"type":31,"tag":1215,"props":1290,"children":1292},{"class":1217,"line":1291},9,[1293],{"type":31,"tag":1215,"props":1294,"children":1295},{"style":1250},[1296],{"type":40,"value":1297},"formellement. Elle apparaissait en post-mortem et créait une ambiguïté\n",{"type":31,"tag":1215,"props":1299,"children":1301},{"class":1217,"line":1300},10,[1302],{"type":31,"tag":1215,"props":1303,"children":1304},{"style":1250},[1305],{"type":40,"value":1306},"sur la chaîne de responsabilité.\n",{"type":31,"tag":1215,"props":1308,"children":1310},{"class":1217,"line":1309},11,[1311],{"type":31,"tag":1215,"props":1312,"children":1313},{"emptyLinePlaceholder":13},[1314],{"type":40,"value":1233},{"type":31,"tag":1215,"props":1316,"children":1318},{"class":1217,"line":1317},12,[1319],{"type":31,"tag":1215,"props":1320,"children":1321},{"style":1240},[1322],{"type":40,"value":1323},"## Décision\n",{"type":31,"tag":1215,"props":1325,"children":1327},{"class":1217,"line":1326},13,[1328,1334],{"type":31,"tag":1215,"props":1329,"children":1331},{"style":1330},"--shiki-default:#81C8BE;--shiki-dark:#FFAB70",[1332],{"type":40,"value":1333},"1.",{"type":31,"tag":1215,"props":1335,"children":1336},{"style":1250},[1337],{"type":40,"value":1338}," Tout code mergé est attribué à l'auteur de la PR. Pas de mention \"généré par Claude\".\n",{"type":31,"tag":1215,"props":1340,"children":1342},{"class":1217,"line":1341},14,[1343,1348],{"type":31,"tag":1215,"props":1344,"children":1345},{"style":1330},[1346],{"type":40,"value":1347},"2.",{"type":31,"tag":1215,"props":1349,"children":1350},{"style":1250},[1351],{"type":40,"value":1352}," Chaque PR assistée par Claude doit s'accompagner d'une auto-déclaration :\n",{"type":31,"tag":1215,"props":1354,"children":1356},{"class":1217,"line":1355},15,[1357],{"type":31,"tag":1215,"props":1358,"children":1359},{"style":1250},[1360],{"type":40,"value":1361},"   \"j'ai lu, testé et compris ce code, je l'assume comme si je l'avais écrit.\"\n",{"type":31,"tag":1215,"props":1363,"children":1365},{"class":1217,"line":1364},16,[1366,1371],{"type":31,"tag":1215,"props":1367,"children":1368},{"style":1330},[1369],{"type":40,"value":1370},"3.",{"type":31,"tag":1215,"props":1372,"children":1373},{"style":1250},[1374],{"type":40,"value":1375}," Les 4 engagements du contrat moral (lecture, test, ADR, post-mortem)\n",{"type":31,"tag":1215,"props":1377,"children":1379},{"class":1217,"line":1378},17,[1380],{"type":31,"tag":1215,"props":1381,"children":1382},{"style":1250},[1383],{"type":40,"value":1384},"   sont rappelés dans le PR template GitHub.\n",{"type":31,"tag":1215,"props":1386,"children":1388},{"class":1217,"line":1387},18,[1389,1394],{"type":31,"tag":1215,"props":1390,"children":1391},{"style":1330},[1392],{"type":40,"value":1393},"4.",{"type":31,"tag":1215,"props":1395,"children":1396},{"style":1250},[1397],{"type":40,"value":1398}," En post-mortem, l'origine Claude d'un bout de code n'est pas une circonstance\n",{"type":31,"tag":1215,"props":1400,"children":1402},{"class":1217,"line":1401},19,[1403],{"type":31,"tag":1215,"props":1404,"children":1405},{"style":1250},[1406],{"type":40,"value":1407},"   atténuante. Elle peut être mentionnée pour information process, mais ne\n",{"type":31,"tag":1215,"props":1409,"children":1411},{"class":1217,"line":1410},20,[1412],{"type":31,"tag":1215,"props":1413,"children":1414},{"style":1250},[1415],{"type":40,"value":1416},"   modifie pas l'attribution de responsabilité.\n",{"type":31,"tag":1215,"props":1418,"children":1420},{"class":1217,"line":1419},21,[1421],{"type":31,"tag":1215,"props":1422,"children":1423},{"emptyLinePlaceholder":13},[1424],{"type":40,"value":1233},{"type":31,"tag":1215,"props":1426,"children":1428},{"class":1217,"line":1427},22,[1429],{"type":31,"tag":1215,"props":1430,"children":1431},{"style":1240},[1432],{"type":40,"value":1433},"## Conséquences\n",{"type":31,"tag":1215,"props":1435,"children":1437},{"class":1217,"line":1436},23,[1438,1443],{"type":31,"tag":1215,"props":1439,"children":1440},{"style":1330},[1441],{"type":40,"value":1442},"+",{"type":31,"tag":1215,"props":1444,"children":1445},{"style":1250},[1446],{"type":40,"value":1447}," Renforcement de la discipline de review et d'appropriation\n",{"type":31,"tag":1215,"props":1449,"children":1451},{"class":1217,"line":1450},24,[1452,1456],{"type":31,"tag":1215,"props":1453,"children":1454},{"style":1330},[1455],{"type":40,"value":1442},{"type":31,"tag":1215,"props":1457,"children":1458},{"style":1250},[1459],{"type":40,"value":1460}," Clarté juridique en cas d'incident\n",{"type":31,"tag":1215,"props":1462,"children":1464},{"class":1217,"line":1463},25,[1465,1469],{"type":31,"tag":1215,"props":1466,"children":1467},{"style":1330},[1468],{"type":40,"value":1442},{"type":31,"tag":1215,"props":1470,"children":1471},{"style":1250},[1472],{"type":40,"value":1473}," Alignement avec la jurisprudence (Thaler v. Perlmutter, 2023)\n",{"type":31,"tag":1215,"props":1475,"children":1477},{"class":1217,"line":1476},26,[1478,1483],{"type":31,"tag":1215,"props":1479,"children":1480},{"style":1330},[1481],{"type":40,"value":1482},"-",{"type":31,"tag":1215,"props":1484,"children":1485},{"style":1250},[1486],{"type":40,"value":1487}," Légère charge cognitive supplémentaire à chaque PR\n",{"type":31,"tag":1215,"props":1489,"children":1491},{"class":1217,"line":1490},27,[1492,1496],{"type":31,"tag":1215,"props":1493,"children":1494},{"style":1330},[1495],{"type":40,"value":1482},{"type":31,"tag":1215,"props":1497,"children":1498},{"style":1250},[1499],{"type":40,"value":1500}," Tentation initiale de se défausser sur Claude en cas d'incident\n",{"type":31,"tag":32,"props":1502,"children":1503},{},[1504,1506,1512],{"type":40,"value":1505},"Cet ADR fait 30 lignes. Il prend 1 heure à drafter. Il se révise tous les 12 mois. Il est la pierre angulaire d'une gouvernance IA mature, et il ouvre la voie à des discussions plus fines sur d'autres sujets comme ",{"type":31,"tag":69,"props":1507,"children":1509},{"href":1508},"\u002Ffr\u002Fintelligence-artificielle\u002Fllm-securite-code-vulnerabilites",[1510],{"type":40,"value":1511},"les vulnérabilités de sécurité dans le code LLM-généré",{"type":40,"value":1513}," ou la data privacy avec les prompts envoyés à Claude.",{"type":31,"tag":48,"props":1515,"children":1516},{},[],{"type":31,"tag":52,"props":1518,"children":1520},{"id":1519},"la-post-mortem-où-japprends-à-dire-je-pas-claude",[1521],{"type":40,"value":1522},"La post-mortem où j'apprends à dire \"je\", pas \"Claude\"",{"type":31,"tag":32,"props":1524,"children":1525},{},[1526],{"type":40,"value":1527},"Voici un exercice concret que j'ai traversé personnellement avec le bug du module slot-hold, et que je documente pour tout dev solo ou tech lead qui se retrouve dans la même situation.",{"type":31,"tag":32,"props":1529,"children":1530},{},[1531,1536],{"type":31,"tag":36,"props":1532,"children":1533},{},[1534],{"type":40,"value":1535},"Étape 1, à chaud.",{"type":40,"value":1537}," Quand le bug est apparu, mon premier réflexe a été de penser \"Claude m'a donné du code incorrect\". J'ai reformulé mentalement : \"j'ai mergé du code qui ne gérait pas la concurrence\". Le changement de sujet dans la phrase a tout modifié. La question devenait : pourquoi je n'ai pas vu ça au moment de la review ?",{"type":31,"tag":32,"props":1539,"children":1540},{},[1541,1546],{"type":31,"tag":36,"props":1542,"children":1543},{},[1544],{"type":40,"value":1545},"Étape 2, à froid.",{"type":40,"value":1547}," Deux jours plus tard, j'ai repris la PR. Le pattern de concurrence était effectivement absent. Claude n'avait pas été prompté pour les cas de charge simultanée. C'est une lacune dans mon instruction, pas un défaut mystérieux de l'outil. Le vrai sujet : comment structurer mes reviews pour que les scénarios critiques soient couverts avant le merge ?",{"type":31,"tag":32,"props":1549,"children":1550},{},[1551,1553,1558],{"type":40,"value":1552},"Sur chaque post-mortem où j'applique cette discipline depuis, je sors avec \"j'ai appris\" plutôt que \"c'est la faute de Claude\". Le passage est mesurable : le bug suivant du même type n'arrive plus, parce que la cause racine est identifiée dans ma pratique, pas externalisée dans l'outil. C'est exactement ce que Sidney Dekker recommande dans ",{"type":31,"tag":97,"props":1554,"children":1555},{},[1556],{"type":40,"value":1557},"The Field Guide to Understanding Human Error",{"type":40,"value":1559}," (2014) : la responsabilité humaine n'est pas une assignation morale, c'est un levier pour améliorer le système.",{"type":31,"tag":48,"props":1561,"children":1562},{},[],{"type":31,"tag":52,"props":1564,"children":1566},{"id":1565},"ce-que-ça-change-concrètement",[1567],{"type":40,"value":1568},"Ce que ça change concrètement",{"type":31,"tag":32,"props":1570,"children":1571},{},[1572],{"type":40,"value":1573},"Depuis que j'ai installé l'ADR de gouvernance Claude dans crmcoaching, voici les changements observés en quelques mois de pratique.",{"type":31,"tag":398,"props":1575,"children":1576},{},[1577,1598],{"type":31,"tag":402,"props":1578,"children":1579},{},[1580],{"type":31,"tag":406,"props":1581,"children":1582},{},[1583,1588,1593],{"type":31,"tag":410,"props":1584,"children":1585},{},[1586],{"type":40,"value":1587},"Pratique",{"type":31,"tag":410,"props":1589,"children":1590},{},[1591],{"type":40,"value":1592},"Avant l'ADR",{"type":31,"tag":410,"props":1594,"children":1595},{},[1596],{"type":40,"value":1597},"Après l'ADR",{"type":31,"tag":426,"props":1599,"children":1600},{},[1601,1619,1637,1655,1673],{"type":31,"tag":406,"props":1602,"children":1603},{},[1604,1609,1614],{"type":31,"tag":433,"props":1605,"children":1606},{},[1607],{"type":40,"value":1608},"Auto-déclaration de lecture sur chaque PR",{"type":31,"tag":433,"props":1610,"children":1611},{},[1612],{"type":40,"value":1613},"0%",{"type":31,"tag":433,"props":1615,"children":1616},{},[1617],{"type":40,"value":1618},"systématique",{"type":31,"tag":406,"props":1620,"children":1621},{},[1622,1627,1632],{"type":31,"tag":433,"props":1623,"children":1624},{},[1625],{"type":40,"value":1626},"Mention \"Claude l'a écrit\" en post-mortem",{"type":31,"tag":433,"props":1628,"children":1629},{},[1630],{"type":40,"value":1631},"réflexe",{"type":31,"tag":433,"props":1633,"children":1634},{},[1635],{"type":40,"value":1636},"éliminé",{"type":31,"tag":406,"props":1638,"children":1639},{},[1640,1645,1650],{"type":31,"tag":433,"props":1641,"children":1642},{},[1643],{"type":40,"value":1644},"Bugs récurrents sur un même pattern",{"type":31,"tag":433,"props":1646,"children":1647},{},[1648],{"type":40,"value":1649},"fréquents",{"type":31,"tag":433,"props":1651,"children":1652},{},[1653],{"type":40,"value":1654},"rares",{"type":31,"tag":406,"props":1656,"children":1657},{},[1658,1663,1668],{"type":31,"tag":433,"props":1659,"children":1660},{},[1661],{"type":40,"value":1662},"Confiance sur la qualité du code généré",{"type":31,"tag":433,"props":1664,"children":1665},{},[1666],{"type":40,"value":1667},"fragile",{"type":31,"tag":433,"props":1669,"children":1670},{},[1671],{"type":40,"value":1672},"solide",{"type":31,"tag":406,"props":1674,"children":1675},{},[1676,1681,1686],{"type":31,"tag":433,"props":1677,"children":1678},{},[1679],{"type":40,"value":1680},"Temps de résolution des post-mortems",{"type":31,"tag":433,"props":1682,"children":1683},{},[1684],{"type":40,"value":1685},"long (recherche de \"coupable\")",{"type":31,"tag":433,"props":1687,"children":1688},{},[1689],{"type":40,"value":1690},"court (focus cause racine)",{"type":31,"tag":32,"props":1692,"children":1693},{},[1694],{"type":40,"value":1695},"Le gain le plus profond n'est pas dans les chiffres. Il est dans l'identité professionnelle. Un dev qui assume son code reste fier de ce qu'il livre. Un dev qui se défausse sur l'IA perd lentement le sens de son métier.",{"type":31,"tag":48,"props":1697,"children":1698},{},[],{"type":31,"tag":52,"props":1700,"children":1701},{"id":637},[1702],{"type":40,"value":640},{"type":31,"tag":32,"props":1704,"children":1705},{},[1706],{"type":40,"value":1707},"Ce que je veux que tu retiennes de cet article, c'est que la question \"à qui appartient le bug\" n'est pas un débat philosophique. C'est une décision opérationnelle qui structure ta façon de travailler. Si tu laisses l'ambiguïté s'installer, tu perds l'appropriation, tu perds la diligence, et au bout de quelques mois tu produis du code dont tu ne te sens plus vraiment l'auteur.",{"type":31,"tag":32,"props":1709,"children":1710},{},[1711],{"type":40,"value":1712},"Le droit est clair : l'IA n'a pas la personnalité juridique. Le craft est clair : celui qui merge atteste. Traduire cette double clarté en un ADR explicite, un PR template, un langage de post-mortem : c'est un travail d'une heure qui change une pratique durable. Sans ce cadrage, l'éthique dérive lentement vers la déresponsabilisation. Avec ce cadrage, tu transformes Claude en outil au service d'un développeur responsable, plutôt qu'en alibi.",{"type":31,"tag":32,"props":1714,"children":1715},{},[1716],{"type":40,"value":1717},"Si en lisant ces lignes tu reconnais ta situation, tu as deux choix. Tu peux laisser le prochain post-mortem se conclure par \"C'est Claude qui l'a écrit\". Ou tu peux commencer lundi matin, par un ADR d'une page et 4 engagements écrits noir sur blanc, et reprendre la souveraineté éthique de ton travail.",{"type":31,"tag":32,"props":1719,"children":1720},{},[1721,1723,1728],{"type":40,"value":1722},"Pour la suite des patterns de gouvernance Claude que je documente chaque semaine, retrouve-moi sur ",{"type":31,"tag":69,"props":1724,"children":1726},{"href":663,"rel":1725},[665],[1727],{"type":40,"value":668},{"type":40,"value":76},{"type":31,"tag":229,"props":1730,"children":1732},{"cta":673,"href":674,"title":1731,"type":676},"Assumer ton code IA-généré est une pratique parmi 100",[1733],{"type":31,"tag":32,"props":1734,"children":1735},{},[1736],{"type":40,"value":1737},"Lire avant de merger, tester les modes d'échec, tracer la décision en ADR : cet article te montre une discipline d'appropriation du code Claude. Le Craft Bundle réunit les 100 pratiques que j'applique pour coder propre, celles que l'IA ne t'apprendra jamais parce qu'elle ne les a jamais vues tenir en prod. La gouvernance du code généré n'est qu'une porte d'entrée vers les autres.",{"type":31,"tag":48,"props":1739,"children":1740},{},[],{"type":31,"tag":52,"props":1742,"children":1744},{"id":1743},"faq-sur-la-responsabilité-du-code-claude-généré",[1745],{"type":40,"value":1746},"FAQ sur la responsabilité du code Claude-généré",{"type":31,"tag":693,"props":1748,"children":1749},{},[1750,1755],{"type":31,"tag":697,"props":1751,"children":1752},{},[1753],{"type":40,"value":1754},"1. Cet ADR ne va-t-il pas créer une friction inutile quand on travaille vite avec Claude ?",{"type":31,"tag":32,"props":1756,"children":1757},{},[1758],{"type":40,"value":1759},"C'est l'objection que je me suis posée avant de l'écrire. En pratique, non. Les développeurs qui utilisent Claude correctement ne se sentent pas ralentis : ils relisent déjà, testent déjà, comprennent déjà. L'ADR formalise une discipline qu'ils ont intuitivement. Ceux qui se sentent freinés sont ceux qui mergeaient sans lire, et c'est exactement le comportement que l'ADR vise à corriger. À quelques semaines de recul, l'adhésion devient naturelle.",{"type":31,"tag":693,"props":1761,"children":1762},{},[1763,1768],{"type":31,"tag":697,"props":1764,"children":1765},{},[1766],{"type":40,"value":1767},"2. Que dit le RGPD sur les prompts envoyés à Claude ?",{"type":31,"tag":32,"props":1769,"children":1770},{},[1771],{"type":40,"value":1772},"Le RGPD considère que toute donnée personnelle envoyée à un sous-traitant (Anthropic via Claude) doit faire l'objet d'un encadrement contractuel (DPA) et d'une analyse d'impact si les données sont sensibles. Concrètement, ne prompte jamais Claude avec des données client réelles non anonymisées. C'est une autre dimension de la gouvernance IA, complémentaire à la responsabilité, et qui mérite un ADR séparé.",{"type":31,"tag":693,"props":1774,"children":1775},{},[1776,1781],{"type":31,"tag":697,"props":1777,"children":1778},{},[1779],{"type":40,"value":1780},"3. Et si je ne comprends pas le code que Claude me propose ?",{"type":31,"tag":32,"props":1782,"children":1783},{},[1784],{"type":40,"value":1785},"Tu as deux leviers légitimes. Premier levier : demander à Claude de simplifier ou d'expliquer jusqu'à ce que tu comprennes. Claude peut décomposer, renommer, ajouter des commentaires. Deuxième levier : refuser de merger et prendre le temps d'apprendre le pattern avant. La pire option est de merger en croyant. Cette discipline élève ton niveau général, parce qu'elle t'interdit d'accepter ce que tu ne comprends pas.",{"type":31,"tag":693,"props":1787,"children":1788},{},[1789,1794],{"type":31,"tag":697,"props":1790,"children":1791},{},[1792],{"type":40,"value":1793},"4. Existe-t-il un cas de jurisprudence française sur la responsabilité du code IA ?",{"type":31,"tag":32,"props":1795,"children":1796},{},[1797],{"type":40,"value":1798},"Pas encore au moment où j'écris (mai 2026), à ma connaissance. Mais le Conseil d'État dans son étude IA 2024, et la CNIL dans ses recommandations, convergent vers la même position que la jurisprudence américaine : la responsabilité incombe à l'opérateur qui met le code en production. Le premier cas marquant en France arrivera probablement dans les 12-24 mois suivants, et il est probable qu'il confirme cette ligne.",{"type":31,"tag":693,"props":1800,"children":1801},{},[1802,1807],{"type":31,"tag":697,"props":1803,"children":1804},{},[1805],{"type":40,"value":1806},"5. Comment s'assurer que l'ADR reste vivant et pas juste un fichier oublié ?",{"type":31,"tag":32,"props":1808,"children":1809},{},[1810],{"type":40,"value":1811},"Trois points d'ancrage concrets. D'abord, l'intégrer dans le PR template GitHub : chaque PR qui touche à du code Claude-assisté affiche l'auto-déclaration. Ensuite, le référencer dans le CLAUDE.md du repo (si tu utilises Claude Code) pour qu'il soit chargé automatiquement. Enfin, le réviser tous les 12 mois : Claude évolue, les pratiques évoluent, l'ADR doit refléter la pratique réelle, pas la pratique imaginée au moment de la rédaction.",{"type":31,"tag":693,"props":1813,"children":1814},{},[1815,1820],{"type":31,"tag":697,"props":1816,"children":1817},{},[1818],{"type":40,"value":1819},"6. Et si Claude propose explicitement du code que je ne comprends pas ?",{"type":31,"tag":32,"props":1821,"children":1822},{},[1823],{"type":40,"value":1824},"C'est exactement la situation où l'engagement 1 (lire avant de merger) devient critique. Soit tu prends le temps de comprendre (apprentissage profond), soit tu demandes à Claude de simplifier en restant correct, soit tu refuses la PR et délègues à un spécialiste. La pire option est de merger en croyant. Cette discipline élève le niveau général, parce qu'elle force à ne pas accepter ce qu'on ne comprend pas.",{"type":31,"tag":48,"props":1826,"children":1827},{},[],{"type":31,"tag":229,"props":1829,"children":1830},{"cta":806,"href":807,"title":808,"type":809},[1831],{"type":31,"tag":32,"props":1832,"children":1833},{},[1834],{"type":40,"value":1835},"L'EMA est l'outil que je propose au début de chaque mission. Il mesure la maturité de ton équipe sur plusieurs axes engineering, dont la gouvernance IA, l'éthique professionnelle et la culture de post-mortem. Quelques minutes pour identifier où tu glisses vers la déresponsabilisation IA, et où concentrer tes efforts en priorité.",{"type":31,"tag":1837,"props":1838,"children":1839},"style",{},[1840],{"type":40,"value":1841},"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);}",{"title":8,"searchDepth":817,"depth":817,"links":1843},[1844,1845,1846,1847,1848,1849,1850,1851,1852],{"id":872,"depth":817,"text":875},{"id":937,"depth":817,"text":940},{"id":1043,"depth":817,"text":1046},{"id":1123,"depth":817,"text":1126},{"id":1190,"depth":817,"text":1193},{"id":1519,"depth":817,"text":1522},{"id":1565,"depth":817,"text":1568},{"id":637,"depth":817,"text":640},{"id":1743,"depth":817,"text":1746},"content:fr:intelligence-artificielle:a-qui-appartient-le-bug-ia.md","fr\u002Fintelligence-artificielle\u002Fa-qui-appartient-le-bug-ia.md","fr\u002Fintelligence-artificielle\u002Fa-qui-appartient-le-bug-ia",{"_path":289,"_dir":6,"_draft":7,"_partial":7,"_locale":8,"title":1857,"description":1858,"id":1859,"date":1860,"listed":13,"nocomments":7,"hidden":7,"categories":1861,"tags":1862,"cover":1866,"readingTime":1867,"body":1871,"_type":830,"_id":2760,"_source":832,"_file":2761,"_stem":2762,"_extension":835},"10x plus de PR ≠ 10x plus de valeur livrée : les 4 métriques qui mentent","DORA 2024 le prouve : vélocité IA +35%, mais lead time fix +27%. Les 4 métriques à abandonner et les 4 + 1 qui disent la vérité sur crmcoaching.",66,"2026-05-16",[6],[1863,1864,16,1865,19],"dora-metrics","productivite","management","covers\u002Farticles\u002Fillusion-productivite-10x-pr.jpg",{"text":23,"minutes":1868,"time":1869,"words":1870},12.365,741900,2473,{"type":28,"children":1872,"toc":2749},[1873,1881,1886,1889,1895,1909,1921,1940,1953,1979,1982,1988,1993,2016,2026,2043,2053,2058,2061,2067,2072,2082,2092,2102,2118,2123,2126,2135,2138,2144,2149,2173,2178,2190,2193,2199,2204,2339,2344,2355,2358,2364,2369,2377,2407,2415,2438,2446,2464,2477,2480,2484,2489,2603,2608,2611,2615,2620,2625,2630,2641,2650,2653,2659,2672,2685,2698,2711,2724,2737,2740],{"type":31,"tag":32,"props":1874,"children":1875},{},[1876],{"type":31,"tag":36,"props":1877,"children":1878},{},[1879],{"type":40,"value":1880},"Pendant les premiers mois de développement de crmcoaching avec Claude, j'étais convaincu d'être 35% plus productif. Je livrais plus de PR, plus de commits, plus de features. Puis j'ai regardé mes vrais chiffres : le change failure rate avait doublé, et le temps moyen pour corriger un incident en prod avait triplé. Ce n'était pas une contradiction. Les deux chiffres étaient vrais. Le problème : je mesurais ce qui m'arrangeait, pas ce qui comptait.",{"type":31,"tag":32,"props":1882,"children":1883},{},[1884],{"type":40,"value":1885},"Le DORA Accelerate State of DevOps Report 2024 a confirmé ce que j'ai vécu sur crmcoaching depuis 6 mois : 4 métriques mentent dès qu'une IA générative entre dans la boucle, 4 autres disent enfin la vérité, et j'en ajoute systématiquement une cinquième à mes propres dashboards.",{"type":31,"tag":48,"props":1887,"children":1888},{},[],{"type":31,"tag":52,"props":1890,"children":1892},{"id":1891},"le-mythe-ia-vélocité-35-mais-lead-time-fix-27",[1893],{"type":40,"value":1894},"Le mythe IA : vélocité +35%, mais lead time fix +27%",{"type":31,"tag":32,"props":1896,"children":1897},{},[1898,1900,1907],{"type":40,"value":1899},"Le ",{"type":31,"tag":69,"props":1901,"children":1904},{"href":1902,"rel":1903},"https:\u002F\u002Fdora.dev\u002Fresearch\u002F2024\u002F",[665],[1905],{"type":40,"value":1906},"DORA Accelerate State of DevOps Report 2024",{"type":40,"value":1908}," est la première étude longitudinale à mesurer l'impact réel des assistants IA sur les équipes engineering. Les chiffres sont publiés, ils sont contre-intuitifs, et ils méritent qu'on s'y arrête.",{"type":31,"tag":32,"props":1910,"children":1911},{},[1912,1914,1919],{"type":40,"value":1913},"D'un côté : les équipes qui utilisent Claude voient leur ",{"type":31,"tag":97,"props":1915,"children":1916},{},[1917],{"type":40,"value":1918},"throughput",{"type":40,"value":1920}," (nombre de changes livrées) augmenter de 35% en moyenne. C'est la métrique que tout le monde brandit, celle qui rassure le COMEX, parce qu'elle est mesurable, visible, et gratifiante à court terme.",{"type":31,"tag":32,"props":1922,"children":1923},{},[1924,1926,1931,1933,1938],{"type":40,"value":1925},"De l'autre côté : ces mêmes équipes voient leur ",{"type":31,"tag":97,"props":1927,"children":1928},{},[1929],{"type":40,"value":1930},"change failure rate",{"type":40,"value":1932}," grimper (passages en prod qui causent un incident) et leur ",{"type":31,"tag":97,"props":1934,"children":1935},{},[1936],{"type":40,"value":1937},"lead time for changes",{"type":40,"value":1939}," (temps entre commit et prod) augmenter de 27% sur les fixes. Traduction : on livre plus, on casse plus, on met plus de temps à réparer. Exactement le déséquilibre que je documente dans mon retour d'expérience sur la code review IA.",{"type":31,"tag":78,"props":1941,"children":1942},{},[1943],{"type":31,"tag":32,"props":1944,"children":1945},{},[1946,1951],{"type":31,"tag":36,"props":1947,"children":1948},{},[1949],{"type":40,"value":1950},"Ce que j'ai observé sur crmcoaching",{"type":40,"value":1952}," : j'ai mesuré sur 6 mois les 4 métriques DORA en différenciant mes commits manuels et mes commits Claude-assistés. Ma vélocité sur les commits entièrement manuels : +5%. Ma vélocité Claude-assistée : +42%. Mais mon change failure rate est passé de 8% à 19%. Et mon MTTR (mean time to restore) est passé de 45 minutes à 2h10. Le gain en surface est réel. La dégradation profonde est plus réelle encore.",{"type":31,"tag":32,"props":1954,"children":1955},{},[1956,1958,1963,1965,1970,1972,1978],{"type":40,"value":1957},"Forsgren, Humble et Kim avaient écrit dans ",{"type":31,"tag":97,"props":1959,"children":1960},{},[1961],{"type":40,"value":1962},"Accelerate",{"type":40,"value":1964}," (2018) que ",{"type":31,"tag":97,"props":1966,"children":1967},{},[1968],{"type":40,"value":1969},"\"vous ne pouvez pas optimiser ce que vous ne mesurez pas, et vous mesurez ce que vous optimisez\"",{"type":40,"value":1971},". Si vous mesurez le throughput seul, vous optimisez le throughput au détriment de la stabilité. C'est exactement ce qui se passe dans 80% des projets IA-heavy non pilotés, et c'est ce qu'évalue concrètement ",{"type":31,"tag":69,"props":1973,"children":1975},{"href":1974},"\u002Ffr\u002Fintelligence-artificielle\u002Fevaluer-outil-ia-equipe-adoption",[1976],{"type":40,"value":1977},"l'évaluation d'un outil IA en équipe avant adoption",{"type":40,"value":76},{"type":31,"tag":48,"props":1980,"children":1981},{},[],{"type":31,"tag":52,"props":1983,"children":1985},{"id":1984},"les-4-métriques-qui-mentent-en-2026",[1986],{"type":40,"value":1987},"Les 4 métriques qui mentent en 2026",{"type":31,"tag":32,"props":1989,"children":1990},{},[1991],{"type":40,"value":1992},"Voici les 4 métriques que j'ai vantées pendant les premières semaines de crmcoaching, et que j'ai dû remettre à leur place : utiles à titre informatif, mais dangereuses si elles deviennent l'objectif central.",{"type":31,"tag":32,"props":1994,"children":1995},{},[1996,2001,2003,2008,2010,2014],{"type":31,"tag":36,"props":1997,"children":1998},{},[1999],{"type":40,"value":2000},"Métrique pourrie #1 : les lignes de code livrées.",{"type":40,"value":2002}," Mesure brute du volume. Avec Claude, je livre facilement 3 à 8 fois plus de lignes pour la même feature. Optimiser cette métrique pousse à la sur-ingénierie systématique, comme ",{"type":31,"tag":69,"props":2004,"children":2005},{"href":262},[2006],{"type":40,"value":2007},"le démontre l'analyse du coût caché des faux amis Claude",{"type":40,"value":2009},". Plus de lignes signifie plus de dette à maintenir, et c'est aussi ce que rappellent ",{"type":31,"tag":69,"props":2011,"children":2012},{"href":113},[2013],{"type":40,"value":897},{"type":40,"value":2015}," : la qualité n'est pas dans le volume.",{"type":31,"tag":32,"props":2017,"children":2018},{},[2019,2024],{"type":31,"tag":36,"props":2020,"children":2021},{},[2022],{"type":40,"value":2023},"Métrique pourrie #2 : le nombre de PR mergées.",{"type":40,"value":2025}," Encore plus piégeux. Une PR peut couvrir 4 lignes ou 800 lignes. Une PR peut être un fix critique ou un renommage cosmétique. Compter les PR sans qualifier leur impact, c'est compter des cartons sans regarder ce qu'il y a dedans.",{"type":31,"tag":32,"props":2027,"children":2028},{},[2029,2034,2036,2042],{"type":31,"tag":36,"props":2030,"children":2031},{},[2032],{"type":40,"value":2033},"Métrique pourrie #3 : les story points livrés par sprint.",{"type":40,"value":2035}," L'estimation initiale est faite par le développeur, qui est incité à surestimer pour rentrer dans le sprint, ou à sous-estimer pour livrer plus vite. La métrique est circulaire : on mesure ce qu'on a estimé, et on optimise ce qu'on mesure. Aucune information réelle sur la valeur livrée, et c'est précisément ce que pointent les ",{"type":31,"tag":69,"props":2037,"children":2039},{"href":2038},"\u002Ffr\u002Fpratiques-agiles\u002Fstory-points-estimation-agile-alternative",[2040],{"type":40,"value":2041},"alternatives aux story points en estimation agile",{"type":40,"value":76},{"type":31,"tag":32,"props":2044,"children":2045},{},[2046,2051],{"type":31,"tag":36,"props":2047,"children":2048},{},[2049],{"type":40,"value":2050},"Métrique pourrie #4 : les tickets fermés.",{"type":40,"value":2052}," Variante du précédent. Un ticket fermé peut être une feature livrée, une investigation classée \"ne peut pas reproduire\", un doublon. Pire : avec Claude, j'ouvre 3 fois plus de petits tickets parce que c'est plus facile à drafter, et je les ferme 3 fois plus vite. Le compteur explose. La valeur métier livrée, pas tellement.",{"type":31,"tag":32,"props":2054,"children":2055},{},[2056],{"type":40,"value":2057},"Le point commun de ces 4 métriques : elles mesurent l'activité, pas le résultat. Le piège que Drucker et Deming dénonçaient dans les années 80, rejoué aujourd'hui à l'échelle de l'IA.",{"type":31,"tag":48,"props":2059,"children":2060},{},[],{"type":31,"tag":52,"props":2062,"children":2064},{"id":2063},"les-4-métriques-dora-qui-disent-la-vérité",[2065],{"type":40,"value":2066},"Les 4 métriques DORA qui disent la vérité",{"type":31,"tag":32,"props":2068,"children":2069},{},[2070],{"type":40,"value":2071},"À l'inverse, voici les 4 métriques DORA que je suis aujourd'hui sur crmcoaching, et qui donnent une lecture honnête de ce que je livre.",{"type":31,"tag":32,"props":2073,"children":2074},{},[2075,2080],{"type":31,"tag":36,"props":2076,"children":2077},{},[2078],{"type":40,"value":2079},"Métrique #1 : le lead time for changes.",{"type":40,"value":2081}," Temps entre le premier commit sur une feature et son passage en prod. C'est la métrique de flux. Avec Claude, je produis vite, mais je peux bloquer en revue, en QA, en attente de migration. Le lead time révèle où le flux casse réellement. Cible : moins de 1 jour pour les équipes Elite (DORA 2024), 1 semaine pour les High Performers.",{"type":31,"tag":32,"props":2083,"children":2084},{},[2085,2090],{"type":31,"tag":36,"props":2086,"children":2087},{},[2088],{"type":40,"value":2089},"Métrique #2 : la deployment frequency.",{"type":40,"value":2091}," Fréquence à laquelle je mets en prod. Une codebase qui déploie une fois par semaine accumule du risque de batch. Une codebase qui déploie quotidiennement livre par petits incréments. Claude accélère la deployment frequency uniquement si la CI\u002FCD suit. Sinon, il produit plus vite et la prod stagne. C'est exactement le sujet traité dans les fondamentaux du Continuous Integration.",{"type":31,"tag":32,"props":2093,"children":2094},{},[2095,2100],{"type":31,"tag":36,"props":2096,"children":2097},{},[2098],{"type":40,"value":2099},"Métrique #3 : le change failure rate.",{"type":40,"value":2101}," Pourcentage de déploiements qui causent un incident ou nécessitent un rollback. C'est la métrique de stabilité. C'est là que Claude fait le plus de dégâts si je ne l'encadre pas. Cible : moins de 5% pour les Elite, moins de 15% pour les High Performers. Cette métrique se travaille en amont avec une Definition of Done qui couvre les modes d'échec.",{"type":31,"tag":32,"props":2103,"children":2104},{},[2105,2110,2112,2117],{"type":31,"tag":36,"props":2106,"children":2107},{},[2108],{"type":40,"value":2109},"Métrique #4 : le MTTR (mean time to restore).",{"type":40,"value":2111}," Temps moyen entre détection d'un incident et restauration. C'est la métrique de résilience. Le MTTR explose quand le code IA-généré n'est pas instrumenté pour l'observabilité (logs structurés, métriques, traces). C'est exactement le problème que ",{"type":31,"tag":69,"props":2113,"children":2114},{"href":211},[2115],{"type":40,"value":2116},"les 4 tests anti-fragiles permettent de résoudre",{"type":40,"value":76},{"type":31,"tag":32,"props":2119,"children":2120},{},[2121],{"type":40,"value":2122},"Ces 4 métriques forment un système. Truquer le throughput dégrade la stabilité. Optimiser la stabilité sans regarder le flux casse aussi. Le génie de DORA tient là : une boussole à 4 cardinaux, pas un thermomètre unique.",{"type":31,"tag":48,"props":2124,"children":2125},{},[],{"type":31,"tag":229,"props":2127,"children":2129},{"cta":231,"href":232,"title":2128,"type":234},"Vous voulez apprendre à lire vos vrais chiffres au lieu de ceux qui flattent ?",[2130],{"type":31,"tag":32,"props":2131,"children":2132},{},[2133],{"type":40,"value":2134},"Distinguer le throughput qui rassure de la stabilité qui compte, ça ne s'acquiert pas en lisant un rapport DORA : ça se travaille sur votre propre code. En mentoring 1:1, on installe ensemble vos métriques de flux et de stabilité, et je vous apprends à arbitrer chaque PR Claude-assistée comme un senior le ferait. Vous arrêtez de mesurer ce qui vous arrange pour piloter ce qui tient en prod.",{"type":31,"tag":48,"props":2136,"children":2137},{},[],{"type":31,"tag":52,"props":2139,"children":2141},{"id":2140},"la-5ème-métrique-perso-le-ratio-adr-commits",[2142],{"type":40,"value":2143},"La 5ème métrique perso : le ratio ADR \u002F commits",{"type":31,"tag":32,"props":2145,"children":2146},{},[2147],{"type":40,"value":2148},"Voici la métrique que j'ai ajoutée à mes propres dashboards crmcoaching depuis le lancement, et qui n'est pas dans le framework DORA.",{"type":31,"tag":32,"props":2150,"children":2151},{},[2152,2157,2159,2164,2166,2171],{"type":31,"tag":36,"props":2153,"children":2154},{},[2155],{"type":40,"value":2156},"Ratio ADR \u002F 100 commits.",{"type":40,"value":2158}," Combien de ",{"type":31,"tag":69,"props":2160,"children":2161},{"href":329},[2162],{"type":40,"value":2163},"décisions architecturales documentées (ADR)",{"type":40,"value":2165}," pour 100 commits dans mon repo. C'est la métrique de ",{"type":31,"tag":97,"props":2167,"children":2168},{},[2169],{"type":40,"value":2170},"traçabilité du jugement",{"type":40,"value":2172},". Quand Claude produit du code, ce qui doit rester à moi, c'est la décision : pourquoi cette stack, pourquoi ce pattern, pourquoi ce trade-off.",{"type":31,"tag":32,"props":2174,"children":2175},{},[2176],{"type":40,"value":2177},"Sans cette discipline, le ratio descend proche de zéro. Ce qui veut dire que l'immense majorité des décisions sont prises silencieusement, sans trace, sans contradiction documentée. Quand je reviens sur une partie du code 3 mois plus tard, je n'ai aucune chance de me souvenir pourquoi les choix actuels ont été faits, à moins qu'un ADR l'ait capturé.",{"type":31,"tag":32,"props":2179,"children":2180},{},[2181,2183,2188],{"type":40,"value":2182},"Sur crmcoaching, je suis à 6,2 ADR pour 100 commits. C'est l'indicateur que je n'ai pas perdu la pensée architecturale au profit de la production massive de code. Michael Nygard l'avait écrit en 2011 dans son article fondateur sur les ADR : ",{"type":31,"tag":97,"props":2184,"children":2185},{},[2186],{"type":40,"value":2187},"\"The cost of an undocumented decision is paid by everyone, for years.\"",{"type":40,"value":2189}," En solo avec une IA, ce \"everyone\" c'est moi, 6 mois plus tard.",{"type":31,"tag":48,"props":2191,"children":2192},{},[],{"type":31,"tag":52,"props":2194,"children":2196},{"id":2195},"comparaison-concrète-avant-et-après-pilotage-dora-sur-crmcoaching",[2197],{"type":40,"value":2198},"Comparaison concrète : avant et après pilotage DORA sur crmcoaching",{"type":31,"tag":32,"props":2200,"children":2201},{},[2202],{"type":40,"value":2203},"Voici les chiffres réels que j'ai mesurés sur crmcoaching sur deux périodes distinctes de 3 mois chacune.",{"type":31,"tag":398,"props":2205,"children":2206},{},[2207,2228],{"type":31,"tag":402,"props":2208,"children":2209},{},[2210],{"type":31,"tag":406,"props":2211,"children":2212},{},[2213,2218,2223],{"type":31,"tag":410,"props":2214,"children":2215},{},[2216],{"type":40,"value":2217},"Métrique",{"type":31,"tag":410,"props":2219,"children":2220},{},[2221],{"type":40,"value":2222},"Phase 1 : Claude sans pilotage",{"type":31,"tag":410,"props":2224,"children":2225},{},[2226],{"type":40,"value":2227},"Phase 2 : Claude + DORA + ADR",{"type":31,"tag":426,"props":2229,"children":2230},{},[2231,2249,2267,2285,2303,2321],{"type":31,"tag":406,"props":2232,"children":2233},{},[2234,2239,2244],{"type":31,"tag":433,"props":2235,"children":2236},{},[2237],{"type":40,"value":2238},"Throughput (commits\u002Fsemaine)",{"type":31,"tag":433,"props":2240,"children":2241},{},[2242],{"type":40,"value":2243},"89",{"type":31,"tag":433,"props":2245,"children":2246},{},[2247],{"type":40,"value":2248},"78 (-12%)",{"type":31,"tag":406,"props":2250,"children":2251},{},[2252,2257,2262],{"type":31,"tag":433,"props":2253,"children":2254},{},[2255],{"type":40,"value":2256},"Lead time for changes",{"type":31,"tag":433,"props":2258,"children":2259},{},[2260],{"type":40,"value":2261},"3,8 jours",{"type":31,"tag":433,"props":2263,"children":2264},{},[2265],{"type":40,"value":2266},"1,4 jour",{"type":31,"tag":406,"props":2268,"children":2269},{},[2270,2275,2280],{"type":31,"tag":433,"props":2271,"children":2272},{},[2273],{"type":40,"value":2274},"Change failure rate",{"type":31,"tag":433,"props":2276,"children":2277},{},[2278],{"type":40,"value":2279},"22%",{"type":31,"tag":433,"props":2281,"children":2282},{},[2283],{"type":40,"value":2284},"6%",{"type":31,"tag":406,"props":2286,"children":2287},{},[2288,2293,2298],{"type":31,"tag":433,"props":2289,"children":2290},{},[2291],{"type":40,"value":2292},"MTTR",{"type":31,"tag":433,"props":2294,"children":2295},{},[2296],{"type":40,"value":2297},"2h15",{"type":31,"tag":433,"props":2299,"children":2300},{},[2301],{"type":40,"value":2302},"38 min",{"type":31,"tag":406,"props":2304,"children":2305},{},[2306,2311,2316],{"type":31,"tag":433,"props":2307,"children":2308},{},[2309],{"type":40,"value":2310},"Ratio ADR \u002F 100 commits",{"type":31,"tag":433,"props":2312,"children":2313},{},[2314],{"type":40,"value":2315},"0,2",{"type":31,"tag":433,"props":2317,"children":2318},{},[2319],{"type":40,"value":2320},"6,2",{"type":31,"tag":406,"props":2322,"children":2323},{},[2324,2329,2334],{"type":31,"tag":433,"props":2325,"children":2326},{},[2327],{"type":40,"value":2328},"Confiance subjective sur la livraison",{"type":31,"tag":433,"props":2330,"children":2331},{},[2332],{"type":40,"value":2333},"\"je produis beaucoup\"",{"type":31,"tag":433,"props":2335,"children":2336},{},[2337],{"type":40,"value":2338},"\"je livre de la valeur\"",{"type":31,"tag":32,"props":2340,"children":2341},{},[2342],{"type":40,"value":2343},"Le verdict est net : en phase 2, je livre moins de commits bruts mais mon code tient en prod. La différence ne tient pas à Claude. Elle tient à la discipline de pilotage. Sans la grille DORA + ADR, Claude est un amplificateur de chaos. Avec la grille, c'est un amplificateur de valeur.",{"type":31,"tag":32,"props":2345,"children":2346},{},[2347,2349,2354],{"type":40,"value":2348},"Pour un solo-dev sur un SaaS, ce tableau devient un outil de pilotage hebdomadaire. Il transforme les intuitions (\"je sens que je suis plus productif\") en faits (\"mon lead time est descendu de 3,8 à 1,4 jours\"). C'est la même logique qu'on retrouve dans ",{"type":31,"tag":69,"props":2350,"children":2351},{"href":297},[2352],{"type":40,"value":2353},"les bonnes métriques de management d'équipe dev",{"type":40,"value":76},{"type":31,"tag":48,"props":2356,"children":2357},{},[],{"type":31,"tag":52,"props":2359,"children":2361},{"id":2360},"comment-piloter-avec-ces-4-1-métriques-en-2026",[2362],{"type":40,"value":2363},"Comment piloter avec ces 4 + 1 métriques en 2026",{"type":31,"tag":32,"props":2365,"children":2366},{},[2367],{"type":40,"value":2368},"Voici la mise en pratique que j'applique sur crmcoaching. Tient sur un tableau de bord Grafana, mis à jour automatiquement à partir de GitHub et de mon outil de monitoring.",{"type":31,"tag":32,"props":2370,"children":2371},{},[2372],{"type":31,"tag":36,"props":2373,"children":2374},{},[2375],{"type":40,"value":2376},"Hebdomadaire (revue personnelle) :",{"type":31,"tag":2378,"props":2379,"children":2380},"ul",{},[2381,2387,2392,2397,2402],{"type":31,"tag":2382,"props":2383,"children":2384},"li",{},[2385],{"type":40,"value":2386},"Lead time for changes (médiane et P90 de la semaine)",{"type":31,"tag":2382,"props":2388,"children":2389},{},[2390],{"type":40,"value":2391},"Deployment frequency (compte des deployments)",{"type":31,"tag":2382,"props":2393,"children":2394},{},[2395],{"type":40,"value":2396},"Change failure rate (% des deployments qui ont causé un incident)",{"type":31,"tag":2382,"props":2398,"children":2399},{},[2400],{"type":40,"value":2401},"MTTR (médiane des incidents résolus cette semaine)",{"type":31,"tag":2382,"props":2403,"children":2404},{},[2405],{"type":40,"value":2406},"ADR créées dans la semaine (compte simple)",{"type":31,"tag":32,"props":2408,"children":2409},{},[2410],{"type":31,"tag":36,"props":2411,"children":2412},{},[2413],{"type":40,"value":2414},"Mensuel (revue de cap) :",{"type":31,"tag":2378,"props":2416,"children":2417},{},[2418,2423,2428,2433],{"type":31,"tag":2382,"props":2419,"children":2420},{},[2421],{"type":40,"value":2422},"Trend des 4 DORA sur 4 semaines glissantes",{"type":31,"tag":2382,"props":2424,"children":2425},{},[2426],{"type":40,"value":2427},"Ratio ADR \u002F 100 commits du mois",{"type":31,"tag":2382,"props":2429,"children":2430},{},[2431],{"type":40,"value":2432},"Top 3 des incidents (avec analyse cause profonde)",{"type":31,"tag":2382,"props":2434,"children":2435},{},[2436],{"type":40,"value":2437},"Sentiment subjectif sur la valeur livrée (note 1-10)",{"type":31,"tag":32,"props":2439,"children":2440},{},[2441],{"type":31,"tag":36,"props":2442,"children":2443},{},[2444],{"type":40,"value":2445},"Trimestriel (revue de direction) :",{"type":31,"tag":2378,"props":2447,"children":2448},{},[2449,2454,2459],{"type":31,"tag":2382,"props":2450,"children":2451},{},[2452],{"type":40,"value":2453},"Comparaison avec les benchmarks DORA Elite \u002F High \u002F Medium \u002F Low",{"type":31,"tag":2382,"props":2455,"children":2456},{},[2457],{"type":40,"value":2458},"Evolution des 5 métriques sur 12 semaines",{"type":31,"tag":2382,"props":2460,"children":2461},{},[2462],{"type":40,"value":2463},"ROI estimé de la discipline IA (coût d'instrumentation vs incidents évités)",{"type":31,"tag":32,"props":2465,"children":2466},{},[2467,2469,2475],{"type":40,"value":2468},"Cette structure évite le piège classique du tableau de bord trop riche. 5 métriques par échelle, lues dans la cadence appropriée. C'est ce que recommandent les pratiques de ",{"type":31,"tag":69,"props":2470,"children":2472},{"href":2471},"\u002Ffr\u002Fdette-technique\u002Fintroduction-maturite-engineering-5-niveaux",[2473],{"type":40,"value":2474},"maturité engineering",{"type":40,"value":2476}," que je mesure dans l'EMA.",{"type":31,"tag":48,"props":2478,"children":2479},{},[],{"type":31,"tag":52,"props":2481,"children":2482},{"id":1565},[2483],{"type":40,"value":1568},{"type":31,"tag":32,"props":2485,"children":2486},{},[2487],{"type":40,"value":2488},"Sur crmcoaching, voici les changements observés entre la phase 1 (sans pilotage) et la phase 2 (avec tableau de bord DORA + ADR) sur 6 mois.",{"type":31,"tag":398,"props":2490,"children":2491},{},[2492,2512],{"type":31,"tag":402,"props":2493,"children":2494},{},[2495],{"type":31,"tag":406,"props":2496,"children":2497},{},[2498,2502,2507],{"type":31,"tag":410,"props":2499,"children":2500},{},[2501],{"type":40,"value":2217},{"type":31,"tag":410,"props":2503,"children":2504},{},[2505],{"type":40,"value":2506},"Phase 1 : sans tableau de bord",{"type":31,"tag":410,"props":2508,"children":2509},{},[2510],{"type":40,"value":2511},"Phase 2 : après 6 mois",{"type":31,"tag":426,"props":2513,"children":2514},{},[2515,2533,2551,2567,2585],{"type":31,"tag":406,"props":2516,"children":2517},{},[2518,2523,2528],{"type":31,"tag":433,"props":2519,"children":2520},{},[2521],{"type":40,"value":2522},"Questions \"est-ce que je suis vraiment plus productif ?\"",{"type":31,"tag":433,"props":2524,"children":2525},{},[2526],{"type":40,"value":2527},"hebdomadaires",{"type":31,"tag":433,"props":2529,"children":2530},{},[2531],{"type":40,"value":2532},"jamais",{"type":31,"tag":406,"props":2534,"children":2535},{},[2536,2541,2546],{"type":31,"tag":433,"props":2537,"children":2538},{},[2539],{"type":40,"value":2540},"Lead time for changes médian",{"type":31,"tag":433,"props":2542,"children":2543},{},[2544],{"type":40,"value":2545},"4,1 jours",{"type":31,"tag":433,"props":2547,"children":2548},{},[2549],{"type":40,"value":2550},"1,7 jour",{"type":31,"tag":406,"props":2552,"children":2553},{},[2554,2558,2562],{"type":31,"tag":433,"props":2555,"children":2556},{},[2557],{"type":40,"value":2274},{"type":31,"tag":433,"props":2559,"children":2560},{},[2561],{"type":40,"value":2279},{"type":31,"tag":433,"props":2563,"children":2564},{},[2565],{"type":40,"value":2566},"7%",{"type":31,"tag":406,"props":2568,"children":2569},{},[2570,2575,2580],{"type":31,"tag":433,"props":2571,"children":2572},{},[2573],{"type":40,"value":2574},"Confiance dans la vélocité affichée (1-10)",{"type":31,"tag":433,"props":2576,"children":2577},{},[2578],{"type":40,"value":2579},"4",{"type":31,"tag":433,"props":2581,"children":2582},{},[2583],{"type":40,"value":2584},"9",{"type":31,"tag":406,"props":2586,"children":2587},{},[2588,2593,2598],{"type":31,"tag":433,"props":2589,"children":2590},{},[2591],{"type":40,"value":2592},"Décisions architecturales tracées en ADR",{"type":31,"tag":433,"props":2594,"children":2595},{},[2596],{"type":40,"value":2597},"moins de 10%",{"type":31,"tag":433,"props":2599,"children":2600},{},[2601],{"type":40,"value":2602},"plus de 75%",{"type":31,"tag":32,"props":2604,"children":2605},{},[2606],{"type":40,"value":2607},"Le gain le plus important n'est pas dans les chiffres. C'est dans la clarté qui devient possible. Avant le tableau de bord, je parlais en croyances (\"je sens que Claude m'aide\"). Après le tableau de bord, je parle en faits (\"le lead time est descendu de 4 à 1,7 jours, le change failure rate est passé de 22 à 7%\"). La conversation avec moi-même devient productive.",{"type":31,"tag":48,"props":2609,"children":2610},{},[],{"type":31,"tag":52,"props":2612,"children":2613},{"id":637},[2614],{"type":40,"value":640},{"type":31,"tag":32,"props":2616,"children":2617},{},[2618],{"type":40,"value":2619},"Ce que je veux que vous reteniez de cet article : la productivité d'un développeur ne se mesure pas en lignes de code, en PR mergées ou en tickets fermés. Elle se mesure en flux (lead time, deployment frequency) et en stabilité (change failure rate, MTTR). Claude peut booster le throughput de 35% à 89%. Il peut aussi dégrader silencieusement les 3 autres métriques si vous ne le pilotez pas.",{"type":31,"tag":32,"props":2621,"children":2622},{},[2623],{"type":40,"value":2624},"La 5ème métrique, le ratio ADR \u002F 100 commits, est ce qui rend la discipline durable. Sans elle, vous mesurez l'output sans tracer la pensée. Et au bout de 18 mois, vous avez une codebase volumineuse sans personne (pas même vous) qui comprend pourquoi elle est faite ainsi.",{"type":31,"tag":32,"props":2626,"children":2627},{},[2628],{"type":40,"value":2629},"Si vous vous reconnaissez dans ce tableau, l'arbitrage est simple : continuer à brandir le +35% de throughput en vous félicitant en fermant les yeux sur les 3 autres chiffres, ou installer dès lundi matin un dashboard à 5 métriques et reprendre une lecture honnête de ce que vous livrez.",{"type":31,"tag":32,"props":2631,"children":2632},{},[2633,2635,2640],{"type":40,"value":2634},"Pour la suite des métriques craft que je documente chaque semaine sur crmcoaching, retrouvez-moi sur ",{"type":31,"tag":69,"props":2636,"children":2638},{"href":663,"rel":2637},[665],[2639],{"type":40,"value":668},{"type":40,"value":76},{"type":31,"tag":229,"props":2642,"children":2644},{"cta":673,"href":674,"title":2643,"type":676},"Piloter ses métriques n'est qu'une pratique parmi les 100 qui font un senior",[2645],{"type":31,"tag":32,"props":2646,"children":2647},{},[2648],{"type":40,"value":2649},"Cet article décortique une seule pratique craft : lire le flux et la stabilité plutôt que le volume brut. Le Craft Bundle réunit les 100 pratiques que j'applique pour coder propre et livrer du code qui tient, depuis le pilotage DORA jusqu'à la traçabilité des décisions en ADR. Ce sont les réflexes que l'IA ne vous apprendra jamais, parce qu'elle ne les a jamais vus tourner en prod.",{"type":31,"tag":48,"props":2651,"children":2652},{},[],{"type":31,"tag":52,"props":2654,"children":2656},{"id":2655},"faq-sur-les-métriques-dora-et-le-pilotage-ia",[2657],{"type":40,"value":2658},"FAQ sur les métriques DORA et le pilotage IA",{"type":31,"tag":693,"props":2660,"children":2661},{},[2662,2667],{"type":31,"tag":697,"props":2663,"children":2664},{},[2665],{"type":40,"value":2666},"1. Faut-il abandonner les story points complètement ?",{"type":31,"tag":32,"props":2668,"children":2669},{},[2670],{"type":40,"value":2671},"Non, mais il faut les remettre à leur juste place : un outil interne d'estimation de capacité de sprint, pas une métrique de productivité. Les story points servent à organiser la charge. Ils ne servent pas à mesurer la valeur livrée ni à comparer des projets entre eux. Quand on les utilise comme KPI, on crée des incitations perverses.",{"type":31,"tag":693,"props":2673,"children":2674},{},[2675,2680],{"type":31,"tag":697,"props":2676,"children":2677},{},[2678],{"type":40,"value":2679},"2. Comment instrumenter automatiquement les métriques DORA ?",{"type":31,"tag":32,"props":2681,"children":2682},{},[2683],{"type":40,"value":2684},"Trois outils gratuits font 90% du travail. GitHub Actions exporte les événements de merge et de deploy. Datadog ou Grafana Cloud agrègent les métriques avec des labels projet \u002F repo \u002F type. Sleuth, FourKeys (open source par Google) ou LinearB sont plus packagés. Pour démarrer, le combo GitHub + Grafana est suffisant. Comptez 2-3 jours d'installation, puis quasi-zéro maintenance.",{"type":31,"tag":693,"props":2686,"children":2687},{},[2688,2693],{"type":31,"tag":697,"props":2689,"children":2690},{},[2691],{"type":40,"value":2692},"3. Le change failure rate, comment le mesurer objectivement ?",{"type":31,"tag":32,"props":2694,"children":2695},{},[2696],{"type":40,"value":2697},"C'est la métrique la plus délicate. Je définis : un déploiement est \"failed\" si dans les 24h suivantes, j'ai déployé un hotfix, fait un rollback, ou ouvert un incident P1\u002FP2. Cette définition évite l'arbitraire. Elle se code en automatique en croisant le log de déploiement avec le tracker d'incidents. La mesure n'est pas parfaite, mais elle est honnête et reproductible d'une semaine à l'autre.",{"type":31,"tag":693,"props":2699,"children":2700},{},[2701,2706],{"type":31,"tag":697,"props":2702,"children":2703},{},[2704],{"type":40,"value":2705},"4. Combien de temps pour qu'un tableau de bord DORA devienne stable ?",{"type":31,"tag":32,"props":2707,"children":2708},{},[2709],{"type":40,"value":2710},"Comptez 4-6 semaines pour que les chiffres se stabilisent et que vous compreniez ce que vous regardez. Pendant les 2 premières semaines, vous verrez beaucoup d'effets de seuil (bug de mesure, événements pas correctement tagués). À partir de la 4ème semaine, les tendances deviennent fiables. À 3 mois, vous pouvez commencer à prendre des décisions basées sur les chiffres.",{"type":31,"tag":693,"props":2712,"children":2713},{},[2714,2719],{"type":31,"tag":697,"props":2715,"children":2716},{},[2717],{"type":40,"value":2718},"5. Comment éviter que les métriques deviennent un outil de pression sur soi-même ?",{"type":31,"tag":32,"props":2720,"children":2721},{},[2722],{"type":40,"value":2723},"Les métriques DORA sont des métriques de flux et de stabilité, pas des métriques de valeur personnelle. Si votre change failure rate monte une semaine, c'est une information sur votre processus, pas un jugement sur vous. L'objectif est de voir les tendances sur plusieurs semaines, pas d'interpréter chaque point de données comme un verdict. En solo, c'est encore plus important de garder cette distance.",{"type":31,"tag":693,"props":2725,"children":2726},{},[2727,2732],{"type":31,"tag":697,"props":2728,"children":2729},{},[2730],{"type":40,"value":2731},"6. Le ratio ADR \u002F 100 commits, quelle cible viser ?",{"type":31,"tag":32,"props":2733,"children":2734},{},[2735],{"type":40,"value":2736},"Je recommande 3 à 6 ADR pour 100 commits comme zone saine. En dessous de 1, vous ne tracez plus la pensée. Au-dessus de 10, vous documentez trop et vous noyez les vraies décisions dans le bruit. Sur crmcoaching je suis à 6,2 et c'est confortable. Si vous démarrez, l'objectif des 3 premiers mois est de passer de 0,1 à 1,0. Ensuite on calibre.",{"type":31,"tag":48,"props":2738,"children":2739},{},[],{"type":31,"tag":229,"props":2741,"children":2743},{"cta":2742,"href":807,"title":808,"type":809},"Évaluer la maturité de mon projet →",[2744],{"type":31,"tag":32,"props":2745,"children":2746},{},[2747],{"type":40,"value":2748},"L'EMA est l'outil que je propose au début de chaque mission. Il mesure la maturité d'un projet sur plusieurs axes engineering : delivery, qualité, gouvernance IA, observabilité. Le module pilotage \u002F métriques DORA y est central. Quelques minutes pour identifier où votre tableau de bord ment ou se tait, et où concentrer vos efforts en priorité.",{"title":8,"searchDepth":817,"depth":817,"links":2750},[2751,2752,2753,2754,2755,2756,2757,2758,2759],{"id":1891,"depth":817,"text":1894},{"id":1984,"depth":817,"text":1987},{"id":2063,"depth":817,"text":2066},{"id":2140,"depth":817,"text":2143},{"id":2195,"depth":817,"text":2198},{"id":2360,"depth":817,"text":2363},{"id":1565,"depth":817,"text":1568},{"id":637,"depth":817,"text":640},{"id":2655,"depth":817,"text":2658},"content:fr:intelligence-artificielle:illusion-productivite-10x-pr.md","fr\u002Fintelligence-artificielle\u002Fillusion-productivite-10x-pr.md","fr\u002Fintelligence-artificielle\u002Fillusion-productivite-10x-pr",{"_path":262,"_dir":6,"_draft":7,"_partial":7,"_locale":8,"title":2764,"description":2765,"id":2766,"date":2767,"listed":13,"nocomments":7,"hidden":7,"categories":2768,"tags":2769,"cover":2773,"readingTime":2774,"body":2779,"_type":830,"_id":5012,"_source":832,"_file":5013,"_stem":5014,"_extension":835},"Le code Claude 'qui marche' est souvent celui qui vous coûte le plus cher","Démonstration chiffrée des 3 faux amis du code IA-généré : copier-coller invisible, abstractions accidentelles, sur-ingénierie. Et la règle YAGNI + AI.",65,"2026-05-13",[6],[16,2770,2771,2772,19],"dette-technique","yagni","sur-ingenierie","covers\u002Farticles\u002Ffaux-ami-code-claude-coute-cher.jpg",{"text":2775,"minutes":2776,"time":2777,"words":2778},"14 min read",13.37,802200,2674,{"type":28,"children":2780,"toc":5001},[2781,2789,2794,2797,2803,2823,2832,2837,2850,2875,2878,2884,2889,2910,3381,3394,3406,3419,3422,3428,3433,3840,3861,3866,3900,3905,3908,3917,3920,3926,3931,4443,4456,4461,4480,4483,4489,4494,4610,4615,4628,4631,4637,4642,4652,4669,4679,4689,4694,4697,4701,4706,4819,4843,4846,4850,4855,4860,4865,4876,4885,4888,4894,4907,4928,4941,4954,4967,4986,4989,4997],{"type":31,"tag":32,"props":2782,"children":2783},{},[2784],{"type":31,"tag":36,"props":2785,"children":2786},{},[2787],{"type":40,"value":2788},"En construisant crmcoaching avec Claude, j'ai vu ces 3 faux amis s'installer dans mes premiers commits bien avant d'avoir mis la discipline en place. Tests verts, ESLint au vert, tout compilait. Et pourtant, chaque nouvelle feature prenait de plus en plus de temps, et modifier un use case existant devenait une navigation dans de la complexité que je n'avais jamais demandée. Pas à cause d'une mauvaise architecture de départ. À cause de 3 patterns que Claude installe silencieusement si on ne lui oppose pas de contraintes explicites.",{"type":31,"tag":32,"props":2790,"children":2791},{},[2792],{"type":40,"value":2793},"Voici les 3 patterns que j'ai identifiés sur mes propres commits, les chiffres de leur coût caché, et la règle craft de 2026 qui les neutralise.",{"type":31,"tag":48,"props":2795,"children":2796},{},[],{"type":31,"tag":52,"props":2798,"children":2800},{"id":2799},"le-mythe-du-ça-compile-cest-bon",[2801],{"type":40,"value":2802},"Le mythe du \"ça compile, c'est bon\"",{"type":31,"tag":32,"props":2804,"children":2805},{},[2806,2808,2815,2817,2822],{"type":40,"value":2807},"Le constat de base est documenté. Selon le ",{"type":31,"tag":69,"props":2809,"children":2812},{"href":2810,"rel":2811},"https:\u002F\u002Fwww.gitclear.com\u002Fai_assistant_code_quality_2024_research",[665],[2813],{"type":40,"value":2814},"GitClear AI Copilot Code Quality Report 2024",{"type":40,"value":2816},", sur les repos qui utilisent intensivement un assistant comme Claude, le ratio de code \"copié-collé\" a augmenté de 8 fois depuis 2022. La taille moyenne des PR a grossi de 12%. Le temps de revue par PR a baissé de 14%. Concrètement : on écrit plus, on revoit moins, on duplique plus, ce qui correspond exactement au pattern que je décris dans ",{"type":31,"tag":69,"props":2818,"children":2819},{"href":71},[2820],{"type":40,"value":2821},"le retour d'expérience sur la code review IA",{"type":40,"value":76},{"type":31,"tag":32,"props":2824,"children":2825},{},[2826],{"type":31,"tag":2827,"props":2828,"children":2831},"img",{"alt":2829,"src":2830},"La gestion des leads dans crmcoaching, le SaaS que je développe avec Claude","\u002Fimages\u002Fcrm\u002Fcrm-coaching-lead.png",[],{"type":31,"tag":32,"props":2833,"children":2834},{},[2835],{"type":40,"value":2836},"Le piège, c'est que tout ça produit du code qui marche. Les tests passent. Les utilisateurs sont contents. ESLint ne crie pas. Le piège n'apparaît pas tout de suite, il apparaît à 3 ou 6 mois, quand vous devez modifier ce code et que vous découvrez le coût réel des choix initiaux.",{"type":31,"tag":78,"props":2838,"children":2839},{},[2840],{"type":31,"tag":32,"props":2841,"children":2842},{},[2843,2848],{"type":31,"tag":36,"props":2844,"children":2845},{},[2846],{"type":40,"value":2847},"Ce que j'ai observé",{"type":40,"value":2849}," : dans les premiers commits de crmcoaching, j'ai compté les fonctions dupliquées à plus de 70% de similarité. Plusieurs paires identifiées, toutes nées du même réflexe Claude : je prompte pour résoudre un problème dans un use case, Claude livre une fonction ressemblant à une qui existe déjà ailleurs dans le repo, je ne vais pas chercher l'existante, je colle la nouvelle. Chaque paire est une dette future : il faudra modifier 2 endroits, faire le diff cognitif, risquer la divergence.",{"type":31,"tag":32,"props":2851,"children":2852},{},[2853,2855,2860,2862,2867,2869,2873],{"type":40,"value":2854},"Donald Knuth avait écrit en 1974 que ",{"type":31,"tag":97,"props":2856,"children":2857},{},[2858],{"type":40,"value":2859},"\"premature optimization is the root of all evil\"",{"type":40,"value":2861},". Sa formule s'applique aussi à l'abstraction. En 2026, je dirais que ",{"type":31,"tag":97,"props":2863,"children":2864},{},[2865],{"type":40,"value":2866},"\"l'abstraction prématurée IA-générée est la racine du mal silencieux\"",{"type":40,"value":2868},". Le mal silencieux, c'est celui qui ne fait pas planter la prod, mais qui ralentit tout le monde sans que personne ne sache pourquoi. C'est aussi ce qu'éclairent ",{"type":31,"tag":69,"props":2870,"children":2871},{"href":113},[2872],{"type":40,"value":897},{"type":40,"value":2874}," : la qualité ne se voit pas dans le code \"qui marche\", elle se voit dans le code \"qu'on peut modifier sans souffrir\".",{"type":31,"tag":48,"props":2876,"children":2877},{},[],{"type":31,"tag":52,"props":2879,"children":2881},{"id":2880},"faux-ami-1-le-copier-coller-invisible",[2882],{"type":40,"value":2883},"Faux ami #1 : le copier-coller invisible",{"type":31,"tag":32,"props":2885,"children":2886},{},[2887],{"type":40,"value":2888},"C'est le plus fréquent et le plus sous-estimé. Claude répète des fonctions parce que c'est plus statistique que de chercher la fonction existante dans le repo. Le développeur l'accepte parce que le code \"marche\".",{"type":31,"tag":32,"props":2890,"children":2891},{},[2892,2894,2900,2902,2908],{"type":40,"value":2893},"Voici un exemple tiré de mes premiers commits sur crmcoaching. Deux use cases différents, ",{"type":31,"tag":169,"props":2895,"children":2897},{"className":2896},[],[2898],{"type":40,"value":2899},"CreateOffer",{"type":40,"value":2901}," et ",{"type":31,"tag":169,"props":2903,"children":2905},{"className":2904},[],[2906],{"type":40,"value":2907},"CreateMentoringCheckoutIntent",{"type":40,"value":2909},", écrits à quelques semaines d'écart. Deux fonctions quasi identiques, à 6 fichiers de distance :",{"type":31,"tag":1206,"props":2911,"children":2915},{"className":2912,"code":2913,"language":2914,"meta":8,"style":8},"language-typescript shiki shiki-themes catppuccin-frappe github-dark","\u002F\u002F apps\u002Fapi\u002Fsrc\u002Fapplication\u002Foffer\u002Fuse-cases\u002Fcreate-offer.use-case.ts\nfunction computeMonthlyOfferPrice(\n  yearlyPrice: number,\n  fees: number,\n): number {\n  const subtotal = yearlyPrice \u002F 12;\n  const withFees = subtotal + fees \u002F 12;\n  return Math.round(withFees * 100) \u002F 100;\n}\n\n\u002F\u002F apps\u002Fapi\u002Fsrc\u002Fapplication\u002Fmentoring\u002Fuse-cases\u002Fcreate-mentoring-checkout-intent.use-case.ts\nfunction calculateSubscriptionAmount(\n  annualAmount: number,\n  charges: number,\n): number {\n  const base = annualAmount \u002F 12;\n  const total = base + charges \u002F 12;\n  return Math.round(total * 100) \u002F 100;\n}\n","typescript",[2916],{"type":31,"tag":169,"props":2917,"children":2918},{"__ignoreMap":8},[2919,2928,2949,2975,2995,3016,3056,3098,3153,3161,3168,3176,3192,3212,3232,3251,3284,3326,3374],{"type":31,"tag":1215,"props":2920,"children":2921},{"class":1217,"line":1218},[2922],{"type":31,"tag":1215,"props":2923,"children":2925},{"style":2924},"--shiki-default:#737994;--shiki-default-font-style:italic;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit",[2926],{"type":40,"value":2927},"\u002F\u002F apps\u002Fapi\u002Fsrc\u002Fapplication\u002Foffer\u002Fuse-cases\u002Fcreate-offer.use-case.ts\n",{"type":31,"tag":1215,"props":2929,"children":2930},{"class":1217,"line":817},[2931,2937,2943],{"type":31,"tag":1215,"props":2932,"children":2934},{"style":2933},"--shiki-default:#CA9EE6;--shiki-dark:#F97583",[2935],{"type":40,"value":2936},"function",{"type":31,"tag":1215,"props":2938,"children":2940},{"style":2939},"--shiki-default:#8CAAEE;--shiki-default-font-style:italic;--shiki-dark:#B392F0;--shiki-dark-font-style:inherit",[2941],{"type":40,"value":2942}," computeMonthlyOfferPrice",{"type":31,"tag":1215,"props":2944,"children":2946},{"style":2945},"--shiki-default:#949CBB;--shiki-dark:#E1E4E8",[2947],{"type":40,"value":2948},"(\n",{"type":31,"tag":1215,"props":2950,"children":2951},{"class":1217,"line":1236},[2952,2958,2964,2970],{"type":31,"tag":1215,"props":2953,"children":2955},{"style":2954},"--shiki-default:#EA999C;--shiki-default-font-style:italic;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit",[2956],{"type":40,"value":2957},"  yearlyPrice",{"type":31,"tag":1215,"props":2959,"children":2961},{"style":2960},"--shiki-default:#81C8BE;--shiki-dark:#F97583",[2962],{"type":40,"value":2963},":",{"type":31,"tag":1215,"props":2965,"children":2967},{"style":2966},"--shiki-default:#CA9EE6;--shiki-dark:#79B8FF",[2968],{"type":40,"value":2969}," number",{"type":31,"tag":1215,"props":2971,"children":2972},{"style":2945},[2973],{"type":40,"value":2974},",\n",{"type":31,"tag":1215,"props":2976,"children":2977},{"class":1217,"line":1246},[2978,2983,2987,2991],{"type":31,"tag":1215,"props":2979,"children":2980},{"style":2954},[2981],{"type":40,"value":2982},"  fees",{"type":31,"tag":1215,"props":2984,"children":2985},{"style":2960},[2986],{"type":40,"value":2963},{"type":31,"tag":1215,"props":2988,"children":2989},{"style":2966},[2990],{"type":40,"value":2969},{"type":31,"tag":1215,"props":2992,"children":2993},{"style":2945},[2994],{"type":40,"value":2974},{"type":31,"tag":1215,"props":2996,"children":2997},{"class":1217,"line":1256},[2998,3003,3007,3011],{"type":31,"tag":1215,"props":2999,"children":3000},{"style":2945},[3001],{"type":40,"value":3002},")",{"type":31,"tag":1215,"props":3004,"children":3005},{"style":2960},[3006],{"type":40,"value":2963},{"type":31,"tag":1215,"props":3008,"children":3009},{"style":2966},[3010],{"type":40,"value":2969},{"type":31,"tag":1215,"props":3012,"children":3013},{"style":2945},[3014],{"type":40,"value":3015}," {\n",{"type":31,"tag":1215,"props":3017,"children":3018},{"class":1217,"line":1264},[3019,3024,3030,3035,3040,3045,3051],{"type":31,"tag":1215,"props":3020,"children":3021},{"style":2933},[3022],{"type":40,"value":3023},"  const",{"type":31,"tag":1215,"props":3025,"children":3027},{"style":3026},"--shiki-default:#C6D0F5;--shiki-dark:#79B8FF",[3028],{"type":40,"value":3029}," subtotal",{"type":31,"tag":1215,"props":3031,"children":3032},{"style":2960},[3033],{"type":40,"value":3034}," =",{"type":31,"tag":1215,"props":3036,"children":3037},{"style":1250},[3038],{"type":40,"value":3039}," yearlyPrice ",{"type":31,"tag":1215,"props":3041,"children":3042},{"style":2960},[3043],{"type":40,"value":3044},"\u002F",{"type":31,"tag":1215,"props":3046,"children":3048},{"style":3047},"--shiki-default:#EF9F76;--shiki-dark:#79B8FF",[3049],{"type":40,"value":3050}," 12",{"type":31,"tag":1215,"props":3052,"children":3053},{"style":2945},[3054],{"type":40,"value":3055},";\n",{"type":31,"tag":1215,"props":3057,"children":3058},{"class":1217,"line":1273},[3059,3063,3068,3072,3077,3081,3086,3090,3094],{"type":31,"tag":1215,"props":3060,"children":3061},{"style":2933},[3062],{"type":40,"value":3023},{"type":31,"tag":1215,"props":3064,"children":3065},{"style":3026},[3066],{"type":40,"value":3067}," withFees",{"type":31,"tag":1215,"props":3069,"children":3070},{"style":2960},[3071],{"type":40,"value":3034},{"type":31,"tag":1215,"props":3073,"children":3074},{"style":1250},[3075],{"type":40,"value":3076}," subtotal ",{"type":31,"tag":1215,"props":3078,"children":3079},{"style":2960},[3080],{"type":40,"value":1442},{"type":31,"tag":1215,"props":3082,"children":3083},{"style":1250},[3084],{"type":40,"value":3085}," fees ",{"type":31,"tag":1215,"props":3087,"children":3088},{"style":2960},[3089],{"type":40,"value":3044},{"type":31,"tag":1215,"props":3091,"children":3092},{"style":3047},[3093],{"type":40,"value":3050},{"type":31,"tag":1215,"props":3095,"children":3096},{"style":2945},[3097],{"type":40,"value":3055},{"type":31,"tag":1215,"props":3099,"children":3100},{"class":1217,"line":1282},[3101,3106,3111,3116,3121,3126,3131,3136,3141,3145,3149],{"type":31,"tag":1215,"props":3102,"children":3103},{"style":2933},[3104],{"type":40,"value":3105},"  return",{"type":31,"tag":1215,"props":3107,"children":3108},{"style":1250},[3109],{"type":40,"value":3110}," Math",{"type":31,"tag":1215,"props":3112,"children":3114},{"style":3113},"--shiki-default:#81C8BE;--shiki-dark:#E1E4E8",[3115],{"type":40,"value":76},{"type":31,"tag":1215,"props":3117,"children":3118},{"style":2939},[3119],{"type":40,"value":3120},"round",{"type":31,"tag":1215,"props":3122,"children":3123},{"style":1250},[3124],{"type":40,"value":3125},"(withFees ",{"type":31,"tag":1215,"props":3127,"children":3128},{"style":2960},[3129],{"type":40,"value":3130},"*",{"type":31,"tag":1215,"props":3132,"children":3133},{"style":3047},[3134],{"type":40,"value":3135}," 100",{"type":31,"tag":1215,"props":3137,"children":3138},{"style":1250},[3139],{"type":40,"value":3140},") ",{"type":31,"tag":1215,"props":3142,"children":3143},{"style":2960},[3144],{"type":40,"value":3044},{"type":31,"tag":1215,"props":3146,"children":3147},{"style":3047},[3148],{"type":40,"value":3135},{"type":31,"tag":1215,"props":3150,"children":3151},{"style":2945},[3152],{"type":40,"value":3055},{"type":31,"tag":1215,"props":3154,"children":3155},{"class":1217,"line":1291},[3156],{"type":31,"tag":1215,"props":3157,"children":3158},{"style":2945},[3159],{"type":40,"value":3160},"}\n",{"type":31,"tag":1215,"props":3162,"children":3163},{"class":1217,"line":1300},[3164],{"type":31,"tag":1215,"props":3165,"children":3166},{"emptyLinePlaceholder":13},[3167],{"type":40,"value":1233},{"type":31,"tag":1215,"props":3169,"children":3170},{"class":1217,"line":1309},[3171],{"type":31,"tag":1215,"props":3172,"children":3173},{"style":2924},[3174],{"type":40,"value":3175},"\u002F\u002F apps\u002Fapi\u002Fsrc\u002Fapplication\u002Fmentoring\u002Fuse-cases\u002Fcreate-mentoring-checkout-intent.use-case.ts\n",{"type":31,"tag":1215,"props":3177,"children":3178},{"class":1217,"line":1317},[3179,3183,3188],{"type":31,"tag":1215,"props":3180,"children":3181},{"style":2933},[3182],{"type":40,"value":2936},{"type":31,"tag":1215,"props":3184,"children":3185},{"style":2939},[3186],{"type":40,"value":3187}," calculateSubscriptionAmount",{"type":31,"tag":1215,"props":3189,"children":3190},{"style":2945},[3191],{"type":40,"value":2948},{"type":31,"tag":1215,"props":3193,"children":3194},{"class":1217,"line":1326},[3195,3200,3204,3208],{"type":31,"tag":1215,"props":3196,"children":3197},{"style":2954},[3198],{"type":40,"value":3199},"  annualAmount",{"type":31,"tag":1215,"props":3201,"children":3202},{"style":2960},[3203],{"type":40,"value":2963},{"type":31,"tag":1215,"props":3205,"children":3206},{"style":2966},[3207],{"type":40,"value":2969},{"type":31,"tag":1215,"props":3209,"children":3210},{"style":2945},[3211],{"type":40,"value":2974},{"type":31,"tag":1215,"props":3213,"children":3214},{"class":1217,"line":1341},[3215,3220,3224,3228],{"type":31,"tag":1215,"props":3216,"children":3217},{"style":2954},[3218],{"type":40,"value":3219},"  charges",{"type":31,"tag":1215,"props":3221,"children":3222},{"style":2960},[3223],{"type":40,"value":2963},{"type":31,"tag":1215,"props":3225,"children":3226},{"style":2966},[3227],{"type":40,"value":2969},{"type":31,"tag":1215,"props":3229,"children":3230},{"style":2945},[3231],{"type":40,"value":2974},{"type":31,"tag":1215,"props":3233,"children":3234},{"class":1217,"line":1355},[3235,3239,3243,3247],{"type":31,"tag":1215,"props":3236,"children":3237},{"style":2945},[3238],{"type":40,"value":3002},{"type":31,"tag":1215,"props":3240,"children":3241},{"style":2960},[3242],{"type":40,"value":2963},{"type":31,"tag":1215,"props":3244,"children":3245},{"style":2966},[3246],{"type":40,"value":2969},{"type":31,"tag":1215,"props":3248,"children":3249},{"style":2945},[3250],{"type":40,"value":3015},{"type":31,"tag":1215,"props":3252,"children":3253},{"class":1217,"line":1364},[3254,3258,3263,3267,3272,3276,3280],{"type":31,"tag":1215,"props":3255,"children":3256},{"style":2933},[3257],{"type":40,"value":3023},{"type":31,"tag":1215,"props":3259,"children":3260},{"style":3026},[3261],{"type":40,"value":3262}," base",{"type":31,"tag":1215,"props":3264,"children":3265},{"style":2960},[3266],{"type":40,"value":3034},{"type":31,"tag":1215,"props":3268,"children":3269},{"style":1250},[3270],{"type":40,"value":3271}," annualAmount ",{"type":31,"tag":1215,"props":3273,"children":3274},{"style":2960},[3275],{"type":40,"value":3044},{"type":31,"tag":1215,"props":3277,"children":3278},{"style":3047},[3279],{"type":40,"value":3050},{"type":31,"tag":1215,"props":3281,"children":3282},{"style":2945},[3283],{"type":40,"value":3055},{"type":31,"tag":1215,"props":3285,"children":3286},{"class":1217,"line":1378},[3287,3291,3296,3300,3305,3309,3314,3318,3322],{"type":31,"tag":1215,"props":3288,"children":3289},{"style":2933},[3290],{"type":40,"value":3023},{"type":31,"tag":1215,"props":3292,"children":3293},{"style":3026},[3294],{"type":40,"value":3295}," total",{"type":31,"tag":1215,"props":3297,"children":3298},{"style":2960},[3299],{"type":40,"value":3034},{"type":31,"tag":1215,"props":3301,"children":3302},{"style":1250},[3303],{"type":40,"value":3304}," base ",{"type":31,"tag":1215,"props":3306,"children":3307},{"style":2960},[3308],{"type":40,"value":1442},{"type":31,"tag":1215,"props":3310,"children":3311},{"style":1250},[3312],{"type":40,"value":3313}," charges ",{"type":31,"tag":1215,"props":3315,"children":3316},{"style":2960},[3317],{"type":40,"value":3044},{"type":31,"tag":1215,"props":3319,"children":3320},{"style":3047},[3321],{"type":40,"value":3050},{"type":31,"tag":1215,"props":3323,"children":3324},{"style":2945},[3325],{"type":40,"value":3055},{"type":31,"tag":1215,"props":3327,"children":3328},{"class":1217,"line":1387},[3329,3333,3337,3341,3345,3350,3354,3358,3362,3366,3370],{"type":31,"tag":1215,"props":3330,"children":3331},{"style":2933},[3332],{"type":40,"value":3105},{"type":31,"tag":1215,"props":3334,"children":3335},{"style":1250},[3336],{"type":40,"value":3110},{"type":31,"tag":1215,"props":3338,"children":3339},{"style":3113},[3340],{"type":40,"value":76},{"type":31,"tag":1215,"props":3342,"children":3343},{"style":2939},[3344],{"type":40,"value":3120},{"type":31,"tag":1215,"props":3346,"children":3347},{"style":1250},[3348],{"type":40,"value":3349},"(total ",{"type":31,"tag":1215,"props":3351,"children":3352},{"style":2960},[3353],{"type":40,"value":3130},{"type":31,"tag":1215,"props":3355,"children":3356},{"style":3047},[3357],{"type":40,"value":3135},{"type":31,"tag":1215,"props":3359,"children":3360},{"style":1250},[3361],{"type":40,"value":3140},{"type":31,"tag":1215,"props":3363,"children":3364},{"style":2960},[3365],{"type":40,"value":3044},{"type":31,"tag":1215,"props":3367,"children":3368},{"style":3047},[3369],{"type":40,"value":3135},{"type":31,"tag":1215,"props":3371,"children":3372},{"style":2945},[3373],{"type":40,"value":3055},{"type":31,"tag":1215,"props":3375,"children":3376},{"class":1217,"line":1401},[3377],{"type":31,"tag":1215,"props":3378,"children":3379},{"style":2945},[3380],{"type":40,"value":3160},{"type":31,"tag":32,"props":3382,"children":3383},{},[3384,3386,3392],{"type":40,"value":3385},"Même calcul, deux noms, deux fichiers, deux tests, deux maintenances. Le jour où la logique d'arrondi change (par exemple passer à ",{"type":31,"tag":169,"props":3387,"children":3389},{"className":3388},[],[3390],{"type":40,"value":3391},"Math.ceil",{"type":40,"value":3393}," pour la comptabilité), il faut retrouver les 2 endroits. La dette n'est pas dans le code lui-même, elle est dans la divergence future.",{"type":31,"tag":32,"props":3395,"children":3396},{},[3397,3399,3404],{"type":40,"value":3398},"Le contre-pattern craft : avant chaque PR, je demande explicitement à Claude ",{"type":31,"tag":97,"props":3400,"children":3401},{},[3402],{"type":40,"value":3403},"\"cherche dans le repo s'il existe déjà une fonction équivalente à ce que tu viens d'écrire, et propose une refacto qui mutualise\"",{"type":40,"value":3405},". Claude fait ce travail très bien si on le lui demande. Il ne le fait jamais spontanément. Et c'est ce réflexe que la Boy Scout Rule impose à chaque PR : on laisse le repo un peu plus propre qu'on l'a trouvé.",{"type":31,"tag":32,"props":3407,"children":3408},{},[3409,3411,3417],{"type":40,"value":3410},"Sur le crmcoaching, j'ai aujourd'hui 3 utilitaires de calcul financier dans ",{"type":31,"tag":169,"props":3412,"children":3414},{"className":3413},[],[3415],{"type":40,"value":3416},"packages\u002Fshared",{"type":40,"value":3418},". Sur un repo équivalent IA-heavy non audité, j'en aurais vu 12 à 15. La différence se voit au bout de 12 mois sur le temps de modification.",{"type":31,"tag":48,"props":3420,"children":3421},{},[],{"type":31,"tag":52,"props":3423,"children":3425},{"id":3424},"faux-ami-2-les-abstractions-accidentelles",[3426],{"type":40,"value":3427},"Faux ami #2 : les abstractions accidentelles",{"type":31,"tag":32,"props":3429,"children":3430},{},[3431],{"type":40,"value":3432},"Le deuxième faux ami est plus subtil. Claude adore les interfaces, les classes abstraites, les patterns enterprise. Si vous lui demandez un service de notation de leads, il vous livre, sur un projet NestJS :",{"type":31,"tag":1206,"props":3434,"children":3436},{"className":2912,"code":3435,"language":2914,"meta":8,"style":8},"\u002F\u002F 1. L'interface\nexport interface LeadScorer {\n  score(lead: Lead): number;\n}\n\n\u002F\u002F 2. L'unique implémentation, déjà injectable via @Injectable()\n@Injectable()\nexport class StandardLeadScorer implements LeadScorer {\n  score(lead: Lead): number {\n    return lead.interactionCount * 10 + (lead.hasEmail ? 20 : 0);\n  }\n}\n\n\u002F\u002F 3. La factory superflue, jamais utilisée par le DI NestJS\nexport class LeadScorerFactory {\n  static create(type: string): LeadScorer {\n    return new StandardLeadScorer();\n  }\n}\n",[3437],{"type":31,"tag":169,"props":3438,"children":3439},{"__ignoreMap":8},[3440,3448,3471,3514,3521,3528,3536,3556,3586,3625,3703,3711,3718,3725,3733,3753,3800,3826,3833],{"type":31,"tag":1215,"props":3441,"children":3442},{"class":1217,"line":1218},[3443],{"type":31,"tag":1215,"props":3444,"children":3445},{"style":2924},[3446],{"type":40,"value":3447},"\u002F\u002F 1. L'interface\n",{"type":31,"tag":1215,"props":3449,"children":3450},{"class":1217,"line":817},[3451,3456,3461,3467],{"type":31,"tag":1215,"props":3452,"children":3453},{"style":2933},[3454],{"type":40,"value":3455},"export",{"type":31,"tag":1215,"props":3457,"children":3458},{"style":2933},[3459],{"type":40,"value":3460}," interface",{"type":31,"tag":1215,"props":3462,"children":3464},{"style":3463},"--shiki-default:#E5C890;--shiki-default-font-style:italic;--shiki-dark:#B392F0;--shiki-dark-font-style:inherit",[3465],{"type":40,"value":3466}," LeadScorer",{"type":31,"tag":1215,"props":3468,"children":3469},{"style":2945},[3470],{"type":40,"value":3015},{"type":31,"tag":1215,"props":3472,"children":3473},{"class":1217,"line":1236},[3474,3479,3484,3489,3493,3498,3502,3506,3510],{"type":31,"tag":1215,"props":3475,"children":3476},{"style":2939},[3477],{"type":40,"value":3478},"  score",{"type":31,"tag":1215,"props":3480,"children":3481},{"style":2945},[3482],{"type":40,"value":3483},"(",{"type":31,"tag":1215,"props":3485,"children":3486},{"style":2954},[3487],{"type":40,"value":3488},"lead",{"type":31,"tag":1215,"props":3490,"children":3491},{"style":2960},[3492],{"type":40,"value":2963},{"type":31,"tag":1215,"props":3494,"children":3495},{"style":3463},[3496],{"type":40,"value":3497}," Lead",{"type":31,"tag":1215,"props":3499,"children":3500},{"style":2945},[3501],{"type":40,"value":3002},{"type":31,"tag":1215,"props":3503,"children":3504},{"style":2960},[3505],{"type":40,"value":2963},{"type":31,"tag":1215,"props":3507,"children":3508},{"style":2966},[3509],{"type":40,"value":2969},{"type":31,"tag":1215,"props":3511,"children":3512},{"style":2945},[3513],{"type":40,"value":3055},{"type":31,"tag":1215,"props":3515,"children":3516},{"class":1217,"line":1246},[3517],{"type":31,"tag":1215,"props":3518,"children":3519},{"style":2945},[3520],{"type":40,"value":3160},{"type":31,"tag":1215,"props":3522,"children":3523},{"class":1217,"line":1256},[3524],{"type":31,"tag":1215,"props":3525,"children":3526},{"emptyLinePlaceholder":13},[3527],{"type":40,"value":1233},{"type":31,"tag":1215,"props":3529,"children":3530},{"class":1217,"line":1264},[3531],{"type":31,"tag":1215,"props":3532,"children":3533},{"style":2924},[3534],{"type":40,"value":3535},"\u002F\u002F 2. L'unique implémentation, déjà injectable via @Injectable()\n",{"type":31,"tag":1215,"props":3537,"children":3538},{"class":1217,"line":1273},[3539,3545,3550],{"type":31,"tag":1215,"props":3540,"children":3542},{"style":3541},"--shiki-default:#8CAAEE;--shiki-default-font-style:italic;--shiki-dark:#E1E4E8;--shiki-dark-font-style:inherit",[3543],{"type":40,"value":3544},"@",{"type":31,"tag":1215,"props":3546,"children":3547},{"style":2939},[3548],{"type":40,"value":3549},"Injectable",{"type":31,"tag":1215,"props":3551,"children":3553},{"style":3552},"--shiki-default:#EF9F76;--shiki-dark:#E1E4E8",[3554],{"type":40,"value":3555},"()\n",{"type":31,"tag":1215,"props":3557,"children":3558},{"class":1217,"line":1282},[3559,3563,3568,3573,3578,3582],{"type":31,"tag":1215,"props":3560,"children":3561},{"style":2933},[3562],{"type":40,"value":3455},{"type":31,"tag":1215,"props":3564,"children":3565},{"style":2933},[3566],{"type":40,"value":3567}," class",{"type":31,"tag":1215,"props":3569,"children":3570},{"style":3463},[3571],{"type":40,"value":3572}," StandardLeadScorer",{"type":31,"tag":1215,"props":3574,"children":3575},{"style":2933},[3576],{"type":40,"value":3577}," implements",{"type":31,"tag":1215,"props":3579,"children":3580},{"style":3463},[3581],{"type":40,"value":3466},{"type":31,"tag":1215,"props":3583,"children":3584},{"style":2945},[3585],{"type":40,"value":3015},{"type":31,"tag":1215,"props":3587,"children":3588},{"class":1217,"line":1291},[3589,3593,3597,3601,3605,3609,3613,3617,3621],{"type":31,"tag":1215,"props":3590,"children":3591},{"style":2939},[3592],{"type":40,"value":3478},{"type":31,"tag":1215,"props":3594,"children":3595},{"style":2945},[3596],{"type":40,"value":3483},{"type":31,"tag":1215,"props":3598,"children":3599},{"style":2954},[3600],{"type":40,"value":3488},{"type":31,"tag":1215,"props":3602,"children":3603},{"style":2960},[3604],{"type":40,"value":2963},{"type":31,"tag":1215,"props":3606,"children":3607},{"style":3463},[3608],{"type":40,"value":3497},{"type":31,"tag":1215,"props":3610,"children":3611},{"style":2945},[3612],{"type":40,"value":3002},{"type":31,"tag":1215,"props":3614,"children":3615},{"style":2960},[3616],{"type":40,"value":2963},{"type":31,"tag":1215,"props":3618,"children":3619},{"style":2966},[3620],{"type":40,"value":2969},{"type":31,"tag":1215,"props":3622,"children":3623},{"style":2945},[3624],{"type":40,"value":3015},{"type":31,"tag":1215,"props":3626,"children":3627},{"class":1217,"line":1300},[3628,3633,3638,3642,3647,3651,3656,3661,3666,3670,3675,3680,3685,3690,3695,3699],{"type":31,"tag":1215,"props":3629,"children":3630},{"style":2933},[3631],{"type":40,"value":3632},"    return",{"type":31,"tag":1215,"props":3634,"children":3635},{"style":1250},[3636],{"type":40,"value":3637}," lead",{"type":31,"tag":1215,"props":3639,"children":3640},{"style":3113},[3641],{"type":40,"value":76},{"type":31,"tag":1215,"props":3643,"children":3644},{"style":1250},[3645],{"type":40,"value":3646},"interactionCount ",{"type":31,"tag":1215,"props":3648,"children":3649},{"style":2960},[3650],{"type":40,"value":3130},{"type":31,"tag":1215,"props":3652,"children":3653},{"style":3047},[3654],{"type":40,"value":3655}," 10",{"type":31,"tag":1215,"props":3657,"children":3658},{"style":2960},[3659],{"type":40,"value":3660}," +",{"type":31,"tag":1215,"props":3662,"children":3663},{"style":1250},[3664],{"type":40,"value":3665}," (lead",{"type":31,"tag":1215,"props":3667,"children":3668},{"style":3113},[3669],{"type":40,"value":76},{"type":31,"tag":1215,"props":3671,"children":3672},{"style":1250},[3673],{"type":40,"value":3674},"hasEmail ",{"type":31,"tag":1215,"props":3676,"children":3677},{"style":2960},[3678],{"type":40,"value":3679},"?",{"type":31,"tag":1215,"props":3681,"children":3682},{"style":3047},[3683],{"type":40,"value":3684}," 20",{"type":31,"tag":1215,"props":3686,"children":3687},{"style":2960},[3688],{"type":40,"value":3689}," :",{"type":31,"tag":1215,"props":3691,"children":3692},{"style":3047},[3693],{"type":40,"value":3694}," 0",{"type":31,"tag":1215,"props":3696,"children":3697},{"style":1250},[3698],{"type":40,"value":3002},{"type":31,"tag":1215,"props":3700,"children":3701},{"style":2945},[3702],{"type":40,"value":3055},{"type":31,"tag":1215,"props":3704,"children":3705},{"class":1217,"line":1309},[3706],{"type":31,"tag":1215,"props":3707,"children":3708},{"style":2945},[3709],{"type":40,"value":3710},"  }\n",{"type":31,"tag":1215,"props":3712,"children":3713},{"class":1217,"line":1317},[3714],{"type":31,"tag":1215,"props":3715,"children":3716},{"style":2945},[3717],{"type":40,"value":3160},{"type":31,"tag":1215,"props":3719,"children":3720},{"class":1217,"line":1326},[3721],{"type":31,"tag":1215,"props":3722,"children":3723},{"emptyLinePlaceholder":13},[3724],{"type":40,"value":1233},{"type":31,"tag":1215,"props":3726,"children":3727},{"class":1217,"line":1341},[3728],{"type":31,"tag":1215,"props":3729,"children":3730},{"style":2924},[3731],{"type":40,"value":3732},"\u002F\u002F 3. La factory superflue, jamais utilisée par le DI NestJS\n",{"type":31,"tag":1215,"props":3734,"children":3735},{"class":1217,"line":1355},[3736,3740,3744,3749],{"type":31,"tag":1215,"props":3737,"children":3738},{"style":2933},[3739],{"type":40,"value":3455},{"type":31,"tag":1215,"props":3741,"children":3742},{"style":2933},[3743],{"type":40,"value":3567},{"type":31,"tag":1215,"props":3745,"children":3746},{"style":3463},[3747],{"type":40,"value":3748}," LeadScorerFactory",{"type":31,"tag":1215,"props":3750,"children":3751},{"style":2945},[3752],{"type":40,"value":3015},{"type":31,"tag":1215,"props":3754,"children":3755},{"class":1217,"line":1364},[3756,3761,3766,3770,3775,3779,3784,3788,3792,3796],{"type":31,"tag":1215,"props":3757,"children":3758},{"style":2933},[3759],{"type":40,"value":3760},"  static",{"type":31,"tag":1215,"props":3762,"children":3763},{"style":2939},[3764],{"type":40,"value":3765}," create",{"type":31,"tag":1215,"props":3767,"children":3768},{"style":2945},[3769],{"type":40,"value":3483},{"type":31,"tag":1215,"props":3771,"children":3772},{"style":2954},[3773],{"type":40,"value":3774},"type",{"type":31,"tag":1215,"props":3776,"children":3777},{"style":2960},[3778],{"type":40,"value":2963},{"type":31,"tag":1215,"props":3780,"children":3781},{"style":2966},[3782],{"type":40,"value":3783}," string",{"type":31,"tag":1215,"props":3785,"children":3786},{"style":2945},[3787],{"type":40,"value":3002},{"type":31,"tag":1215,"props":3789,"children":3790},{"style":2960},[3791],{"type":40,"value":2963},{"type":31,"tag":1215,"props":3793,"children":3794},{"style":3463},[3795],{"type":40,"value":3466},{"type":31,"tag":1215,"props":3797,"children":3798},{"style":2945},[3799],{"type":40,"value":3015},{"type":31,"tag":1215,"props":3801,"children":3802},{"class":1217,"line":1378},[3803,3807,3813,3817,3822],{"type":31,"tag":1215,"props":3804,"children":3805},{"style":2933},[3806],{"type":40,"value":3632},{"type":31,"tag":1215,"props":3808,"children":3810},{"style":3809},"--shiki-default:#CA9EE6;--shiki-default-font-weight:bold;--shiki-dark:#F97583;--shiki-dark-font-weight:inherit",[3811],{"type":40,"value":3812}," new",{"type":31,"tag":1215,"props":3814,"children":3815},{"style":2939},[3816],{"type":40,"value":3572},{"type":31,"tag":1215,"props":3818,"children":3819},{"style":1250},[3820],{"type":40,"value":3821},"()",{"type":31,"tag":1215,"props":3823,"children":3824},{"style":2945},[3825],{"type":40,"value":3055},{"type":31,"tag":1215,"props":3827,"children":3828},{"class":1217,"line":1387},[3829],{"type":31,"tag":1215,"props":3830,"children":3831},{"style":2945},[3832],{"type":40,"value":3710},{"type":31,"tag":1215,"props":3834,"children":3835},{"class":1217,"line":1401},[3836],{"type":31,"tag":1215,"props":3837,"children":3838},{"style":2945},[3839],{"type":40,"value":3160},{"type":31,"tag":32,"props":3841,"children":3842},{},[3843,3845,3851,3853,3859],{"type":40,"value":3844},"Une interface, un ",{"type":31,"tag":169,"props":3846,"children":3848},{"className":3847},[],[3849],{"type":40,"value":3850},"@Injectable()",{"type":40,"value":3852},", une factory. Pour calculer un score avec 2 opérations. Une seule implémentation concrète. NestJS injecte directement ",{"type":31,"tag":169,"props":3854,"children":3856},{"className":3855},[],[3857],{"type":40,"value":3858},"StandardLeadScorer",{"type":40,"value":3860}," partout. La factory n'est appelée nulle part, sauf dans un test. Aucun besoin d'inversion réel.",{"type":31,"tag":32,"props":3862,"children":3863},{},[3864],{"type":40,"value":3865},"Cette sur-ingénierie a un coût concret. À chaque modification, vous passez par 3 fichiers au lieu d'un. Vous écrivez 2 fois plus de tests (le mock de l'interface + le test de l'implémentation). Vous augmentez la charge cognitive pour tout nouveau dev. Et le pire : si le métier demande un jour une vraie variation, vous ne la verrez pas dans le pattern existant parce que tout est déjà \"préparé\" pour des variations qui n'existent pas.",{"type":31,"tag":32,"props":3867,"children":3868},{},[3869,3871,3876,3878,3883,3885,3891,3893,3899],{"type":40,"value":3870},"Le contre-pattern craft, c'est ce que Martin Fowler appelait ",{"type":31,"tag":97,"props":3872,"children":3873},{},[3874],{"type":40,"value":3875},"\"Rule of Three\"",{"type":40,"value":3877}," dans ",{"type":31,"tag":97,"props":3879,"children":3880},{},[3881],{"type":40,"value":3882},"Refactoring",{"type":40,"value":3884}," (2018). Vous ne créez une abstraction qu'à partir du troisième cas concret. Si une fonction n'a qu'une implémentation, c'est une fonction. Pas une interface, pas une factory, pas une classe abstraite. Une fonction. C'est exactement la philosophie défendue dans ",{"type":31,"tag":69,"props":3886,"children":3888},{"href":3887},"\u002Ffr\u002Fdette-technique\u002Fyagni-ecrire-code-qualite-sans-fonctionnalites-inutiles",[3889],{"type":40,"value":3890},"YAGNI : écrire du code de qualité sans fonctionnalités inutiles",{"type":40,"value":3892}," et dans le ",{"type":31,"tag":69,"props":3894,"children":3896},{"href":3895},"\u002Ffr\u002Fdette-technique\u002Fprincipe-kiss-bonnes-pratiques-java",[3897],{"type":40,"value":3898},"principe KISS appliqué en Java",{"type":40,"value":76},{"type":31,"tag":32,"props":3901,"children":3902},{},[3903],{"type":40,"value":3904},"Sur le crmcoaching, j'ai 47 use cases sans aucune interface devant eux. Quand j'aurai une vraie variation à modéliser (typiquement après 6 mois de production), je créerai l'interface à partir des 3 cas réels. Pas avant.",{"type":31,"tag":48,"props":3906,"children":3907},{},[],{"type":31,"tag":229,"props":3909,"children":3911},{"cta":231,"href":232,"title":3910,"type":234},"Vous voulez repérer ces abstractions accidentelles dans vos PR Claude avant de les merger ?",[3912],{"type":31,"tag":32,"props":3913,"children":3914},{},[3915],{"type":40,"value":3916},"Refuser une interface inutile ou une factory qui ne sert à rien, ça ne s'apprend pas en lisant un article : ça se travaille sur votre propre code. En mentoring 1:1, je relis vos PR Claude avec vous, je vous montre où l'IA installe de la sur-ingénierie en silence, et vous repartez avec le réflexe YAGNI qui garde votre repo simple. C'est du face à face pour monter en niveau, pas un audit d'équipe.",{"type":31,"tag":48,"props":3918,"children":3919},{},[],{"type":31,"tag":52,"props":3921,"children":3923},{"id":3922},"faux-ami-3-la-sur-ingénierie-déguisée",[3924],{"type":40,"value":3925},"Faux ami #3 : la sur-ingénierie déguisée",{"type":31,"tag":32,"props":3927,"children":3928},{},[3929],{"type":40,"value":3930},"Le troisième faux ami est le plus visible mais le plus accepté. Claude adore livrer des architectures enterprise pour des problèmes triviaux. Vous demandez un handler pour récupérer un client par id, vous recevez :",{"type":31,"tag":1206,"props":3932,"children":3934},{"className":2912,"code":3933,"language":2914,"meta":8,"style":8},"\u002F\u002F QueryHandler + ClientRepositoryPort + EventBusPort + MetricsPort + 2 adapters\n@Injectable()\nexport class GetClientQueryHandler {\n  constructor(\n    private readonly clientRepository: ClientRepositoryPort,\n    private readonly eventBus: EventBusPort,\n    private readonly metrics: MetricsPort,\n  ) {}\n\n  async handle(query: GetClientQuery): Promise\u003CGetClientResult> {\n    const client = await this.clientRepository.findById(query.clientId);\n    if (!client) {\n      throw new ClientNotFoundException(query.clientId);\n    }\n    await this.eventBus.publish(new ClientRetrievedEvent(query.clientId));\n    this.metrics.increment('client.retrieved');\n    return new GetClientResult(client);\n  }\n}\n",[3935],{"type":31,"tag":169,"props":3936,"children":3937},{"__ignoreMap":8},[3938,3946,3961,3981,3993,4024,4053,4082,4095,4102,4166,4230,4258,4291,4299,4360,4404,4429,4436],{"type":31,"tag":1215,"props":3939,"children":3940},{"class":1217,"line":1218},[3941],{"type":31,"tag":1215,"props":3942,"children":3943},{"style":2924},[3944],{"type":40,"value":3945},"\u002F\u002F QueryHandler + ClientRepositoryPort + EventBusPort + MetricsPort + 2 adapters\n",{"type":31,"tag":1215,"props":3947,"children":3948},{"class":1217,"line":817},[3949,3953,3957],{"type":31,"tag":1215,"props":3950,"children":3951},{"style":3541},[3952],{"type":40,"value":3544},{"type":31,"tag":1215,"props":3954,"children":3955},{"style":2939},[3956],{"type":40,"value":3549},{"type":31,"tag":1215,"props":3958,"children":3959},{"style":3552},[3960],{"type":40,"value":3555},{"type":31,"tag":1215,"props":3962,"children":3963},{"class":1217,"line":1236},[3964,3968,3972,3977],{"type":31,"tag":1215,"props":3965,"children":3966},{"style":2933},[3967],{"type":40,"value":3455},{"type":31,"tag":1215,"props":3969,"children":3970},{"style":2933},[3971],{"type":40,"value":3567},{"type":31,"tag":1215,"props":3973,"children":3974},{"style":3463},[3975],{"type":40,"value":3976}," GetClientQueryHandler",{"type":31,"tag":1215,"props":3978,"children":3979},{"style":2945},[3980],{"type":40,"value":3015},{"type":31,"tag":1215,"props":3982,"children":3983},{"class":1217,"line":1246},[3984,3989],{"type":31,"tag":1215,"props":3985,"children":3986},{"style":2933},[3987],{"type":40,"value":3988},"  constructor",{"type":31,"tag":1215,"props":3990,"children":3991},{"style":2945},[3992],{"type":40,"value":2948},{"type":31,"tag":1215,"props":3994,"children":3995},{"class":1217,"line":1256},[3996,4001,4006,4011,4015,4020],{"type":31,"tag":1215,"props":3997,"children":3998},{"style":2933},[3999],{"type":40,"value":4000},"    private",{"type":31,"tag":1215,"props":4002,"children":4003},{"style":2933},[4004],{"type":40,"value":4005}," readonly",{"type":31,"tag":1215,"props":4007,"children":4008},{"style":2954},[4009],{"type":40,"value":4010}," clientRepository",{"type":31,"tag":1215,"props":4012,"children":4013},{"style":2960},[4014],{"type":40,"value":2963},{"type":31,"tag":1215,"props":4016,"children":4017},{"style":3463},[4018],{"type":40,"value":4019}," ClientRepositoryPort",{"type":31,"tag":1215,"props":4021,"children":4022},{"style":2945},[4023],{"type":40,"value":2974},{"type":31,"tag":1215,"props":4025,"children":4026},{"class":1217,"line":1264},[4027,4031,4035,4040,4044,4049],{"type":31,"tag":1215,"props":4028,"children":4029},{"style":2933},[4030],{"type":40,"value":4000},{"type":31,"tag":1215,"props":4032,"children":4033},{"style":2933},[4034],{"type":40,"value":4005},{"type":31,"tag":1215,"props":4036,"children":4037},{"style":2954},[4038],{"type":40,"value":4039}," eventBus",{"type":31,"tag":1215,"props":4041,"children":4042},{"style":2960},[4043],{"type":40,"value":2963},{"type":31,"tag":1215,"props":4045,"children":4046},{"style":3463},[4047],{"type":40,"value":4048}," EventBusPort",{"type":31,"tag":1215,"props":4050,"children":4051},{"style":2945},[4052],{"type":40,"value":2974},{"type":31,"tag":1215,"props":4054,"children":4055},{"class":1217,"line":1273},[4056,4060,4064,4069,4073,4078],{"type":31,"tag":1215,"props":4057,"children":4058},{"style":2933},[4059],{"type":40,"value":4000},{"type":31,"tag":1215,"props":4061,"children":4062},{"style":2933},[4063],{"type":40,"value":4005},{"type":31,"tag":1215,"props":4065,"children":4066},{"style":2954},[4067],{"type":40,"value":4068}," metrics",{"type":31,"tag":1215,"props":4070,"children":4071},{"style":2960},[4072],{"type":40,"value":2963},{"type":31,"tag":1215,"props":4074,"children":4075},{"style":3463},[4076],{"type":40,"value":4077}," MetricsPort",{"type":31,"tag":1215,"props":4079,"children":4080},{"style":2945},[4081],{"type":40,"value":2974},{"type":31,"tag":1215,"props":4083,"children":4084},{"class":1217,"line":1282},[4085,4090],{"type":31,"tag":1215,"props":4086,"children":4087},{"style":2945},[4088],{"type":40,"value":4089},"  )",{"type":31,"tag":1215,"props":4091,"children":4092},{"style":2945},[4093],{"type":40,"value":4094}," {}\n",{"type":31,"tag":1215,"props":4096,"children":4097},{"class":1217,"line":1291},[4098],{"type":31,"tag":1215,"props":4099,"children":4100},{"emptyLinePlaceholder":13},[4101],{"type":40,"value":1233},{"type":31,"tag":1215,"props":4103,"children":4104},{"class":1217,"line":1300},[4105,4110,4115,4119,4124,4128,4133,4137,4141,4146,4152,4157,4162],{"type":31,"tag":1215,"props":4106,"children":4107},{"style":2933},[4108],{"type":40,"value":4109},"  async",{"type":31,"tag":1215,"props":4111,"children":4112},{"style":2939},[4113],{"type":40,"value":4114}," handle",{"type":31,"tag":1215,"props":4116,"children":4117},{"style":2945},[4118],{"type":40,"value":3483},{"type":31,"tag":1215,"props":4120,"children":4121},{"style":2954},[4122],{"type":40,"value":4123},"query",{"type":31,"tag":1215,"props":4125,"children":4126},{"style":2960},[4127],{"type":40,"value":2963},{"type":31,"tag":1215,"props":4129,"children":4130},{"style":3463},[4131],{"type":40,"value":4132}," GetClientQuery",{"type":31,"tag":1215,"props":4134,"children":4135},{"style":2945},[4136],{"type":40,"value":3002},{"type":31,"tag":1215,"props":4138,"children":4139},{"style":2960},[4140],{"type":40,"value":2963},{"type":31,"tag":1215,"props":4142,"children":4143},{"style":3463},[4144],{"type":40,"value":4145}," Promise",{"type":31,"tag":1215,"props":4147,"children":4149},{"style":4148},"--shiki-default:#99D1DB;--shiki-dark:#E1E4E8",[4150],{"type":40,"value":4151},"\u003C",{"type":31,"tag":1215,"props":4153,"children":4154},{"style":3463},[4155],{"type":40,"value":4156},"GetClientResult",{"type":31,"tag":1215,"props":4158,"children":4159},{"style":4148},[4160],{"type":40,"value":4161},">",{"type":31,"tag":1215,"props":4163,"children":4164},{"style":2945},[4165],{"type":40,"value":3015},{"type":31,"tag":1215,"props":4167,"children":4168},{"class":1217,"line":1309},[4169,4174,4179,4183,4188,4194,4198,4203,4207,4212,4217,4221,4226],{"type":31,"tag":1215,"props":4170,"children":4171},{"style":2933},[4172],{"type":40,"value":4173},"    const",{"type":31,"tag":1215,"props":4175,"children":4176},{"style":3026},[4177],{"type":40,"value":4178}," client",{"type":31,"tag":1215,"props":4180,"children":4181},{"style":2960},[4182],{"type":40,"value":3034},{"type":31,"tag":1215,"props":4184,"children":4185},{"style":2933},[4186],{"type":40,"value":4187}," await",{"type":31,"tag":1215,"props":4189,"children":4191},{"style":4190},"--shiki-default:#E78284;--shiki-dark:#79B8FF",[4192],{"type":40,"value":4193}," this",{"type":31,"tag":1215,"props":4195,"children":4196},{"style":3113},[4197],{"type":40,"value":76},{"type":31,"tag":1215,"props":4199,"children":4200},{"style":1250},[4201],{"type":40,"value":4202},"clientRepository",{"type":31,"tag":1215,"props":4204,"children":4205},{"style":3113},[4206],{"type":40,"value":76},{"type":31,"tag":1215,"props":4208,"children":4209},{"style":2939},[4210],{"type":40,"value":4211},"findById",{"type":31,"tag":1215,"props":4213,"children":4214},{"style":1250},[4215],{"type":40,"value":4216},"(query",{"type":31,"tag":1215,"props":4218,"children":4219},{"style":3113},[4220],{"type":40,"value":76},{"type":31,"tag":1215,"props":4222,"children":4223},{"style":1250},[4224],{"type":40,"value":4225},"clientId)",{"type":31,"tag":1215,"props":4227,"children":4228},{"style":2945},[4229],{"type":40,"value":3055},{"type":31,"tag":1215,"props":4231,"children":4232},{"class":1217,"line":1317},[4233,4238,4243,4248,4253],{"type":31,"tag":1215,"props":4234,"children":4235},{"style":2933},[4236],{"type":40,"value":4237},"    if",{"type":31,"tag":1215,"props":4239,"children":4240},{"style":1250},[4241],{"type":40,"value":4242}," (",{"type":31,"tag":1215,"props":4244,"children":4245},{"style":2960},[4246],{"type":40,"value":4247},"!",{"type":31,"tag":1215,"props":4249,"children":4250},{"style":1250},[4251],{"type":40,"value":4252},"client) ",{"type":31,"tag":1215,"props":4254,"children":4255},{"style":2945},[4256],{"type":40,"value":4257},"{\n",{"type":31,"tag":1215,"props":4259,"children":4260},{"class":1217,"line":1326},[4261,4266,4270,4275,4279,4283,4287],{"type":31,"tag":1215,"props":4262,"children":4263},{"style":2933},[4264],{"type":40,"value":4265},"      throw",{"type":31,"tag":1215,"props":4267,"children":4268},{"style":3809},[4269],{"type":40,"value":3812},{"type":31,"tag":1215,"props":4271,"children":4272},{"style":2939},[4273],{"type":40,"value":4274}," ClientNotFoundException",{"type":31,"tag":1215,"props":4276,"children":4277},{"style":1250},[4278],{"type":40,"value":4216},{"type":31,"tag":1215,"props":4280,"children":4281},{"style":3113},[4282],{"type":40,"value":76},{"type":31,"tag":1215,"props":4284,"children":4285},{"style":1250},[4286],{"type":40,"value":4225},{"type":31,"tag":1215,"props":4288,"children":4289},{"style":2945},[4290],{"type":40,"value":3055},{"type":31,"tag":1215,"props":4292,"children":4293},{"class":1217,"line":1341},[4294],{"type":31,"tag":1215,"props":4295,"children":4296},{"style":2945},[4297],{"type":40,"value":4298},"    }\n",{"type":31,"tag":1215,"props":4300,"children":4301},{"class":1217,"line":1355},[4302,4307,4311,4315,4320,4324,4329,4333,4338,4343,4347,4351,4356],{"type":31,"tag":1215,"props":4303,"children":4304},{"style":2933},[4305],{"type":40,"value":4306},"    await",{"type":31,"tag":1215,"props":4308,"children":4309},{"style":4190},[4310],{"type":40,"value":4193},{"type":31,"tag":1215,"props":4312,"children":4313},{"style":3113},[4314],{"type":40,"value":76},{"type":31,"tag":1215,"props":4316,"children":4317},{"style":1250},[4318],{"type":40,"value":4319},"eventBus",{"type":31,"tag":1215,"props":4321,"children":4322},{"style":3113},[4323],{"type":40,"value":76},{"type":31,"tag":1215,"props":4325,"children":4326},{"style":2939},[4327],{"type":40,"value":4328},"publish",{"type":31,"tag":1215,"props":4330,"children":4331},{"style":1250},[4332],{"type":40,"value":3483},{"type":31,"tag":1215,"props":4334,"children":4335},{"style":3809},[4336],{"type":40,"value":4337},"new",{"type":31,"tag":1215,"props":4339,"children":4340},{"style":2939},[4341],{"type":40,"value":4342}," ClientRetrievedEvent",{"type":31,"tag":1215,"props":4344,"children":4345},{"style":1250},[4346],{"type":40,"value":4216},{"type":31,"tag":1215,"props":4348,"children":4349},{"style":3113},[4350],{"type":40,"value":76},{"type":31,"tag":1215,"props":4352,"children":4353},{"style":1250},[4354],{"type":40,"value":4355},"clientId))",{"type":31,"tag":1215,"props":4357,"children":4358},{"style":2945},[4359],{"type":40,"value":3055},{"type":31,"tag":1215,"props":4361,"children":4362},{"class":1217,"line":1364},[4363,4368,4372,4377,4381,4386,4390,4396,4400],{"type":31,"tag":1215,"props":4364,"children":4365},{"style":4190},[4366],{"type":40,"value":4367},"    this",{"type":31,"tag":1215,"props":4369,"children":4370},{"style":3113},[4371],{"type":40,"value":76},{"type":31,"tag":1215,"props":4373,"children":4374},{"style":1250},[4375],{"type":40,"value":4376},"metrics",{"type":31,"tag":1215,"props":4378,"children":4379},{"style":3113},[4380],{"type":40,"value":76},{"type":31,"tag":1215,"props":4382,"children":4383},{"style":2939},[4384],{"type":40,"value":4385},"increment",{"type":31,"tag":1215,"props":4387,"children":4388},{"style":1250},[4389],{"type":40,"value":3483},{"type":31,"tag":1215,"props":4391,"children":4393},{"style":4392},"--shiki-default:#A6D189;--shiki-dark:#9ECBFF",[4394],{"type":40,"value":4395},"'client.retrieved'",{"type":31,"tag":1215,"props":4397,"children":4398},{"style":1250},[4399],{"type":40,"value":3002},{"type":31,"tag":1215,"props":4401,"children":4402},{"style":2945},[4403],{"type":40,"value":3055},{"type":31,"tag":1215,"props":4405,"children":4406},{"class":1217,"line":1378},[4407,4411,4415,4420,4425],{"type":31,"tag":1215,"props":4408,"children":4409},{"style":2933},[4410],{"type":40,"value":3632},{"type":31,"tag":1215,"props":4412,"children":4413},{"style":3809},[4414],{"type":40,"value":3812},{"type":31,"tag":1215,"props":4416,"children":4417},{"style":2939},[4418],{"type":40,"value":4419}," GetClientResult",{"type":31,"tag":1215,"props":4421,"children":4422},{"style":1250},[4423],{"type":40,"value":4424},"(client)",{"type":31,"tag":1215,"props":4426,"children":4427},{"style":2945},[4428],{"type":40,"value":3055},{"type":31,"tag":1215,"props":4430,"children":4431},{"class":1217,"line":1387},[4432],{"type":31,"tag":1215,"props":4433,"children":4434},{"style":2945},[4435],{"type":40,"value":3710},{"type":31,"tag":1215,"props":4437,"children":4438},{"class":1217,"line":1401},[4439],{"type":31,"tag":1215,"props":4440,"children":4441},{"style":2945},[4442],{"type":40,"value":3160},{"type":31,"tag":32,"props":4444,"children":4445},{},[4446,4448,4454],{"type":40,"value":4447},"30 lignes, 4 fichiers, 3 ports, 1 event qui n'est lu par personne. Pour faire ",{"type":31,"tag":169,"props":4449,"children":4451},{"className":4450},[],[4452],{"type":40,"value":4453},"prisma.client.findUnique({ where: { id } })",{"type":40,"value":4455},". Le coût direct : 4x plus de code à lire pour un nouveau dev. Le coût indirect : la signature de tous les handlers du repo devient ce pattern, donc même les use cases qui justifieraient un découpage simple le portent.",{"type":31,"tag":32,"props":4457,"children":4458},{},[4459],{"type":40,"value":4460},"Le contre-pattern, c'est le principe de proportionnalité. Sur le crmcoaching, j'applique cette règle : si l'opération métier tient en une phrase (\"lire un client par id\"), le code tient en une fonction avec une query Prisma directe. Si elle nécessite 3 étapes coordonnées avec compensation possible, alors un handler peut se justifier. Sinon, c'est de la dette emballée en pattern.",{"type":31,"tag":32,"props":4462,"children":4463},{},[4464,4466,4471,4473,4478],{"type":40,"value":4465},"Robert C. Martin, dans ",{"type":31,"tag":97,"props":4467,"children":4468},{},[4469],{"type":40,"value":4470},"Clean Architecture",{"type":40,"value":4472}," (2017), insiste sur ce point : l'architecture doit ",{"type":31,"tag":97,"props":4474,"children":4475},{},[4476],{"type":40,"value":4477},"suivre",{"type":40,"value":4479}," la complexité du domaine, pas la précéder. Si vous précédez une complexité qui n'existe pas, vous payez aujourd'hui un coût pour des cas qui ne viendront jamais. C'est aussi ce que rappelle le guide du code lisible en software craftsmanship.",{"type":31,"tag":48,"props":4481,"children":4482},{},[],{"type":31,"tag":52,"props":4484,"children":4486},{"id":4485},"calcul-du-vrai-coût-caché",[4487],{"type":40,"value":4488},"Calcul du vrai coût caché",{"type":31,"tag":32,"props":4490,"children":4491},{},[4492],{"type":40,"value":4493},"J'ai mesuré le coût cumulé des 3 faux amis en comparant mes commits de début de projet (avant discipline) avec l'état actuel de crmcoaching. Voici les chiffres, comparés à un repo équivalent où la discipline craft est tenue depuis le début.",{"type":31,"tag":398,"props":4495,"children":4496},{},[4497,4517],{"type":31,"tag":402,"props":4498,"children":4499},{},[4500],{"type":31,"tag":406,"props":4501,"children":4502},{},[4503,4507,4512],{"type":31,"tag":410,"props":4504,"children":4505},{},[4506],{"type":40,"value":2217},{"type":31,"tag":410,"props":4508,"children":4509},{},[4510],{"type":40,"value":4511},"Repo IA non audité",{"type":31,"tag":410,"props":4513,"children":4514},{},[4515],{"type":40,"value":4516},"Repo IA + discipline craft",{"type":31,"tag":426,"props":4518,"children":4519},{},[4520,4538,4556,4574,4592],{"type":31,"tag":406,"props":4521,"children":4522},{},[4523,4528,4533],{"type":31,"tag":433,"props":4524,"children":4525},{},[4526],{"type":40,"value":4527},"Temps moyen pour ajouter une feature simple",{"type":31,"tag":433,"props":4529,"children":4530},{},[4531],{"type":40,"value":4532},"3,5 jours",{"type":31,"tag":433,"props":4534,"children":4535},{},[4536],{"type":40,"value":4537},"1,2 jour",{"type":31,"tag":406,"props":4539,"children":4540},{},[4541,4546,4551],{"type":31,"tag":433,"props":4542,"children":4543},{},[4544],{"type":40,"value":4545},"Temps moyen pour modifier une feature existante",{"type":31,"tag":433,"props":4547,"children":4548},{},[4549],{"type":40,"value":4550},"5 jours",{"type":31,"tag":433,"props":4552,"children":4553},{},[4554],{"type":40,"value":4555},"1,8 jour",{"type":31,"tag":406,"props":4557,"children":4558},{},[4559,4564,4569],{"type":31,"tag":433,"props":4560,"children":4561},{},[4562],{"type":40,"value":4563},"Onboarding d'un nouveau dev (jusqu'à 1ère PR mergée)",{"type":31,"tag":433,"props":4565,"children":4566},{},[4567],{"type":40,"value":4568},"7 semaines",{"type":31,"tag":433,"props":4570,"children":4571},{},[4572],{"type":40,"value":4573},"3 semaines",{"type":31,"tag":406,"props":4575,"children":4576},{},[4577,4582,4587],{"type":31,"tag":433,"props":4578,"children":4579},{},[4580],{"type":40,"value":4581},"Bugs régression par release",{"type":31,"tag":433,"props":4583,"children":4584},{},[4585],{"type":40,"value":4586},"4-6",{"type":31,"tag":433,"props":4588,"children":4589},{},[4590],{"type":40,"value":4591},"0-1",{"type":31,"tag":406,"props":4593,"children":4594},{},[4595,4600,4605],{"type":31,"tag":433,"props":4596,"children":4597},{},[4598],{"type":40,"value":4599},"Lignes de code pour 1 use case métier",{"type":31,"tag":433,"props":4601,"children":4602},{},[4603],{"type":40,"value":4604},"280 LOC en moyenne",{"type":31,"tag":433,"props":4606,"children":4607},{},[4608],{"type":40,"value":4609},"95 LOC en moyenne",{"type":31,"tag":32,"props":4611,"children":4612},{},[4613],{"type":40,"value":4614},"Le ratio est de 1 à 3 sur la plupart des métriques. Concrètement, sans discipline craft, une équipe de 5 développeurs paie l'équivalent d'1,5 développeur de \"taxe complexité\" chaque mois, sans valeur ajoutée pour les utilisateurs. À 8 000 euros de coût total mensuel chargé par développeur, c'est 12 000 euros par mois. 144 000 euros par an. Sans qu'aucune feature ne s'ajoute en plus.",{"type":31,"tag":32,"props":4616,"children":4617},{},[4618,4620,4626],{"type":40,"value":4619},"C'est le coût silencieux dont je parle quand je dis qu'une codebase IA-heavy non auditée coûte cher, et c'est l'angle business que je détaille dans ",{"type":31,"tag":69,"props":4621,"children":4623},{"href":4622},"\u002Ffr\u002Fdette-technique\u002Fingenierie-logicielle-avantage-concurrentiel",[4624],{"type":40,"value":4625},"l'ingénierie logicielle comme avantage concurrentiel",{"type":40,"value":4627},". Personne ne le voit dans le P&L, parce que c'est de la vélocité non gagnée, pas une dépense identifiable. Mais il est là.",{"type":31,"tag":48,"props":4629,"children":4630},{},[],{"type":31,"tag":52,"props":4632,"children":4634},{"id":4633},"la-règle-2026-yagni-ai",[4635],{"type":40,"value":4636},"La règle 2026 : YAGNI + AI",{"type":31,"tag":32,"props":4638,"children":4639},{},[4640],{"type":40,"value":4641},"Voici la règle que j'applique sur crmcoaching et que je recommande à toutes les équipes IA-heavy.",{"type":31,"tag":32,"props":4643,"children":4644},{},[4645,4650],{"type":31,"tag":36,"props":4646,"children":4647},{},[4648],{"type":40,"value":4649},"Règle 1 : pas d'abstraction sans 3 implémentations réelles.",{"type":40,"value":4651}," Pas d'interface devant une seule classe. Pas de factory pour 1 type. Pas de port hexagonal devant un module qui ne peut pas être remplacé. La règle des 3 vient de Martin Fowler, elle date des années 2000, elle s'applique parfaitement à 2026.",{"type":31,"tag":32,"props":4653,"children":4654},{},[4655,4660,4662,4667],{"type":31,"tag":36,"props":4656,"children":4657},{},[4658],{"type":40,"value":4659},"Règle 2 : avant chaque PR Claude, prompter \"cherche les doublons\".",{"type":40,"value":4661}," ",{"type":31,"tag":97,"props":4663,"children":4664},{},[4665],{"type":40,"value":4666},"\"Avant de finaliser, vérifie dans le repo s'il existe déjà une fonction qui fait à 80% la même chose. Si oui, refactore pour mutualiser plutôt que dupliquer.\"",{"type":40,"value":4668}," Claude le fait très bien quand on lui demande. Il ne le fait jamais spontanément.",{"type":31,"tag":32,"props":4670,"children":4671},{},[4672,4677],{"type":31,"tag":36,"props":4673,"children":4674},{},[4675],{"type":40,"value":4676},"Règle 3 : proportionnalité code\u002Fdomaine.",{"type":40,"value":4678}," Une opération métier triviale = une fonction. Une opération métier coordonnée = un handler\u002Fservice. Une opération métier critique avec compensation = un saga ou un pattern de coordination explicite. Le code suit le domaine, pas l'inverse.",{"type":31,"tag":32,"props":4680,"children":4681},{},[4682,4687],{"type":31,"tag":36,"props":4683,"children":4684},{},[4685],{"type":40,"value":4686},"Règle 4 : audit des LOC par feature.",{"type":40,"value":4688}," Je mesure tous les 3 mois le nombre de lignes ajoutées par feature livrée. Si la métrique grimpe de plus de 30%, c'est un signal de sur-ingénierie qui s'installe. C'est la même logique qu'en clean code : on ne contrôle que ce qu'on mesure, et la Definition of Done doit inclure ce signal.",{"type":31,"tag":32,"props":4690,"children":4691},{},[4692],{"type":40,"value":4693},"Avec ces 4 règles, le repo crmcoaching tourne à environ 95 LOC par use case métier complet (avec tests + adapter + handler). C'est 3x moins que la moyenne IA-heavy que je vois sur des repos partagés. C'est aussi 3x plus rapide à modifier.",{"type":31,"tag":48,"props":4695,"children":4696},{},[],{"type":31,"tag":52,"props":4698,"children":4699},{"id":1565},[4700],{"type":40,"value":1568},{"type":31,"tag":32,"props":4702,"children":4703},{},[4704],{"type":40,"value":4705},"Sur les 3 dernières missions où j'ai installé cette discipline (audit + refacto + grille de review), voici les chiffres avant\u002Faprès mesurés sur 6 mois.",{"type":31,"tag":398,"props":4707,"children":4708},{},[4709,4729],{"type":31,"tag":402,"props":4710,"children":4711},{},[4712],{"type":31,"tag":406,"props":4713,"children":4714},{},[4715,4719,4724],{"type":31,"tag":410,"props":4716,"children":4717},{},[4718],{"type":40,"value":2217},{"type":31,"tag":410,"props":4720,"children":4721},{},[4722],{"type":40,"value":4723},"Avant audit",{"type":31,"tag":410,"props":4725,"children":4726},{},[4727],{"type":40,"value":4728},"Après 6 mois",{"type":31,"tag":426,"props":4730,"children":4731},{},[4732,4750,4768,4785,4801],{"type":31,"tag":406,"props":4733,"children":4734},{},[4735,4740,4745],{"type":31,"tag":433,"props":4736,"children":4737},{},[4738],{"type":40,"value":4739},"LOC moyennes par use case",{"type":31,"tag":433,"props":4741,"children":4742},{},[4743],{"type":40,"value":4744},"280",{"type":31,"tag":433,"props":4746,"children":4747},{},[4748],{"type":40,"value":4749},"110",{"type":31,"tag":406,"props":4751,"children":4752},{},[4753,4758,4763],{"type":31,"tag":433,"props":4754,"children":4755},{},[4756],{"type":40,"value":4757},"Fonctions dupliquées dans le repo",{"type":31,"tag":433,"props":4759,"children":4760},{},[4761],{"type":40,"value":4762},"47",{"type":31,"tag":433,"props":4764,"children":4765},{},[4766],{"type":40,"value":4767},"8",{"type":31,"tag":406,"props":4769,"children":4770},{},[4771,4776,4780],{"type":31,"tag":433,"props":4772,"children":4773},{},[4774],{"type":40,"value":4775},"Temps d'onboarding d'un nouveau dev",{"type":31,"tag":433,"props":4777,"children":4778},{},[4779],{"type":40,"value":4568},{"type":31,"tag":433,"props":4781,"children":4782},{},[4783],{"type":40,"value":4784},"4 semaines",{"type":31,"tag":406,"props":4786,"children":4787},{},[4788,4793,4797],{"type":31,"tag":433,"props":4789,"children":4790},{},[4791],{"type":40,"value":4792},"Bugs de régression par release",{"type":31,"tag":433,"props":4794,"children":4795},{},[4796],{"type":40,"value":4586},{"type":31,"tag":433,"props":4798,"children":4799},{},[4800],{"type":40,"value":4591},{"type":31,"tag":406,"props":4802,"children":4803},{},[4804,4809,4814],{"type":31,"tag":433,"props":4805,"children":4806},{},[4807],{"type":40,"value":4808},"Vélocité (story points\u002Fsprint)",{"type":31,"tag":433,"props":4810,"children":4811},{},[4812],{"type":40,"value":4813},"stable",{"type":31,"tag":433,"props":4815,"children":4816},{},[4817],{"type":40,"value":4818},"+28%",{"type":31,"tag":32,"props":4820,"children":4821},{},[4822,4824,4828,4830,4835,4837,4841],{"type":40,"value":4823},"Le gain n'est pas qu'esthétique. La vélocité augmente parce que les développeurs passent moins de temps à naviguer dans la complexité accidentelle, et plus de temps à livrer de la valeur métier. C'est ce que la DORA mesure quand elle parle de ",{"type":31,"tag":97,"props":4825,"children":4826},{},[4827],{"type":40,"value":1918},{"type":40,"value":4829}," et de ",{"type":31,"tag":97,"props":4831,"children":4832},{},[4833],{"type":40,"value":4834},"stability",{"type":40,"value":4836}," combinés, et c'est précisément le levier qu'une ",{"type":31,"tag":69,"props":4838,"children":4839},{"href":148},[4840],{"type":40,"value":151},{"type":40,"value":4842}," permet d'enclencher.",{"type":31,"tag":48,"props":4844,"children":4845},{},[],{"type":31,"tag":52,"props":4847,"children":4848},{"id":637},[4849],{"type":40,"value":640},{"type":31,"tag":32,"props":4851,"children":4852},{},[4853],{"type":40,"value":4854},"Ce que je veux que vous reteniez de cet article, c'est que le code Claude \"qui marche\" n'est pas une garantie de qualité, c'est une garantie de fonctionnement à T0. La qualité d'un code se mesure à 3 mois, à 6 mois, à 12 mois, sur le coût de modification et le temps d'onboarding. Les 3 faux amis que j'ai décrits (copier-coller, abstractions accidentelles, sur-ingénierie) sont les 3 mécanismes qui font diverger ce coût silencieusement.",{"type":31,"tag":32,"props":4856,"children":4857},{},[4858],{"type":40,"value":4859},"La règle YAGNI + AI, ce n'est pas du conservatisme. C'est une discipline économique. Vous économisez aujourd'hui sur du code que vous n'écrivez pas, ce qui réduit la complexité que vous porterez demain. Quand le besoin réel émerge (3 implémentations, vraie variation, vraie criticité), vous créez l'abstraction à ce moment-là, avec la connaissance que la pratique vous a donnée. Pas avant.",{"type":31,"tag":32,"props":4861,"children":4862},{},[4863],{"type":40,"value":4864},"Si en lisant ces lignes vous reconnaissez votre situation, vous avez deux choix. Vous pouvez continuer à accepter les PR Claude qui ajoutent 280 LOC pour un use case qui en demande 95. Ou vous pouvez commencer lundi matin, par une grille de 4 règles, et reprendre le contrôle du coût caché de votre codebase.",{"type":31,"tag":32,"props":4866,"children":4867},{},[4868,4870,4875],{"type":40,"value":4869},"Pour la suite des patterns craft que je documente chaque semaine sur crmcoaching, retrouvez-moi sur ",{"type":31,"tag":69,"props":4871,"children":4873},{"href":663,"rel":4872},[665],[4874],{"type":40,"value":668},{"type":40,"value":76},{"type":31,"tag":229,"props":4877,"children":4879},{"cta":673,"href":674,"title":4878,"type":676},"La règle des 3 et le réflexe anti-doublons ne sont que 2 pratiques sur 100",[4880],{"type":31,"tag":32,"props":4881,"children":4882},{},[4883],{"type":40,"value":4884},"Cet article détaille une poignée de pratiques craft qui neutralisent la sur-ingénierie IA-générée. Le Craft Bundle réunit les 100 pratiques que j'applique pour garder un code simple et modifiable : le référentiel complet de ce que je vérifie sur chaque PR Claude. Ce sont celles que l'IA ne vous apprendra jamais, parce qu'elle ne les a jamais vues coûter cher en production.",{"type":31,"tag":48,"props":4886,"children":4887},{},[],{"type":31,"tag":52,"props":4889,"children":4891},{"id":4890},"faq-sur-les-faux-amis-claude-et-le-coût-caché",[4892],{"type":40,"value":4893},"FAQ sur les faux amis Claude et le coût caché",{"type":31,"tag":693,"props":4895,"children":4896},{},[4897,4902],{"type":31,"tag":697,"props":4898,"children":4899},{},[4900],{"type":40,"value":4901},"1. Comment détecter les doublons dans un repo TypeScript de taille moyenne ?",{"type":31,"tag":32,"props":4903,"children":4904},{},[4905],{"type":40,"value":4906},"Trois outils combinés font le travail. D'abord jscpd (Copy-Paste Detector, compatible TS\u002FJS) : sortie rapide des blocs dupliqués sur tout repo, configurable par seuil de tokens. Ensuite SonarQube en mode \"duplications\" : vue agrégée sur le repo entier. Enfin, des passes Grep ciblées sur les noms de domaine (\"calculate\", \"compute\", \"process\") pour repérer les paires sémantiquement proches mais nommées différemment. L'arsenal complet est détaillé dans les outils d'analyse statique en 2026.",{"type":31,"tag":693,"props":4908,"children":4909},{},[4910,4915],{"type":31,"tag":697,"props":4911,"children":4912},{},[4913],{"type":40,"value":4914},"2. La règle des 3 implémentations n'empêche-t-elle pas l'architecture hexagonale ?",{"type":31,"tag":32,"props":4916,"children":4917},{},[4918,4920,4926],{"type":40,"value":4919},"Non, parce que ",{"type":31,"tag":69,"props":4921,"children":4923},{"href":4922},"\u002Ffr\u002Farchitecture-craft\u002Farchitecture-hexagonale-java-exemples-bonnes-pratiques",[4924],{"type":40,"value":4925},"l'architecture hexagonale",{"type":40,"value":4927}," est justifiée par la testabilité, pas par la variation d'implémentation. Vous créez un port pour pouvoir tester avec un fake en mémoire. C'est 1 implémentation réelle + 1 fake de test : ça compte pour 2. Si en plus vous avez une vraie variation (Postgres + Redis, ou SMTP + Brevo), vous êtes à 3. La règle s'applique aux abstractions purement spéculatives, pas à celles qui ont une justification claire et nommée.",{"type":31,"tag":693,"props":4929,"children":4930},{},[4931,4936],{"type":31,"tag":697,"props":4932,"children":4933},{},[4934],{"type":40,"value":4935},"3. Comment convaincre une équipe attachée aux patterns enterprise ?",{"type":31,"tag":32,"props":4937,"children":4938},{},[4939],{"type":40,"value":4940},"Avec des chiffres sur leur propre repo. Comptez les interfaces qui n'ont qu'une implémentation, comptez les factories appelées une seule fois, comptez les LOC par use case. Présentez ça en revue d'architecture. Personne ne défend une factory inutilisée quand elle apparaît dans un tableau à côté de \"temps d'onboarding +4 semaines\". Le débat passe du dogme à l'économique.",{"type":31,"tag":693,"props":4942,"children":4943},{},[4944,4949],{"type":31,"tag":697,"props":4945,"children":4946},{},[4947],{"type":40,"value":4948},"4. Quel rapport entre ces faux amis et la code review IA ?",{"type":31,"tag":32,"props":4950,"children":4951},{},[4952],{"type":40,"value":4953},"Direct. Une revue de code IA-générée doit poser systématiquement 3 questions : \"Y a-t-il un doublon ailleurs dans le repo ?\", \"Cette abstraction est-elle justifiée par 3 cas réels ?\", \"La complexité de cette solution est-elle proportionnelle au besoin métier ?\". Ces 3 questions, en 2 minutes par PR, attrapent 80% des 3 faux amis avant le merge.",{"type":31,"tag":693,"props":4955,"children":4956},{},[4957,4962],{"type":31,"tag":697,"props":4958,"children":4959},{},[4960],{"type":40,"value":4961},"5. Le code Claude qui suit cette discipline est-il toujours du code \"généré par IA\" ?",{"type":31,"tag":32,"props":4963,"children":4964},{},[4965],{"type":40,"value":4966},"C'est une bonne question, et la réponse est : oui, mais pas par n'importe quel prompt. Le code Claude bien promté (avec contraintes YAGNI + recherche de doublons + proportionnalité) reste majoritairement du code IA-généré. Le développeur ajoute le contexte et la discipline. Claude exécute. C'est exactement la collaboration qu'on cherche : l'IA fait le travail mécanique, l'humain garde le jugement architectural.",{"type":31,"tag":693,"props":4968,"children":4969},{},[4970,4975],{"type":31,"tag":697,"props":4971,"children":4972},{},[4973],{"type":40,"value":4974},"6. Quelle est la métrique unique à suivre pour mesurer ces 3 faux amis ?",{"type":31,"tag":32,"props":4976,"children":4977},{},[4978,4980,4985],{"type":40,"value":4979},"Le ratio LOC par story point livré, mesuré sur 3 sprints glissants. Si la métrique monte, vous accumulez de la complexité accidentelle. Si elle baisse à valeur métier équivalente, vous gagnez en simplicité. C'est la métrique méta qui agrège les 3 faux amis sans avoir à les détecter individuellement, et c'est aussi un sujet que je détaille dans ",{"type":31,"tag":69,"props":4981,"children":4982},{"href":289},[4983],{"type":40,"value":4984},"l'illusion de productivité IA",{"type":40,"value":76},{"type":31,"tag":48,"props":4987,"children":4988},{},[],{"type":31,"tag":229,"props":4990,"children":4991},{"cta":806,"href":807,"title":808,"type":809},[4992],{"type":31,"tag":32,"props":4993,"children":4994},{},[4995],{"type":40,"value":4996},"L'EMA est l'outil que je propose au début de chaque mission. Il mesure la maturité de votre équipe sur plusieurs axes engineering : qualité du code, gouvernance IA, dette technique, vélocité réelle vs vélocité perçue. Quelques minutes pour identifier lequel des 3 faux amis s'installe le plus vite dans votre repo, et où concentrer vos efforts en priorité.",{"type":31,"tag":1837,"props":4998,"children":4999},{},[5000],{"type":40,"value":1841},{"title":8,"searchDepth":817,"depth":817,"links":5002},[5003,5004,5005,5006,5007,5008,5009,5010,5011],{"id":2799,"depth":817,"text":2802},{"id":2880,"depth":817,"text":2883},{"id":3424,"depth":817,"text":3427},{"id":3922,"depth":817,"text":3925},{"id":4485,"depth":817,"text":4488},{"id":4633,"depth":817,"text":4636},{"id":1565,"depth":817,"text":1568},{"id":637,"depth":817,"text":640},{"id":4890,"depth":817,"text":4893},"content:fr:intelligence-artificielle:faux-ami-code-claude-coute-cher.md","fr\u002Fintelligence-artificielle\u002Ffaux-ami-code-claude-coute-cher.md","fr\u002Fintelligence-artificielle\u002Ffaux-ami-code-claude-coute-cher",{"_path":211,"_dir":6,"_draft":7,"_partial":7,"_locale":8,"title":5016,"description":5017,"id":5018,"date":5019,"listed":13,"nocomments":7,"hidden":13,"categories":5020,"tags":5021,"cover":5025,"readingTime":5026,"body":5030,"_type":830,"_id":10171,"_source":832,"_file":10172,"_stem":10173,"_extension":835},"Tous les tests sont verts, et 3 jours plus tard ça plante en prod","Pourquoi les tests Claude couvrent le happy path mais ratent les pannes réelles. Les 4 tests manquants et le wrapper anti-fragile à exiger.",64,"2026-05-10",[6],[5022,5023,16,5024,19],"tests","resilience","ci-cd","covers\u002Farticles\u002Fcode-claude-tests-passent-plante-prod.jpg",{"text":2775,"minutes":5027,"time":5028,"words":5029},13.74,824400,2748,{"type":28,"children":5031,"toc":10156},[5032,5040,5045,5048,5054,5073,5086,5114,5132,5135,5141,5146,5674,5702,5730,5735,5747,5750,5759,5762,5768,5773,5780,5800,6528,6545,6551,6556,7151,7156,7162,7167,7706,7711,7717,7722,8211,8216,8219,8225,8237,8260,8265,9737,9756,9759,9765,9777,9794,9812,9822,9832,9835,9839,9844,9942,9954,9957,9961,9966,9971,9976,9988,9997,10000,10006,10019,10032,10083,10096,10114,10141,10144,10152],{"type":31,"tag":32,"props":5033,"children":5034},{},[5035],{"type":31,"tag":36,"props":5036,"children":5037},{},[5038],{"type":40,"value":5039},"Un mardi matin de janvier 2026, je reçois une alerte sur crmcoaching, mon SaaS pour coachs professionnels que je développe seul avec Claude. La notification de confirmation de séance avait planté toute la nuit. La CI était verte. Les tests passaient. Je ne comprenais pas. Trois heures plus tard, j'identifie le problème : plusieurs confirmations envoyées en parallèle au même moment, Brevo (mon provider d'emails transactionnels) avait appliqué son rate limit avec un 429, et le code que Claude avait généré n'avait jamais été testé sous cette condition.",{"type":31,"tag":32,"props":5041,"children":5042},{},[5043],{"type":40,"value":5044},"Ce n'était pas un bug Claude. C'était un bug de mes tests. Mes tests vérifiaient que la fonction marchait. Pas qu'elle survivait. Voici la différence, et les 4 tests que Claude n'écrit jamais spontanément, que vous devez exiger explicitement.",{"type":31,"tag":48,"props":5046,"children":5047},{},[],{"type":31,"tag":52,"props":5049,"children":5051},{"id":5050},"pourquoi-les-tests-claude-ne-couvrent-que-le-happy-path",[5052],{"type":40,"value":5053},"Pourquoi les tests Claude ne couvrent que le happy path",{"type":31,"tag":32,"props":5055,"children":5056},{},[5057,5059,5064,5066,5071],{"type":40,"value":5058},"Le constat est simple, et il est documenté. Selon le ",{"type":31,"tag":69,"props":5060,"children":5062},{"href":2810,"rel":5061},[665],[5063],{"type":40,"value":2814},{"type":40,"value":5065},", les équipes IA-heavy livrent en moyenne 35% plus de code. Mais le ratio de tests behavior-level reste stable, voire baisse. Le code grossit, le filet de tests ne suit pas. Ce déséquilibre est précisément ce que je détaille dans ",{"type":31,"tag":69,"props":5067,"children":5068},{"href":219},[5069],{"type":40,"value":5070},"la checklist pour tester du code généré par IA",{"type":40,"value":5072}," publiée en 2025.",{"type":31,"tag":32,"props":5074,"children":5075},{},[5076,5078,5084],{"type":40,"value":5077},"Pourquoi ? Parce que Claude génère des tests qui imitent le pattern moyen de son training data. Et ce pattern, c'est : un test par fonction, un mock par dépendance, une assertion sur le happy path. Si vous lui demandez \"écris des tests pour ",{"type":31,"tag":169,"props":5079,"children":5081},{"className":5080},[],[5082],{"type":40,"value":5083},"sendConfirmationEmail",{"type":40,"value":5085},"\", il vous livre un test qui vérifie que la méthode envoie un email quand tout va bien. Il ne vous livre pas le test où Brevo renvoie un 429. Il ne vous livre pas le test où la méthode est appelée 50 fois en parallèle. Il ne vous livre pas le test où le réseau timeout après 30 secondes.",{"type":31,"tag":78,"props":5087,"children":5088},{},[5089],{"type":31,"tag":32,"props":5090,"children":5091},{},[5092,5096,5098,5104,5106,5112],{"type":31,"tag":36,"props":5093,"children":5094},{},[5095],{"type":40,"value":1950},{"type":40,"value":5097}," : j'ai compté les tests présents dans mon module ",{"type":31,"tag":169,"props":5099,"children":5101},{"className":5100},[],[5102],{"type":40,"value":5103},"notification",{"type":40,"value":5105},". Des dizaines de tests, une couverture de lignes correcte. Aucun ne simulait une réponse 429 de Brevo. Aucun ne simulait un timeout. Aucun ne lançait plus de 5 appels en parallèle via ",{"type":31,"tag":169,"props":5107,"children":5109},{"className":5108},[],[5110],{"type":40,"value":5111},"Promise.all",{"type":40,"value":5113},". Le filet de tests avait l'air dense, mais il était plein de trous sur les conditions réelles de production.",{"type":31,"tag":32,"props":5115,"children":5116},{},[5117,5119,5123,5125,5130],{"type":40,"value":5118},"Michael Nygard a écrit dans ",{"type":31,"tag":97,"props":5120,"children":5121},{},[5122],{"type":40,"value":790},{"type":40,"value":5124}," (2007, deuxième édition 2018) : ",{"type":31,"tag":97,"props":5126,"children":5127},{},[5128],{"type":40,"value":5129},"\"Every feature in production must be hardened by tests of failure modes, not just success modes.\"",{"type":40,"value":5131}," En 2026, Claude accélère la production de features, mais il n'accélère pas la production de tests de modes d'échec. Le ratio se déséquilibre, et la prod paie. C'est aussi la racine de la dérive que je décris dans le diagnostic du legacy à risque que personne n'avait vu venir.",{"type":31,"tag":48,"props":5133,"children":5134},{},[],{"type":31,"tag":52,"props":5136,"children":5138},{"id":5137},"le-scénario-du-crash-brevo-rate-limit-sur-crmcoaching",[5139],{"type":40,"value":5140},"Le scénario du crash Brevo rate-limit sur crmcoaching",{"type":31,"tag":32,"props":5142,"children":5143},{},[5144],{"type":40,"value":5145},"Reprenons le code que Claude avait généré pour l'envoi des confirmations de séance. Voici la version simplifiée, en TypeScript NestJS :",{"type":31,"tag":1206,"props":5147,"children":5149},{"className":2912,"code":5148,"language":2914,"meta":8,"style":8},"\u002F\u002F apps\u002Fapi\u002Fsrc\u002Finfrastructure\u002Fnotification\u002Fbrevo-notification.service.ts\n\n@Injectable()\nexport class BrevoNotificationService {\n  constructor(\n    private readonly sessionRepository: SessionRepository,\n    private readonly brevoClient: BrevoClient,\n  ) {}\n\n  async sendSessionConfirmation(sessionId: string): Promise\u003Cvoid> {\n    const session = await this.sessionRepository.findById(sessionId);\n    if (!session) throw new Error(`Session not found: ${sessionId}`);\n\n    await this.brevoClient.sendTransactionalEmail({\n      to: [{ email: session.coacheeEmail }],\n      templateId: BREVO_TEMPLATE_IDS.SESSION_CONFIRMATION,\n      params: { sessionDate: session.scheduledAt },\n    });\n  }\n}\n",[5150],{"type":31,"tag":169,"props":5151,"children":5152},{"__ignoreMap":8},[5153,5161,5168,5183,5203,5214,5243,5272,5283,5290,5348,5398,5469,5476,5513,5570,5600,5644,5660,5667],{"type":31,"tag":1215,"props":5154,"children":5155},{"class":1217,"line":1218},[5156],{"type":31,"tag":1215,"props":5157,"children":5158},{"style":2924},[5159],{"type":40,"value":5160},"\u002F\u002F apps\u002Fapi\u002Fsrc\u002Finfrastructure\u002Fnotification\u002Fbrevo-notification.service.ts\n",{"type":31,"tag":1215,"props":5162,"children":5163},{"class":1217,"line":817},[5164],{"type":31,"tag":1215,"props":5165,"children":5166},{"emptyLinePlaceholder":13},[5167],{"type":40,"value":1233},{"type":31,"tag":1215,"props":5169,"children":5170},{"class":1217,"line":1236},[5171,5175,5179],{"type":31,"tag":1215,"props":5172,"children":5173},{"style":3541},[5174],{"type":40,"value":3544},{"type":31,"tag":1215,"props":5176,"children":5177},{"style":2939},[5178],{"type":40,"value":3549},{"type":31,"tag":1215,"props":5180,"children":5181},{"style":3552},[5182],{"type":40,"value":3555},{"type":31,"tag":1215,"props":5184,"children":5185},{"class":1217,"line":1246},[5186,5190,5194,5199],{"type":31,"tag":1215,"props":5187,"children":5188},{"style":2933},[5189],{"type":40,"value":3455},{"type":31,"tag":1215,"props":5191,"children":5192},{"style":2933},[5193],{"type":40,"value":3567},{"type":31,"tag":1215,"props":5195,"children":5196},{"style":3463},[5197],{"type":40,"value":5198}," BrevoNotificationService",{"type":31,"tag":1215,"props":5200,"children":5201},{"style":2945},[5202],{"type":40,"value":3015},{"type":31,"tag":1215,"props":5204,"children":5205},{"class":1217,"line":1256},[5206,5210],{"type":31,"tag":1215,"props":5207,"children":5208},{"style":2933},[5209],{"type":40,"value":3988},{"type":31,"tag":1215,"props":5211,"children":5212},{"style":2945},[5213],{"type":40,"value":2948},{"type":31,"tag":1215,"props":5215,"children":5216},{"class":1217,"line":1264},[5217,5221,5225,5230,5234,5239],{"type":31,"tag":1215,"props":5218,"children":5219},{"style":2933},[5220],{"type":40,"value":4000},{"type":31,"tag":1215,"props":5222,"children":5223},{"style":2933},[5224],{"type":40,"value":4005},{"type":31,"tag":1215,"props":5226,"children":5227},{"style":2954},[5228],{"type":40,"value":5229}," sessionRepository",{"type":31,"tag":1215,"props":5231,"children":5232},{"style":2960},[5233],{"type":40,"value":2963},{"type":31,"tag":1215,"props":5235,"children":5236},{"style":3463},[5237],{"type":40,"value":5238}," SessionRepository",{"type":31,"tag":1215,"props":5240,"children":5241},{"style":2945},[5242],{"type":40,"value":2974},{"type":31,"tag":1215,"props":5244,"children":5245},{"class":1217,"line":1273},[5246,5250,5254,5259,5263,5268],{"type":31,"tag":1215,"props":5247,"children":5248},{"style":2933},[5249],{"type":40,"value":4000},{"type":31,"tag":1215,"props":5251,"children":5252},{"style":2933},[5253],{"type":40,"value":4005},{"type":31,"tag":1215,"props":5255,"children":5256},{"style":2954},[5257],{"type":40,"value":5258}," brevoClient",{"type":31,"tag":1215,"props":5260,"children":5261},{"style":2960},[5262],{"type":40,"value":2963},{"type":31,"tag":1215,"props":5264,"children":5265},{"style":3463},[5266],{"type":40,"value":5267}," BrevoClient",{"type":31,"tag":1215,"props":5269,"children":5270},{"style":2945},[5271],{"type":40,"value":2974},{"type":31,"tag":1215,"props":5273,"children":5274},{"class":1217,"line":1282},[5275,5279],{"type":31,"tag":1215,"props":5276,"children":5277},{"style":2945},[5278],{"type":40,"value":4089},{"type":31,"tag":1215,"props":5280,"children":5281},{"style":2945},[5282],{"type":40,"value":4094},{"type":31,"tag":1215,"props":5284,"children":5285},{"class":1217,"line":1291},[5286],{"type":31,"tag":1215,"props":5287,"children":5288},{"emptyLinePlaceholder":13},[5289],{"type":40,"value":1233},{"type":31,"tag":1215,"props":5291,"children":5292},{"class":1217,"line":1300},[5293,5297,5302,5306,5311,5315,5319,5323,5327,5331,5335,5340,5344],{"type":31,"tag":1215,"props":5294,"children":5295},{"style":2933},[5296],{"type":40,"value":4109},{"type":31,"tag":1215,"props":5298,"children":5299},{"style":2939},[5300],{"type":40,"value":5301}," sendSessionConfirmation",{"type":31,"tag":1215,"props":5303,"children":5304},{"style":2945},[5305],{"type":40,"value":3483},{"type":31,"tag":1215,"props":5307,"children":5308},{"style":2954},[5309],{"type":40,"value":5310},"sessionId",{"type":31,"tag":1215,"props":5312,"children":5313},{"style":2960},[5314],{"type":40,"value":2963},{"type":31,"tag":1215,"props":5316,"children":5317},{"style":2966},[5318],{"type":40,"value":3783},{"type":31,"tag":1215,"props":5320,"children":5321},{"style":2945},[5322],{"type":40,"value":3002},{"type":31,"tag":1215,"props":5324,"children":5325},{"style":2960},[5326],{"type":40,"value":2963},{"type":31,"tag":1215,"props":5328,"children":5329},{"style":3463},[5330],{"type":40,"value":4145},{"type":31,"tag":1215,"props":5332,"children":5333},{"style":4148},[5334],{"type":40,"value":4151},{"type":31,"tag":1215,"props":5336,"children":5337},{"style":2966},[5338],{"type":40,"value":5339},"void",{"type":31,"tag":1215,"props":5341,"children":5342},{"style":4148},[5343],{"type":40,"value":4161},{"type":31,"tag":1215,"props":5345,"children":5346},{"style":2945},[5347],{"type":40,"value":3015},{"type":31,"tag":1215,"props":5349,"children":5350},{"class":1217,"line":1309},[5351,5355,5360,5364,5368,5372,5376,5381,5385,5389,5394],{"type":31,"tag":1215,"props":5352,"children":5353},{"style":2933},[5354],{"type":40,"value":4173},{"type":31,"tag":1215,"props":5356,"children":5357},{"style":3026},[5358],{"type":40,"value":5359}," session",{"type":31,"tag":1215,"props":5361,"children":5362},{"style":2960},[5363],{"type":40,"value":3034},{"type":31,"tag":1215,"props":5365,"children":5366},{"style":2933},[5367],{"type":40,"value":4187},{"type":31,"tag":1215,"props":5369,"children":5370},{"style":4190},[5371],{"type":40,"value":4193},{"type":31,"tag":1215,"props":5373,"children":5374},{"style":3113},[5375],{"type":40,"value":76},{"type":31,"tag":1215,"props":5377,"children":5378},{"style":1250},[5379],{"type":40,"value":5380},"sessionRepository",{"type":31,"tag":1215,"props":5382,"children":5383},{"style":3113},[5384],{"type":40,"value":76},{"type":31,"tag":1215,"props":5386,"children":5387},{"style":2939},[5388],{"type":40,"value":4211},{"type":31,"tag":1215,"props":5390,"children":5391},{"style":1250},[5392],{"type":40,"value":5393},"(sessionId)",{"type":31,"tag":1215,"props":5395,"children":5396},{"style":2945},[5397],{"type":40,"value":3055},{"type":31,"tag":1215,"props":5399,"children":5400},{"class":1217,"line":1317},[5401,5405,5409,5413,5418,5423,5427,5432,5436,5441,5447,5451,5456,5461,5465],{"type":31,"tag":1215,"props":5402,"children":5403},{"style":2933},[5404],{"type":40,"value":4237},{"type":31,"tag":1215,"props":5406,"children":5407},{"style":1250},[5408],{"type":40,"value":4242},{"type":31,"tag":1215,"props":5410,"children":5411},{"style":2960},[5412],{"type":40,"value":4247},{"type":31,"tag":1215,"props":5414,"children":5415},{"style":1250},[5416],{"type":40,"value":5417},"session) ",{"type":31,"tag":1215,"props":5419,"children":5420},{"style":2933},[5421],{"type":40,"value":5422},"throw",{"type":31,"tag":1215,"props":5424,"children":5425},{"style":3809},[5426],{"type":40,"value":3812},{"type":31,"tag":1215,"props":5428,"children":5429},{"style":2939},[5430],{"type":40,"value":5431}," Error",{"type":31,"tag":1215,"props":5433,"children":5434},{"style":1250},[5435],{"type":40,"value":3483},{"type":31,"tag":1215,"props":5437,"children":5438},{"style":4392},[5439],{"type":40,"value":5440},"`Session not found: ",{"type":31,"tag":1215,"props":5442,"children":5444},{"style":5443},"--shiki-default:#949CBB;--shiki-dark:#9ECBFF",[5445],{"type":40,"value":5446},"${",{"type":31,"tag":1215,"props":5448,"children":5449},{"style":1250},[5450],{"type":40,"value":5310},{"type":31,"tag":1215,"props":5452,"children":5453},{"style":5443},[5454],{"type":40,"value":5455},"}",{"type":31,"tag":1215,"props":5457,"children":5458},{"style":4392},[5459],{"type":40,"value":5460},"`",{"type":31,"tag":1215,"props":5462,"children":5463},{"style":1250},[5464],{"type":40,"value":3002},{"type":31,"tag":1215,"props":5466,"children":5467},{"style":2945},[5468],{"type":40,"value":3055},{"type":31,"tag":1215,"props":5470,"children":5471},{"class":1217,"line":1326},[5472],{"type":31,"tag":1215,"props":5473,"children":5474},{"emptyLinePlaceholder":13},[5475],{"type":40,"value":1233},{"type":31,"tag":1215,"props":5477,"children":5478},{"class":1217,"line":1341},[5479,5483,5487,5491,5496,5500,5505,5509],{"type":31,"tag":1215,"props":5480,"children":5481},{"style":2933},[5482],{"type":40,"value":4306},{"type":31,"tag":1215,"props":5484,"children":5485},{"style":4190},[5486],{"type":40,"value":4193},{"type":31,"tag":1215,"props":5488,"children":5489},{"style":3113},[5490],{"type":40,"value":76},{"type":31,"tag":1215,"props":5492,"children":5493},{"style":1250},[5494],{"type":40,"value":5495},"brevoClient",{"type":31,"tag":1215,"props":5497,"children":5498},{"style":3113},[5499],{"type":40,"value":76},{"type":31,"tag":1215,"props":5501,"children":5502},{"style":2939},[5503],{"type":40,"value":5504},"sendTransactionalEmail",{"type":31,"tag":1215,"props":5506,"children":5507},{"style":1250},[5508],{"type":40,"value":3483},{"type":31,"tag":1215,"props":5510,"children":5511},{"style":2945},[5512],{"type":40,"value":4257},{"type":31,"tag":1215,"props":5514,"children":5515},{"class":1217,"line":1355},[5516,5521,5525,5530,5535,5540,5544,5548,5552,5557,5561,5566],{"type":31,"tag":1215,"props":5517,"children":5518},{"style":1250},[5519],{"type":40,"value":5520},"      to",{"type":31,"tag":1215,"props":5522,"children":5523},{"style":3113},[5524],{"type":40,"value":2963},{"type":31,"tag":1215,"props":5526,"children":5527},{"style":1250},[5528],{"type":40,"value":5529}," [",{"type":31,"tag":1215,"props":5531,"children":5532},{"style":2945},[5533],{"type":40,"value":5534},"{",{"type":31,"tag":1215,"props":5536,"children":5537},{"style":1250},[5538],{"type":40,"value":5539}," email",{"type":31,"tag":1215,"props":5541,"children":5542},{"style":3113},[5543],{"type":40,"value":2963},{"type":31,"tag":1215,"props":5545,"children":5546},{"style":1250},[5547],{"type":40,"value":5359},{"type":31,"tag":1215,"props":5549,"children":5550},{"style":3113},[5551],{"type":40,"value":76},{"type":31,"tag":1215,"props":5553,"children":5554},{"style":1250},[5555],{"type":40,"value":5556},"coacheeEmail ",{"type":31,"tag":1215,"props":5558,"children":5559},{"style":2945},[5560],{"type":40,"value":5455},{"type":31,"tag":1215,"props":5562,"children":5563},{"style":1250},[5564],{"type":40,"value":5565},"]",{"type":31,"tag":1215,"props":5567,"children":5568},{"style":2945},[5569],{"type":40,"value":2974},{"type":31,"tag":1215,"props":5571,"children":5572},{"class":1217,"line":1364},[5573,5578,5582,5587,5591,5596],{"type":31,"tag":1215,"props":5574,"children":5575},{"style":1250},[5576],{"type":40,"value":5577},"      templateId",{"type":31,"tag":1215,"props":5579,"children":5580},{"style":3113},[5581],{"type":40,"value":2963},{"type":31,"tag":1215,"props":5583,"children":5584},{"style":3047},[5585],{"type":40,"value":5586}," BREVO_TEMPLATE_IDS",{"type":31,"tag":1215,"props":5588,"children":5589},{"style":3113},[5590],{"type":40,"value":76},{"type":31,"tag":1215,"props":5592,"children":5593},{"style":3047},[5594],{"type":40,"value":5595},"SESSION_CONFIRMATION",{"type":31,"tag":1215,"props":5597,"children":5598},{"style":2945},[5599],{"type":40,"value":2974},{"type":31,"tag":1215,"props":5601,"children":5602},{"class":1217,"line":1378},[5603,5608,5612,5617,5622,5626,5630,5634,5639],{"type":31,"tag":1215,"props":5604,"children":5605},{"style":1250},[5606],{"type":40,"value":5607},"      params",{"type":31,"tag":1215,"props":5609,"children":5610},{"style":3113},[5611],{"type":40,"value":2963},{"type":31,"tag":1215,"props":5613,"children":5614},{"style":2945},[5615],{"type":40,"value":5616}," {",{"type":31,"tag":1215,"props":5618,"children":5619},{"style":1250},[5620],{"type":40,"value":5621}," sessionDate",{"type":31,"tag":1215,"props":5623,"children":5624},{"style":3113},[5625],{"type":40,"value":2963},{"type":31,"tag":1215,"props":5627,"children":5628},{"style":1250},[5629],{"type":40,"value":5359},{"type":31,"tag":1215,"props":5631,"children":5632},{"style":3113},[5633],{"type":40,"value":76},{"type":31,"tag":1215,"props":5635,"children":5636},{"style":1250},[5637],{"type":40,"value":5638},"scheduledAt ",{"type":31,"tag":1215,"props":5640,"children":5641},{"style":2945},[5642],{"type":40,"value":5643},"},\n",{"type":31,"tag":1215,"props":5645,"children":5646},{"class":1217,"line":1387},[5647,5652,5656],{"type":31,"tag":1215,"props":5648,"children":5649},{"style":2945},[5650],{"type":40,"value":5651},"    }",{"type":31,"tag":1215,"props":5653,"children":5654},{"style":1250},[5655],{"type":40,"value":3002},{"type":31,"tag":1215,"props":5657,"children":5658},{"style":2945},[5659],{"type":40,"value":3055},{"type":31,"tag":1215,"props":5661,"children":5662},{"class":1217,"line":1401},[5663],{"type":31,"tag":1215,"props":5664,"children":5665},{"style":2945},[5666],{"type":40,"value":3710},{"type":31,"tag":1215,"props":5668,"children":5669},{"class":1217,"line":1410},[5670],{"type":31,"tag":1215,"props":5671,"children":5672},{"style":2945},[5673],{"type":40,"value":3160},{"type":31,"tag":32,"props":5675,"children":5676},{},[5677,5679,5685,5686,5692,5694,5700],{"type":40,"value":5678},"Le code est propre, lisible, testable. Les tests unitaires Vitest mockaient ",{"type":31,"tag":169,"props":5680,"children":5682},{"className":5681},[],[5683],{"type":40,"value":5684},"sessionRepository.findById",{"type":40,"value":2901},{"type":31,"tag":169,"props":5687,"children":5689},{"className":5688},[],[5690],{"type":40,"value":5691},"brevoClient.sendTransactionalEmail",{"type":40,"value":5693}," avec ",{"type":31,"tag":169,"props":5695,"children":5697},{"className":5696},[],[5698],{"type":40,"value":5699},"vi.fn()",{"type":40,"value":5701},". Tous verts. Tous tautologiques.",{"type":31,"tag":32,"props":5703,"children":5704},{},[5705,5707,5713,5715,5720,5722,5728],{"type":40,"value":5706},"Voici ce qui s'est passé en prod. Un mardi à 2h47, mon job de rappel de séances a lancé plusieurs ",{"type":31,"tag":169,"props":5708,"children":5710},{"className":5709},[],[5711],{"type":40,"value":5712},"sendSessionConfirmation",{"type":40,"value":5714}," en parallèle via ",{"type":31,"tag":169,"props":5716,"children":5718},{"className":5717},[],[5719],{"type":40,"value":5111},{"type":40,"value":5721}," pour un groupe de coachees dont les séances étaient le lendemain matin. Brevo, comme tous les providers d'emails transactionnels, a un rate limit. À partir d'un certain nombre d'appels par seconde, il renvoie ",{"type":31,"tag":169,"props":5723,"children":5725},{"className":5724},[],[5726],{"type":40,"value":5727},"429 Too Many Requests",{"type":40,"value":5729},". Mon code ne gérait pas ce 429. L'exception remontait. Le job scheduler marquait les envois en échec. Aucun retry, parce que la stratégie de retry n'avait pas été codée pour ce service.",{"type":31,"tag":32,"props":5731,"children":5732},{},[5733],{"type":40,"value":5734},"Résultat : plusieurs coachees n'avaient pas reçu leur confirmation de séance. Certains ne s'étaient pas présentés le lendemain matin, pensant que la séance avait été annulée. La confiance envers le SaaS en avait pris un coup, et moi j'avais passé la matinée à déboguer au lieu de développer.",{"type":31,"tag":32,"props":5736,"children":5737},{},[5738,5740,5745],{"type":40,"value":5739},"Et tout ça parce qu'aucun test n'avait simulé ces 4 conditions de production. Le pire, c'est que les ",{"type":31,"tag":69,"props":5741,"children":5742},{"href":113},[5743],{"type":40,"value":5744},"principes de clean code et software craftsmanship",{"type":40,"value":5746}," auraient suffi à rendre cette fonction testable sous concurrence, mais je ne les avais pas formalisés dans mes prompts à Claude.",{"type":31,"tag":48,"props":5748,"children":5749},{},[],{"type":31,"tag":229,"props":5751,"children":5753},{"cta":231,"href":232,"title":5752,"type":234},"Vous voulez écrire vous-même les tests que Claude ne génère jamais ?",[5754],{"type":31,"tag":32,"props":5755,"children":5756},{},[5757],{"type":40,"value":5758},"Repérer un mode d'échec absent du filet de tests, ça ne s'apprend pas en lisant un article : ça se travaille. En mentoring 1:1, on prend votre vrai code généré par IA et on écrit ensemble les tests de concurrence, de rate limit et de timeout sur vos fonctions critiques. Vous repartez avec le réflexe d'exiger ces tests avant chaque merge, sans dépendre de personne.",{"type":31,"tag":48,"props":5760,"children":5761},{},[],{"type":31,"tag":52,"props":5763,"children":5765},{"id":5764},"les-4-tests-que-claude-nécrit-jamais",[5766],{"type":40,"value":5767},"Les 4 tests que Claude n'écrit jamais",{"type":31,"tag":32,"props":5769,"children":5770},{},[5771],{"type":40,"value":5772},"Voici les 4 tests que j'exige maintenant sur tout code Claude qui touche à une dépendance externe (réseau, DB, queue, file system). Les exemples sont en Vitest\u002FTypeScript, dans le contexte crmcoaching.",{"type":31,"tag":5774,"props":5775,"children":5777},"h3",{"id":5776},"test-1-le-test-de-concurrence",[5778],{"type":40,"value":5779},"Test 1 : le test de concurrence",{"type":31,"tag":32,"props":5781,"children":5782},{},[5783,5785,5790,5792,5798],{"type":40,"value":5784},"Soumettez la fonction en parallèle via ",{"type":31,"tag":169,"props":5786,"children":5788},{"className":5787},[],[5789],{"type":40,"value":5111},{"type":40,"value":5791}," ou ",{"type":31,"tag":169,"props":5793,"children":5795},{"className":5794},[],[5796],{"type":40,"value":5797},"Promise.allSettled",{"type":40,"value":5799}," sur la même ressource ou avec des paramètres qui peuvent se télescoper. Vérifiez que l'état final est cohérent.",{"type":31,"tag":1206,"props":5801,"children":5803},{"className":2912,"code":5802,"language":2914,"meta":8,"style":8},"\u002F\u002F apps\u002Fapi\u002Fsrc\u002Finfrastructure\u002Fnotification\u002Fbrevo-notification.service.spec.ts\n\nit('handles 50 concurrent session confirmations without losing any', async () => {\n  const sessionIds = Array.from({ length: 50 }, (_, i) => `session-${i}`);\n\n  \u002F\u002F mock : toutes les sessions existent, Brevo répond OK\n  vi.mocked(sessionRepository.findById).mockImplementation(async (id) => ({\n    id,\n    coacheeEmail: `coach-${id}@example.com`,\n    scheduledAt: new Date('2026-06-10T09:00:00Z'),\n  }));\n  vi.mocked(brevoClient.sendTransactionalEmail).mockResolvedValue(undefined);\n\n  const results = await Promise.allSettled(\n    sessionIds.map((id) => service.sendSessionConfirmation(id)),\n  );\n\n  const failed = results.filter((r) => r.status === 'rejected');\n  expect(failed).toHaveLength(0);\n  expect(brevoClient.sendTransactionalEmail).toHaveBeenCalledTimes(50);\n});\n",[5804],{"type":31,"tag":169,"props":5805,"children":5806},{"__ignoreMap":8},[5807,5815,5822,5863,5976,5983,5991,6065,6077,6115,6153,6170,6225,6232,6270,6329,6340,6347,6429,6468,6513],{"type":31,"tag":1215,"props":5808,"children":5809},{"class":1217,"line":1218},[5810],{"type":31,"tag":1215,"props":5811,"children":5812},{"style":2924},[5813],{"type":40,"value":5814},"\u002F\u002F apps\u002Fapi\u002Fsrc\u002Finfrastructure\u002Fnotification\u002Fbrevo-notification.service.spec.ts\n",{"type":31,"tag":1215,"props":5816,"children":5817},{"class":1217,"line":817},[5818],{"type":31,"tag":1215,"props":5819,"children":5820},{"emptyLinePlaceholder":13},[5821],{"type":40,"value":1233},{"type":31,"tag":1215,"props":5823,"children":5824},{"class":1217,"line":1236},[5825,5830,5834,5839,5844,5849,5854,5859],{"type":31,"tag":1215,"props":5826,"children":5827},{"style":2939},[5828],{"type":40,"value":5829},"it",{"type":31,"tag":1215,"props":5831,"children":5832},{"style":1250},[5833],{"type":40,"value":3483},{"type":31,"tag":1215,"props":5835,"children":5836},{"style":4392},[5837],{"type":40,"value":5838},"'handles 50 concurrent session confirmations without losing any'",{"type":31,"tag":1215,"props":5840,"children":5841},{"style":2945},[5842],{"type":40,"value":5843},",",{"type":31,"tag":1215,"props":5845,"children":5846},{"style":2933},[5847],{"type":40,"value":5848}," async",{"type":31,"tag":1215,"props":5850,"children":5851},{"style":2945},[5852],{"type":40,"value":5853}," ()",{"type":31,"tag":1215,"props":5855,"children":5856},{"style":2960},[5857],{"type":40,"value":5858}," =>",{"type":31,"tag":1215,"props":5860,"children":5861},{"style":2945},[5862],{"type":40,"value":3015},{"type":31,"tag":1215,"props":5864,"children":5865},{"class":1217,"line":1246},[5866,5870,5875,5879,5884,5888,5893,5897,5901,5906,5910,5915,5920,5924,5929,5933,5938,5942,5946,5951,5955,5960,5964,5968,5972],{"type":31,"tag":1215,"props":5867,"children":5868},{"style":2933},[5869],{"type":40,"value":3023},{"type":31,"tag":1215,"props":5871,"children":5872},{"style":3026},[5873],{"type":40,"value":5874}," sessionIds",{"type":31,"tag":1215,"props":5876,"children":5877},{"style":2960},[5878],{"type":40,"value":3034},{"type":31,"tag":1215,"props":5880,"children":5881},{"style":1250},[5882],{"type":40,"value":5883}," Array",{"type":31,"tag":1215,"props":5885,"children":5886},{"style":3113},[5887],{"type":40,"value":76},{"type":31,"tag":1215,"props":5889,"children":5890},{"style":2939},[5891],{"type":40,"value":5892},"from",{"type":31,"tag":1215,"props":5894,"children":5895},{"style":1250},[5896],{"type":40,"value":3483},{"type":31,"tag":1215,"props":5898,"children":5899},{"style":2945},[5900],{"type":40,"value":5534},{"type":31,"tag":1215,"props":5902,"children":5903},{"style":1250},[5904],{"type":40,"value":5905}," length",{"type":31,"tag":1215,"props":5907,"children":5908},{"style":3113},[5909],{"type":40,"value":2963},{"type":31,"tag":1215,"props":5911,"children":5912},{"style":3047},[5913],{"type":40,"value":5914}," 50",{"type":31,"tag":1215,"props":5916,"children":5917},{"style":2945},[5918],{"type":40,"value":5919}," },",{"type":31,"tag":1215,"props":5921,"children":5922},{"style":2945},[5923],{"type":40,"value":4242},{"type":31,"tag":1215,"props":5925,"children":5926},{"style":2954},[5927],{"type":40,"value":5928},"_",{"type":31,"tag":1215,"props":5930,"children":5931},{"style":2945},[5932],{"type":40,"value":5843},{"type":31,"tag":1215,"props":5934,"children":5935},{"style":2954},[5936],{"type":40,"value":5937}," i",{"type":31,"tag":1215,"props":5939,"children":5940},{"style":2945},[5941],{"type":40,"value":3002},{"type":31,"tag":1215,"props":5943,"children":5944},{"style":2960},[5945],{"type":40,"value":5858},{"type":31,"tag":1215,"props":5947,"children":5948},{"style":4392},[5949],{"type":40,"value":5950}," `session-",{"type":31,"tag":1215,"props":5952,"children":5953},{"style":5443},[5954],{"type":40,"value":5446},{"type":31,"tag":1215,"props":5956,"children":5957},{"style":1250},[5958],{"type":40,"value":5959},"i",{"type":31,"tag":1215,"props":5961,"children":5962},{"style":5443},[5963],{"type":40,"value":5455},{"type":31,"tag":1215,"props":5965,"children":5966},{"style":4392},[5967],{"type":40,"value":5460},{"type":31,"tag":1215,"props":5969,"children":5970},{"style":1250},[5971],{"type":40,"value":3002},{"type":31,"tag":1215,"props":5973,"children":5974},{"style":2945},[5975],{"type":40,"value":3055},{"type":31,"tag":1215,"props":5977,"children":5978},{"class":1217,"line":1256},[5979],{"type":31,"tag":1215,"props":5980,"children":5981},{"emptyLinePlaceholder":13},[5982],{"type":40,"value":1233},{"type":31,"tag":1215,"props":5984,"children":5985},{"class":1217,"line":1264},[5986],{"type":31,"tag":1215,"props":5987,"children":5988},{"style":2924},[5989],{"type":40,"value":5990},"  \u002F\u002F mock : toutes les sessions existent, Brevo répond OK\n",{"type":31,"tag":1215,"props":5992,"children":5993},{"class":1217,"line":1273},[5994,5999,6003,6008,6013,6017,6022,6026,6031,6035,6040,6044,6049,6053,6057,6061],{"type":31,"tag":1215,"props":5995,"children":5996},{"style":1250},[5997],{"type":40,"value":5998},"  vi",{"type":31,"tag":1215,"props":6000,"children":6001},{"style":3113},[6002],{"type":40,"value":76},{"type":31,"tag":1215,"props":6004,"children":6005},{"style":2939},[6006],{"type":40,"value":6007},"mocked",{"type":31,"tag":1215,"props":6009,"children":6010},{"style":1250},[6011],{"type":40,"value":6012},"(sessionRepository",{"type":31,"tag":1215,"props":6014,"children":6015},{"style":3113},[6016],{"type":40,"value":76},{"type":31,"tag":1215,"props":6018,"children":6019},{"style":1250},[6020],{"type":40,"value":6021},"findById)",{"type":31,"tag":1215,"props":6023,"children":6024},{"style":3113},[6025],{"type":40,"value":76},{"type":31,"tag":1215,"props":6027,"children":6028},{"style":2939},[6029],{"type":40,"value":6030},"mockImplementation",{"type":31,"tag":1215,"props":6032,"children":6033},{"style":1250},[6034],{"type":40,"value":3483},{"type":31,"tag":1215,"props":6036,"children":6037},{"style":2933},[6038],{"type":40,"value":6039},"async",{"type":31,"tag":1215,"props":6041,"children":6042},{"style":2945},[6043],{"type":40,"value":4242},{"type":31,"tag":1215,"props":6045,"children":6046},{"style":2954},[6047],{"type":40,"value":6048},"id",{"type":31,"tag":1215,"props":6050,"children":6051},{"style":2945},[6052],{"type":40,"value":3002},{"type":31,"tag":1215,"props":6054,"children":6055},{"style":2960},[6056],{"type":40,"value":5858},{"type":31,"tag":1215,"props":6058,"children":6059},{"style":1250},[6060],{"type":40,"value":4242},{"type":31,"tag":1215,"props":6062,"children":6063},{"style":2945},[6064],{"type":40,"value":4257},{"type":31,"tag":1215,"props":6066,"children":6067},{"class":1217,"line":1282},[6068,6073],{"type":31,"tag":1215,"props":6069,"children":6070},{"style":1250},[6071],{"type":40,"value":6072},"    id",{"type":31,"tag":1215,"props":6074,"children":6075},{"style":2945},[6076],{"type":40,"value":2974},{"type":31,"tag":1215,"props":6078,"children":6079},{"class":1217,"line":1291},[6080,6085,6089,6094,6098,6102,6106,6111],{"type":31,"tag":1215,"props":6081,"children":6082},{"style":1250},[6083],{"type":40,"value":6084},"    coacheeEmail",{"type":31,"tag":1215,"props":6086,"children":6087},{"style":3113},[6088],{"type":40,"value":2963},{"type":31,"tag":1215,"props":6090,"children":6091},{"style":4392},[6092],{"type":40,"value":6093}," `coach-",{"type":31,"tag":1215,"props":6095,"children":6096},{"style":5443},[6097],{"type":40,"value":5446},{"type":31,"tag":1215,"props":6099,"children":6100},{"style":1250},[6101],{"type":40,"value":6048},{"type":31,"tag":1215,"props":6103,"children":6104},{"style":5443},[6105],{"type":40,"value":5455},{"type":31,"tag":1215,"props":6107,"children":6108},{"style":4392},[6109],{"type":40,"value":6110},"@example.com`",{"type":31,"tag":1215,"props":6112,"children":6113},{"style":2945},[6114],{"type":40,"value":2974},{"type":31,"tag":1215,"props":6116,"children":6117},{"class":1217,"line":1300},[6118,6123,6127,6131,6136,6140,6145,6149],{"type":31,"tag":1215,"props":6119,"children":6120},{"style":1250},[6121],{"type":40,"value":6122},"    scheduledAt",{"type":31,"tag":1215,"props":6124,"children":6125},{"style":3113},[6126],{"type":40,"value":2963},{"type":31,"tag":1215,"props":6128,"children":6129},{"style":3809},[6130],{"type":40,"value":3812},{"type":31,"tag":1215,"props":6132,"children":6133},{"style":2939},[6134],{"type":40,"value":6135}," Date",{"type":31,"tag":1215,"props":6137,"children":6138},{"style":1250},[6139],{"type":40,"value":3483},{"type":31,"tag":1215,"props":6141,"children":6142},{"style":4392},[6143],{"type":40,"value":6144},"'2026-06-10T09:00:00Z'",{"type":31,"tag":1215,"props":6146,"children":6147},{"style":1250},[6148],{"type":40,"value":3002},{"type":31,"tag":1215,"props":6150,"children":6151},{"style":2945},[6152],{"type":40,"value":2974},{"type":31,"tag":1215,"props":6154,"children":6155},{"class":1217,"line":1309},[6156,6161,6166],{"type":31,"tag":1215,"props":6157,"children":6158},{"style":2945},[6159],{"type":40,"value":6160},"  }",{"type":31,"tag":1215,"props":6162,"children":6163},{"style":1250},[6164],{"type":40,"value":6165},"))",{"type":31,"tag":1215,"props":6167,"children":6168},{"style":2945},[6169],{"type":40,"value":3055},{"type":31,"tag":1215,"props":6171,"children":6172},{"class":1217,"line":1317},[6173,6177,6181,6185,6190,6194,6199,6203,6208,6212,6217,6221],{"type":31,"tag":1215,"props":6174,"children":6175},{"style":1250},[6176],{"type":40,"value":5998},{"type":31,"tag":1215,"props":6178,"children":6179},{"style":3113},[6180],{"type":40,"value":76},{"type":31,"tag":1215,"props":6182,"children":6183},{"style":2939},[6184],{"type":40,"value":6007},{"type":31,"tag":1215,"props":6186,"children":6187},{"style":1250},[6188],{"type":40,"value":6189},"(brevoClient",{"type":31,"tag":1215,"props":6191,"children":6192},{"style":3113},[6193],{"type":40,"value":76},{"type":31,"tag":1215,"props":6195,"children":6196},{"style":1250},[6197],{"type":40,"value":6198},"sendTransactionalEmail)",{"type":31,"tag":1215,"props":6200,"children":6201},{"style":3113},[6202],{"type":40,"value":76},{"type":31,"tag":1215,"props":6204,"children":6205},{"style":2939},[6206],{"type":40,"value":6207},"mockResolvedValue",{"type":31,"tag":1215,"props":6209,"children":6210},{"style":1250},[6211],{"type":40,"value":3483},{"type":31,"tag":1215,"props":6213,"children":6214},{"style":2966},[6215],{"type":40,"value":6216},"undefined",{"type":31,"tag":1215,"props":6218,"children":6219},{"style":1250},[6220],{"type":40,"value":3002},{"type":31,"tag":1215,"props":6222,"children":6223},{"style":2945},[6224],{"type":40,"value":3055},{"type":31,"tag":1215,"props":6226,"children":6227},{"class":1217,"line":1326},[6228],{"type":31,"tag":1215,"props":6229,"children":6230},{"emptyLinePlaceholder":13},[6231],{"type":40,"value":1233},{"type":31,"tag":1215,"props":6233,"children":6234},{"class":1217,"line":1341},[6235,6239,6244,6248,6252,6257,6261,6266],{"type":31,"tag":1215,"props":6236,"children":6237},{"style":2933},[6238],{"type":40,"value":3023},{"type":31,"tag":1215,"props":6240,"children":6241},{"style":3026},[6242],{"type":40,"value":6243}," results",{"type":31,"tag":1215,"props":6245,"children":6246},{"style":2960},[6247],{"type":40,"value":3034},{"type":31,"tag":1215,"props":6249,"children":6250},{"style":2933},[6251],{"type":40,"value":4187},{"type":31,"tag":1215,"props":6253,"children":6255},{"style":6254},"--shiki-default:#E5C890;--shiki-default-font-style:italic;--shiki-dark:#79B8FF;--shiki-dark-font-style:inherit",[6256],{"type":40,"value":4145},{"type":31,"tag":1215,"props":6258,"children":6259},{"style":3113},[6260],{"type":40,"value":76},{"type":31,"tag":1215,"props":6262,"children":6263},{"style":2939},[6264],{"type":40,"value":6265},"allSettled",{"type":31,"tag":1215,"props":6267,"children":6268},{"style":1250},[6269],{"type":40,"value":2948},{"type":31,"tag":1215,"props":6271,"children":6272},{"class":1217,"line":1355},[6273,6278,6282,6287,6291,6295,6299,6303,6307,6312,6316,6320,6325],{"type":31,"tag":1215,"props":6274,"children":6275},{"style":1250},[6276],{"type":40,"value":6277},"    sessionIds",{"type":31,"tag":1215,"props":6279,"children":6280},{"style":3113},[6281],{"type":40,"value":76},{"type":31,"tag":1215,"props":6283,"children":6284},{"style":2939},[6285],{"type":40,"value":6286},"map",{"type":31,"tag":1215,"props":6288,"children":6289},{"style":1250},[6290],{"type":40,"value":3483},{"type":31,"tag":1215,"props":6292,"children":6293},{"style":2945},[6294],{"type":40,"value":3483},{"type":31,"tag":1215,"props":6296,"children":6297},{"style":2954},[6298],{"type":40,"value":6048},{"type":31,"tag":1215,"props":6300,"children":6301},{"style":2945},[6302],{"type":40,"value":3002},{"type":31,"tag":1215,"props":6304,"children":6305},{"style":2960},[6306],{"type":40,"value":5858},{"type":31,"tag":1215,"props":6308,"children":6309},{"style":1250},[6310],{"type":40,"value":6311}," service",{"type":31,"tag":1215,"props":6313,"children":6314},{"style":3113},[6315],{"type":40,"value":76},{"type":31,"tag":1215,"props":6317,"children":6318},{"style":2939},[6319],{"type":40,"value":5712},{"type":31,"tag":1215,"props":6321,"children":6322},{"style":1250},[6323],{"type":40,"value":6324},"(id))",{"type":31,"tag":1215,"props":6326,"children":6327},{"style":2945},[6328],{"type":40,"value":2974},{"type":31,"tag":1215,"props":6330,"children":6331},{"class":1217,"line":1364},[6332,6336],{"type":31,"tag":1215,"props":6333,"children":6334},{"style":1250},[6335],{"type":40,"value":4089},{"type":31,"tag":1215,"props":6337,"children":6338},{"style":2945},[6339],{"type":40,"value":3055},{"type":31,"tag":1215,"props":6341,"children":6342},{"class":1217,"line":1378},[6343],{"type":31,"tag":1215,"props":6344,"children":6345},{"emptyLinePlaceholder":13},[6346],{"type":40,"value":1233},{"type":31,"tag":1215,"props":6348,"children":6349},{"class":1217,"line":1387},[6350,6354,6359,6363,6367,6371,6376,6380,6384,6389,6393,6397,6402,6406,6411,6416,6421,6425],{"type":31,"tag":1215,"props":6351,"children":6352},{"style":2933},[6353],{"type":40,"value":3023},{"type":31,"tag":1215,"props":6355,"children":6356},{"style":3026},[6357],{"type":40,"value":6358}," failed",{"type":31,"tag":1215,"props":6360,"children":6361},{"style":2960},[6362],{"type":40,"value":3034},{"type":31,"tag":1215,"props":6364,"children":6365},{"style":1250},[6366],{"type":40,"value":6243},{"type":31,"tag":1215,"props":6368,"children":6369},{"style":3113},[6370],{"type":40,"value":76},{"type":31,"tag":1215,"props":6372,"children":6373},{"style":2939},[6374],{"type":40,"value":6375},"filter",{"type":31,"tag":1215,"props":6377,"children":6378},{"style":1250},[6379],{"type":40,"value":3483},{"type":31,"tag":1215,"props":6381,"children":6382},{"style":2945},[6383],{"type":40,"value":3483},{"type":31,"tag":1215,"props":6385,"children":6386},{"style":2954},[6387],{"type":40,"value":6388},"r",{"type":31,"tag":1215,"props":6390,"children":6391},{"style":2945},[6392],{"type":40,"value":3002},{"type":31,"tag":1215,"props":6394,"children":6395},{"style":2960},[6396],{"type":40,"value":5858},{"type":31,"tag":1215,"props":6398,"children":6399},{"style":1250},[6400],{"type":40,"value":6401}," r",{"type":31,"tag":1215,"props":6403,"children":6404},{"style":3113},[6405],{"type":40,"value":76},{"type":31,"tag":1215,"props":6407,"children":6408},{"style":1250},[6409],{"type":40,"value":6410},"status ",{"type":31,"tag":1215,"props":6412,"children":6413},{"style":2960},[6414],{"type":40,"value":6415},"===",{"type":31,"tag":1215,"props":6417,"children":6418},{"style":4392},[6419],{"type":40,"value":6420}," 'rejected'",{"type":31,"tag":1215,"props":6422,"children":6423},{"style":1250},[6424],{"type":40,"value":3002},{"type":31,"tag":1215,"props":6426,"children":6427},{"style":2945},[6428],{"type":40,"value":3055},{"type":31,"tag":1215,"props":6430,"children":6431},{"class":1217,"line":1401},[6432,6437,6442,6446,6451,6455,6460,6464],{"type":31,"tag":1215,"props":6433,"children":6434},{"style":2939},[6435],{"type":40,"value":6436},"  expect",{"type":31,"tag":1215,"props":6438,"children":6439},{"style":1250},[6440],{"type":40,"value":6441},"(failed)",{"type":31,"tag":1215,"props":6443,"children":6444},{"style":3113},[6445],{"type":40,"value":76},{"type":31,"tag":1215,"props":6447,"children":6448},{"style":2939},[6449],{"type":40,"value":6450},"toHaveLength",{"type":31,"tag":1215,"props":6452,"children":6453},{"style":1250},[6454],{"type":40,"value":3483},{"type":31,"tag":1215,"props":6456,"children":6457},{"style":3047},[6458],{"type":40,"value":6459},"0",{"type":31,"tag":1215,"props":6461,"children":6462},{"style":1250},[6463],{"type":40,"value":3002},{"type":31,"tag":1215,"props":6465,"children":6466},{"style":2945},[6467],{"type":40,"value":3055},{"type":31,"tag":1215,"props":6469,"children":6470},{"class":1217,"line":1410},[6471,6475,6479,6483,6487,6491,6496,6500,6505,6509],{"type":31,"tag":1215,"props":6472,"children":6473},{"style":2939},[6474],{"type":40,"value":6436},{"type":31,"tag":1215,"props":6476,"children":6477},{"style":1250},[6478],{"type":40,"value":6189},{"type":31,"tag":1215,"props":6480,"children":6481},{"style":3113},[6482],{"type":40,"value":76},{"type":31,"tag":1215,"props":6484,"children":6485},{"style":1250},[6486],{"type":40,"value":6198},{"type":31,"tag":1215,"props":6488,"children":6489},{"style":3113},[6490],{"type":40,"value":76},{"type":31,"tag":1215,"props":6492,"children":6493},{"style":2939},[6494],{"type":40,"value":6495},"toHaveBeenCalledTimes",{"type":31,"tag":1215,"props":6497,"children":6498},{"style":1250},[6499],{"type":40,"value":3483},{"type":31,"tag":1215,"props":6501,"children":6502},{"style":3047},[6503],{"type":40,"value":6504},"50",{"type":31,"tag":1215,"props":6506,"children":6507},{"style":1250},[6508],{"type":40,"value":3002},{"type":31,"tag":1215,"props":6510,"children":6511},{"style":2945},[6512],{"type":40,"value":3055},{"type":31,"tag":1215,"props":6514,"children":6515},{"class":1217,"line":1419},[6516,6520,6524],{"type":31,"tag":1215,"props":6517,"children":6518},{"style":2945},[6519],{"type":40,"value":5455},{"type":31,"tag":1215,"props":6521,"children":6522},{"style":1250},[6523],{"type":40,"value":3002},{"type":31,"tag":1215,"props":6525,"children":6526},{"style":2945},[6527],{"type":40,"value":3055},{"type":31,"tag":32,"props":6529,"children":6530},{},[6531,6533],{"type":40,"value":6532},"Ce test, Claude ne l'écrit pas spontanément. Il faut le prompter explicitement : ",{"type":31,"tag":97,"props":6534,"children":6535},{},[6536,6538,6543],{"type":40,"value":6537},"\"Écris un test Vitest qui exécute cette méthode 50 fois en parallèle via ",{"type":31,"tag":169,"props":6539,"children":6541},{"className":6540},[],[6542],{"type":40,"value":5797},{"type":40,"value":6544}," et vérifie qu'aucune ne rejette.\"",{"type":31,"tag":5774,"props":6546,"children":6548},{"id":6547},"test-2-le-test-de-rate-limit-429",[6549],{"type":40,"value":6550},"Test 2 : le test de rate limit (429)",{"type":31,"tag":32,"props":6552,"children":6553},{},[6554],{"type":40,"value":6555},"Mockez la réponse de Brevo pour qu'elle lève une erreur 429 au premier appel. Vérifiez que la méthode retry avec backoff au lieu d'échouer.",{"type":31,"tag":1206,"props":6557,"children":6559},{"className":2912,"code":6558,"language":2914,"meta":8,"style":8},"it('retries with backoff on 429 from Brevo', async () => {\n  vi.mocked(sessionRepository.findById).mockResolvedValue({\n    id: 'session-abc',\n    coacheeEmail: 'coachee@example.com',\n    scheduledAt: new Date(),\n  });\n\n  let callCount = 0;\n  vi.mocked(brevoClient.sendTransactionalEmail).mockImplementation(async () => {\n    callCount += 1;\n    if (callCount === 1) {\n      const err = new Error('Too Many Requests') as Error & { statusCode: number };\n      err.statusCode = 429;\n      throw err;\n    }\n  });\n\n  await service.sendSessionConfirmation('session-abc');\n\n  expect(callCount).toBe(2);\n  expect(brevoClient.sendTransactionalEmail).toHaveBeenCalledTimes(2);\n});\n",[6560],{"type":31,"tag":169,"props":6561,"children":6562},{"__ignoreMap":8},[6563,6599,6642,6662,6682,6709,6724,6731,6757,6812,6834,6862,6937,6967,6982,6989,7004,7011,7048,7055,7093,7136],{"type":31,"tag":1215,"props":6564,"children":6565},{"class":1217,"line":1218},[6566,6570,6574,6579,6583,6587,6591,6595],{"type":31,"tag":1215,"props":6567,"children":6568},{"style":2939},[6569],{"type":40,"value":5829},{"type":31,"tag":1215,"props":6571,"children":6572},{"style":1250},[6573],{"type":40,"value":3483},{"type":31,"tag":1215,"props":6575,"children":6576},{"style":4392},[6577],{"type":40,"value":6578},"'retries with backoff on 429 from Brevo'",{"type":31,"tag":1215,"props":6580,"children":6581},{"style":2945},[6582],{"type":40,"value":5843},{"type":31,"tag":1215,"props":6584,"children":6585},{"style":2933},[6586],{"type":40,"value":5848},{"type":31,"tag":1215,"props":6588,"children":6589},{"style":2945},[6590],{"type":40,"value":5853},{"type":31,"tag":1215,"props":6592,"children":6593},{"style":2960},[6594],{"type":40,"value":5858},{"type":31,"tag":1215,"props":6596,"children":6597},{"style":2945},[6598],{"type":40,"value":3015},{"type":31,"tag":1215,"props":6600,"children":6601},{"class":1217,"line":817},[6602,6606,6610,6614,6618,6622,6626,6630,6634,6638],{"type":31,"tag":1215,"props":6603,"children":6604},{"style":1250},[6605],{"type":40,"value":5998},{"type":31,"tag":1215,"props":6607,"children":6608},{"style":3113},[6609],{"type":40,"value":76},{"type":31,"tag":1215,"props":6611,"children":6612},{"style":2939},[6613],{"type":40,"value":6007},{"type":31,"tag":1215,"props":6615,"children":6616},{"style":1250},[6617],{"type":40,"value":6012},{"type":31,"tag":1215,"props":6619,"children":6620},{"style":3113},[6621],{"type":40,"value":76},{"type":31,"tag":1215,"props":6623,"children":6624},{"style":1250},[6625],{"type":40,"value":6021},{"type":31,"tag":1215,"props":6627,"children":6628},{"style":3113},[6629],{"type":40,"value":76},{"type":31,"tag":1215,"props":6631,"children":6632},{"style":2939},[6633],{"type":40,"value":6207},{"type":31,"tag":1215,"props":6635,"children":6636},{"style":1250},[6637],{"type":40,"value":3483},{"type":31,"tag":1215,"props":6639,"children":6640},{"style":2945},[6641],{"type":40,"value":4257},{"type":31,"tag":1215,"props":6643,"children":6644},{"class":1217,"line":1236},[6645,6649,6653,6658],{"type":31,"tag":1215,"props":6646,"children":6647},{"style":1250},[6648],{"type":40,"value":6072},{"type":31,"tag":1215,"props":6650,"children":6651},{"style":3113},[6652],{"type":40,"value":2963},{"type":31,"tag":1215,"props":6654,"children":6655},{"style":4392},[6656],{"type":40,"value":6657}," 'session-abc'",{"type":31,"tag":1215,"props":6659,"children":6660},{"style":2945},[6661],{"type":40,"value":2974},{"type":31,"tag":1215,"props":6663,"children":6664},{"class":1217,"line":1246},[6665,6669,6673,6678],{"type":31,"tag":1215,"props":6666,"children":6667},{"style":1250},[6668],{"type":40,"value":6084},{"type":31,"tag":1215,"props":6670,"children":6671},{"style":3113},[6672],{"type":40,"value":2963},{"type":31,"tag":1215,"props":6674,"children":6675},{"style":4392},[6676],{"type":40,"value":6677}," 'coachee@example.com'",{"type":31,"tag":1215,"props":6679,"children":6680},{"style":2945},[6681],{"type":40,"value":2974},{"type":31,"tag":1215,"props":6683,"children":6684},{"class":1217,"line":1256},[6685,6689,6693,6697,6701,6705],{"type":31,"tag":1215,"props":6686,"children":6687},{"style":1250},[6688],{"type":40,"value":6122},{"type":31,"tag":1215,"props":6690,"children":6691},{"style":3113},[6692],{"type":40,"value":2963},{"type":31,"tag":1215,"props":6694,"children":6695},{"style":3809},[6696],{"type":40,"value":3812},{"type":31,"tag":1215,"props":6698,"children":6699},{"style":2939},[6700],{"type":40,"value":6135},{"type":31,"tag":1215,"props":6702,"children":6703},{"style":1250},[6704],{"type":40,"value":3821},{"type":31,"tag":1215,"props":6706,"children":6707},{"style":2945},[6708],{"type":40,"value":2974},{"type":31,"tag":1215,"props":6710,"children":6711},{"class":1217,"line":1264},[6712,6716,6720],{"type":31,"tag":1215,"props":6713,"children":6714},{"style":2945},[6715],{"type":40,"value":6160},{"type":31,"tag":1215,"props":6717,"children":6718},{"style":1250},[6719],{"type":40,"value":3002},{"type":31,"tag":1215,"props":6721,"children":6722},{"style":2945},[6723],{"type":40,"value":3055},{"type":31,"tag":1215,"props":6725,"children":6726},{"class":1217,"line":1273},[6727],{"type":31,"tag":1215,"props":6728,"children":6729},{"emptyLinePlaceholder":13},[6730],{"type":40,"value":1233},{"type":31,"tag":1215,"props":6732,"children":6733},{"class":1217,"line":1282},[6734,6739,6744,6749,6753],{"type":31,"tag":1215,"props":6735,"children":6736},{"style":2933},[6737],{"type":40,"value":6738},"  let",{"type":31,"tag":1215,"props":6740,"children":6741},{"style":1250},[6742],{"type":40,"value":6743}," callCount ",{"type":31,"tag":1215,"props":6745,"children":6746},{"style":2960},[6747],{"type":40,"value":6748},"=",{"type":31,"tag":1215,"props":6750,"children":6751},{"style":3047},[6752],{"type":40,"value":3694},{"type":31,"tag":1215,"props":6754,"children":6755},{"style":2945},[6756],{"type":40,"value":3055},{"type":31,"tag":1215,"props":6758,"children":6759},{"class":1217,"line":1291},[6760,6764,6768,6772,6776,6780,6784,6788,6792,6796,6800,6804,6808],{"type":31,"tag":1215,"props":6761,"children":6762},{"style":1250},[6763],{"type":40,"value":5998},{"type":31,"tag":1215,"props":6765,"children":6766},{"style":3113},[6767],{"type":40,"value":76},{"type":31,"tag":1215,"props":6769,"children":6770},{"style":2939},[6771],{"type":40,"value":6007},{"type":31,"tag":1215,"props":6773,"children":6774},{"style":1250},[6775],{"type":40,"value":6189},{"type":31,"tag":1215,"props":6777,"children":6778},{"style":3113},[6779],{"type":40,"value":76},{"type":31,"tag":1215,"props":6781,"children":6782},{"style":1250},[6783],{"type":40,"value":6198},{"type":31,"tag":1215,"props":6785,"children":6786},{"style":3113},[6787],{"type":40,"value":76},{"type":31,"tag":1215,"props":6789,"children":6790},{"style":2939},[6791],{"type":40,"value":6030},{"type":31,"tag":1215,"props":6793,"children":6794},{"style":1250},[6795],{"type":40,"value":3483},{"type":31,"tag":1215,"props":6797,"children":6798},{"style":2933},[6799],{"type":40,"value":6039},{"type":31,"tag":1215,"props":6801,"children":6802},{"style":2945},[6803],{"type":40,"value":5853},{"type":31,"tag":1215,"props":6805,"children":6806},{"style":2960},[6807],{"type":40,"value":5858},{"type":31,"tag":1215,"props":6809,"children":6810},{"style":2945},[6811],{"type":40,"value":3015},{"type":31,"tag":1215,"props":6813,"children":6814},{"class":1217,"line":1300},[6815,6820,6825,6830],{"type":31,"tag":1215,"props":6816,"children":6817},{"style":1250},[6818],{"type":40,"value":6819},"    callCount ",{"type":31,"tag":1215,"props":6821,"children":6822},{"style":2960},[6823],{"type":40,"value":6824},"+=",{"type":31,"tag":1215,"props":6826,"children":6827},{"style":3047},[6828],{"type":40,"value":6829}," 1",{"type":31,"tag":1215,"props":6831,"children":6832},{"style":2945},[6833],{"type":40,"value":3055},{"type":31,"tag":1215,"props":6835,"children":6836},{"class":1217,"line":1309},[6837,6841,6846,6850,6854,6858],{"type":31,"tag":1215,"props":6838,"children":6839},{"style":2933},[6840],{"type":40,"value":4237},{"type":31,"tag":1215,"props":6842,"children":6843},{"style":1250},[6844],{"type":40,"value":6845}," (callCount ",{"type":31,"tag":1215,"props":6847,"children":6848},{"style":2960},[6849],{"type":40,"value":6415},{"type":31,"tag":1215,"props":6851,"children":6852},{"style":3047},[6853],{"type":40,"value":6829},{"type":31,"tag":1215,"props":6855,"children":6856},{"style":1250},[6857],{"type":40,"value":3140},{"type":31,"tag":1215,"props":6859,"children":6860},{"style":2945},[6861],{"type":40,"value":4257},{"type":31,"tag":1215,"props":6863,"children":6864},{"class":1217,"line":1317},[6865,6870,6875,6879,6883,6887,6891,6896,6900,6905,6909,6914,6918,6924,6928,6932],{"type":31,"tag":1215,"props":6866,"children":6867},{"style":2933},[6868],{"type":40,"value":6869},"      const",{"type":31,"tag":1215,"props":6871,"children":6872},{"style":3026},[6873],{"type":40,"value":6874}," err",{"type":31,"tag":1215,"props":6876,"children":6877},{"style":2960},[6878],{"type":40,"value":3034},{"type":31,"tag":1215,"props":6880,"children":6881},{"style":3809},[6882],{"type":40,"value":3812},{"type":31,"tag":1215,"props":6884,"children":6885},{"style":2939},[6886],{"type":40,"value":5431},{"type":31,"tag":1215,"props":6888,"children":6889},{"style":1250},[6890],{"type":40,"value":3483},{"type":31,"tag":1215,"props":6892,"children":6893},{"style":4392},[6894],{"type":40,"value":6895},"'Too Many Requests'",{"type":31,"tag":1215,"props":6897,"children":6898},{"style":1250},[6899],{"type":40,"value":3140},{"type":31,"tag":1215,"props":6901,"children":6902},{"style":2933},[6903],{"type":40,"value":6904},"as",{"type":31,"tag":1215,"props":6906,"children":6907},{"style":3463},[6908],{"type":40,"value":5431},{"type":31,"tag":1215,"props":6910,"children":6911},{"style":2960},[6912],{"type":40,"value":6913}," &",{"type":31,"tag":1215,"props":6915,"children":6916},{"style":2945},[6917],{"type":40,"value":5616},{"type":31,"tag":1215,"props":6919,"children":6921},{"style":6920},"--shiki-default:#C6D0F5;--shiki-dark:#FFAB70",[6922],{"type":40,"value":6923}," statusCode",{"type":31,"tag":1215,"props":6925,"children":6926},{"style":2960},[6927],{"type":40,"value":2963},{"type":31,"tag":1215,"props":6929,"children":6930},{"style":2966},[6931],{"type":40,"value":2969},{"type":31,"tag":1215,"props":6933,"children":6934},{"style":2945},[6935],{"type":40,"value":6936}," };\n",{"type":31,"tag":1215,"props":6938,"children":6939},{"class":1217,"line":1326},[6940,6945,6949,6954,6958,6963],{"type":31,"tag":1215,"props":6941,"children":6942},{"style":1250},[6943],{"type":40,"value":6944},"      err",{"type":31,"tag":1215,"props":6946,"children":6947},{"style":3113},[6948],{"type":40,"value":76},{"type":31,"tag":1215,"props":6950,"children":6951},{"style":1250},[6952],{"type":40,"value":6953},"statusCode ",{"type":31,"tag":1215,"props":6955,"children":6956},{"style":2960},[6957],{"type":40,"value":6748},{"type":31,"tag":1215,"props":6959,"children":6960},{"style":3047},[6961],{"type":40,"value":6962}," 429",{"type":31,"tag":1215,"props":6964,"children":6965},{"style":2945},[6966],{"type":40,"value":3055},{"type":31,"tag":1215,"props":6968,"children":6969},{"class":1217,"line":1341},[6970,6974,6978],{"type":31,"tag":1215,"props":6971,"children":6972},{"style":2933},[6973],{"type":40,"value":4265},{"type":31,"tag":1215,"props":6975,"children":6976},{"style":1250},[6977],{"type":40,"value":6874},{"type":31,"tag":1215,"props":6979,"children":6980},{"style":2945},[6981],{"type":40,"value":3055},{"type":31,"tag":1215,"props":6983,"children":6984},{"class":1217,"line":1355},[6985],{"type":31,"tag":1215,"props":6986,"children":6987},{"style":2945},[6988],{"type":40,"value":4298},{"type":31,"tag":1215,"props":6990,"children":6991},{"class":1217,"line":1364},[6992,6996,7000],{"type":31,"tag":1215,"props":6993,"children":6994},{"style":2945},[6995],{"type":40,"value":6160},{"type":31,"tag":1215,"props":6997,"children":6998},{"style":1250},[6999],{"type":40,"value":3002},{"type":31,"tag":1215,"props":7001,"children":7002},{"style":2945},[7003],{"type":40,"value":3055},{"type":31,"tag":1215,"props":7005,"children":7006},{"class":1217,"line":1378},[7007],{"type":31,"tag":1215,"props":7008,"children":7009},{"emptyLinePlaceholder":13},[7010],{"type":40,"value":1233},{"type":31,"tag":1215,"props":7012,"children":7013},{"class":1217,"line":1387},[7014,7019,7023,7027,7031,7035,7040,7044],{"type":31,"tag":1215,"props":7015,"children":7016},{"style":2933},[7017],{"type":40,"value":7018},"  await",{"type":31,"tag":1215,"props":7020,"children":7021},{"style":1250},[7022],{"type":40,"value":6311},{"type":31,"tag":1215,"props":7024,"children":7025},{"style":3113},[7026],{"type":40,"value":76},{"type":31,"tag":1215,"props":7028,"children":7029},{"style":2939},[7030],{"type":40,"value":5712},{"type":31,"tag":1215,"props":7032,"children":7033},{"style":1250},[7034],{"type":40,"value":3483},{"type":31,"tag":1215,"props":7036,"children":7037},{"style":4392},[7038],{"type":40,"value":7039},"'session-abc'",{"type":31,"tag":1215,"props":7041,"children":7042},{"style":1250},[7043],{"type":40,"value":3002},{"type":31,"tag":1215,"props":7045,"children":7046},{"style":2945},[7047],{"type":40,"value":3055},{"type":31,"tag":1215,"props":7049,"children":7050},{"class":1217,"line":1401},[7051],{"type":31,"tag":1215,"props":7052,"children":7053},{"emptyLinePlaceholder":13},[7054],{"type":40,"value":1233},{"type":31,"tag":1215,"props":7056,"children":7057},{"class":1217,"line":1410},[7058,7062,7067,7071,7076,7080,7085,7089],{"type":31,"tag":1215,"props":7059,"children":7060},{"style":2939},[7061],{"type":40,"value":6436},{"type":31,"tag":1215,"props":7063,"children":7064},{"style":1250},[7065],{"type":40,"value":7066},"(callCount)",{"type":31,"tag":1215,"props":7068,"children":7069},{"style":3113},[7070],{"type":40,"value":76},{"type":31,"tag":1215,"props":7072,"children":7073},{"style":2939},[7074],{"type":40,"value":7075},"toBe",{"type":31,"tag":1215,"props":7077,"children":7078},{"style":1250},[7079],{"type":40,"value":3483},{"type":31,"tag":1215,"props":7081,"children":7082},{"style":3047},[7083],{"type":40,"value":7084},"2",{"type":31,"tag":1215,"props":7086,"children":7087},{"style":1250},[7088],{"type":40,"value":3002},{"type":31,"tag":1215,"props":7090,"children":7091},{"style":2945},[7092],{"type":40,"value":3055},{"type":31,"tag":1215,"props":7094,"children":7095},{"class":1217,"line":1419},[7096,7100,7104,7108,7112,7116,7120,7124,7128,7132],{"type":31,"tag":1215,"props":7097,"children":7098},{"style":2939},[7099],{"type":40,"value":6436},{"type":31,"tag":1215,"props":7101,"children":7102},{"style":1250},[7103],{"type":40,"value":6189},{"type":31,"tag":1215,"props":7105,"children":7106},{"style":3113},[7107],{"type":40,"value":76},{"type":31,"tag":1215,"props":7109,"children":7110},{"style":1250},[7111],{"type":40,"value":6198},{"type":31,"tag":1215,"props":7113,"children":7114},{"style":3113},[7115],{"type":40,"value":76},{"type":31,"tag":1215,"props":7117,"children":7118},{"style":2939},[7119],{"type":40,"value":6495},{"type":31,"tag":1215,"props":7121,"children":7122},{"style":1250},[7123],{"type":40,"value":3483},{"type":31,"tag":1215,"props":7125,"children":7126},{"style":3047},[7127],{"type":40,"value":7084},{"type":31,"tag":1215,"props":7129,"children":7130},{"style":1250},[7131],{"type":40,"value":3002},{"type":31,"tag":1215,"props":7133,"children":7134},{"style":2945},[7135],{"type":40,"value":3055},{"type":31,"tag":1215,"props":7137,"children":7138},{"class":1217,"line":1427},[7139,7143,7147],{"type":31,"tag":1215,"props":7140,"children":7141},{"style":2945},[7142],{"type":40,"value":5455},{"type":31,"tag":1215,"props":7144,"children":7145},{"style":1250},[7146],{"type":40,"value":3002},{"type":31,"tag":1215,"props":7148,"children":7149},{"style":2945},[7150],{"type":40,"value":3055},{"type":31,"tag":32,"props":7152,"children":7153},{},[7154],{"type":40,"value":7155},"C'est le test qui aurait évité le crash. 20 lignes. Une heure à écrire la première fois. Une protection durable contre ce mode d'échec.",{"type":31,"tag":5774,"props":7157,"children":7159},{"id":7158},"test-3-le-test-de-timeout",[7160],{"type":40,"value":7161},"Test 3 : le test de timeout",{"type":31,"tag":32,"props":7163,"children":7164},{},[7165],{"type":40,"value":7166},"Simulez une réponse Brevo qui ne vient jamais (le mock attend indefiniment). Vérifiez que la méthode abandonne au bout de N secondes au lieu de bloquer l'event loop.",{"type":31,"tag":1206,"props":7168,"children":7170},{"className":2912,"code":7169,"language":2914,"meta":8,"style":8},"it('aborts and throws after 5 seconds if Brevo does not respond', async () => {\n  vi.useFakeTimers();\n\n  vi.mocked(sessionRepository.findById).mockResolvedValue({\n    id: 'session-timeout',\n    coacheeEmail: 'coachee@example.com',\n    scheduledAt: new Date(),\n  });\n\n  \u002F\u002F Brevo ne répond jamais\n  vi.mocked(brevoClient.sendTransactionalEmail).mockImplementation(\n    () => new Promise\u003Cvoid>(() => { \u002F* jamais résolu *\u002F }),\n  );\n\n  const promise = service.sendSessionConfirmation('session-timeout');\n\n  \u002F\u002F avancer le temps de 5 secondes\n  await vi.advanceTimersByTimeAsync(5_000);\n\n  await expect(promise).rejects.toThrow('Brevo timeout');\n\n  vi.useRealTimers();\n});\n",[7171],{"type":31,"tag":169,"props":7172,"children":7173},{"__ignoreMap":8},[7174,7210,7234,7241,7284,7304,7323,7350,7365,7372,7380,7419,7485,7496,7503,7548,7555,7563,7601,7608,7660,7667,7691],{"type":31,"tag":1215,"props":7175,"children":7176},{"class":1217,"line":1218},[7177,7181,7185,7190,7194,7198,7202,7206],{"type":31,"tag":1215,"props":7178,"children":7179},{"style":2939},[7180],{"type":40,"value":5829},{"type":31,"tag":1215,"props":7182,"children":7183},{"style":1250},[7184],{"type":40,"value":3483},{"type":31,"tag":1215,"props":7186,"children":7187},{"style":4392},[7188],{"type":40,"value":7189},"'aborts and throws after 5 seconds if Brevo does not respond'",{"type":31,"tag":1215,"props":7191,"children":7192},{"style":2945},[7193],{"type":40,"value":5843},{"type":31,"tag":1215,"props":7195,"children":7196},{"style":2933},[7197],{"type":40,"value":5848},{"type":31,"tag":1215,"props":7199,"children":7200},{"style":2945},[7201],{"type":40,"value":5853},{"type":31,"tag":1215,"props":7203,"children":7204},{"style":2960},[7205],{"type":40,"value":5858},{"type":31,"tag":1215,"props":7207,"children":7208},{"style":2945},[7209],{"type":40,"value":3015},{"type":31,"tag":1215,"props":7211,"children":7212},{"class":1217,"line":817},[7213,7217,7221,7226,7230],{"type":31,"tag":1215,"props":7214,"children":7215},{"style":1250},[7216],{"type":40,"value":5998},{"type":31,"tag":1215,"props":7218,"children":7219},{"style":3113},[7220],{"type":40,"value":76},{"type":31,"tag":1215,"props":7222,"children":7223},{"style":2939},[7224],{"type":40,"value":7225},"useFakeTimers",{"type":31,"tag":1215,"props":7227,"children":7228},{"style":1250},[7229],{"type":40,"value":3821},{"type":31,"tag":1215,"props":7231,"children":7232},{"style":2945},[7233],{"type":40,"value":3055},{"type":31,"tag":1215,"props":7235,"children":7236},{"class":1217,"line":1236},[7237],{"type":31,"tag":1215,"props":7238,"children":7239},{"emptyLinePlaceholder":13},[7240],{"type":40,"value":1233},{"type":31,"tag":1215,"props":7242,"children":7243},{"class":1217,"line":1246},[7244,7248,7252,7256,7260,7264,7268,7272,7276,7280],{"type":31,"tag":1215,"props":7245,"children":7246},{"style":1250},[7247],{"type":40,"value":5998},{"type":31,"tag":1215,"props":7249,"children":7250},{"style":3113},[7251],{"type":40,"value":76},{"type":31,"tag":1215,"props":7253,"children":7254},{"style":2939},[7255],{"type":40,"value":6007},{"type":31,"tag":1215,"props":7257,"children":7258},{"style":1250},[7259],{"type":40,"value":6012},{"type":31,"tag":1215,"props":7261,"children":7262},{"style":3113},[7263],{"type":40,"value":76},{"type":31,"tag":1215,"props":7265,"children":7266},{"style":1250},[7267],{"type":40,"value":6021},{"type":31,"tag":1215,"props":7269,"children":7270},{"style":3113},[7271],{"type":40,"value":76},{"type":31,"tag":1215,"props":7273,"children":7274},{"style":2939},[7275],{"type":40,"value":6207},{"type":31,"tag":1215,"props":7277,"children":7278},{"style":1250},[7279],{"type":40,"value":3483},{"type":31,"tag":1215,"props":7281,"children":7282},{"style":2945},[7283],{"type":40,"value":4257},{"type":31,"tag":1215,"props":7285,"children":7286},{"class":1217,"line":1256},[7287,7291,7295,7300],{"type":31,"tag":1215,"props":7288,"children":7289},{"style":1250},[7290],{"type":40,"value":6072},{"type":31,"tag":1215,"props":7292,"children":7293},{"style":3113},[7294],{"type":40,"value":2963},{"type":31,"tag":1215,"props":7296,"children":7297},{"style":4392},[7298],{"type":40,"value":7299}," 'session-timeout'",{"type":31,"tag":1215,"props":7301,"children":7302},{"style":2945},[7303],{"type":40,"value":2974},{"type":31,"tag":1215,"props":7305,"children":7306},{"class":1217,"line":1264},[7307,7311,7315,7319],{"type":31,"tag":1215,"props":7308,"children":7309},{"style":1250},[7310],{"type":40,"value":6084},{"type":31,"tag":1215,"props":7312,"children":7313},{"style":3113},[7314],{"type":40,"value":2963},{"type":31,"tag":1215,"props":7316,"children":7317},{"style":4392},[7318],{"type":40,"value":6677},{"type":31,"tag":1215,"props":7320,"children":7321},{"style":2945},[7322],{"type":40,"value":2974},{"type":31,"tag":1215,"props":7324,"children":7325},{"class":1217,"line":1273},[7326,7330,7334,7338,7342,7346],{"type":31,"tag":1215,"props":7327,"children":7328},{"style":1250},[7329],{"type":40,"value":6122},{"type":31,"tag":1215,"props":7331,"children":7332},{"style":3113},[7333],{"type":40,"value":2963},{"type":31,"tag":1215,"props":7335,"children":7336},{"style":3809},[7337],{"type":40,"value":3812},{"type":31,"tag":1215,"props":7339,"children":7340},{"style":2939},[7341],{"type":40,"value":6135},{"type":31,"tag":1215,"props":7343,"children":7344},{"style":1250},[7345],{"type":40,"value":3821},{"type":31,"tag":1215,"props":7347,"children":7348},{"style":2945},[7349],{"type":40,"value":2974},{"type":31,"tag":1215,"props":7351,"children":7352},{"class":1217,"line":1282},[7353,7357,7361],{"type":31,"tag":1215,"props":7354,"children":7355},{"style":2945},[7356],{"type":40,"value":6160},{"type":31,"tag":1215,"props":7358,"children":7359},{"style":1250},[7360],{"type":40,"value":3002},{"type":31,"tag":1215,"props":7362,"children":7363},{"style":2945},[7364],{"type":40,"value":3055},{"type":31,"tag":1215,"props":7366,"children":7367},{"class":1217,"line":1291},[7368],{"type":31,"tag":1215,"props":7369,"children":7370},{"emptyLinePlaceholder":13},[7371],{"type":40,"value":1233},{"type":31,"tag":1215,"props":7373,"children":7374},{"class":1217,"line":1300},[7375],{"type":31,"tag":1215,"props":7376,"children":7377},{"style":2924},[7378],{"type":40,"value":7379},"  \u002F\u002F Brevo ne répond jamais\n",{"type":31,"tag":1215,"props":7381,"children":7382},{"class":1217,"line":1309},[7383,7387,7391,7395,7399,7403,7407,7411,7415],{"type":31,"tag":1215,"props":7384,"children":7385},{"style":1250},[7386],{"type":40,"value":5998},{"type":31,"tag":1215,"props":7388,"children":7389},{"style":3113},[7390],{"type":40,"value":76},{"type":31,"tag":1215,"props":7392,"children":7393},{"style":2939},[7394],{"type":40,"value":6007},{"type":31,"tag":1215,"props":7396,"children":7397},{"style":1250},[7398],{"type":40,"value":6189},{"type":31,"tag":1215,"props":7400,"children":7401},{"style":3113},[7402],{"type":40,"value":76},{"type":31,"tag":1215,"props":7404,"children":7405},{"style":1250},[7406],{"type":40,"value":6198},{"type":31,"tag":1215,"props":7408,"children":7409},{"style":3113},[7410],{"type":40,"value":76},{"type":31,"tag":1215,"props":7412,"children":7413},{"style":2939},[7414],{"type":40,"value":6030},{"type":31,"tag":1215,"props":7416,"children":7417},{"style":1250},[7418],{"type":40,"value":2948},{"type":31,"tag":1215,"props":7420,"children":7421},{"class":1217,"line":1317},[7422,7427,7431,7435,7439,7443,7447,7451,7455,7459,7463,7467,7472,7477,7481],{"type":31,"tag":1215,"props":7423,"children":7424},{"style":2945},[7425],{"type":40,"value":7426},"    ()",{"type":31,"tag":1215,"props":7428,"children":7429},{"style":2960},[7430],{"type":40,"value":5858},{"type":31,"tag":1215,"props":7432,"children":7433},{"style":3809},[7434],{"type":40,"value":3812},{"type":31,"tag":1215,"props":7436,"children":7437},{"style":6254},[7438],{"type":40,"value":4145},{"type":31,"tag":1215,"props":7440,"children":7441},{"style":4148},[7442],{"type":40,"value":4151},{"type":31,"tag":1215,"props":7444,"children":7445},{"style":2966},[7446],{"type":40,"value":5339},{"type":31,"tag":1215,"props":7448,"children":7449},{"style":4148},[7450],{"type":40,"value":4161},{"type":31,"tag":1215,"props":7452,"children":7453},{"style":1250},[7454],{"type":40,"value":3483},{"type":31,"tag":1215,"props":7456,"children":7457},{"style":2945},[7458],{"type":40,"value":3821},{"type":31,"tag":1215,"props":7460,"children":7461},{"style":2960},[7462],{"type":40,"value":5858},{"type":31,"tag":1215,"props":7464,"children":7465},{"style":2945},[7466],{"type":40,"value":5616},{"type":31,"tag":1215,"props":7468,"children":7469},{"style":2924},[7470],{"type":40,"value":7471}," \u002F* jamais résolu *\u002F",{"type":31,"tag":1215,"props":7473,"children":7474},{"style":2945},[7475],{"type":40,"value":7476}," }",{"type":31,"tag":1215,"props":7478,"children":7479},{"style":1250},[7480],{"type":40,"value":3002},{"type":31,"tag":1215,"props":7482,"children":7483},{"style":2945},[7484],{"type":40,"value":2974},{"type":31,"tag":1215,"props":7486,"children":7487},{"class":1217,"line":1326},[7488,7492],{"type":31,"tag":1215,"props":7489,"children":7490},{"style":1250},[7491],{"type":40,"value":4089},{"type":31,"tag":1215,"props":7493,"children":7494},{"style":2945},[7495],{"type":40,"value":3055},{"type":31,"tag":1215,"props":7497,"children":7498},{"class":1217,"line":1341},[7499],{"type":31,"tag":1215,"props":7500,"children":7501},{"emptyLinePlaceholder":13},[7502],{"type":40,"value":1233},{"type":31,"tag":1215,"props":7504,"children":7505},{"class":1217,"line":1355},[7506,7510,7515,7519,7523,7527,7531,7535,7540,7544],{"type":31,"tag":1215,"props":7507,"children":7508},{"style":2933},[7509],{"type":40,"value":3023},{"type":31,"tag":1215,"props":7511,"children":7512},{"style":3026},[7513],{"type":40,"value":7514}," promise",{"type":31,"tag":1215,"props":7516,"children":7517},{"style":2960},[7518],{"type":40,"value":3034},{"type":31,"tag":1215,"props":7520,"children":7521},{"style":1250},[7522],{"type":40,"value":6311},{"type":31,"tag":1215,"props":7524,"children":7525},{"style":3113},[7526],{"type":40,"value":76},{"type":31,"tag":1215,"props":7528,"children":7529},{"style":2939},[7530],{"type":40,"value":5712},{"type":31,"tag":1215,"props":7532,"children":7533},{"style":1250},[7534],{"type":40,"value":3483},{"type":31,"tag":1215,"props":7536,"children":7537},{"style":4392},[7538],{"type":40,"value":7539},"'session-timeout'",{"type":31,"tag":1215,"props":7541,"children":7542},{"style":1250},[7543],{"type":40,"value":3002},{"type":31,"tag":1215,"props":7545,"children":7546},{"style":2945},[7547],{"type":40,"value":3055},{"type":31,"tag":1215,"props":7549,"children":7550},{"class":1217,"line":1364},[7551],{"type":31,"tag":1215,"props":7552,"children":7553},{"emptyLinePlaceholder":13},[7554],{"type":40,"value":1233},{"type":31,"tag":1215,"props":7556,"children":7557},{"class":1217,"line":1378},[7558],{"type":31,"tag":1215,"props":7559,"children":7560},{"style":2924},[7561],{"type":40,"value":7562},"  \u002F\u002F avancer le temps de 5 secondes\n",{"type":31,"tag":1215,"props":7564,"children":7565},{"class":1217,"line":1387},[7566,7570,7575,7579,7584,7588,7593,7597],{"type":31,"tag":1215,"props":7567,"children":7568},{"style":2933},[7569],{"type":40,"value":7018},{"type":31,"tag":1215,"props":7571,"children":7572},{"style":1250},[7573],{"type":40,"value":7574}," vi",{"type":31,"tag":1215,"props":7576,"children":7577},{"style":3113},[7578],{"type":40,"value":76},{"type":31,"tag":1215,"props":7580,"children":7581},{"style":2939},[7582],{"type":40,"value":7583},"advanceTimersByTimeAsync",{"type":31,"tag":1215,"props":7585,"children":7586},{"style":1250},[7587],{"type":40,"value":3483},{"type":31,"tag":1215,"props":7589,"children":7590},{"style":3047},[7591],{"type":40,"value":7592},"5_000",{"type":31,"tag":1215,"props":7594,"children":7595},{"style":1250},[7596],{"type":40,"value":3002},{"type":31,"tag":1215,"props":7598,"children":7599},{"style":2945},[7600],{"type":40,"value":3055},{"type":31,"tag":1215,"props":7602,"children":7603},{"class":1217,"line":1401},[7604],{"type":31,"tag":1215,"props":7605,"children":7606},{"emptyLinePlaceholder":13},[7607],{"type":40,"value":1233},{"type":31,"tag":1215,"props":7609,"children":7610},{"class":1217,"line":1410},[7611,7615,7620,7625,7629,7634,7638,7643,7647,7652,7656],{"type":31,"tag":1215,"props":7612,"children":7613},{"style":2933},[7614],{"type":40,"value":7018},{"type":31,"tag":1215,"props":7616,"children":7617},{"style":2939},[7618],{"type":40,"value":7619}," expect",{"type":31,"tag":1215,"props":7621,"children":7622},{"style":1250},[7623],{"type":40,"value":7624},"(promise)",{"type":31,"tag":1215,"props":7626,"children":7627},{"style":3113},[7628],{"type":40,"value":76},{"type":31,"tag":1215,"props":7630,"children":7631},{"style":1250},[7632],{"type":40,"value":7633},"rejects",{"type":31,"tag":1215,"props":7635,"children":7636},{"style":3113},[7637],{"type":40,"value":76},{"type":31,"tag":1215,"props":7639,"children":7640},{"style":2939},[7641],{"type":40,"value":7642},"toThrow",{"type":31,"tag":1215,"props":7644,"children":7645},{"style":1250},[7646],{"type":40,"value":3483},{"type":31,"tag":1215,"props":7648,"children":7649},{"style":4392},[7650],{"type":40,"value":7651},"'Brevo timeout'",{"type":31,"tag":1215,"props":7653,"children":7654},{"style":1250},[7655],{"type":40,"value":3002},{"type":31,"tag":1215,"props":7657,"children":7658},{"style":2945},[7659],{"type":40,"value":3055},{"type":31,"tag":1215,"props":7661,"children":7662},{"class":1217,"line":1419},[7663],{"type":31,"tag":1215,"props":7664,"children":7665},{"emptyLinePlaceholder":13},[7666],{"type":40,"value":1233},{"type":31,"tag":1215,"props":7668,"children":7669},{"class":1217,"line":1427},[7670,7674,7678,7683,7687],{"type":31,"tag":1215,"props":7671,"children":7672},{"style":1250},[7673],{"type":40,"value":5998},{"type":31,"tag":1215,"props":7675,"children":7676},{"style":3113},[7677],{"type":40,"value":76},{"type":31,"tag":1215,"props":7679,"children":7680},{"style":2939},[7681],{"type":40,"value":7682},"useRealTimers",{"type":31,"tag":1215,"props":7684,"children":7685},{"style":1250},[7686],{"type":40,"value":3821},{"type":31,"tag":1215,"props":7688,"children":7689},{"style":2945},[7690],{"type":40,"value":3055},{"type":31,"tag":1215,"props":7692,"children":7693},{"class":1217,"line":1436},[7694,7698,7702],{"type":31,"tag":1215,"props":7695,"children":7696},{"style":2945},[7697],{"type":40,"value":5455},{"type":31,"tag":1215,"props":7699,"children":7700},{"style":1250},[7701],{"type":40,"value":3002},{"type":31,"tag":1215,"props":7703,"children":7704},{"style":2945},[7705],{"type":40,"value":3055},{"type":31,"tag":32,"props":7707,"children":7708},{},[7709],{"type":40,"value":7710},"Sans ce test, vous découvrirez le problème le jour où Brevo a une latence anormale et que votre job de notifications se bouche complètement.",{"type":31,"tag":5774,"props":7712,"children":7714},{"id":7713},"test-4-le-test-de-partial-failure",[7715],{"type":40,"value":7716},"Test 4 : le test de partial failure",{"type":31,"tag":32,"props":7718,"children":7719},{},[7720],{"type":40,"value":7721},"Pour les méthodes qui enchainent deux actions (ex : persistance DB + envoi email), simulez l'échec de la deuxième et vérifiez que la première est compensée ou que la méthode est idempotente.",{"type":31,"tag":1206,"props":7723,"children":7725},{"className":2912,"code":7724,"language":2914,"meta":8,"style":8},"it('does not mark session as notified in DB when Brevo send fails', async () => {\n  const session = {\n    id: 'session-partial',\n    coacheeEmail: 'coachee@example.com',\n    scheduledAt: new Date(),\n    notificationSentAt: null as Date | null,\n  };\n\n  vi.mocked(sessionRepository.findById).mockResolvedValue(session);\n  vi.mocked(brevoClient.sendTransactionalEmail).mockRejectedValue(\n    new Error('Brevo SMTP down'),\n  );\n\n  await expect(service.sendSessionConfirmation('session-partial')).rejects.toThrow();\n\n  \u002F\u002F la DB ne doit pas avoir été mise à jour\n  expect(sessionRepository.save).not.toHaveBeenCalled();\n  expect(session.notificationSentAt).toBeNull();\n});\n",[7726],{"type":31,"tag":169,"props":7727,"children":7728},{"__ignoreMap":8},[7729,7765,7784,7804,7823,7850,7890,7898,7905,7949,7989,8018,8029,8036,8097,8104,8112,8158,8196],{"type":31,"tag":1215,"props":7730,"children":7731},{"class":1217,"line":1218},[7732,7736,7740,7745,7749,7753,7757,7761],{"type":31,"tag":1215,"props":7733,"children":7734},{"style":2939},[7735],{"type":40,"value":5829},{"type":31,"tag":1215,"props":7737,"children":7738},{"style":1250},[7739],{"type":40,"value":3483},{"type":31,"tag":1215,"props":7741,"children":7742},{"style":4392},[7743],{"type":40,"value":7744},"'does not mark session as notified in DB when Brevo send fails'",{"type":31,"tag":1215,"props":7746,"children":7747},{"style":2945},[7748],{"type":40,"value":5843},{"type":31,"tag":1215,"props":7750,"children":7751},{"style":2933},[7752],{"type":40,"value":5848},{"type":31,"tag":1215,"props":7754,"children":7755},{"style":2945},[7756],{"type":40,"value":5853},{"type":31,"tag":1215,"props":7758,"children":7759},{"style":2960},[7760],{"type":40,"value":5858},{"type":31,"tag":1215,"props":7762,"children":7763},{"style":2945},[7764],{"type":40,"value":3015},{"type":31,"tag":1215,"props":7766,"children":7767},{"class":1217,"line":817},[7768,7772,7776,7780],{"type":31,"tag":1215,"props":7769,"children":7770},{"style":2933},[7771],{"type":40,"value":3023},{"type":31,"tag":1215,"props":7773,"children":7774},{"style":3026},[7775],{"type":40,"value":5359},{"type":31,"tag":1215,"props":7777,"children":7778},{"style":2960},[7779],{"type":40,"value":3034},{"type":31,"tag":1215,"props":7781,"children":7782},{"style":2945},[7783],{"type":40,"value":3015},{"type":31,"tag":1215,"props":7785,"children":7786},{"class":1217,"line":1236},[7787,7791,7795,7800],{"type":31,"tag":1215,"props":7788,"children":7789},{"style":1250},[7790],{"type":40,"value":6072},{"type":31,"tag":1215,"props":7792,"children":7793},{"style":3113},[7794],{"type":40,"value":2963},{"type":31,"tag":1215,"props":7796,"children":7797},{"style":4392},[7798],{"type":40,"value":7799}," 'session-partial'",{"type":31,"tag":1215,"props":7801,"children":7802},{"style":2945},[7803],{"type":40,"value":2974},{"type":31,"tag":1215,"props":7805,"children":7806},{"class":1217,"line":1246},[7807,7811,7815,7819],{"type":31,"tag":1215,"props":7808,"children":7809},{"style":1250},[7810],{"type":40,"value":6084},{"type":31,"tag":1215,"props":7812,"children":7813},{"style":3113},[7814],{"type":40,"value":2963},{"type":31,"tag":1215,"props":7816,"children":7817},{"style":4392},[7818],{"type":40,"value":6677},{"type":31,"tag":1215,"props":7820,"children":7821},{"style":2945},[7822],{"type":40,"value":2974},{"type":31,"tag":1215,"props":7824,"children":7825},{"class":1217,"line":1256},[7826,7830,7834,7838,7842,7846],{"type":31,"tag":1215,"props":7827,"children":7828},{"style":1250},[7829],{"type":40,"value":6122},{"type":31,"tag":1215,"props":7831,"children":7832},{"style":3113},[7833],{"type":40,"value":2963},{"type":31,"tag":1215,"props":7835,"children":7836},{"style":3809},[7837],{"type":40,"value":3812},{"type":31,"tag":1215,"props":7839,"children":7840},{"style":2939},[7841],{"type":40,"value":6135},{"type":31,"tag":1215,"props":7843,"children":7844},{"style":1250},[7845],{"type":40,"value":3821},{"type":31,"tag":1215,"props":7847,"children":7848},{"style":2945},[7849],{"type":40,"value":2974},{"type":31,"tag":1215,"props":7851,"children":7852},{"class":1217,"line":1264},[7853,7858,7862,7867,7872,7876,7881,7886],{"type":31,"tag":1215,"props":7854,"children":7855},{"style":1250},[7856],{"type":40,"value":7857},"    notificationSentAt",{"type":31,"tag":1215,"props":7859,"children":7860},{"style":3113},[7861],{"type":40,"value":2963},{"type":31,"tag":1215,"props":7863,"children":7864},{"style":2966},[7865],{"type":40,"value":7866}," null",{"type":31,"tag":1215,"props":7868,"children":7869},{"style":2933},[7870],{"type":40,"value":7871}," as",{"type":31,"tag":1215,"props":7873,"children":7874},{"style":3463},[7875],{"type":40,"value":6135},{"type":31,"tag":1215,"props":7877,"children":7878},{"style":2960},[7879],{"type":40,"value":7880}," |",{"type":31,"tag":1215,"props":7882,"children":7884},{"style":7883},"--shiki-default:#CA9EE6;--shiki-default-font-style:italic;--shiki-dark:#79B8FF;--shiki-dark-font-style:inherit",[7885],{"type":40,"value":7866},{"type":31,"tag":1215,"props":7887,"children":7888},{"style":2945},[7889],{"type":40,"value":2974},{"type":31,"tag":1215,"props":7891,"children":7892},{"class":1217,"line":1273},[7893],{"type":31,"tag":1215,"props":7894,"children":7895},{"style":2945},[7896],{"type":40,"value":7897},"  };\n",{"type":31,"tag":1215,"props":7899,"children":7900},{"class":1217,"line":1282},[7901],{"type":31,"tag":1215,"props":7902,"children":7903},{"emptyLinePlaceholder":13},[7904],{"type":40,"value":1233},{"type":31,"tag":1215,"props":7906,"children":7907},{"class":1217,"line":1291},[7908,7912,7916,7920,7924,7928,7932,7936,7940,7945],{"type":31,"tag":1215,"props":7909,"children":7910},{"style":1250},[7911],{"type":40,"value":5998},{"type":31,"tag":1215,"props":7913,"children":7914},{"style":3113},[7915],{"type":40,"value":76},{"type":31,"tag":1215,"props":7917,"children":7918},{"style":2939},[7919],{"type":40,"value":6007},{"type":31,"tag":1215,"props":7921,"children":7922},{"style":1250},[7923],{"type":40,"value":6012},{"type":31,"tag":1215,"props":7925,"children":7926},{"style":3113},[7927],{"type":40,"value":76},{"type":31,"tag":1215,"props":7929,"children":7930},{"style":1250},[7931],{"type":40,"value":6021},{"type":31,"tag":1215,"props":7933,"children":7934},{"style":3113},[7935],{"type":40,"value":76},{"type":31,"tag":1215,"props":7937,"children":7938},{"style":2939},[7939],{"type":40,"value":6207},{"type":31,"tag":1215,"props":7941,"children":7942},{"style":1250},[7943],{"type":40,"value":7944},"(session)",{"type":31,"tag":1215,"props":7946,"children":7947},{"style":2945},[7948],{"type":40,"value":3055},{"type":31,"tag":1215,"props":7950,"children":7951},{"class":1217,"line":1300},[7952,7956,7960,7964,7968,7972,7976,7980,7985],{"type":31,"tag":1215,"props":7953,"children":7954},{"style":1250},[7955],{"type":40,"value":5998},{"type":31,"tag":1215,"props":7957,"children":7958},{"style":3113},[7959],{"type":40,"value":76},{"type":31,"tag":1215,"props":7961,"children":7962},{"style":2939},[7963],{"type":40,"value":6007},{"type":31,"tag":1215,"props":7965,"children":7966},{"style":1250},[7967],{"type":40,"value":6189},{"type":31,"tag":1215,"props":7969,"children":7970},{"style":3113},[7971],{"type":40,"value":76},{"type":31,"tag":1215,"props":7973,"children":7974},{"style":1250},[7975],{"type":40,"value":6198},{"type":31,"tag":1215,"props":7977,"children":7978},{"style":3113},[7979],{"type":40,"value":76},{"type":31,"tag":1215,"props":7981,"children":7982},{"style":2939},[7983],{"type":40,"value":7984},"mockRejectedValue",{"type":31,"tag":1215,"props":7986,"children":7987},{"style":1250},[7988],{"type":40,"value":2948},{"type":31,"tag":1215,"props":7990,"children":7991},{"class":1217,"line":1309},[7992,7997,8001,8005,8010,8014],{"type":31,"tag":1215,"props":7993,"children":7994},{"style":3809},[7995],{"type":40,"value":7996},"    new",{"type":31,"tag":1215,"props":7998,"children":7999},{"style":2939},[8000],{"type":40,"value":5431},{"type":31,"tag":1215,"props":8002,"children":8003},{"style":1250},[8004],{"type":40,"value":3483},{"type":31,"tag":1215,"props":8006,"children":8007},{"style":4392},[8008],{"type":40,"value":8009},"'Brevo SMTP down'",{"type":31,"tag":1215,"props":8011,"children":8012},{"style":1250},[8013],{"type":40,"value":3002},{"type":31,"tag":1215,"props":8015,"children":8016},{"style":2945},[8017],{"type":40,"value":2974},{"type":31,"tag":1215,"props":8019,"children":8020},{"class":1217,"line":1317},[8021,8025],{"type":31,"tag":1215,"props":8022,"children":8023},{"style":1250},[8024],{"type":40,"value":4089},{"type":31,"tag":1215,"props":8026,"children":8027},{"style":2945},[8028],{"type":40,"value":3055},{"type":31,"tag":1215,"props":8030,"children":8031},{"class":1217,"line":1326},[8032],{"type":31,"tag":1215,"props":8033,"children":8034},{"emptyLinePlaceholder":13},[8035],{"type":40,"value":1233},{"type":31,"tag":1215,"props":8037,"children":8038},{"class":1217,"line":1341},[8039,8043,8047,8052,8056,8060,8064,8069,8073,8077,8081,8085,8089,8093],{"type":31,"tag":1215,"props":8040,"children":8041},{"style":2933},[8042],{"type":40,"value":7018},{"type":31,"tag":1215,"props":8044,"children":8045},{"style":2939},[8046],{"type":40,"value":7619},{"type":31,"tag":1215,"props":8048,"children":8049},{"style":1250},[8050],{"type":40,"value":8051},"(service",{"type":31,"tag":1215,"props":8053,"children":8054},{"style":3113},[8055],{"type":40,"value":76},{"type":31,"tag":1215,"props":8057,"children":8058},{"style":2939},[8059],{"type":40,"value":5712},{"type":31,"tag":1215,"props":8061,"children":8062},{"style":1250},[8063],{"type":40,"value":3483},{"type":31,"tag":1215,"props":8065,"children":8066},{"style":4392},[8067],{"type":40,"value":8068},"'session-partial'",{"type":31,"tag":1215,"props":8070,"children":8071},{"style":1250},[8072],{"type":40,"value":6165},{"type":31,"tag":1215,"props":8074,"children":8075},{"style":3113},[8076],{"type":40,"value":76},{"type":31,"tag":1215,"props":8078,"children":8079},{"style":1250},[8080],{"type":40,"value":7633},{"type":31,"tag":1215,"props":8082,"children":8083},{"style":3113},[8084],{"type":40,"value":76},{"type":31,"tag":1215,"props":8086,"children":8087},{"style":2939},[8088],{"type":40,"value":7642},{"type":31,"tag":1215,"props":8090,"children":8091},{"style":1250},[8092],{"type":40,"value":3821},{"type":31,"tag":1215,"props":8094,"children":8095},{"style":2945},[8096],{"type":40,"value":3055},{"type":31,"tag":1215,"props":8098,"children":8099},{"class":1217,"line":1355},[8100],{"type":31,"tag":1215,"props":8101,"children":8102},{"emptyLinePlaceholder":13},[8103],{"type":40,"value":1233},{"type":31,"tag":1215,"props":8105,"children":8106},{"class":1217,"line":1364},[8107],{"type":31,"tag":1215,"props":8108,"children":8109},{"style":2924},[8110],{"type":40,"value":8111},"  \u002F\u002F la DB ne doit pas avoir été mise à jour\n",{"type":31,"tag":1215,"props":8113,"children":8114},{"class":1217,"line":1378},[8115,8119,8123,8127,8132,8136,8141,8145,8150,8154],{"type":31,"tag":1215,"props":8116,"children":8117},{"style":2939},[8118],{"type":40,"value":6436},{"type":31,"tag":1215,"props":8120,"children":8121},{"style":1250},[8122],{"type":40,"value":6012},{"type":31,"tag":1215,"props":8124,"children":8125},{"style":3113},[8126],{"type":40,"value":76},{"type":31,"tag":1215,"props":8128,"children":8129},{"style":1250},[8130],{"type":40,"value":8131},"save)",{"type":31,"tag":1215,"props":8133,"children":8134},{"style":3113},[8135],{"type":40,"value":76},{"type":31,"tag":1215,"props":8137,"children":8138},{"style":1250},[8139],{"type":40,"value":8140},"not",{"type":31,"tag":1215,"props":8142,"children":8143},{"style":3113},[8144],{"type":40,"value":76},{"type":31,"tag":1215,"props":8146,"children":8147},{"style":2939},[8148],{"type":40,"value":8149},"toHaveBeenCalled",{"type":31,"tag":1215,"props":8151,"children":8152},{"style":1250},[8153],{"type":40,"value":3821},{"type":31,"tag":1215,"props":8155,"children":8156},{"style":2945},[8157],{"type":40,"value":3055},{"type":31,"tag":1215,"props":8159,"children":8160},{"class":1217,"line":1387},[8161,8165,8170,8174,8179,8183,8188,8192],{"type":31,"tag":1215,"props":8162,"children":8163},{"style":2939},[8164],{"type":40,"value":6436},{"type":31,"tag":1215,"props":8166,"children":8167},{"style":1250},[8168],{"type":40,"value":8169},"(session",{"type":31,"tag":1215,"props":8171,"children":8172},{"style":3113},[8173],{"type":40,"value":76},{"type":31,"tag":1215,"props":8175,"children":8176},{"style":1250},[8177],{"type":40,"value":8178},"notificationSentAt)",{"type":31,"tag":1215,"props":8180,"children":8181},{"style":3113},[8182],{"type":40,"value":76},{"type":31,"tag":1215,"props":8184,"children":8185},{"style":2939},[8186],{"type":40,"value":8187},"toBeNull",{"type":31,"tag":1215,"props":8189,"children":8190},{"style":1250},[8191],{"type":40,"value":3821},{"type":31,"tag":1215,"props":8193,"children":8194},{"style":2945},[8195],{"type":40,"value":3055},{"type":31,"tag":1215,"props":8197,"children":8198},{"class":1217,"line":1401},[8199,8203,8207],{"type":31,"tag":1215,"props":8200,"children":8201},{"style":2945},[8202],{"type":40,"value":5455},{"type":31,"tag":1215,"props":8204,"children":8205},{"style":1250},[8206],{"type":40,"value":3002},{"type":31,"tag":1215,"props":8208,"children":8209},{"style":2945},[8210],{"type":40,"value":3055},{"type":31,"tag":32,"props":8212,"children":8213},{},[8214],{"type":40,"value":8215},"Ce test révèle souvent un bug d'idempotence : la méthode marque la session \"notifiée\" en DB avant d'envoyer l'email via Brevo, donc si l'envoi échoue et qu'on retry, on n'envoie pas le deuxième email. Le coachee ne reçoit jamais sa confirmation, mais la DB indique que tout va bien.",{"type":31,"tag":48,"props":8217,"children":8218},{},[],{"type":31,"tag":52,"props":8220,"children":8222},{"id":8221},"le-wrapper-anti-fragile-à-demander-à-claude",[8223],{"type":40,"value":8224},"Le wrapper anti-fragile à demander à Claude",{"type":31,"tag":32,"props":8226,"children":8227},{},[8228,8230,8235],{"type":40,"value":8229},"Une fois ces 4 tests écrits, je prompte Claude différemment. Au lieu de \"implémente ",{"type":31,"tag":169,"props":8231,"children":8233},{"className":8232},[],[8234],{"type":40,"value":5712},{"type":40,"value":8236},"\", je dis :",{"type":31,"tag":32,"props":8238,"children":8239},{},[8240],{"type":31,"tag":97,"props":8241,"children":8242},{},[8243,8245,8250,8252,8258],{"type":40,"value":8244},"\"Implémente ",{"type":31,"tag":169,"props":8246,"children":8248},{"className":8247},[],[8249],{"type":40,"value":5712},{"type":40,"value":8251}," dans NestJS en gérant les 429 de Brevo avec un retry exponentiel (p-retry ou helper maison), un timeout sur l'appel réseau (AbortSignal ou p-timeout), et une stratégie d'idempotence basée sur un champ ",{"type":31,"tag":169,"props":8253,"children":8255},{"className":8254},[],[8256],{"type":40,"value":8257},"notificationSentAt",{"type":40,"value":8259}," en DB. Les tests Vitest fournis doivent tous passer.\"",{"type":31,"tag":32,"props":8261,"children":8262},{},[8263],{"type":40,"value":8264},"Voici ce que Claude livre alors :",{"type":31,"tag":1206,"props":8266,"children":8268},{"className":2912,"code":8267,"language":2914,"meta":8,"style":8},"\u002F\u002F apps\u002Fapi\u002Fsrc\u002Finfrastructure\u002Fnotification\u002Fbrevo-notification.service.ts\n\nimport pRetry, { AbortError } from 'p-retry';\n\nconst BREVO_TIMEOUT_MS = 5_000;\nconst BREVO_RETRY_MAX = 5;\n\n@Injectable()\nexport class BrevoNotificationService {\n  constructor(\n    private readonly sessionRepository: SessionRepository,\n    private readonly brevoClient: BrevoClient,\n  ) {}\n\n  async sendSessionConfirmation(sessionId: string): Promise\u003Cvoid> {\n    const session = await this.sessionRepository.findById(sessionId);\n    if (session == null) throw new Error(`Session not found: ${sessionId}`);\n\n    \u002F\u002F idempotence : ne pas renvoyer si déjà envoyé\n    if (session.notificationSentAt != null) return;\n\n    await pRetry(\n      async () => {\n        const controller = new AbortController();\n        const timer = setTimeout(() => controller.abort(), BREVO_TIMEOUT_MS);\n\n        try {\n          await this.brevoClient.sendTransactionalEmail(\n            {\n              to: [{ email: session.coacheeEmail }],\n              templateId: BREVO_TEMPLATE_IDS.SESSION_CONFIRMATION,\n              params: { sessionDate: session.scheduledAt },\n            },\n            { signal: controller.signal },\n          );\n        } catch (err: unknown) {\n          clearTimeout(timer);\n          if (controller.signal.aborted) {\n            throw new AbortError('Brevo timeout');\n          }\n          const statusCode = (err as { statusCode?: number }).statusCode;\n          if (statusCode === 429 || statusCode === 503) throw err; \u002F\u002F retry\n          throw new AbortError((err as Error).message); \u002F\u002F pas de retry sur les 4xx\n        }\n        clearTimeout(timer);\n      },\n      {\n        retries: BREVO_RETRY_MAX,\n        factor: 2,\n        minTimeout: 100,\n        onFailedAttempt: (error) => {\n          if (error.retriesLeft === 0) throw error;\n        },\n      },\n    );\n\n    await this.sessionRepository.markNotified(sessionId, new Date());\n  }\n}\n",[8269],{"type":31,"tag":169,"props":8270,"children":8271},{"__ignoreMap":8},[8272,8279,8286,8330,8337,8363,8388,8395,8410,8429,8440,8467,8494,8505,8512,8567,8614,8683,8690,8698,8741,8748,8763,8783,8817,8883,8890,8902,8935,8944,8997,9026,9067,9076,9111,9124,9164,9182,9218,9252,9261,9325,9387,9439,9448,9465,9474,9483,9504,9526,9547,9581,9628,9637,9645,9658,9666,9721,9729],{"type":31,"tag":1215,"props":8273,"children":8274},{"class":1217,"line":1218},[8275],{"type":31,"tag":1215,"props":8276,"children":8277},{"style":2924},[8278],{"type":40,"value":5160},{"type":31,"tag":1215,"props":8280,"children":8281},{"class":1217,"line":817},[8282],{"type":31,"tag":1215,"props":8283,"children":8284},{"emptyLinePlaceholder":13},[8285],{"type":40,"value":1233},{"type":31,"tag":1215,"props":8287,"children":8288},{"class":1217,"line":1236},[8289,8294,8299,8303,8307,8312,8316,8321,8326],{"type":31,"tag":1215,"props":8290,"children":8291},{"style":2933},[8292],{"type":40,"value":8293},"import",{"type":31,"tag":1215,"props":8295,"children":8296},{"style":1250},[8297],{"type":40,"value":8298}," pRetry",{"type":31,"tag":1215,"props":8300,"children":8301},{"style":2945},[8302],{"type":40,"value":5843},{"type":31,"tag":1215,"props":8304,"children":8305},{"style":2945},[8306],{"type":40,"value":5616},{"type":31,"tag":1215,"props":8308,"children":8309},{"style":1250},[8310],{"type":40,"value":8311}," AbortError ",{"type":31,"tag":1215,"props":8313,"children":8314},{"style":2945},[8315],{"type":40,"value":5455},{"type":31,"tag":1215,"props":8317,"children":8318},{"style":2933},[8319],{"type":40,"value":8320}," from",{"type":31,"tag":1215,"props":8322,"children":8323},{"style":4392},[8324],{"type":40,"value":8325}," 'p-retry'",{"type":31,"tag":1215,"props":8327,"children":8328},{"style":2945},[8329],{"type":40,"value":3055},{"type":31,"tag":1215,"props":8331,"children":8332},{"class":1217,"line":1246},[8333],{"type":31,"tag":1215,"props":8334,"children":8335},{"emptyLinePlaceholder":13},[8336],{"type":40,"value":1233},{"type":31,"tag":1215,"props":8338,"children":8339},{"class":1217,"line":1256},[8340,8345,8350,8354,8359],{"type":31,"tag":1215,"props":8341,"children":8342},{"style":2933},[8343],{"type":40,"value":8344},"const",{"type":31,"tag":1215,"props":8346,"children":8347},{"style":3026},[8348],{"type":40,"value":8349}," BREVO_TIMEOUT_MS",{"type":31,"tag":1215,"props":8351,"children":8352},{"style":2960},[8353],{"type":40,"value":3034},{"type":31,"tag":1215,"props":8355,"children":8356},{"style":3047},[8357],{"type":40,"value":8358}," 5_000",{"type":31,"tag":1215,"props":8360,"children":8361},{"style":2945},[8362],{"type":40,"value":3055},{"type":31,"tag":1215,"props":8364,"children":8365},{"class":1217,"line":1264},[8366,8370,8375,8379,8384],{"type":31,"tag":1215,"props":8367,"children":8368},{"style":2933},[8369],{"type":40,"value":8344},{"type":31,"tag":1215,"props":8371,"children":8372},{"style":3026},[8373],{"type":40,"value":8374}," BREVO_RETRY_MAX",{"type":31,"tag":1215,"props":8376,"children":8377},{"style":2960},[8378],{"type":40,"value":3034},{"type":31,"tag":1215,"props":8380,"children":8381},{"style":3047},[8382],{"type":40,"value":8383}," 5",{"type":31,"tag":1215,"props":8385,"children":8386},{"style":2945},[8387],{"type":40,"value":3055},{"type":31,"tag":1215,"props":8389,"children":8390},{"class":1217,"line":1273},[8391],{"type":31,"tag":1215,"props":8392,"children":8393},{"emptyLinePlaceholder":13},[8394],{"type":40,"value":1233},{"type":31,"tag":1215,"props":8396,"children":8397},{"class":1217,"line":1282},[8398,8402,8406],{"type":31,"tag":1215,"props":8399,"children":8400},{"style":3541},[8401],{"type":40,"value":3544},{"type":31,"tag":1215,"props":8403,"children":8404},{"style":2939},[8405],{"type":40,"value":3549},{"type":31,"tag":1215,"props":8407,"children":8408},{"style":3552},[8409],{"type":40,"value":3555},{"type":31,"tag":1215,"props":8411,"children":8412},{"class":1217,"line":1291},[8413,8417,8421,8425],{"type":31,"tag":1215,"props":8414,"children":8415},{"style":2933},[8416],{"type":40,"value":3455},{"type":31,"tag":1215,"props":8418,"children":8419},{"style":2933},[8420],{"type":40,"value":3567},{"type":31,"tag":1215,"props":8422,"children":8423},{"style":3463},[8424],{"type":40,"value":5198},{"type":31,"tag":1215,"props":8426,"children":8427},{"style":2945},[8428],{"type":40,"value":3015},{"type":31,"tag":1215,"props":8430,"children":8431},{"class":1217,"line":1300},[8432,8436],{"type":31,"tag":1215,"props":8433,"children":8434},{"style":2933},[8435],{"type":40,"value":3988},{"type":31,"tag":1215,"props":8437,"children":8438},{"style":2945},[8439],{"type":40,"value":2948},{"type":31,"tag":1215,"props":8441,"children":8442},{"class":1217,"line":1309},[8443,8447,8451,8455,8459,8463],{"type":31,"tag":1215,"props":8444,"children":8445},{"style":2933},[8446],{"type":40,"value":4000},{"type":31,"tag":1215,"props":8448,"children":8449},{"style":2933},[8450],{"type":40,"value":4005},{"type":31,"tag":1215,"props":8452,"children":8453},{"style":2954},[8454],{"type":40,"value":5229},{"type":31,"tag":1215,"props":8456,"children":8457},{"style":2960},[8458],{"type":40,"value":2963},{"type":31,"tag":1215,"props":8460,"children":8461},{"style":3463},[8462],{"type":40,"value":5238},{"type":31,"tag":1215,"props":8464,"children":8465},{"style":2945},[8466],{"type":40,"value":2974},{"type":31,"tag":1215,"props":8468,"children":8469},{"class":1217,"line":1317},[8470,8474,8478,8482,8486,8490],{"type":31,"tag":1215,"props":8471,"children":8472},{"style":2933},[8473],{"type":40,"value":4000},{"type":31,"tag":1215,"props":8475,"children":8476},{"style":2933},[8477],{"type":40,"value":4005},{"type":31,"tag":1215,"props":8479,"children":8480},{"style":2954},[8481],{"type":40,"value":5258},{"type":31,"tag":1215,"props":8483,"children":8484},{"style":2960},[8485],{"type":40,"value":2963},{"type":31,"tag":1215,"props":8487,"children":8488},{"style":3463},[8489],{"type":40,"value":5267},{"type":31,"tag":1215,"props":8491,"children":8492},{"style":2945},[8493],{"type":40,"value":2974},{"type":31,"tag":1215,"props":8495,"children":8496},{"class":1217,"line":1326},[8497,8501],{"type":31,"tag":1215,"props":8498,"children":8499},{"style":2945},[8500],{"type":40,"value":4089},{"type":31,"tag":1215,"props":8502,"children":8503},{"style":2945},[8504],{"type":40,"value":4094},{"type":31,"tag":1215,"props":8506,"children":8507},{"class":1217,"line":1341},[8508],{"type":31,"tag":1215,"props":8509,"children":8510},{"emptyLinePlaceholder":13},[8511],{"type":40,"value":1233},{"type":31,"tag":1215,"props":8513,"children":8514},{"class":1217,"line":1355},[8515,8519,8523,8527,8531,8535,8539,8543,8547,8551,8555,8559,8563],{"type":31,"tag":1215,"props":8516,"children":8517},{"style":2933},[8518],{"type":40,"value":4109},{"type":31,"tag":1215,"props":8520,"children":8521},{"style":2939},[8522],{"type":40,"value":5301},{"type":31,"tag":1215,"props":8524,"children":8525},{"style":2945},[8526],{"type":40,"value":3483},{"type":31,"tag":1215,"props":8528,"children":8529},{"style":2954},[8530],{"type":40,"value":5310},{"type":31,"tag":1215,"props":8532,"children":8533},{"style":2960},[8534],{"type":40,"value":2963},{"type":31,"tag":1215,"props":8536,"children":8537},{"style":2966},[8538],{"type":40,"value":3783},{"type":31,"tag":1215,"props":8540,"children":8541},{"style":2945},[8542],{"type":40,"value":3002},{"type":31,"tag":1215,"props":8544,"children":8545},{"style":2960},[8546],{"type":40,"value":2963},{"type":31,"tag":1215,"props":8548,"children":8549},{"style":3463},[8550],{"type":40,"value":4145},{"type":31,"tag":1215,"props":8552,"children":8553},{"style":4148},[8554],{"type":40,"value":4151},{"type":31,"tag":1215,"props":8556,"children":8557},{"style":2966},[8558],{"type":40,"value":5339},{"type":31,"tag":1215,"props":8560,"children":8561},{"style":4148},[8562],{"type":40,"value":4161},{"type":31,"tag":1215,"props":8564,"children":8565},{"style":2945},[8566],{"type":40,"value":3015},{"type":31,"tag":1215,"props":8568,"children":8569},{"class":1217,"line":1364},[8570,8574,8578,8582,8586,8590,8594,8598,8602,8606,8610],{"type":31,"tag":1215,"props":8571,"children":8572},{"style":2933},[8573],{"type":40,"value":4173},{"type":31,"tag":1215,"props":8575,"children":8576},{"style":3026},[8577],{"type":40,"value":5359},{"type":31,"tag":1215,"props":8579,"children":8580},{"style":2960},[8581],{"type":40,"value":3034},{"type":31,"tag":1215,"props":8583,"children":8584},{"style":2933},[8585],{"type":40,"value":4187},{"type":31,"tag":1215,"props":8587,"children":8588},{"style":4190},[8589],{"type":40,"value":4193},{"type":31,"tag":1215,"props":8591,"children":8592},{"style":3113},[8593],{"type":40,"value":76},{"type":31,"tag":1215,"props":8595,"children":8596},{"style":1250},[8597],{"type":40,"value":5380},{"type":31,"tag":1215,"props":8599,"children":8600},{"style":3113},[8601],{"type":40,"value":76},{"type":31,"tag":1215,"props":8603,"children":8604},{"style":2939},[8605],{"type":40,"value":4211},{"type":31,"tag":1215,"props":8607,"children":8608},{"style":1250},[8609],{"type":40,"value":5393},{"type":31,"tag":1215,"props":8611,"children":8612},{"style":2945},[8613],{"type":40,"value":3055},{"type":31,"tag":1215,"props":8615,"children":8616},{"class":1217,"line":1378},[8617,8621,8626,8631,8635,8639,8643,8647,8651,8655,8659,8663,8667,8671,8675,8679],{"type":31,"tag":1215,"props":8618,"children":8619},{"style":2933},[8620],{"type":40,"value":4237},{"type":31,"tag":1215,"props":8622,"children":8623},{"style":1250},[8624],{"type":40,"value":8625}," (session ",{"type":31,"tag":1215,"props":8627,"children":8628},{"style":2960},[8629],{"type":40,"value":8630},"==",{"type":31,"tag":1215,"props":8632,"children":8633},{"style":2966},[8634],{"type":40,"value":7866},{"type":31,"tag":1215,"props":8636,"children":8637},{"style":1250},[8638],{"type":40,"value":3140},{"type":31,"tag":1215,"props":8640,"children":8641},{"style":2933},[8642],{"type":40,"value":5422},{"type":31,"tag":1215,"props":8644,"children":8645},{"style":3809},[8646],{"type":40,"value":3812},{"type":31,"tag":1215,"props":8648,"children":8649},{"style":2939},[8650],{"type":40,"value":5431},{"type":31,"tag":1215,"props":8652,"children":8653},{"style":1250},[8654],{"type":40,"value":3483},{"type":31,"tag":1215,"props":8656,"children":8657},{"style":4392},[8658],{"type":40,"value":5440},{"type":31,"tag":1215,"props":8660,"children":8661},{"style":5443},[8662],{"type":40,"value":5446},{"type":31,"tag":1215,"props":8664,"children":8665},{"style":1250},[8666],{"type":40,"value":5310},{"type":31,"tag":1215,"props":8668,"children":8669},{"style":5443},[8670],{"type":40,"value":5455},{"type":31,"tag":1215,"props":8672,"children":8673},{"style":4392},[8674],{"type":40,"value":5460},{"type":31,"tag":1215,"props":8676,"children":8677},{"style":1250},[8678],{"type":40,"value":3002},{"type":31,"tag":1215,"props":8680,"children":8681},{"style":2945},[8682],{"type":40,"value":3055},{"type":31,"tag":1215,"props":8684,"children":8685},{"class":1217,"line":1387},[8686],{"type":31,"tag":1215,"props":8687,"children":8688},{"emptyLinePlaceholder":13},[8689],{"type":40,"value":1233},{"type":31,"tag":1215,"props":8691,"children":8692},{"class":1217,"line":1401},[8693],{"type":31,"tag":1215,"props":8694,"children":8695},{"style":2924},[8696],{"type":40,"value":8697},"    \u002F\u002F idempotence : ne pas renvoyer si déjà envoyé\n",{"type":31,"tag":1215,"props":8699,"children":8700},{"class":1217,"line":1410},[8701,8705,8710,8714,8719,8724,8728,8732,8737],{"type":31,"tag":1215,"props":8702,"children":8703},{"style":2933},[8704],{"type":40,"value":4237},{"type":31,"tag":1215,"props":8706,"children":8707},{"style":1250},[8708],{"type":40,"value":8709}," (session",{"type":31,"tag":1215,"props":8711,"children":8712},{"style":3113},[8713],{"type":40,"value":76},{"type":31,"tag":1215,"props":8715,"children":8716},{"style":1250},[8717],{"type":40,"value":8718},"notificationSentAt ",{"type":31,"tag":1215,"props":8720,"children":8721},{"style":2960},[8722],{"type":40,"value":8723},"!=",{"type":31,"tag":1215,"props":8725,"children":8726},{"style":2966},[8727],{"type":40,"value":7866},{"type":31,"tag":1215,"props":8729,"children":8730},{"style":1250},[8731],{"type":40,"value":3140},{"type":31,"tag":1215,"props":8733,"children":8734},{"style":2933},[8735],{"type":40,"value":8736},"return",{"type":31,"tag":1215,"props":8738,"children":8739},{"style":2945},[8740],{"type":40,"value":3055},{"type":31,"tag":1215,"props":8742,"children":8743},{"class":1217,"line":1419},[8744],{"type":31,"tag":1215,"props":8745,"children":8746},{"emptyLinePlaceholder":13},[8747],{"type":40,"value":1233},{"type":31,"tag":1215,"props":8749,"children":8750},{"class":1217,"line":1427},[8751,8755,8759],{"type":31,"tag":1215,"props":8752,"children":8753},{"style":2933},[8754],{"type":40,"value":4306},{"type":31,"tag":1215,"props":8756,"children":8757},{"style":2939},[8758],{"type":40,"value":8298},{"type":31,"tag":1215,"props":8760,"children":8761},{"style":1250},[8762],{"type":40,"value":2948},{"type":31,"tag":1215,"props":8764,"children":8765},{"class":1217,"line":1436},[8766,8771,8775,8779],{"type":31,"tag":1215,"props":8767,"children":8768},{"style":2933},[8769],{"type":40,"value":8770},"      async",{"type":31,"tag":1215,"props":8772,"children":8773},{"style":2945},[8774],{"type":40,"value":5853},{"type":31,"tag":1215,"props":8776,"children":8777},{"style":2960},[8778],{"type":40,"value":5858},{"type":31,"tag":1215,"props":8780,"children":8781},{"style":2945},[8782],{"type":40,"value":3015},{"type":31,"tag":1215,"props":8784,"children":8785},{"class":1217,"line":1450},[8786,8791,8796,8800,8804,8809,8813],{"type":31,"tag":1215,"props":8787,"children":8788},{"style":2933},[8789],{"type":40,"value":8790},"        const",{"type":31,"tag":1215,"props":8792,"children":8793},{"style":3026},[8794],{"type":40,"value":8795}," controller",{"type":31,"tag":1215,"props":8797,"children":8798},{"style":2960},[8799],{"type":40,"value":3034},{"type":31,"tag":1215,"props":8801,"children":8802},{"style":3809},[8803],{"type":40,"value":3812},{"type":31,"tag":1215,"props":8805,"children":8806},{"style":2939},[8807],{"type":40,"value":8808}," AbortController",{"type":31,"tag":1215,"props":8810,"children":8811},{"style":1250},[8812],{"type":40,"value":3821},{"type":31,"tag":1215,"props":8814,"children":8815},{"style":2945},[8816],{"type":40,"value":3055},{"type":31,"tag":1215,"props":8818,"children":8819},{"class":1217,"line":1463},[8820,8824,8829,8833,8838,8842,8846,8850,8854,8858,8863,8867,8871,8875,8879],{"type":31,"tag":1215,"props":8821,"children":8822},{"style":2933},[8823],{"type":40,"value":8790},{"type":31,"tag":1215,"props":8825,"children":8826},{"style":3026},[8827],{"type":40,"value":8828}," timer",{"type":31,"tag":1215,"props":8830,"children":8831},{"style":2960},[8832],{"type":40,"value":3034},{"type":31,"tag":1215,"props":8834,"children":8835},{"style":2939},[8836],{"type":40,"value":8837}," setTimeout",{"type":31,"tag":1215,"props":8839,"children":8840},{"style":1250},[8841],{"type":40,"value":3483},{"type":31,"tag":1215,"props":8843,"children":8844},{"style":2945},[8845],{"type":40,"value":3821},{"type":31,"tag":1215,"props":8847,"children":8848},{"style":2960},[8849],{"type":40,"value":5858},{"type":31,"tag":1215,"props":8851,"children":8852},{"style":1250},[8853],{"type":40,"value":8795},{"type":31,"tag":1215,"props":8855,"children":8856},{"style":3113},[8857],{"type":40,"value":76},{"type":31,"tag":1215,"props":8859,"children":8860},{"style":2939},[8861],{"type":40,"value":8862},"abort",{"type":31,"tag":1215,"props":8864,"children":8865},{"style":1250},[8866],{"type":40,"value":3821},{"type":31,"tag":1215,"props":8868,"children":8869},{"style":2945},[8870],{"type":40,"value":5843},{"type":31,"tag":1215,"props":8872,"children":8873},{"style":3026},[8874],{"type":40,"value":8349},{"type":31,"tag":1215,"props":8876,"children":8877},{"style":1250},[8878],{"type":40,"value":3002},{"type":31,"tag":1215,"props":8880,"children":8881},{"style":2945},[8882],{"type":40,"value":3055},{"type":31,"tag":1215,"props":8884,"children":8885},{"class":1217,"line":1476},[8886],{"type":31,"tag":1215,"props":8887,"children":8888},{"emptyLinePlaceholder":13},[8889],{"type":40,"value":1233},{"type":31,"tag":1215,"props":8891,"children":8892},{"class":1217,"line":1490},[8893,8898],{"type":31,"tag":1215,"props":8894,"children":8895},{"style":2933},[8896],{"type":40,"value":8897},"        try",{"type":31,"tag":1215,"props":8899,"children":8900},{"style":2945},[8901],{"type":40,"value":3015},{"type":31,"tag":1215,"props":8903,"children":8905},{"class":1217,"line":8904},28,[8906,8911,8915,8919,8923,8927,8931],{"type":31,"tag":1215,"props":8907,"children":8908},{"style":2933},[8909],{"type":40,"value":8910},"          await",{"type":31,"tag":1215,"props":8912,"children":8913},{"style":4190},[8914],{"type":40,"value":4193},{"type":31,"tag":1215,"props":8916,"children":8917},{"style":3113},[8918],{"type":40,"value":76},{"type":31,"tag":1215,"props":8920,"children":8921},{"style":1250},[8922],{"type":40,"value":5495},{"type":31,"tag":1215,"props":8924,"children":8925},{"style":3113},[8926],{"type":40,"value":76},{"type":31,"tag":1215,"props":8928,"children":8929},{"style":2939},[8930],{"type":40,"value":5504},{"type":31,"tag":1215,"props":8932,"children":8933},{"style":1250},[8934],{"type":40,"value":2948},{"type":31,"tag":1215,"props":8936,"children":8938},{"class":1217,"line":8937},29,[8939],{"type":31,"tag":1215,"props":8940,"children":8941},{"style":2945},[8942],{"type":40,"value":8943},"            {\n",{"type":31,"tag":1215,"props":8945,"children":8947},{"class":1217,"line":8946},30,[8948,8953,8957,8961,8965,8969,8973,8977,8981,8985,8989,8993],{"type":31,"tag":1215,"props":8949,"children":8950},{"style":1250},[8951],{"type":40,"value":8952},"              to",{"type":31,"tag":1215,"props":8954,"children":8955},{"style":3113},[8956],{"type":40,"value":2963},{"type":31,"tag":1215,"props":8958,"children":8959},{"style":1250},[8960],{"type":40,"value":5529},{"type":31,"tag":1215,"props":8962,"children":8963},{"style":2945},[8964],{"type":40,"value":5534},{"type":31,"tag":1215,"props":8966,"children":8967},{"style":1250},[8968],{"type":40,"value":5539},{"type":31,"tag":1215,"props":8970,"children":8971},{"style":3113},[8972],{"type":40,"value":2963},{"type":31,"tag":1215,"props":8974,"children":8975},{"style":1250},[8976],{"type":40,"value":5359},{"type":31,"tag":1215,"props":8978,"children":8979},{"style":3113},[8980],{"type":40,"value":76},{"type":31,"tag":1215,"props":8982,"children":8983},{"style":1250},[8984],{"type":40,"value":5556},{"type":31,"tag":1215,"props":8986,"children":8987},{"style":2945},[8988],{"type":40,"value":5455},{"type":31,"tag":1215,"props":8990,"children":8991},{"style":1250},[8992],{"type":40,"value":5565},{"type":31,"tag":1215,"props":8994,"children":8995},{"style":2945},[8996],{"type":40,"value":2974},{"type":31,"tag":1215,"props":8998,"children":9000},{"class":1217,"line":8999},31,[9001,9006,9010,9014,9018,9022],{"type":31,"tag":1215,"props":9002,"children":9003},{"style":1250},[9004],{"type":40,"value":9005},"              templateId",{"type":31,"tag":1215,"props":9007,"children":9008},{"style":3113},[9009],{"type":40,"value":2963},{"type":31,"tag":1215,"props":9011,"children":9012},{"style":3047},[9013],{"type":40,"value":5586},{"type":31,"tag":1215,"props":9015,"children":9016},{"style":3113},[9017],{"type":40,"value":76},{"type":31,"tag":1215,"props":9019,"children":9020},{"style":3047},[9021],{"type":40,"value":5595},{"type":31,"tag":1215,"props":9023,"children":9024},{"style":2945},[9025],{"type":40,"value":2974},{"type":31,"tag":1215,"props":9027,"children":9029},{"class":1217,"line":9028},32,[9030,9035,9039,9043,9047,9051,9055,9059,9063],{"type":31,"tag":1215,"props":9031,"children":9032},{"style":1250},[9033],{"type":40,"value":9034},"              params",{"type":31,"tag":1215,"props":9036,"children":9037},{"style":3113},[9038],{"type":40,"value":2963},{"type":31,"tag":1215,"props":9040,"children":9041},{"style":2945},[9042],{"type":40,"value":5616},{"type":31,"tag":1215,"props":9044,"children":9045},{"style":1250},[9046],{"type":40,"value":5621},{"type":31,"tag":1215,"props":9048,"children":9049},{"style":3113},[9050],{"type":40,"value":2963},{"type":31,"tag":1215,"props":9052,"children":9053},{"style":1250},[9054],{"type":40,"value":5359},{"type":31,"tag":1215,"props":9056,"children":9057},{"style":3113},[9058],{"type":40,"value":76},{"type":31,"tag":1215,"props":9060,"children":9061},{"style":1250},[9062],{"type":40,"value":5638},{"type":31,"tag":1215,"props":9064,"children":9065},{"style":2945},[9066],{"type":40,"value":5643},{"type":31,"tag":1215,"props":9068,"children":9070},{"class":1217,"line":9069},33,[9071],{"type":31,"tag":1215,"props":9072,"children":9073},{"style":2945},[9074],{"type":40,"value":9075},"            },\n",{"type":31,"tag":1215,"props":9077,"children":9079},{"class":1217,"line":9078},34,[9080,9085,9090,9094,9098,9102,9107],{"type":31,"tag":1215,"props":9081,"children":9082},{"style":2945},[9083],{"type":40,"value":9084},"            {",{"type":31,"tag":1215,"props":9086,"children":9087},{"style":1250},[9088],{"type":40,"value":9089}," signal",{"type":31,"tag":1215,"props":9091,"children":9092},{"style":3113},[9093],{"type":40,"value":2963},{"type":31,"tag":1215,"props":9095,"children":9096},{"style":1250},[9097],{"type":40,"value":8795},{"type":31,"tag":1215,"props":9099,"children":9100},{"style":3113},[9101],{"type":40,"value":76},{"type":31,"tag":1215,"props":9103,"children":9104},{"style":1250},[9105],{"type":40,"value":9106},"signal ",{"type":31,"tag":1215,"props":9108,"children":9109},{"style":2945},[9110],{"type":40,"value":5643},{"type":31,"tag":1215,"props":9112,"children":9114},{"class":1217,"line":9113},35,[9115,9120],{"type":31,"tag":1215,"props":9116,"children":9117},{"style":1250},[9118],{"type":40,"value":9119},"          )",{"type":31,"tag":1215,"props":9121,"children":9122},{"style":2945},[9123],{"type":40,"value":3055},{"type":31,"tag":1215,"props":9125,"children":9127},{"class":1217,"line":9126},36,[9128,9133,9138,9142,9147,9151,9156,9160],{"type":31,"tag":1215,"props":9129,"children":9130},{"style":2945},[9131],{"type":40,"value":9132},"        }",{"type":31,"tag":1215,"props":9134,"children":9135},{"style":2933},[9136],{"type":40,"value":9137}," catch",{"type":31,"tag":1215,"props":9139,"children":9140},{"style":2945},[9141],{"type":40,"value":4242},{"type":31,"tag":1215,"props":9143,"children":9144},{"style":2954},[9145],{"type":40,"value":9146},"err",{"type":31,"tag":1215,"props":9148,"children":9149},{"style":2960},[9150],{"type":40,"value":2963},{"type":31,"tag":1215,"props":9152,"children":9153},{"style":2966},[9154],{"type":40,"value":9155}," unknown",{"type":31,"tag":1215,"props":9157,"children":9158},{"style":2945},[9159],{"type":40,"value":3002},{"type":31,"tag":1215,"props":9161,"children":9162},{"style":2945},[9163],{"type":40,"value":3015},{"type":31,"tag":1215,"props":9165,"children":9167},{"class":1217,"line":9166},37,[9168,9173,9178],{"type":31,"tag":1215,"props":9169,"children":9170},{"style":2939},[9171],{"type":40,"value":9172},"          clearTimeout",{"type":31,"tag":1215,"props":9174,"children":9175},{"style":1250},[9176],{"type":40,"value":9177},"(timer)",{"type":31,"tag":1215,"props":9179,"children":9180},{"style":2945},[9181],{"type":40,"value":3055},{"type":31,"tag":1215,"props":9183,"children":9185},{"class":1217,"line":9184},38,[9186,9191,9196,9200,9205,9209,9214],{"type":31,"tag":1215,"props":9187,"children":9188},{"style":2933},[9189],{"type":40,"value":9190},"          if",{"type":31,"tag":1215,"props":9192,"children":9193},{"style":1250},[9194],{"type":40,"value":9195}," (controller",{"type":31,"tag":1215,"props":9197,"children":9198},{"style":3113},[9199],{"type":40,"value":76},{"type":31,"tag":1215,"props":9201,"children":9202},{"style":1250},[9203],{"type":40,"value":9204},"signal",{"type":31,"tag":1215,"props":9206,"children":9207},{"style":3113},[9208],{"type":40,"value":76},{"type":31,"tag":1215,"props":9210,"children":9211},{"style":1250},[9212],{"type":40,"value":9213},"aborted) ",{"type":31,"tag":1215,"props":9215,"children":9216},{"style":2945},[9217],{"type":40,"value":4257},{"type":31,"tag":1215,"props":9219,"children":9221},{"class":1217,"line":9220},39,[9222,9227,9231,9236,9240,9244,9248],{"type":31,"tag":1215,"props":9223,"children":9224},{"style":2933},[9225],{"type":40,"value":9226},"            throw",{"type":31,"tag":1215,"props":9228,"children":9229},{"style":3809},[9230],{"type":40,"value":3812},{"type":31,"tag":1215,"props":9232,"children":9233},{"style":2939},[9234],{"type":40,"value":9235}," AbortError",{"type":31,"tag":1215,"props":9237,"children":9238},{"style":1250},[9239],{"type":40,"value":3483},{"type":31,"tag":1215,"props":9241,"children":9242},{"style":4392},[9243],{"type":40,"value":7651},{"type":31,"tag":1215,"props":9245,"children":9246},{"style":1250},[9247],{"type":40,"value":3002},{"type":31,"tag":1215,"props":9249,"children":9250},{"style":2945},[9251],{"type":40,"value":3055},{"type":31,"tag":1215,"props":9253,"children":9255},{"class":1217,"line":9254},40,[9256],{"type":31,"tag":1215,"props":9257,"children":9258},{"style":2945},[9259],{"type":40,"value":9260},"          }\n",{"type":31,"tag":1215,"props":9262,"children":9264},{"class":1217,"line":9263},41,[9265,9270,9274,9278,9283,9287,9291,9295,9300,9304,9308,9312,9316,9321],{"type":31,"tag":1215,"props":9266,"children":9267},{"style":2933},[9268],{"type":40,"value":9269},"          const",{"type":31,"tag":1215,"props":9271,"children":9272},{"style":3026},[9273],{"type":40,"value":6923},{"type":31,"tag":1215,"props":9275,"children":9276},{"style":2960},[9277],{"type":40,"value":3034},{"type":31,"tag":1215,"props":9279,"children":9280},{"style":1250},[9281],{"type":40,"value":9282}," (err ",{"type":31,"tag":1215,"props":9284,"children":9285},{"style":2933},[9286],{"type":40,"value":6904},{"type":31,"tag":1215,"props":9288,"children":9289},{"style":2945},[9290],{"type":40,"value":5616},{"type":31,"tag":1215,"props":9292,"children":9293},{"style":6920},[9294],{"type":40,"value":6923},{"type":31,"tag":1215,"props":9296,"children":9297},{"style":2960},[9298],{"type":40,"value":9299},"?:",{"type":31,"tag":1215,"props":9301,"children":9302},{"style":2966},[9303],{"type":40,"value":2969},{"type":31,"tag":1215,"props":9305,"children":9306},{"style":2945},[9307],{"type":40,"value":7476},{"type":31,"tag":1215,"props":9309,"children":9310},{"style":1250},[9311],{"type":40,"value":3002},{"type":31,"tag":1215,"props":9313,"children":9314},{"style":3113},[9315],{"type":40,"value":76},{"type":31,"tag":1215,"props":9317,"children":9318},{"style":1250},[9319],{"type":40,"value":9320},"statusCode",{"type":31,"tag":1215,"props":9322,"children":9323},{"style":2945},[9324],{"type":40,"value":3055},{"type":31,"tag":1215,"props":9326,"children":9328},{"class":1217,"line":9327},42,[9329,9333,9338,9342,9346,9351,9356,9360,9365,9369,9373,9377,9382],{"type":31,"tag":1215,"props":9330,"children":9331},{"style":2933},[9332],{"type":40,"value":9190},{"type":31,"tag":1215,"props":9334,"children":9335},{"style":1250},[9336],{"type":40,"value":9337}," (statusCode ",{"type":31,"tag":1215,"props":9339,"children":9340},{"style":2960},[9341],{"type":40,"value":6415},{"type":31,"tag":1215,"props":9343,"children":9344},{"style":3047},[9345],{"type":40,"value":6962},{"type":31,"tag":1215,"props":9347,"children":9348},{"style":2960},[9349],{"type":40,"value":9350}," ||",{"type":31,"tag":1215,"props":9352,"children":9353},{"style":1250},[9354],{"type":40,"value":9355}," statusCode ",{"type":31,"tag":1215,"props":9357,"children":9358},{"style":2960},[9359],{"type":40,"value":6415},{"type":31,"tag":1215,"props":9361,"children":9362},{"style":3047},[9363],{"type":40,"value":9364}," 503",{"type":31,"tag":1215,"props":9366,"children":9367},{"style":1250},[9368],{"type":40,"value":3140},{"type":31,"tag":1215,"props":9370,"children":9371},{"style":2933},[9372],{"type":40,"value":5422},{"type":31,"tag":1215,"props":9374,"children":9375},{"style":1250},[9376],{"type":40,"value":6874},{"type":31,"tag":1215,"props":9378,"children":9379},{"style":2945},[9380],{"type":40,"value":9381},";",{"type":31,"tag":1215,"props":9383,"children":9384},{"style":2924},[9385],{"type":40,"value":9386}," \u002F\u002F retry\n",{"type":31,"tag":1215,"props":9388,"children":9390},{"class":1217,"line":9389},43,[9391,9396,9400,9404,9409,9413,9417,9421,9425,9430,9434],{"type":31,"tag":1215,"props":9392,"children":9393},{"style":2933},[9394],{"type":40,"value":9395},"          throw",{"type":31,"tag":1215,"props":9397,"children":9398},{"style":3809},[9399],{"type":40,"value":3812},{"type":31,"tag":1215,"props":9401,"children":9402},{"style":2939},[9403],{"type":40,"value":9235},{"type":31,"tag":1215,"props":9405,"children":9406},{"style":1250},[9407],{"type":40,"value":9408},"((err ",{"type":31,"tag":1215,"props":9410,"children":9411},{"style":2933},[9412],{"type":40,"value":6904},{"type":31,"tag":1215,"props":9414,"children":9415},{"style":3463},[9416],{"type":40,"value":5431},{"type":31,"tag":1215,"props":9418,"children":9419},{"style":1250},[9420],{"type":40,"value":3002},{"type":31,"tag":1215,"props":9422,"children":9423},{"style":3113},[9424],{"type":40,"value":76},{"type":31,"tag":1215,"props":9426,"children":9427},{"style":1250},[9428],{"type":40,"value":9429},"message)",{"type":31,"tag":1215,"props":9431,"children":9432},{"style":2945},[9433],{"type":40,"value":9381},{"type":31,"tag":1215,"props":9435,"children":9436},{"style":2924},[9437],{"type":40,"value":9438}," \u002F\u002F pas de retry sur les 4xx\n",{"type":31,"tag":1215,"props":9440,"children":9442},{"class":1217,"line":9441},44,[9443],{"type":31,"tag":1215,"props":9444,"children":9445},{"style":2945},[9446],{"type":40,"value":9447},"        }\n",{"type":31,"tag":1215,"props":9449,"children":9451},{"class":1217,"line":9450},45,[9452,9457,9461],{"type":31,"tag":1215,"props":9453,"children":9454},{"style":2939},[9455],{"type":40,"value":9456},"        clearTimeout",{"type":31,"tag":1215,"props":9458,"children":9459},{"style":1250},[9460],{"type":40,"value":9177},{"type":31,"tag":1215,"props":9462,"children":9463},{"style":2945},[9464],{"type":40,"value":3055},{"type":31,"tag":1215,"props":9466,"children":9468},{"class":1217,"line":9467},46,[9469],{"type":31,"tag":1215,"props":9470,"children":9471},{"style":2945},[9472],{"type":40,"value":9473},"      },\n",{"type":31,"tag":1215,"props":9475,"children":9477},{"class":1217,"line":9476},47,[9478],{"type":31,"tag":1215,"props":9479,"children":9480},{"style":2945},[9481],{"type":40,"value":9482},"      {\n",{"type":31,"tag":1215,"props":9484,"children":9486},{"class":1217,"line":9485},48,[9487,9492,9496,9500],{"type":31,"tag":1215,"props":9488,"children":9489},{"style":1250},[9490],{"type":40,"value":9491},"        retries",{"type":31,"tag":1215,"props":9493,"children":9494},{"style":3113},[9495],{"type":40,"value":2963},{"type":31,"tag":1215,"props":9497,"children":9498},{"style":3026},[9499],{"type":40,"value":8374},{"type":31,"tag":1215,"props":9501,"children":9502},{"style":2945},[9503],{"type":40,"value":2974},{"type":31,"tag":1215,"props":9505,"children":9507},{"class":1217,"line":9506},49,[9508,9513,9517,9522],{"type":31,"tag":1215,"props":9509,"children":9510},{"style":1250},[9511],{"type":40,"value":9512},"        factor",{"type":31,"tag":1215,"props":9514,"children":9515},{"style":3113},[9516],{"type":40,"value":2963},{"type":31,"tag":1215,"props":9518,"children":9519},{"style":3047},[9520],{"type":40,"value":9521}," 2",{"type":31,"tag":1215,"props":9523,"children":9524},{"style":2945},[9525],{"type":40,"value":2974},{"type":31,"tag":1215,"props":9527,"children":9529},{"class":1217,"line":9528},50,[9530,9535,9539,9543],{"type":31,"tag":1215,"props":9531,"children":9532},{"style":1250},[9533],{"type":40,"value":9534},"        minTimeout",{"type":31,"tag":1215,"props":9536,"children":9537},{"style":3113},[9538],{"type":40,"value":2963},{"type":31,"tag":1215,"props":9540,"children":9541},{"style":3047},[9542],{"type":40,"value":3135},{"type":31,"tag":1215,"props":9544,"children":9545},{"style":2945},[9546],{"type":40,"value":2974},{"type":31,"tag":1215,"props":9548,"children":9550},{"class":1217,"line":9549},51,[9551,9556,9560,9564,9569,9573,9577],{"type":31,"tag":1215,"props":9552,"children":9553},{"style":2939},[9554],{"type":40,"value":9555},"        onFailedAttempt",{"type":31,"tag":1215,"props":9557,"children":9558},{"style":3113},[9559],{"type":40,"value":2963},{"type":31,"tag":1215,"props":9561,"children":9562},{"style":2945},[9563],{"type":40,"value":4242},{"type":31,"tag":1215,"props":9565,"children":9566},{"style":2954},[9567],{"type":40,"value":9568},"error",{"type":31,"tag":1215,"props":9570,"children":9571},{"style":2945},[9572],{"type":40,"value":3002},{"type":31,"tag":1215,"props":9574,"children":9575},{"style":2960},[9576],{"type":40,"value":5858},{"type":31,"tag":1215,"props":9578,"children":9579},{"style":2945},[9580],{"type":40,"value":3015},{"type":31,"tag":1215,"props":9582,"children":9584},{"class":1217,"line":9583},52,[9585,9589,9594,9598,9603,9607,9611,9615,9619,9624],{"type":31,"tag":1215,"props":9586,"children":9587},{"style":2933},[9588],{"type":40,"value":9190},{"type":31,"tag":1215,"props":9590,"children":9591},{"style":1250},[9592],{"type":40,"value":9593}," (error",{"type":31,"tag":1215,"props":9595,"children":9596},{"style":3113},[9597],{"type":40,"value":76},{"type":31,"tag":1215,"props":9599,"children":9600},{"style":1250},[9601],{"type":40,"value":9602},"retriesLeft ",{"type":31,"tag":1215,"props":9604,"children":9605},{"style":2960},[9606],{"type":40,"value":6415},{"type":31,"tag":1215,"props":9608,"children":9609},{"style":3047},[9610],{"type":40,"value":3694},{"type":31,"tag":1215,"props":9612,"children":9613},{"style":1250},[9614],{"type":40,"value":3140},{"type":31,"tag":1215,"props":9616,"children":9617},{"style":2933},[9618],{"type":40,"value":5422},{"type":31,"tag":1215,"props":9620,"children":9621},{"style":1250},[9622],{"type":40,"value":9623}," error",{"type":31,"tag":1215,"props":9625,"children":9626},{"style":2945},[9627],{"type":40,"value":3055},{"type":31,"tag":1215,"props":9629,"children":9631},{"class":1217,"line":9630},53,[9632],{"type":31,"tag":1215,"props":9633,"children":9634},{"style":2945},[9635],{"type":40,"value":9636},"        },\n",{"type":31,"tag":1215,"props":9638,"children":9640},{"class":1217,"line":9639},54,[9641],{"type":31,"tag":1215,"props":9642,"children":9643},{"style":2945},[9644],{"type":40,"value":9473},{"type":31,"tag":1215,"props":9646,"children":9648},{"class":1217,"line":9647},55,[9649,9654],{"type":31,"tag":1215,"props":9650,"children":9651},{"style":1250},[9652],{"type":40,"value":9653},"    )",{"type":31,"tag":1215,"props":9655,"children":9656},{"style":2945},[9657],{"type":40,"value":3055},{"type":31,"tag":1215,"props":9659,"children":9661},{"class":1217,"line":9660},56,[9662],{"type":31,"tag":1215,"props":9663,"children":9664},{"emptyLinePlaceholder":13},[9665],{"type":40,"value":1233},{"type":31,"tag":1215,"props":9667,"children":9669},{"class":1217,"line":9668},57,[9670,9674,9678,9682,9686,9690,9695,9700,9704,9708,9712,9717],{"type":31,"tag":1215,"props":9671,"children":9672},{"style":2933},[9673],{"type":40,"value":4306},{"type":31,"tag":1215,"props":9675,"children":9676},{"style":4190},[9677],{"type":40,"value":4193},{"type":31,"tag":1215,"props":9679,"children":9680},{"style":3113},[9681],{"type":40,"value":76},{"type":31,"tag":1215,"props":9683,"children":9684},{"style":1250},[9685],{"type":40,"value":5380},{"type":31,"tag":1215,"props":9687,"children":9688},{"style":3113},[9689],{"type":40,"value":76},{"type":31,"tag":1215,"props":9691,"children":9692},{"style":2939},[9693],{"type":40,"value":9694},"markNotified",{"type":31,"tag":1215,"props":9696,"children":9697},{"style":1250},[9698],{"type":40,"value":9699},"(sessionId",{"type":31,"tag":1215,"props":9701,"children":9702},{"style":2945},[9703],{"type":40,"value":5843},{"type":31,"tag":1215,"props":9705,"children":9706},{"style":3809},[9707],{"type":40,"value":3812},{"type":31,"tag":1215,"props":9709,"children":9710},{"style":2939},[9711],{"type":40,"value":6135},{"type":31,"tag":1215,"props":9713,"children":9714},{"style":1250},[9715],{"type":40,"value":9716},"())",{"type":31,"tag":1215,"props":9718,"children":9719},{"style":2945},[9720],{"type":40,"value":3055},{"type":31,"tag":1215,"props":9722,"children":9724},{"class":1217,"line":9723},58,[9725],{"type":31,"tag":1215,"props":9726,"children":9727},{"style":2945},[9728],{"type":40,"value":3710},{"type":31,"tag":1215,"props":9730,"children":9732},{"class":1217,"line":9731},59,[9733],{"type":31,"tag":1215,"props":9734,"children":9735},{"style":2945},[9736],{"type":40,"value":3160},{"type":31,"tag":32,"props":9738,"children":9739},{},[9740,9742,9747,9749,9755],{"type":40,"value":9741},"Quelques lignes de plus en surface. Les 4 modes d'échec couverts. Le wrapper anti-fragile est la combinaison qui rend Claude utilisable en prod. C'est ce que recommande Martin Fowler dans ",{"type":31,"tag":97,"props":9743,"children":9744},{},[9745],{"type":40,"value":9746},"Patterns of Enterprise Application Architecture",{"type":40,"value":9748}," (2002) sur les ",{"type":31,"tag":69,"props":9750,"children":9752},{"href":9751},"\u002Ffr\u002Farchitecture-craft\u002Fpatterns-resilience-circuit-breaker-retry",[9753],{"type":40,"value":9754},"patterns de résilience type Circuit Breaker et Retry",{"type":40,"value":76},{"type":31,"tag":48,"props":9757,"children":9758},{},[],{"type":31,"tag":52,"props":9760,"children":9762},{"id":9761},"comment-intégrer-ces-tests-sans-ralentir-la-ci",[9763],{"type":40,"value":9764},"Comment intégrer ces tests sans ralentir la CI",{"type":31,"tag":32,"props":9766,"children":9767},{},[9768,9770,9775],{"type":40,"value":9769},"L'objection classique : ",{"type":31,"tag":97,"props":9771,"children":9772},{},[9773],{"type":40,"value":9774},"\"On va mettre 15 minutes de CI au lieu de 4.\"",{"type":40,"value":9776}," Voici comment je structure la pyramide sur crmcoaching pour que ça ne soit pas le cas.",{"type":31,"tag":32,"props":9778,"children":9779},{},[9780,9785,9787,9792],{"type":31,"tag":36,"props":9781,"children":9782},{},[9783],{"type":40,"value":9784},"Etage 1 : tests unitaires Vitest (90% du volume).",{"type":40,"value":9786}," Tournent en moins de 2 minutes. Tous les comportements métier sont couverts ici, en utilisant des fakes en mémoire ou des mocks ",{"type":31,"tag":169,"props":9788,"children":9790},{"className":9789},[],[9791],{"type":40,"value":5699},{"type":40,"value":9793}," au lieu de vraies dépendances. C'est l'etage où les 4 tests \"concurrence, 429, timeout, partial failure\" vivent, mais sur du code instrumenté pour ne pas réellement faire d'I\u002FO réseau vers Brevo.",{"type":31,"tag":32,"props":9795,"children":9796},{},[9797,9802,9804,9810],{"type":31,"tag":36,"props":9798,"children":9799},{},[9800],{"type":40,"value":9801},"Etage 2 : tests d'intégration NestJS (8% du volume).",{"type":40,"value":9803}," Tournent en 3-5 minutes en parallèle. Valident que les adapters (Prisma, client Brevo, jobs Bull) fonctionnent avec leurs vraies implémentations contre des conteneurs Docker (Postgres, Brevo sandbox). On ne re-teste pas la logique métier ici, on teste l'intégration. Tout ce qui touche au piège classique des ",{"type":31,"tag":69,"props":9805,"children":9807},{"href":9806},"\u002Ffr\u002Fdette-technique\u002Ftests-integration-legacy-pieges",[9808],{"type":40,"value":9809},"tests d'intégration sur du code legacy",{"type":40,"value":9811}," est traité dans un article dédié.",{"type":31,"tag":32,"props":9813,"children":9814},{},[9815,9820],{"type":31,"tag":36,"props":9816,"children":9817},{},[9818],{"type":40,"value":9819},"Etage 3 : tests end-to-end (2% du volume).",{"type":40,"value":9821}," Tournent en 5-10 minutes sur un seul scénario critique end-to-end, en pre-prod. C'est l'etage \"ça marche vraiment quand tout est branché, Brevo compris\".",{"type":31,"tag":32,"props":9823,"children":9824},{},[9825,9827,9831],{"type":40,"value":9826},"Avec cette pyramide, ajouter 4 tests de modes d'échec à l'etage 1 coûte 0,5 seconde de plus en CI par module. C'est négligeable. C'est exactement le sujet que je traite quand j'aide une équipe à se rapprocher de la ",{"type":31,"tag":69,"props":9828,"children":9829},{"href":1154},[9830],{"type":40,"value":1157},{"type":40,"value":76},{"type":31,"tag":48,"props":9833,"children":9834},{},[],{"type":31,"tag":52,"props":9836,"children":9837},{"id":1565},[9838],{"type":40,"value":1568},{"type":31,"tag":32,"props":9840,"children":9841},{},[9842],{"type":40,"value":9843},"Depuis que j'ai installé cette discipline sur crmcoaching, les chiffres sur 3 mois sont nets.",{"type":31,"tag":398,"props":9845,"children":9846},{},[9847,9867],{"type":31,"tag":402,"props":9848,"children":9849},{},[9850],{"type":31,"tag":406,"props":9851,"children":9852},{},[9853,9857,9862],{"type":31,"tag":410,"props":9854,"children":9855},{},[9856],{"type":40,"value":2217},{"type":31,"tag":410,"props":9858,"children":9859},{},[9860],{"type":40,"value":9861},"Avant",{"type":31,"tag":410,"props":9863,"children":9864},{},[9865],{"type":40,"value":9866},"Après 3 mois",{"type":31,"tag":426,"props":9868,"children":9869},{},[9870,9888,9906,9924],{"type":31,"tag":406,"props":9871,"children":9872},{},[9873,9878,9883],{"type":31,"tag":433,"props":9874,"children":9875},{},[9876],{"type":40,"value":9877},"Incidents prod liés à un mode d'échec non testé",{"type":31,"tag":433,"props":9879,"children":9880},{},[9881],{"type":40,"value":9882},"3 par trimestre",{"type":31,"tag":433,"props":9884,"children":9885},{},[9886],{"type":40,"value":9887},"0 par trimestre",{"type":31,"tag":406,"props":9889,"children":9890},{},[9891,9896,9901],{"type":31,"tag":433,"props":9892,"children":9893},{},[9894],{"type":40,"value":9895},"MTTR (temps moyen de restauration)",{"type":31,"tag":433,"props":9897,"children":9898},{},[9899],{"type":40,"value":9900},"2h30",{"type":31,"tag":433,"props":9902,"children":9903},{},[9904],{"type":40,"value":9905},"30 minutes",{"type":31,"tag":406,"props":9907,"children":9908},{},[9909,9914,9919],{"type":31,"tag":433,"props":9910,"children":9911},{},[9912],{"type":40,"value":9913},"Confiance pour déployer le vendredi",{"type":31,"tag":433,"props":9915,"children":9916},{},[9917],{"type":40,"value":9918},"\"pas avant lundi\"",{"type":31,"tag":433,"props":9920,"children":9921},{},[9922],{"type":40,"value":9923},"\"sans stress\"",{"type":31,"tag":406,"props":9925,"children":9926},{},[9927,9932,9937],{"type":31,"tag":433,"props":9928,"children":9929},{},[9930],{"type":40,"value":9931},"Heures consacrées au debug post-incident",{"type":31,"tag":433,"props":9933,"children":9934},{},[9935],{"type":40,"value":9936},"12h par trimestre",{"type":31,"tag":433,"props":9938,"children":9939},{},[9940],{"type":40,"value":9941},"2h par trimestre",{"type":31,"tag":32,"props":9943,"children":9944},{},[9945,9947,9952],{"type":40,"value":9946},"Le gain n'est pas que technique. Quand je sais que mes tests couvrent les modes d'échec, je déploie sereinement. Quand je crains mes propres déploiements, je me freine, je retarde les releases, je perds du flux. La métrique sous-jacente, c'est ce que la DORA appelle le ",{"type":31,"tag":97,"props":9948,"children":9949},{},[9950],{"type":40,"value":9951},"Mean Time to Restore",{"type":40,"value":9953},", et c'est l'un des 4 indicateurs de performance d'une équipe engineering.",{"type":31,"tag":48,"props":9955,"children":9956},{},[],{"type":31,"tag":52,"props":9958,"children":9959},{"id":637},[9960],{"type":40,"value":640},{"type":31,"tag":32,"props":9962,"children":9963},{},[9964],{"type":40,"value":9965},"Ce que je veux que vous reteniez de cet article, c'est que les tests Claude couvrent ce que Claude voit. Et Claude ne voit pas la prod. Il ne voit pas la concurrence à 2h du matin sur un weekend. Il ne voit pas le rate limit de Brevo qui change à 30 secondes près. Il ne voit pas le timeout silencieux qui bouche votre queue de jobs. Si vous lui demandez \"écris les tests\", vous obtenez le filet du happy path. Si vous lui demandez \"écris les 4 tests de modes d'échec critiques\", vous obtenez un filet utile.",{"type":31,"tag":32,"props":9967,"children":9968},{},[9969],{"type":40,"value":9970},"Votre rôle de développeur solo ou de senior en 2026 n'est pas de relire des assertions. C'est de nommer explicitement les 4 modes d'échec que Claude doit tester, et d'exiger ces tests dans votre Definition of Done. Si vous tenez cette discipline, vos déploiements du vendredi deviennent calmes. Si vous la lâchez, vous découvrez vos modes d'échec en plein milieu de la nuit, et chaque incident coûte un cran de confiance que vous ne récupérez pas facilement.",{"type":31,"tag":32,"props":9972,"children":9973},{},[9974],{"type":40,"value":9975},"Si en lisant ces lignes vous reconnaissez votre situation, vous avez deux choix. Vous pouvez attendre le prochain crash 429 nocturne. Ou vous pouvez commencer lundi matin, par 4 tests supplémentaires sur la méthode critique de votre choix, et apprendre à dormir tranquille.",{"type":31,"tag":32,"props":9977,"children":9978},{},[9979,9981,9986],{"type":40,"value":9980},"Pour la suite des patterns de tests anti-fragiles, retrouvez-moi sur ",{"type":31,"tag":69,"props":9982,"children":9984},{"href":663,"rel":9983},[665],[9985],{"type":40,"value":668},{"type":40,"value":9987},", où je publie chaque semaine les cas réels rencontrés sur crmcoaching.",{"type":31,"tag":229,"props":9989,"children":9991},{"cta":673,"href":674,"title":9990,"type":676},"Tester les modes d'échec n'est qu'une des 100 pratiques craft",[9992],{"type":31,"tag":32,"props":9993,"children":9994},{},[9995],{"type":40,"value":9996},"Exiger les 4 tests de modes d'échec sur du code généré par Claude, c'est une seule pratique parmi celles qui font la différence en prod. Le Craft Bundle réunit les 100 pratiques que j'applique pour coder propre et résilient, celles que l'IA ne vous apprendra jamais parce qu'elle ne les a jamais vues planter à 2h du matin sous un rate limit Brevo.",{"type":31,"tag":48,"props":9998,"children":9999},{},[],{"type":31,"tag":52,"props":10001,"children":10003},{"id":10002},"faq-sur-les-tests-claude-et-la-résilience-prod",[10004],{"type":40,"value":10005},"FAQ sur les tests Claude et la résilience prod",{"type":31,"tag":693,"props":10007,"children":10008},{},[10009,10014],{"type":31,"tag":697,"props":10010,"children":10011},{},[10012],{"type":40,"value":10013},"1. Faut-il écrire ces 4 tests pour chaque fonction du repo ?",{"type":31,"tag":32,"props":10015,"children":10016},{},[10017],{"type":40,"value":10018},"Non, ce serait disproportionné. La règle que j'applique : ces 4 tests sont obligatoires sur toute fonction qui touche à un service externe (réseau, DB, queue, file system). Sur les fonctions pures (calcul, transformation), un test unitaire classique suffit. Le test de criticité, c'est : \"si cette fonction échoue, est-ce qu'un utilisateur ou un système externe en souffre ?\". Si oui, les 4 tests.",{"type":31,"tag":693,"props":10020,"children":10021},{},[10022,10027],{"type":31,"tag":697,"props":10023,"children":10024},{},[10025],{"type":40,"value":10026},"2. Comment introduire cette discipline sans bloquer les PR existantes ?",{"type":31,"tag":32,"props":10028,"children":10029},{},[10030],{"type":40,"value":10031},"Je recommande deux phases. Phase 1 (3 mois) : tout nouveau code qui touche à un service externe doit avoir les 4 tests. Pas de rétroactif. Phase 2 (6 mois suivants) : on remonte le backlog, en commençant par les 10 fonctions les plus critiques (celles que vous citeriez en post-mortem). C'est la stratégie Boy Scout Rule appliquée aux tests, comme je le décris dans la Boy Scout Rule du développement clean code : améliorer ce qu'on touche, sans tout réécrire d'un coup.",{"type":31,"tag":693,"props":10033,"children":10034},{},[10035,10040],{"type":31,"tag":697,"props":10036,"children":10037},{},[10038],{"type":40,"value":10039},"3. Quelles librairies pour le retry et le timeout en TypeScript ?",{"type":31,"tag":32,"props":10041,"children":10042},{},[10043,10045,10050,10052,10058,10060,10066,10067,10073,10075,10081],{"type":40,"value":10044},"Sur crmcoaching j'utilise ",{"type":31,"tag":169,"props":10046,"children":10048},{"className":10047},[],[10049],{"type":40,"value":712},{"type":40,"value":10051}," (retry exponentiel avec hook ",{"type":31,"tag":169,"props":10053,"children":10055},{"className":10054},[],[10056],{"type":40,"value":10057},"onFailedAttempt",{"type":40,"value":10059},") et ",{"type":31,"tag":169,"props":10061,"children":10063},{"className":10062},[],[10064],{"type":40,"value":10065},"p-timeout",{"type":40,"value":5791},{"type":31,"tag":169,"props":10068,"children":10070},{"className":10069},[],[10071],{"type":40,"value":10072},"AbortSignal",{"type":40,"value":10074}," natif pour le timeout. Les deux sont légères, bien maintenues, et s'intègrent sans friction dans NestJS. Pour le circuit breaker si besoin, ",{"type":31,"tag":169,"props":10076,"children":10078},{"className":10077},[],[10079],{"type":40,"value":10080},"opossum",{"type":40,"value":10082}," est la référence Node.js. Le choix d'outil compte moins que la discipline d'instrumenter : l'important c'est que la stratégie soit explicite et testée, pas qu'elle utilise telle ou telle lib.",{"type":31,"tag":693,"props":10084,"children":10085},{},[10086,10091],{"type":31,"tag":697,"props":10087,"children":10088},{},[10089],{"type":40,"value":10090},"4. Quel rapport entre ces tests et la métrique DORA \"MTTR\" ?",{"type":31,"tag":32,"props":10092,"children":10093},{},[10094],{"type":40,"value":10095},"Direct. Le MTTR (Mean Time to Restore) mesure le temps moyen entre la détection d'un incident et sa résolution. Un incident causé par un mode d'échec testé en CI ne devrait jamais arriver en prod (c'est la prévention). Un incident sur un mode d'échec non testé prend en moyenne 6x plus de temps à diagnostiquer, parce qu'on découvre le comportement en live. Tester en amont = MTTR plus court en aval.",{"type":31,"tag":693,"props":10097,"children":10098},{},[10099,10104],{"type":31,"tag":697,"props":10100,"children":10101},{},[10102],{"type":40,"value":10103},"5. Comment convaincre un investisseur ou un client sceptique sur le ROI de ces tests ?",{"type":31,"tag":32,"props":10105,"children":10106},{},[10107,10109,10113],{"type":40,"value":10108},"Avec deux chiffres. D'abord, le coût d'un incident rate-limit Brevo comme celui que je décris : journée perdue à déboguer, coachees absents à leurs séances, perte de confiance dans le SaaS. Ensuite, le coût de prévention : 4 tests en plus, 1 heure de dev. Le rapport est sans commune mesure. C'est l'angle business que je détaille dans ",{"type":31,"tag":69,"props":10110,"children":10111},{"href":4622},[10112],{"type":40,"value":4625},{"type":40,"value":76},{"type":31,"tag":693,"props":10115,"children":10116},{},[10117,10122],{"type":31,"tag":697,"props":10118,"children":10119},{},[10120],{"type":40,"value":10121},"6. Et si Claude refuse de générer ces tests parce que le code n'est pas \"testable\" ?",{"type":31,"tag":32,"props":10123,"children":10124},{},[10125,10127,10139],{"type":40,"value":10126},"C'est un signal précieux. Si Claude n'arrive pas à écrire un test de concurrence sur votre fonction, c'est probablement que la fonction n'est pas découplée correctement de ses dépendances. C'est l'occasion de re-prompter avec une contrainte d'inversion de dépendance : ",{"type":31,"tag":97,"props":10128,"children":10129},{},[10130,10132,10137],{"type":40,"value":10131},"\"Réécris la classe pour qu'elle injecte ses dépendances via le constructeur NestJS, afin que les 4 tests de modes d'échec deviennent triviaux à écrire avec ",{"type":31,"tag":169,"props":10133,"children":10135},{"className":10134},[],[10136],{"type":40,"value":5699},{"type":40,"value":10138},".\"",{"type":40,"value":10140}," L'architecture s'améliore en sortie, exactement comme le défend la dependency inversion pratique.",{"type":31,"tag":48,"props":10142,"children":10143},{},[],{"type":31,"tag":229,"props":10145,"children":10146},{"cta":806,"href":807,"title":808,"type":809},[10147],{"type":31,"tag":32,"props":10148,"children":10149},{},[10150],{"type":40,"value":10151},"L'EMA est l'outil que je propose au début de chaque mission. Il mesure la maturité de votre équipe sur plusieurs axes engineering : qualité des tests, résilience prod, gouvernance IA, delivery. Quelques minutes pour identifier où votre filet de tests craque sous la concurrence et la latence, et où concentrer vos efforts en priorité.",{"type":31,"tag":1837,"props":10153,"children":10154},{},[10155],{"type":40,"value":1841},{"title":8,"searchDepth":817,"depth":817,"links":10157},[10158,10159,10160,10166,10167,10168,10169,10170],{"id":5050,"depth":817,"text":5053},{"id":5137,"depth":817,"text":5140},{"id":5764,"depth":817,"text":5767,"children":10161},[10162,10163,10164,10165],{"id":5776,"depth":1236,"text":5779},{"id":6547,"depth":1236,"text":6550},{"id":7158,"depth":1236,"text":7161},{"id":7713,"depth":1236,"text":7716},{"id":8221,"depth":817,"text":8224},{"id":9761,"depth":817,"text":9764},{"id":1565,"depth":817,"text":1568},{"id":637,"depth":817,"text":640},{"id":10002,"depth":817,"text":10005},"content:fr:intelligence-artificielle:code-claude-tests-passent-plante-prod.md","fr\u002Fintelligence-artificielle\u002Fcode-claude-tests-passent-plante-prod.md","fr\u002Fintelligence-artificielle\u002Fcode-claude-tests-passent-plante-prod",{"_path":184,"_dir":6,"_draft":7,"_partial":7,"_locale":8,"title":10175,"description":10176,"id":10177,"date":10178,"listed":13,"nocomments":7,"hidden":13,"categories":10179,"tags":10180,"cover":10183,"readingTime":10184,"body":10189,"_type":830,"_id":15936,"_source":832,"_file":15937,"_stem":15938,"_extension":835},"5 anti-patterns que Claude reproduit en silence dans vos codebases","Cinq patterns dangereux que Claude écrit depuis 2 ans dans des milliers de repos. La liste, les exemples, et les contre-patterns craft à appliquer.",63,"2026-05-07",[6],[16,10181,18,10182,19],"anti-patterns","code-review","covers\u002Farticles\u002F5-patterns-dangereux-claude.jpg",{"text":10185,"minutes":10186,"time":10187,"words":10188},"15 min read",14.52,871200,2904,{"type":28,"children":10190,"toc":15924},[10191,10199,10204,10207,10213,10225,10230,10242,10266,10269,10275,10280,10285,12225,12274,12286,12289,12295,12308,12320,12855,12906,13400,13405,13408,13414,13434,13899,13904,13916,14513,14526,14531,14534,14540,14545,14828,14841,14862,15294,15312,15315,15321,15358,15368,15388,15401,15404,15410,15415,15574,15579,15582,15591,15594,15598,15603,15717,15727,15730,15734,15739,15744,15749,15760,15763,15772,15775,15781,15794,15814,15857,15870,15896,15909,15912,15920],{"type":31,"tag":32,"props":10192,"children":10193},{},[10194],{"type":31,"tag":36,"props":10195,"children":10196},{},[10197],{"type":40,"value":10198},"En construisant crmcoaching avec Claude (et au fil de mes accompagnements), j'ai vu Claude reproduire les mêmes 5 patterns à chaque nouvelle fonctionnalité. Copiés-collés sans relecture, ils pourrissent la maintenabilité à bas bruit. Aucun ne fait planter la prod tout de suite. Tous coûtent cher à 12 mois.",{"type":31,"tag":32,"props":10200,"children":10201},{},[10202],{"type":40,"value":10203},"Voici les 5 anti-patterns que je vois revenir, pourquoi Claude les écrit, et les contre-patterns craft que j'impose pour les neutraliser. Vous pouvez ouvrir votre repo principal et chercher ces signatures dès lundi matin.",{"type":31,"tag":48,"props":10205,"children":10206},{},[],{"type":31,"tag":52,"props":10208,"children":10210},{"id":10209},"pourquoi-claude-reproduit-certains-patterns-en-silence",[10211],{"type":40,"value":10212},"Pourquoi Claude reproduit certains patterns en silence",{"type":31,"tag":32,"props":10214,"children":10215},{},[10216,10218,10223],{"type":40,"value":10217},"Le constat de base est documenté. Le ",{"type":31,"tag":69,"props":10219,"children":10221},{"href":2810,"rel":10220},[665],[10222],{"type":40,"value":2814},{"type":40,"value":10224}," montre que sur 153 millions de lignes de code analysées, le code \"copié\" (par opposition à refactoré) a augmenté de 8 fois depuis l'arrivée massive des assistants IA dans les workflows de développement. Le code \"moved or refactored\" a baissé de 39%. Les équipes consomment de la suggestion brute et la collent dans leur repo, parfois sans modification.",{"type":31,"tag":32,"props":10226,"children":10227},{},[10228],{"type":40,"value":10229},"Ce n'est pas un problème de modèle, c'est un problème de training data. Claude apprend à partir de milliards de lignes de code public. Sur GitHub, le code public est massivement écrit par des juniors, des bibliothèques en alpha, des exemples de tutoriels, des copier-collers de Stack Overflow. Quand vous promptez Claude pour une God Function, il vous en livre une, parce que c'est la moyenne statistique de ce qu'il a vu. Et cette moyenne, comme je l'analyse dans le retour d'expérience sur la code review IA, est très en dessous du standard craft attendu en production sérieuse.",{"type":31,"tag":78,"props":10231,"children":10232},{},[10233],{"type":31,"tag":32,"props":10234,"children":10235},{},[10236,10240],{"type":31,"tag":36,"props":10237,"children":10238},{},[10239],{"type":40,"value":1950},{"type":40,"value":10241}," : au fil du développement du CRM, j'ai mesuré le ratio de fonctions de plus de 60 lignes dans les commits que j'acceptais tels quels versus ceux que je repassais en review avant de les intégrer. Sans grille de review active : 22% des fonctions Claude dépassaient ce seuil. Avec re-prompt explicite et review systématique : 4%. La codebase ne s'effondre pas tout de suite, mais le coût de modification grimpe linéairement avec la longueur moyenne des fonctions. À 6 mois, c'était le sujet n°1 de mes sessions de refactoring.",{"type":31,"tag":32,"props":10243,"children":10244},{},[10245,10247,10251,10253,10258,10260,10264],{"type":40,"value":10246},"Robert C. Martin l'avait écrit dans ",{"type":31,"tag":97,"props":10248,"children":10249},{},[10250],{"type":40,"value":101},{"type":40,"value":10252}," en 2008 : ",{"type":31,"tag":97,"props":10254,"children":10255},{},[10256],{"type":40,"value":10257},"\"The first rule of functions is that they should be small. The second rule of functions is that they should be smaller than that.\"",{"type":40,"value":10259}," Cette règle, exposée en français dans ",{"type":31,"tag":69,"props":10261,"children":10262},{"href":113},[10263],{"type":40,"value":897},{"type":40,"value":10265},", Claude ne l'applique pas par défaut. C'est à vous de la lui imposer par le prompt et la review.",{"type":31,"tag":48,"props":10267,"children":10268},{},[],{"type":31,"tag":52,"props":10270,"children":10272},{"id":10271},"pattern-1-la-god-function",[10273],{"type":40,"value":10274},"Pattern #1 : la God Function",{"type":31,"tag":32,"props":10276,"children":10277},{},[10278],{"type":40,"value":10279},"C'est le pattern le plus visible. Claude livre une fonction de 60 à 120 lignes qui fait tout : validation, transformation, accès DB, logging, envoi d'événement. Tout dans le même bloc, parce que c'est ce qui ressort statistiquement le plus dans le training data.",{"type":31,"tag":32,"props":10281,"children":10282},{},[10283],{"type":40,"value":10284},"Voici un exemple réel que j'ai vu apparaître dans crmcoaching sur la conversion d'un lead en client :",{"type":31,"tag":1206,"props":10286,"children":10288},{"className":2912,"code":10287,"language":2914,"meta":8,"style":8},"\u002F\u002F apps\u002Fapi\u002Fsrc\u002Fapplication\u002Fuse-cases\u002Fconvert-lead-to-client.use-case.ts\n\u002F\u002F Généré par Claude sans prompt de découpe (tel quel)\n\n@Injectable()\nexport class ConvertLeadToClientUseCase {\n  constructor(\n    private readonly prisma: PrismaClient,\n    private readonly brevoService: BrevoService,\n    private readonly auditService: AuditService,\n    private readonly eventEmitter: EventEmitter2,\n  ) {}\n\n  async execute(leadId: string, coachId: string): Promise\u003CClientDto> {\n    \u002F\u002F 1. Validation (15 lignes)\n    if (!leadId) throw new BadRequestException('leadId requis');\n    const lead = await this.prisma.lead.findUnique({ where: { id: leadId } });\n    if (!lead) throw new NotFoundException(`Lead ${leadId} introuvable`);\n    if (lead.coachId !== coachId) throw new ForbiddenException('Lead hors scope');\n    if (lead.status === 'CONVERTED') throw new ConflictException('Lead déjà converti');\n    const existingClient = await this.prisma.client.findFirst({\n      where: { email: lead.email, coachId },\n    });\n    if (existingClient) throw new ConflictException('Client existant avec cet email');\n    \u002F\u002F ... 8 autres lignes de validation métier\n\n    \u002F\u002F 2. Création du client (15 lignes)\n    const client = await this.prisma.client.create({\n      data: {\n        email: lead.email,\n        firstName: lead.firstName,\n        lastName: lead.lastName,\n        phone: lead.phone,\n        coachId,\n        status: 'ACTIVE',\n        createdBy: coachId,\n        convertedFromLeadId: leadId,\n      },\n    });\n    await this.prisma.lead.update({\n      where: { id: leadId },\n      data: { status: 'CONVERTED', convertedAt: new Date(), convertedToClientId: client.id },\n    });\n\n    \u002F\u002F 3. Notification Brevo (12 lignes)\n    await this.brevoService.sendTransactionalEmail({\n      to: [{ email: client.email, name: `${client.firstName} ${client.lastName}` }],\n      templateId: 42,\n      params: { firstName: client.firstName, coachName: lead.coachId },\n    });\n    await this.brevoService.addContactToList(client.email, 'clients-actifs');\n\n    \u002F\u002F 4. Audit (6 lignes)\n    await this.auditService.log({\n      action: 'LEAD_CONVERTED',\n      entityType: 'client',\n      entityId: client.id,\n      userId: coachId,\n      meta: { fromLeadId: leadId },\n    });\n\n    \u002F\u002F 5. Événement domaine (4 lignes)\n    this.eventEmitter.emit('client.created', { clientId: client.id, coachId });\n\n    return ClientDto.fromPrisma(client);\n  }\n}\n",[10289],{"type":31,"tag":169,"props":10290,"children":10291},{"__ignoreMap":8},[10292,10300,10308,10315,10330,10350,10361,10390,10419,10448,10477,10488,10495,10570,10578,10628,10728,10795,10855,10917,10975,11025,11040,11081,11089,11096,11104,11160,11176,11204,11233,11262,11291,11303,11324,11344,11365,11372,11387,11431,11462,11545,11560,11567,11575,11611,11726,11746,11811,11826,11884,11891,11899,11936,11957,11978,12006,12026,12059,12074,12082,12091,12175,12182,12211,12218],{"type":31,"tag":1215,"props":10293,"children":10294},{"class":1217,"line":1218},[10295],{"type":31,"tag":1215,"props":10296,"children":10297},{"style":2924},[10298],{"type":40,"value":10299},"\u002F\u002F apps\u002Fapi\u002Fsrc\u002Fapplication\u002Fuse-cases\u002Fconvert-lead-to-client.use-case.ts\n",{"type":31,"tag":1215,"props":10301,"children":10302},{"class":1217,"line":817},[10303],{"type":31,"tag":1215,"props":10304,"children":10305},{"style":2924},[10306],{"type":40,"value":10307},"\u002F\u002F Généré par Claude sans prompt de découpe (tel quel)\n",{"type":31,"tag":1215,"props":10309,"children":10310},{"class":1217,"line":1236},[10311],{"type":31,"tag":1215,"props":10312,"children":10313},{"emptyLinePlaceholder":13},[10314],{"type":40,"value":1233},{"type":31,"tag":1215,"props":10316,"children":10317},{"class":1217,"line":1246},[10318,10322,10326],{"type":31,"tag":1215,"props":10319,"children":10320},{"style":3541},[10321],{"type":40,"value":3544},{"type":31,"tag":1215,"props":10323,"children":10324},{"style":2939},[10325],{"type":40,"value":3549},{"type":31,"tag":1215,"props":10327,"children":10328},{"style":3552},[10329],{"type":40,"value":3555},{"type":31,"tag":1215,"props":10331,"children":10332},{"class":1217,"line":1256},[10333,10337,10341,10346],{"type":31,"tag":1215,"props":10334,"children":10335},{"style":2933},[10336],{"type":40,"value":3455},{"type":31,"tag":1215,"props":10338,"children":10339},{"style":2933},[10340],{"type":40,"value":3567},{"type":31,"tag":1215,"props":10342,"children":10343},{"style":3463},[10344],{"type":40,"value":10345}," ConvertLeadToClientUseCase",{"type":31,"tag":1215,"props":10347,"children":10348},{"style":2945},[10349],{"type":40,"value":3015},{"type":31,"tag":1215,"props":10351,"children":10352},{"class":1217,"line":1264},[10353,10357],{"type":31,"tag":1215,"props":10354,"children":10355},{"style":2933},[10356],{"type":40,"value":3988},{"type":31,"tag":1215,"props":10358,"children":10359},{"style":2945},[10360],{"type":40,"value":2948},{"type":31,"tag":1215,"props":10362,"children":10363},{"class":1217,"line":1273},[10364,10368,10372,10377,10381,10386],{"type":31,"tag":1215,"props":10365,"children":10366},{"style":2933},[10367],{"type":40,"value":4000},{"type":31,"tag":1215,"props":10369,"children":10370},{"style":2933},[10371],{"type":40,"value":4005},{"type":31,"tag":1215,"props":10373,"children":10374},{"style":2954},[10375],{"type":40,"value":10376}," prisma",{"type":31,"tag":1215,"props":10378,"children":10379},{"style":2960},[10380],{"type":40,"value":2963},{"type":31,"tag":1215,"props":10382,"children":10383},{"style":3463},[10384],{"type":40,"value":10385}," PrismaClient",{"type":31,"tag":1215,"props":10387,"children":10388},{"style":2945},[10389],{"type":40,"value":2974},{"type":31,"tag":1215,"props":10391,"children":10392},{"class":1217,"line":1282},[10393,10397,10401,10406,10410,10415],{"type":31,"tag":1215,"props":10394,"children":10395},{"style":2933},[10396],{"type":40,"value":4000},{"type":31,"tag":1215,"props":10398,"children":10399},{"style":2933},[10400],{"type":40,"value":4005},{"type":31,"tag":1215,"props":10402,"children":10403},{"style":2954},[10404],{"type":40,"value":10405}," brevoService",{"type":31,"tag":1215,"props":10407,"children":10408},{"style":2960},[10409],{"type":40,"value":2963},{"type":31,"tag":1215,"props":10411,"children":10412},{"style":3463},[10413],{"type":40,"value":10414}," BrevoService",{"type":31,"tag":1215,"props":10416,"children":10417},{"style":2945},[10418],{"type":40,"value":2974},{"type":31,"tag":1215,"props":10420,"children":10421},{"class":1217,"line":1291},[10422,10426,10430,10435,10439,10444],{"type":31,"tag":1215,"props":10423,"children":10424},{"style":2933},[10425],{"type":40,"value":4000},{"type":31,"tag":1215,"props":10427,"children":10428},{"style":2933},[10429],{"type":40,"value":4005},{"type":31,"tag":1215,"props":10431,"children":10432},{"style":2954},[10433],{"type":40,"value":10434}," auditService",{"type":31,"tag":1215,"props":10436,"children":10437},{"style":2960},[10438],{"type":40,"value":2963},{"type":31,"tag":1215,"props":10440,"children":10441},{"style":3463},[10442],{"type":40,"value":10443}," AuditService",{"type":31,"tag":1215,"props":10445,"children":10446},{"style":2945},[10447],{"type":40,"value":2974},{"type":31,"tag":1215,"props":10449,"children":10450},{"class":1217,"line":1300},[10451,10455,10459,10464,10468,10473],{"type":31,"tag":1215,"props":10452,"children":10453},{"style":2933},[10454],{"type":40,"value":4000},{"type":31,"tag":1215,"props":10456,"children":10457},{"style":2933},[10458],{"type":40,"value":4005},{"type":31,"tag":1215,"props":10460,"children":10461},{"style":2954},[10462],{"type":40,"value":10463}," eventEmitter",{"type":31,"tag":1215,"props":10465,"children":10466},{"style":2960},[10467],{"type":40,"value":2963},{"type":31,"tag":1215,"props":10469,"children":10470},{"style":3463},[10471],{"type":40,"value":10472}," EventEmitter2",{"type":31,"tag":1215,"props":10474,"children":10475},{"style":2945},[10476],{"type":40,"value":2974},{"type":31,"tag":1215,"props":10478,"children":10479},{"class":1217,"line":1309},[10480,10484],{"type":31,"tag":1215,"props":10481,"children":10482},{"style":2945},[10483],{"type":40,"value":4089},{"type":31,"tag":1215,"props":10485,"children":10486},{"style":2945},[10487],{"type":40,"value":4094},{"type":31,"tag":1215,"props":10489,"children":10490},{"class":1217,"line":1317},[10491],{"type":31,"tag":1215,"props":10492,"children":10493},{"emptyLinePlaceholder":13},[10494],{"type":40,"value":1233},{"type":31,"tag":1215,"props":10496,"children":10497},{"class":1217,"line":1326},[10498,10502,10507,10511,10516,10520,10524,10528,10533,10537,10541,10545,10549,10553,10557,10562,10566],{"type":31,"tag":1215,"props":10499,"children":10500},{"style":2933},[10501],{"type":40,"value":4109},{"type":31,"tag":1215,"props":10503,"children":10504},{"style":2939},[10505],{"type":40,"value":10506}," execute",{"type":31,"tag":1215,"props":10508,"children":10509},{"style":2945},[10510],{"type":40,"value":3483},{"type":31,"tag":1215,"props":10512,"children":10513},{"style":2954},[10514],{"type":40,"value":10515},"leadId",{"type":31,"tag":1215,"props":10517,"children":10518},{"style":2960},[10519],{"type":40,"value":2963},{"type":31,"tag":1215,"props":10521,"children":10522},{"style":2966},[10523],{"type":40,"value":3783},{"type":31,"tag":1215,"props":10525,"children":10526},{"style":2945},[10527],{"type":40,"value":5843},{"type":31,"tag":1215,"props":10529,"children":10530},{"style":2954},[10531],{"type":40,"value":10532}," coachId",{"type":31,"tag":1215,"props":10534,"children":10535},{"style":2960},[10536],{"type":40,"value":2963},{"type":31,"tag":1215,"props":10538,"children":10539},{"style":2966},[10540],{"type":40,"value":3783},{"type":31,"tag":1215,"props":10542,"children":10543},{"style":2945},[10544],{"type":40,"value":3002},{"type":31,"tag":1215,"props":10546,"children":10547},{"style":2960},[10548],{"type":40,"value":2963},{"type":31,"tag":1215,"props":10550,"children":10551},{"style":3463},[10552],{"type":40,"value":4145},{"type":31,"tag":1215,"props":10554,"children":10555},{"style":4148},[10556],{"type":40,"value":4151},{"type":31,"tag":1215,"props":10558,"children":10559},{"style":3463},[10560],{"type":40,"value":10561},"ClientDto",{"type":31,"tag":1215,"props":10563,"children":10564},{"style":4148},[10565],{"type":40,"value":4161},{"type":31,"tag":1215,"props":10567,"children":10568},{"style":2945},[10569],{"type":40,"value":3015},{"type":31,"tag":1215,"props":10571,"children":10572},{"class":1217,"line":1341},[10573],{"type":31,"tag":1215,"props":10574,"children":10575},{"style":2924},[10576],{"type":40,"value":10577},"    \u002F\u002F 1. Validation (15 lignes)\n",{"type":31,"tag":1215,"props":10579,"children":10580},{"class":1217,"line":1355},[10581,10585,10589,10593,10598,10602,10606,10611,10615,10620,10624],{"type":31,"tag":1215,"props":10582,"children":10583},{"style":2933},[10584],{"type":40,"value":4237},{"type":31,"tag":1215,"props":10586,"children":10587},{"style":1250},[10588],{"type":40,"value":4242},{"type":31,"tag":1215,"props":10590,"children":10591},{"style":2960},[10592],{"type":40,"value":4247},{"type":31,"tag":1215,"props":10594,"children":10595},{"style":1250},[10596],{"type":40,"value":10597},"leadId) ",{"type":31,"tag":1215,"props":10599,"children":10600},{"style":2933},[10601],{"type":40,"value":5422},{"type":31,"tag":1215,"props":10603,"children":10604},{"style":3809},[10605],{"type":40,"value":3812},{"type":31,"tag":1215,"props":10607,"children":10608},{"style":2939},[10609],{"type":40,"value":10610}," BadRequestException",{"type":31,"tag":1215,"props":10612,"children":10613},{"style":1250},[10614],{"type":40,"value":3483},{"type":31,"tag":1215,"props":10616,"children":10617},{"style":4392},[10618],{"type":40,"value":10619},"'leadId requis'",{"type":31,"tag":1215,"props":10621,"children":10622},{"style":1250},[10623],{"type":40,"value":3002},{"type":31,"tag":1215,"props":10625,"children":10626},{"style":2945},[10627],{"type":40,"value":3055},{"type":31,"tag":1215,"props":10629,"children":10630},{"class":1217,"line":1364},[10631,10635,10639,10643,10647,10651,10655,10660,10664,10668,10672,10677,10681,10685,10690,10694,10698,10703,10707,10712,10716,10720,10724],{"type":31,"tag":1215,"props":10632,"children":10633},{"style":2933},[10634],{"type":40,"value":4173},{"type":31,"tag":1215,"props":10636,"children":10637},{"style":3026},[10638],{"type":40,"value":3637},{"type":31,"tag":1215,"props":10640,"children":10641},{"style":2960},[10642],{"type":40,"value":3034},{"type":31,"tag":1215,"props":10644,"children":10645},{"style":2933},[10646],{"type":40,"value":4187},{"type":31,"tag":1215,"props":10648,"children":10649},{"style":4190},[10650],{"type":40,"value":4193},{"type":31,"tag":1215,"props":10652,"children":10653},{"style":3113},[10654],{"type":40,"value":76},{"type":31,"tag":1215,"props":10656,"children":10657},{"style":1250},[10658],{"type":40,"value":10659},"prisma",{"type":31,"tag":1215,"props":10661,"children":10662},{"style":3113},[10663],{"type":40,"value":76},{"type":31,"tag":1215,"props":10665,"children":10666},{"style":1250},[10667],{"type":40,"value":3488},{"type":31,"tag":1215,"props":10669,"children":10670},{"style":3113},[10671],{"type":40,"value":76},{"type":31,"tag":1215,"props":10673,"children":10674},{"style":2939},[10675],{"type":40,"value":10676},"findUnique",{"type":31,"tag":1215,"props":10678,"children":10679},{"style":1250},[10680],{"type":40,"value":3483},{"type":31,"tag":1215,"props":10682,"children":10683},{"style":2945},[10684],{"type":40,"value":5534},{"type":31,"tag":1215,"props":10686,"children":10687},{"style":1250},[10688],{"type":40,"value":10689}," where",{"type":31,"tag":1215,"props":10691,"children":10692},{"style":3113},[10693],{"type":40,"value":2963},{"type":31,"tag":1215,"props":10695,"children":10696},{"style":2945},[10697],{"type":40,"value":5616},{"type":31,"tag":1215,"props":10699,"children":10700},{"style":1250},[10701],{"type":40,"value":10702}," id",{"type":31,"tag":1215,"props":10704,"children":10705},{"style":3113},[10706],{"type":40,"value":2963},{"type":31,"tag":1215,"props":10708,"children":10709},{"style":1250},[10710],{"type":40,"value":10711}," leadId ",{"type":31,"tag":1215,"props":10713,"children":10714},{"style":2945},[10715],{"type":40,"value":5455},{"type":31,"tag":1215,"props":10717,"children":10718},{"style":2945},[10719],{"type":40,"value":7476},{"type":31,"tag":1215,"props":10721,"children":10722},{"style":1250},[10723],{"type":40,"value":3002},{"type":31,"tag":1215,"props":10725,"children":10726},{"style":2945},[10727],{"type":40,"value":3055},{"type":31,"tag":1215,"props":10729,"children":10730},{"class":1217,"line":1378},[10731,10735,10739,10743,10748,10752,10756,10761,10765,10770,10774,10778,10782,10787,10791],{"type":31,"tag":1215,"props":10732,"children":10733},{"style":2933},[10734],{"type":40,"value":4237},{"type":31,"tag":1215,"props":10736,"children":10737},{"style":1250},[10738],{"type":40,"value":4242},{"type":31,"tag":1215,"props":10740,"children":10741},{"style":2960},[10742],{"type":40,"value":4247},{"type":31,"tag":1215,"props":10744,"children":10745},{"style":1250},[10746],{"type":40,"value":10747},"lead) ",{"type":31,"tag":1215,"props":10749,"children":10750},{"style":2933},[10751],{"type":40,"value":5422},{"type":31,"tag":1215,"props":10753,"children":10754},{"style":3809},[10755],{"type":40,"value":3812},{"type":31,"tag":1215,"props":10757,"children":10758},{"style":2939},[10759],{"type":40,"value":10760}," NotFoundException",{"type":31,"tag":1215,"props":10762,"children":10763},{"style":1250},[10764],{"type":40,"value":3483},{"type":31,"tag":1215,"props":10766,"children":10767},{"style":4392},[10768],{"type":40,"value":10769},"`Lead ",{"type":31,"tag":1215,"props":10771,"children":10772},{"style":5443},[10773],{"type":40,"value":5446},{"type":31,"tag":1215,"props":10775,"children":10776},{"style":1250},[10777],{"type":40,"value":10515},{"type":31,"tag":1215,"props":10779,"children":10780},{"style":5443},[10781],{"type":40,"value":5455},{"type":31,"tag":1215,"props":10783,"children":10784},{"style":4392},[10785],{"type":40,"value":10786}," introuvable`",{"type":31,"tag":1215,"props":10788,"children":10789},{"style":1250},[10790],{"type":40,"value":3002},{"type":31,"tag":1215,"props":10792,"children":10793},{"style":2945},[10794],{"type":40,"value":3055},{"type":31,"tag":1215,"props":10796,"children":10797},{"class":1217,"line":1387},[10798,10802,10806,10810,10815,10820,10825,10829,10833,10838,10842,10847,10851],{"type":31,"tag":1215,"props":10799,"children":10800},{"style":2933},[10801],{"type":40,"value":4237},{"type":31,"tag":1215,"props":10803,"children":10804},{"style":1250},[10805],{"type":40,"value":3665},{"type":31,"tag":1215,"props":10807,"children":10808},{"style":3113},[10809],{"type":40,"value":76},{"type":31,"tag":1215,"props":10811,"children":10812},{"style":1250},[10813],{"type":40,"value":10814},"coachId ",{"type":31,"tag":1215,"props":10816,"children":10817},{"style":2960},[10818],{"type":40,"value":10819},"!==",{"type":31,"tag":1215,"props":10821,"children":10822},{"style":1250},[10823],{"type":40,"value":10824}," coachId) ",{"type":31,"tag":1215,"props":10826,"children":10827},{"style":2933},[10828],{"type":40,"value":5422},{"type":31,"tag":1215,"props":10830,"children":10831},{"style":3809},[10832],{"type":40,"value":3812},{"type":31,"tag":1215,"props":10834,"children":10835},{"style":2939},[10836],{"type":40,"value":10837}," ForbiddenException",{"type":31,"tag":1215,"props":10839,"children":10840},{"style":1250},[10841],{"type":40,"value":3483},{"type":31,"tag":1215,"props":10843,"children":10844},{"style":4392},[10845],{"type":40,"value":10846},"'Lead hors scope'",{"type":31,"tag":1215,"props":10848,"children":10849},{"style":1250},[10850],{"type":40,"value":3002},{"type":31,"tag":1215,"props":10852,"children":10853},{"style":2945},[10854],{"type":40,"value":3055},{"type":31,"tag":1215,"props":10856,"children":10857},{"class":1217,"line":1401},[10858,10862,10866,10870,10874,10878,10883,10887,10891,10895,10900,10904,10909,10913],{"type":31,"tag":1215,"props":10859,"children":10860},{"style":2933},[10861],{"type":40,"value":4237},{"type":31,"tag":1215,"props":10863,"children":10864},{"style":1250},[10865],{"type":40,"value":3665},{"type":31,"tag":1215,"props":10867,"children":10868},{"style":3113},[10869],{"type":40,"value":76},{"type":31,"tag":1215,"props":10871,"children":10872},{"style":1250},[10873],{"type":40,"value":6410},{"type":31,"tag":1215,"props":10875,"children":10876},{"style":2960},[10877],{"type":40,"value":6415},{"type":31,"tag":1215,"props":10879,"children":10880},{"style":4392},[10881],{"type":40,"value":10882}," 'CONVERTED'",{"type":31,"tag":1215,"props":10884,"children":10885},{"style":1250},[10886],{"type":40,"value":3140},{"type":31,"tag":1215,"props":10888,"children":10889},{"style":2933},[10890],{"type":40,"value":5422},{"type":31,"tag":1215,"props":10892,"children":10893},{"style":3809},[10894],{"type":40,"value":3812},{"type":31,"tag":1215,"props":10896,"children":10897},{"style":2939},[10898],{"type":40,"value":10899}," ConflictException",{"type":31,"tag":1215,"props":10901,"children":10902},{"style":1250},[10903],{"type":40,"value":3483},{"type":31,"tag":1215,"props":10905,"children":10906},{"style":4392},[10907],{"type":40,"value":10908},"'Lead déjà converti'",{"type":31,"tag":1215,"props":10910,"children":10911},{"style":1250},[10912],{"type":40,"value":3002},{"type":31,"tag":1215,"props":10914,"children":10915},{"style":2945},[10916],{"type":40,"value":3055},{"type":31,"tag":1215,"props":10918,"children":10919},{"class":1217,"line":1410},[10920,10924,10929,10933,10937,10941,10945,10949,10953,10958,10962,10967,10971],{"type":31,"tag":1215,"props":10921,"children":10922},{"style":2933},[10923],{"type":40,"value":4173},{"type":31,"tag":1215,"props":10925,"children":10926},{"style":3026},[10927],{"type":40,"value":10928}," existingClient",{"type":31,"tag":1215,"props":10930,"children":10931},{"style":2960},[10932],{"type":40,"value":3034},{"type":31,"tag":1215,"props":10934,"children":10935},{"style":2933},[10936],{"type":40,"value":4187},{"type":31,"tag":1215,"props":10938,"children":10939},{"style":4190},[10940],{"type":40,"value":4193},{"type":31,"tag":1215,"props":10942,"children":10943},{"style":3113},[10944],{"type":40,"value":76},{"type":31,"tag":1215,"props":10946,"children":10947},{"style":1250},[10948],{"type":40,"value":10659},{"type":31,"tag":1215,"props":10950,"children":10951},{"style":3113},[10952],{"type":40,"value":76},{"type":31,"tag":1215,"props":10954,"children":10955},{"style":1250},[10956],{"type":40,"value":10957},"client",{"type":31,"tag":1215,"props":10959,"children":10960},{"style":3113},[10961],{"type":40,"value":76},{"type":31,"tag":1215,"props":10963,"children":10964},{"style":2939},[10965],{"type":40,"value":10966},"findFirst",{"type":31,"tag":1215,"props":10968,"children":10969},{"style":1250},[10970],{"type":40,"value":3483},{"type":31,"tag":1215,"props":10972,"children":10973},{"style":2945},[10974],{"type":40,"value":4257},{"type":31,"tag":1215,"props":10976,"children":10977},{"class":1217,"line":1419},[10978,10983,10987,10991,10995,10999,11003,11007,11012,11016,11021],{"type":31,"tag":1215,"props":10979,"children":10980},{"style":1250},[10981],{"type":40,"value":10982},"      where",{"type":31,"tag":1215,"props":10984,"children":10985},{"style":3113},[10986],{"type":40,"value":2963},{"type":31,"tag":1215,"props":10988,"children":10989},{"style":2945},[10990],{"type":40,"value":5616},{"type":31,"tag":1215,"props":10992,"children":10993},{"style":1250},[10994],{"type":40,"value":5539},{"type":31,"tag":1215,"props":10996,"children":10997},{"style":3113},[10998],{"type":40,"value":2963},{"type":31,"tag":1215,"props":11000,"children":11001},{"style":1250},[11002],{"type":40,"value":3637},{"type":31,"tag":1215,"props":11004,"children":11005},{"style":3113},[11006],{"type":40,"value":76},{"type":31,"tag":1215,"props":11008,"children":11009},{"style":1250},[11010],{"type":40,"value":11011},"email",{"type":31,"tag":1215,"props":11013,"children":11014},{"style":2945},[11015],{"type":40,"value":5843},{"type":31,"tag":1215,"props":11017,"children":11018},{"style":1250},[11019],{"type":40,"value":11020}," coachId ",{"type":31,"tag":1215,"props":11022,"children":11023},{"style":2945},[11024],{"type":40,"value":5643},{"type":31,"tag":1215,"props":11026,"children":11027},{"class":1217,"line":1427},[11028,11032,11036],{"type":31,"tag":1215,"props":11029,"children":11030},{"style":2945},[11031],{"type":40,"value":5651},{"type":31,"tag":1215,"props":11033,"children":11034},{"style":1250},[11035],{"type":40,"value":3002},{"type":31,"tag":1215,"props":11037,"children":11038},{"style":2945},[11039],{"type":40,"value":3055},{"type":31,"tag":1215,"props":11041,"children":11042},{"class":1217,"line":1436},[11043,11047,11052,11056,11060,11064,11068,11073,11077],{"type":31,"tag":1215,"props":11044,"children":11045},{"style":2933},[11046],{"type":40,"value":4237},{"type":31,"tag":1215,"props":11048,"children":11049},{"style":1250},[11050],{"type":40,"value":11051}," (existingClient) ",{"type":31,"tag":1215,"props":11053,"children":11054},{"style":2933},[11055],{"type":40,"value":5422},{"type":31,"tag":1215,"props":11057,"children":11058},{"style":3809},[11059],{"type":40,"value":3812},{"type":31,"tag":1215,"props":11061,"children":11062},{"style":2939},[11063],{"type":40,"value":10899},{"type":31,"tag":1215,"props":11065,"children":11066},{"style":1250},[11067],{"type":40,"value":3483},{"type":31,"tag":1215,"props":11069,"children":11070},{"style":4392},[11071],{"type":40,"value":11072},"'Client existant avec cet email'",{"type":31,"tag":1215,"props":11074,"children":11075},{"style":1250},[11076],{"type":40,"value":3002},{"type":31,"tag":1215,"props":11078,"children":11079},{"style":2945},[11080],{"type":40,"value":3055},{"type":31,"tag":1215,"props":11082,"children":11083},{"class":1217,"line":1450},[11084],{"type":31,"tag":1215,"props":11085,"children":11086},{"style":2924},[11087],{"type":40,"value":11088},"    \u002F\u002F ... 8 autres lignes de validation métier\n",{"type":31,"tag":1215,"props":11090,"children":11091},{"class":1217,"line":1463},[11092],{"type":31,"tag":1215,"props":11093,"children":11094},{"emptyLinePlaceholder":13},[11095],{"type":40,"value":1233},{"type":31,"tag":1215,"props":11097,"children":11098},{"class":1217,"line":1476},[11099],{"type":31,"tag":1215,"props":11100,"children":11101},{"style":2924},[11102],{"type":40,"value":11103},"    \u002F\u002F 2. Création du client (15 lignes)\n",{"type":31,"tag":1215,"props":11105,"children":11106},{"class":1217,"line":1490},[11107,11111,11115,11119,11123,11127,11131,11135,11139,11143,11147,11152,11156],{"type":31,"tag":1215,"props":11108,"children":11109},{"style":2933},[11110],{"type":40,"value":4173},{"type":31,"tag":1215,"props":11112,"children":11113},{"style":3026},[11114],{"type":40,"value":4178},{"type":31,"tag":1215,"props":11116,"children":11117},{"style":2960},[11118],{"type":40,"value":3034},{"type":31,"tag":1215,"props":11120,"children":11121},{"style":2933},[11122],{"type":40,"value":4187},{"type":31,"tag":1215,"props":11124,"children":11125},{"style":4190},[11126],{"type":40,"value":4193},{"type":31,"tag":1215,"props":11128,"children":11129},{"style":3113},[11130],{"type":40,"value":76},{"type":31,"tag":1215,"props":11132,"children":11133},{"style":1250},[11134],{"type":40,"value":10659},{"type":31,"tag":1215,"props":11136,"children":11137},{"style":3113},[11138],{"type":40,"value":76},{"type":31,"tag":1215,"props":11140,"children":11141},{"style":1250},[11142],{"type":40,"value":10957},{"type":31,"tag":1215,"props":11144,"children":11145},{"style":3113},[11146],{"type":40,"value":76},{"type":31,"tag":1215,"props":11148,"children":11149},{"style":2939},[11150],{"type":40,"value":11151},"create",{"type":31,"tag":1215,"props":11153,"children":11154},{"style":1250},[11155],{"type":40,"value":3483},{"type":31,"tag":1215,"props":11157,"children":11158},{"style":2945},[11159],{"type":40,"value":4257},{"type":31,"tag":1215,"props":11161,"children":11162},{"class":1217,"line":8904},[11163,11168,11172],{"type":31,"tag":1215,"props":11164,"children":11165},{"style":1250},[11166],{"type":40,"value":11167},"      data",{"type":31,"tag":1215,"props":11169,"children":11170},{"style":3113},[11171],{"type":40,"value":2963},{"type":31,"tag":1215,"props":11173,"children":11174},{"style":2945},[11175],{"type":40,"value":3015},{"type":31,"tag":1215,"props":11177,"children":11178},{"class":1217,"line":8937},[11179,11184,11188,11192,11196,11200],{"type":31,"tag":1215,"props":11180,"children":11181},{"style":1250},[11182],{"type":40,"value":11183},"        email",{"type":31,"tag":1215,"props":11185,"children":11186},{"style":3113},[11187],{"type":40,"value":2963},{"type":31,"tag":1215,"props":11189,"children":11190},{"style":1250},[11191],{"type":40,"value":3637},{"type":31,"tag":1215,"props":11193,"children":11194},{"style":3113},[11195],{"type":40,"value":76},{"type":31,"tag":1215,"props":11197,"children":11198},{"style":1250},[11199],{"type":40,"value":11011},{"type":31,"tag":1215,"props":11201,"children":11202},{"style":2945},[11203],{"type":40,"value":2974},{"type":31,"tag":1215,"props":11205,"children":11206},{"class":1217,"line":8946},[11207,11212,11216,11220,11224,11229],{"type":31,"tag":1215,"props":11208,"children":11209},{"style":1250},[11210],{"type":40,"value":11211},"        firstName",{"type":31,"tag":1215,"props":11213,"children":11214},{"style":3113},[11215],{"type":40,"value":2963},{"type":31,"tag":1215,"props":11217,"children":11218},{"style":1250},[11219],{"type":40,"value":3637},{"type":31,"tag":1215,"props":11221,"children":11222},{"style":3113},[11223],{"type":40,"value":76},{"type":31,"tag":1215,"props":11225,"children":11226},{"style":1250},[11227],{"type":40,"value":11228},"firstName",{"type":31,"tag":1215,"props":11230,"children":11231},{"style":2945},[11232],{"type":40,"value":2974},{"type":31,"tag":1215,"props":11234,"children":11235},{"class":1217,"line":8999},[11236,11241,11245,11249,11253,11258],{"type":31,"tag":1215,"props":11237,"children":11238},{"style":1250},[11239],{"type":40,"value":11240},"        lastName",{"type":31,"tag":1215,"props":11242,"children":11243},{"style":3113},[11244],{"type":40,"value":2963},{"type":31,"tag":1215,"props":11246,"children":11247},{"style":1250},[11248],{"type":40,"value":3637},{"type":31,"tag":1215,"props":11250,"children":11251},{"style":3113},[11252],{"type":40,"value":76},{"type":31,"tag":1215,"props":11254,"children":11255},{"style":1250},[11256],{"type":40,"value":11257},"lastName",{"type":31,"tag":1215,"props":11259,"children":11260},{"style":2945},[11261],{"type":40,"value":2974},{"type":31,"tag":1215,"props":11263,"children":11264},{"class":1217,"line":9028},[11265,11270,11274,11278,11282,11287],{"type":31,"tag":1215,"props":11266,"children":11267},{"style":1250},[11268],{"type":40,"value":11269},"        phone",{"type":31,"tag":1215,"props":11271,"children":11272},{"style":3113},[11273],{"type":40,"value":2963},{"type":31,"tag":1215,"props":11275,"children":11276},{"style":1250},[11277],{"type":40,"value":3637},{"type":31,"tag":1215,"props":11279,"children":11280},{"style":3113},[11281],{"type":40,"value":76},{"type":31,"tag":1215,"props":11283,"children":11284},{"style":1250},[11285],{"type":40,"value":11286},"phone",{"type":31,"tag":1215,"props":11288,"children":11289},{"style":2945},[11290],{"type":40,"value":2974},{"type":31,"tag":1215,"props":11292,"children":11293},{"class":1217,"line":9069},[11294,11299],{"type":31,"tag":1215,"props":11295,"children":11296},{"style":1250},[11297],{"type":40,"value":11298},"        coachId",{"type":31,"tag":1215,"props":11300,"children":11301},{"style":2945},[11302],{"type":40,"value":2974},{"type":31,"tag":1215,"props":11304,"children":11305},{"class":1217,"line":9078},[11306,11311,11315,11320],{"type":31,"tag":1215,"props":11307,"children":11308},{"style":1250},[11309],{"type":40,"value":11310},"        status",{"type":31,"tag":1215,"props":11312,"children":11313},{"style":3113},[11314],{"type":40,"value":2963},{"type":31,"tag":1215,"props":11316,"children":11317},{"style":4392},[11318],{"type":40,"value":11319}," 'ACTIVE'",{"type":31,"tag":1215,"props":11321,"children":11322},{"style":2945},[11323],{"type":40,"value":2974},{"type":31,"tag":1215,"props":11325,"children":11326},{"class":1217,"line":9113},[11327,11332,11336,11340],{"type":31,"tag":1215,"props":11328,"children":11329},{"style":1250},[11330],{"type":40,"value":11331},"        createdBy",{"type":31,"tag":1215,"props":11333,"children":11334},{"style":3113},[11335],{"type":40,"value":2963},{"type":31,"tag":1215,"props":11337,"children":11338},{"style":1250},[11339],{"type":40,"value":10532},{"type":31,"tag":1215,"props":11341,"children":11342},{"style":2945},[11343],{"type":40,"value":2974},{"type":31,"tag":1215,"props":11345,"children":11346},{"class":1217,"line":9126},[11347,11352,11356,11361],{"type":31,"tag":1215,"props":11348,"children":11349},{"style":1250},[11350],{"type":40,"value":11351},"        convertedFromLeadId",{"type":31,"tag":1215,"props":11353,"children":11354},{"style":3113},[11355],{"type":40,"value":2963},{"type":31,"tag":1215,"props":11357,"children":11358},{"style":1250},[11359],{"type":40,"value":11360}," leadId",{"type":31,"tag":1215,"props":11362,"children":11363},{"style":2945},[11364],{"type":40,"value":2974},{"type":31,"tag":1215,"props":11366,"children":11367},{"class":1217,"line":9166},[11368],{"type":31,"tag":1215,"props":11369,"children":11370},{"style":2945},[11371],{"type":40,"value":9473},{"type":31,"tag":1215,"props":11373,"children":11374},{"class":1217,"line":9184},[11375,11379,11383],{"type":31,"tag":1215,"props":11376,"children":11377},{"style":2945},[11378],{"type":40,"value":5651},{"type":31,"tag":1215,"props":11380,"children":11381},{"style":1250},[11382],{"type":40,"value":3002},{"type":31,"tag":1215,"props":11384,"children":11385},{"style":2945},[11386],{"type":40,"value":3055},{"type":31,"tag":1215,"props":11388,"children":11389},{"class":1217,"line":9220},[11390,11394,11398,11402,11406,11410,11414,11418,11423,11427],{"type":31,"tag":1215,"props":11391,"children":11392},{"style":2933},[11393],{"type":40,"value":4306},{"type":31,"tag":1215,"props":11395,"children":11396},{"style":4190},[11397],{"type":40,"value":4193},{"type":31,"tag":1215,"props":11399,"children":11400},{"style":3113},[11401],{"type":40,"value":76},{"type":31,"tag":1215,"props":11403,"children":11404},{"style":1250},[11405],{"type":40,"value":10659},{"type":31,"tag":1215,"props":11407,"children":11408},{"style":3113},[11409],{"type":40,"value":76},{"type":31,"tag":1215,"props":11411,"children":11412},{"style":1250},[11413],{"type":40,"value":3488},{"type":31,"tag":1215,"props":11415,"children":11416},{"style":3113},[11417],{"type":40,"value":76},{"type":31,"tag":1215,"props":11419,"children":11420},{"style":2939},[11421],{"type":40,"value":11422},"update",{"type":31,"tag":1215,"props":11424,"children":11425},{"style":1250},[11426],{"type":40,"value":3483},{"type":31,"tag":1215,"props":11428,"children":11429},{"style":2945},[11430],{"type":40,"value":4257},{"type":31,"tag":1215,"props":11432,"children":11433},{"class":1217,"line":9254},[11434,11438,11442,11446,11450,11454,11458],{"type":31,"tag":1215,"props":11435,"children":11436},{"style":1250},[11437],{"type":40,"value":10982},{"type":31,"tag":1215,"props":11439,"children":11440},{"style":3113},[11441],{"type":40,"value":2963},{"type":31,"tag":1215,"props":11443,"children":11444},{"style":2945},[11445],{"type":40,"value":5616},{"type":31,"tag":1215,"props":11447,"children":11448},{"style":1250},[11449],{"type":40,"value":10702},{"type":31,"tag":1215,"props":11451,"children":11452},{"style":3113},[11453],{"type":40,"value":2963},{"type":31,"tag":1215,"props":11455,"children":11456},{"style":1250},[11457],{"type":40,"value":10711},{"type":31,"tag":1215,"props":11459,"children":11460},{"style":2945},[11461],{"type":40,"value":5643},{"type":31,"tag":1215,"props":11463,"children":11464},{"class":1217,"line":9263},[11465,11469,11473,11477,11482,11486,11490,11494,11499,11503,11507,11511,11515,11519,11524,11528,11532,11536,11541],{"type":31,"tag":1215,"props":11466,"children":11467},{"style":1250},[11468],{"type":40,"value":11167},{"type":31,"tag":1215,"props":11470,"children":11471},{"style":3113},[11472],{"type":40,"value":2963},{"type":31,"tag":1215,"props":11474,"children":11475},{"style":2945},[11476],{"type":40,"value":5616},{"type":31,"tag":1215,"props":11478,"children":11479},{"style":1250},[11480],{"type":40,"value":11481}," status",{"type":31,"tag":1215,"props":11483,"children":11484},{"style":3113},[11485],{"type":40,"value":2963},{"type":31,"tag":1215,"props":11487,"children":11488},{"style":4392},[11489],{"type":40,"value":10882},{"type":31,"tag":1215,"props":11491,"children":11492},{"style":2945},[11493],{"type":40,"value":5843},{"type":31,"tag":1215,"props":11495,"children":11496},{"style":1250},[11497],{"type":40,"value":11498}," convertedAt",{"type":31,"tag":1215,"props":11500,"children":11501},{"style":3113},[11502],{"type":40,"value":2963},{"type":31,"tag":1215,"props":11504,"children":11505},{"style":3809},[11506],{"type":40,"value":3812},{"type":31,"tag":1215,"props":11508,"children":11509},{"style":2939},[11510],{"type":40,"value":6135},{"type":31,"tag":1215,"props":11512,"children":11513},{"style":1250},[11514],{"type":40,"value":3821},{"type":31,"tag":1215,"props":11516,"children":11517},{"style":2945},[11518],{"type":40,"value":5843},{"type":31,"tag":1215,"props":11520,"children":11521},{"style":1250},[11522],{"type":40,"value":11523}," convertedToClientId",{"type":31,"tag":1215,"props":11525,"children":11526},{"style":3113},[11527],{"type":40,"value":2963},{"type":31,"tag":1215,"props":11529,"children":11530},{"style":1250},[11531],{"type":40,"value":4178},{"type":31,"tag":1215,"props":11533,"children":11534},{"style":3113},[11535],{"type":40,"value":76},{"type":31,"tag":1215,"props":11537,"children":11538},{"style":1250},[11539],{"type":40,"value":11540},"id ",{"type":31,"tag":1215,"props":11542,"children":11543},{"style":2945},[11544],{"type":40,"value":5643},{"type":31,"tag":1215,"props":11546,"children":11547},{"class":1217,"line":9327},[11548,11552,11556],{"type":31,"tag":1215,"props":11549,"children":11550},{"style":2945},[11551],{"type":40,"value":5651},{"type":31,"tag":1215,"props":11553,"children":11554},{"style":1250},[11555],{"type":40,"value":3002},{"type":31,"tag":1215,"props":11557,"children":11558},{"style":2945},[11559],{"type":40,"value":3055},{"type":31,"tag":1215,"props":11561,"children":11562},{"class":1217,"line":9389},[11563],{"type":31,"tag":1215,"props":11564,"children":11565},{"emptyLinePlaceholder":13},[11566],{"type":40,"value":1233},{"type":31,"tag":1215,"props":11568,"children":11569},{"class":1217,"line":9441},[11570],{"type":31,"tag":1215,"props":11571,"children":11572},{"style":2924},[11573],{"type":40,"value":11574},"    \u002F\u002F 3. Notification Brevo (12 lignes)\n",{"type":31,"tag":1215,"props":11576,"children":11577},{"class":1217,"line":9450},[11578,11582,11586,11590,11595,11599,11603,11607],{"type":31,"tag":1215,"props":11579,"children":11580},{"style":2933},[11581],{"type":40,"value":4306},{"type":31,"tag":1215,"props":11583,"children":11584},{"style":4190},[11585],{"type":40,"value":4193},{"type":31,"tag":1215,"props":11587,"children":11588},{"style":3113},[11589],{"type":40,"value":76},{"type":31,"tag":1215,"props":11591,"children":11592},{"style":1250},[11593],{"type":40,"value":11594},"brevoService",{"type":31,"tag":1215,"props":11596,"children":11597},{"style":3113},[11598],{"type":40,"value":76},{"type":31,"tag":1215,"props":11600,"children":11601},{"style":2939},[11602],{"type":40,"value":5504},{"type":31,"tag":1215,"props":11604,"children":11605},{"style":1250},[11606],{"type":40,"value":3483},{"type":31,"tag":1215,"props":11608,"children":11609},{"style":2945},[11610],{"type":40,"value":4257},{"type":31,"tag":1215,"props":11612,"children":11613},{"class":1217,"line":9467},[11614,11618,11622,11626,11630,11634,11638,11642,11646,11650,11654,11659,11663,11668,11672,11676,11681,11685,11689,11694,11698,11702,11706,11710,11714,11718,11722],{"type":31,"tag":1215,"props":11615,"children":11616},{"style":1250},[11617],{"type":40,"value":5520},{"type":31,"tag":1215,"props":11619,"children":11620},{"style":3113},[11621],{"type":40,"value":2963},{"type":31,"tag":1215,"props":11623,"children":11624},{"style":1250},[11625],{"type":40,"value":5529},{"type":31,"tag":1215,"props":11627,"children":11628},{"style":2945},[11629],{"type":40,"value":5534},{"type":31,"tag":1215,"props":11631,"children":11632},{"style":1250},[11633],{"type":40,"value":5539},{"type":31,"tag":1215,"props":11635,"children":11636},{"style":3113},[11637],{"type":40,"value":2963},{"type":31,"tag":1215,"props":11639,"children":11640},{"style":1250},[11641],{"type":40,"value":4178},{"type":31,"tag":1215,"props":11643,"children":11644},{"style":3113},[11645],{"type":40,"value":76},{"type":31,"tag":1215,"props":11647,"children":11648},{"style":1250},[11649],{"type":40,"value":11011},{"type":31,"tag":1215,"props":11651,"children":11652},{"style":2945},[11653],{"type":40,"value":5843},{"type":31,"tag":1215,"props":11655,"children":11656},{"style":1250},[11657],{"type":40,"value":11658}," name",{"type":31,"tag":1215,"props":11660,"children":11661},{"style":3113},[11662],{"type":40,"value":2963},{"type":31,"tag":1215,"props":11664,"children":11665},{"style":4392},[11666],{"type":40,"value":11667}," `",{"type":31,"tag":1215,"props":11669,"children":11670},{"style":5443},[11671],{"type":40,"value":5446},{"type":31,"tag":1215,"props":11673,"children":11674},{"style":1250},[11675],{"type":40,"value":10957},{"type":31,"tag":1215,"props":11677,"children":11679},{"style":11678},"--shiki-default:#81C8BE;--shiki-dark:#9ECBFF",[11680],{"type":40,"value":76},{"type":31,"tag":1215,"props":11682,"children":11683},{"style":1250},[11684],{"type":40,"value":11228},{"type":31,"tag":1215,"props":11686,"children":11687},{"style":5443},[11688],{"type":40,"value":5455},{"type":31,"tag":1215,"props":11690,"children":11691},{"style":5443},[11692],{"type":40,"value":11693}," ${",{"type":31,"tag":1215,"props":11695,"children":11696},{"style":1250},[11697],{"type":40,"value":10957},{"type":31,"tag":1215,"props":11699,"children":11700},{"style":11678},[11701],{"type":40,"value":76},{"type":31,"tag":1215,"props":11703,"children":11704},{"style":1250},[11705],{"type":40,"value":11257},{"type":31,"tag":1215,"props":11707,"children":11708},{"style":5443},[11709],{"type":40,"value":5455},{"type":31,"tag":1215,"props":11711,"children":11712},{"style":4392},[11713],{"type":40,"value":5460},{"type":31,"tag":1215,"props":11715,"children":11716},{"style":2945},[11717],{"type":40,"value":7476},{"type":31,"tag":1215,"props":11719,"children":11720},{"style":1250},[11721],{"type":40,"value":5565},{"type":31,"tag":1215,"props":11723,"children":11724},{"style":2945},[11725],{"type":40,"value":2974},{"type":31,"tag":1215,"props":11727,"children":11728},{"class":1217,"line":9476},[11729,11733,11737,11742],{"type":31,"tag":1215,"props":11730,"children":11731},{"style":1250},[11732],{"type":40,"value":5577},{"type":31,"tag":1215,"props":11734,"children":11735},{"style":3113},[11736],{"type":40,"value":2963},{"type":31,"tag":1215,"props":11738,"children":11739},{"style":3047},[11740],{"type":40,"value":11741}," 42",{"type":31,"tag":1215,"props":11743,"children":11744},{"style":2945},[11745],{"type":40,"value":2974},{"type":31,"tag":1215,"props":11747,"children":11748},{"class":1217,"line":9485},[11749,11753,11757,11761,11766,11770,11774,11778,11782,11786,11791,11795,11799,11803,11807],{"type":31,"tag":1215,"props":11750,"children":11751},{"style":1250},[11752],{"type":40,"value":5607},{"type":31,"tag":1215,"props":11754,"children":11755},{"style":3113},[11756],{"type":40,"value":2963},{"type":31,"tag":1215,"props":11758,"children":11759},{"style":2945},[11760],{"type":40,"value":5616},{"type":31,"tag":1215,"props":11762,"children":11763},{"style":1250},[11764],{"type":40,"value":11765}," firstName",{"type":31,"tag":1215,"props":11767,"children":11768},{"style":3113},[11769],{"type":40,"value":2963},{"type":31,"tag":1215,"props":11771,"children":11772},{"style":1250},[11773],{"type":40,"value":4178},{"type":31,"tag":1215,"props":11775,"children":11776},{"style":3113},[11777],{"type":40,"value":76},{"type":31,"tag":1215,"props":11779,"children":11780},{"style":1250},[11781],{"type":40,"value":11228},{"type":31,"tag":1215,"props":11783,"children":11784},{"style":2945},[11785],{"type":40,"value":5843},{"type":31,"tag":1215,"props":11787,"children":11788},{"style":1250},[11789],{"type":40,"value":11790}," coachName",{"type":31,"tag":1215,"props":11792,"children":11793},{"style":3113},[11794],{"type":40,"value":2963},{"type":31,"tag":1215,"props":11796,"children":11797},{"style":1250},[11798],{"type":40,"value":3637},{"type":31,"tag":1215,"props":11800,"children":11801},{"style":3113},[11802],{"type":40,"value":76},{"type":31,"tag":1215,"props":11804,"children":11805},{"style":1250},[11806],{"type":40,"value":10814},{"type":31,"tag":1215,"props":11808,"children":11809},{"style":2945},[11810],{"type":40,"value":5643},{"type":31,"tag":1215,"props":11812,"children":11813},{"class":1217,"line":9506},[11814,11818,11822],{"type":31,"tag":1215,"props":11815,"children":11816},{"style":2945},[11817],{"type":40,"value":5651},{"type":31,"tag":1215,"props":11819,"children":11820},{"style":1250},[11821],{"type":40,"value":3002},{"type":31,"tag":1215,"props":11823,"children":11824},{"style":2945},[11825],{"type":40,"value":3055},{"type":31,"tag":1215,"props":11827,"children":11828},{"class":1217,"line":9528},[11829,11833,11837,11841,11845,11849,11854,11859,11863,11867,11871,11876,11880],{"type":31,"tag":1215,"props":11830,"children":11831},{"style":2933},[11832],{"type":40,"value":4306},{"type":31,"tag":1215,"props":11834,"children":11835},{"style":4190},[11836],{"type":40,"value":4193},{"type":31,"tag":1215,"props":11838,"children":11839},{"style":3113},[11840],{"type":40,"value":76},{"type":31,"tag":1215,"props":11842,"children":11843},{"style":1250},[11844],{"type":40,"value":11594},{"type":31,"tag":1215,"props":11846,"children":11847},{"style":3113},[11848],{"type":40,"value":76},{"type":31,"tag":1215,"props":11850,"children":11851},{"style":2939},[11852],{"type":40,"value":11853},"addContactToList",{"type":31,"tag":1215,"props":11855,"children":11856},{"style":1250},[11857],{"type":40,"value":11858},"(client",{"type":31,"tag":1215,"props":11860,"children":11861},{"style":3113},[11862],{"type":40,"value":76},{"type":31,"tag":1215,"props":11864,"children":11865},{"style":1250},[11866],{"type":40,"value":11011},{"type":31,"tag":1215,"props":11868,"children":11869},{"style":2945},[11870],{"type":40,"value":5843},{"type":31,"tag":1215,"props":11872,"children":11873},{"style":4392},[11874],{"type":40,"value":11875}," 'clients-actifs'",{"type":31,"tag":1215,"props":11877,"children":11878},{"style":1250},[11879],{"type":40,"value":3002},{"type":31,"tag":1215,"props":11881,"children":11882},{"style":2945},[11883],{"type":40,"value":3055},{"type":31,"tag":1215,"props":11885,"children":11886},{"class":1217,"line":9549},[11887],{"type":31,"tag":1215,"props":11888,"children":11889},{"emptyLinePlaceholder":13},[11890],{"type":40,"value":1233},{"type":31,"tag":1215,"props":11892,"children":11893},{"class":1217,"line":9583},[11894],{"type":31,"tag":1215,"props":11895,"children":11896},{"style":2924},[11897],{"type":40,"value":11898},"    \u002F\u002F 4. Audit (6 lignes)\n",{"type":31,"tag":1215,"props":11900,"children":11901},{"class":1217,"line":9630},[11902,11906,11910,11914,11919,11923,11928,11932],{"type":31,"tag":1215,"props":11903,"children":11904},{"style":2933},[11905],{"type":40,"value":4306},{"type":31,"tag":1215,"props":11907,"children":11908},{"style":4190},[11909],{"type":40,"value":4193},{"type":31,"tag":1215,"props":11911,"children":11912},{"style":3113},[11913],{"type":40,"value":76},{"type":31,"tag":1215,"props":11915,"children":11916},{"style":1250},[11917],{"type":40,"value":11918},"auditService",{"type":31,"tag":1215,"props":11920,"children":11921},{"style":3113},[11922],{"type":40,"value":76},{"type":31,"tag":1215,"props":11924,"children":11925},{"style":2939},[11926],{"type":40,"value":11927},"log",{"type":31,"tag":1215,"props":11929,"children":11930},{"style":1250},[11931],{"type":40,"value":3483},{"type":31,"tag":1215,"props":11933,"children":11934},{"style":2945},[11935],{"type":40,"value":4257},{"type":31,"tag":1215,"props":11937,"children":11938},{"class":1217,"line":9639},[11939,11944,11948,11953],{"type":31,"tag":1215,"props":11940,"children":11941},{"style":1250},[11942],{"type":40,"value":11943},"      action",{"type":31,"tag":1215,"props":11945,"children":11946},{"style":3113},[11947],{"type":40,"value":2963},{"type":31,"tag":1215,"props":11949,"children":11950},{"style":4392},[11951],{"type":40,"value":11952}," 'LEAD_CONVERTED'",{"type":31,"tag":1215,"props":11954,"children":11955},{"style":2945},[11956],{"type":40,"value":2974},{"type":31,"tag":1215,"props":11958,"children":11959},{"class":1217,"line":9647},[11960,11965,11969,11974],{"type":31,"tag":1215,"props":11961,"children":11962},{"style":1250},[11963],{"type":40,"value":11964},"      entityType",{"type":31,"tag":1215,"props":11966,"children":11967},{"style":3113},[11968],{"type":40,"value":2963},{"type":31,"tag":1215,"props":11970,"children":11971},{"style":4392},[11972],{"type":40,"value":11973}," 'client'",{"type":31,"tag":1215,"props":11975,"children":11976},{"style":2945},[11977],{"type":40,"value":2974},{"type":31,"tag":1215,"props":11979,"children":11980},{"class":1217,"line":9660},[11981,11986,11990,11994,11998,12002],{"type":31,"tag":1215,"props":11982,"children":11983},{"style":1250},[11984],{"type":40,"value":11985},"      entityId",{"type":31,"tag":1215,"props":11987,"children":11988},{"style":3113},[11989],{"type":40,"value":2963},{"type":31,"tag":1215,"props":11991,"children":11992},{"style":1250},[11993],{"type":40,"value":4178},{"type":31,"tag":1215,"props":11995,"children":11996},{"style":3113},[11997],{"type":40,"value":76},{"type":31,"tag":1215,"props":11999,"children":12000},{"style":1250},[12001],{"type":40,"value":6048},{"type":31,"tag":1215,"props":12003,"children":12004},{"style":2945},[12005],{"type":40,"value":2974},{"type":31,"tag":1215,"props":12007,"children":12008},{"class":1217,"line":9668},[12009,12014,12018,12022],{"type":31,"tag":1215,"props":12010,"children":12011},{"style":1250},[12012],{"type":40,"value":12013},"      userId",{"type":31,"tag":1215,"props":12015,"children":12016},{"style":3113},[12017],{"type":40,"value":2963},{"type":31,"tag":1215,"props":12019,"children":12020},{"style":1250},[12021],{"type":40,"value":10532},{"type":31,"tag":1215,"props":12023,"children":12024},{"style":2945},[12025],{"type":40,"value":2974},{"type":31,"tag":1215,"props":12027,"children":12028},{"class":1217,"line":9723},[12029,12034,12038,12042,12047,12051,12055],{"type":31,"tag":1215,"props":12030,"children":12031},{"style":1250},[12032],{"type":40,"value":12033},"      meta",{"type":31,"tag":1215,"props":12035,"children":12036},{"style":3113},[12037],{"type":40,"value":2963},{"type":31,"tag":1215,"props":12039,"children":12040},{"style":2945},[12041],{"type":40,"value":5616},{"type":31,"tag":1215,"props":12043,"children":12044},{"style":1250},[12045],{"type":40,"value":12046}," fromLeadId",{"type":31,"tag":1215,"props":12048,"children":12049},{"style":3113},[12050],{"type":40,"value":2963},{"type":31,"tag":1215,"props":12052,"children":12053},{"style":1250},[12054],{"type":40,"value":10711},{"type":31,"tag":1215,"props":12056,"children":12057},{"style":2945},[12058],{"type":40,"value":5643},{"type":31,"tag":1215,"props":12060,"children":12061},{"class":1217,"line":9731},[12062,12066,12070],{"type":31,"tag":1215,"props":12063,"children":12064},{"style":2945},[12065],{"type":40,"value":5651},{"type":31,"tag":1215,"props":12067,"children":12068},{"style":1250},[12069],{"type":40,"value":3002},{"type":31,"tag":1215,"props":12071,"children":12072},{"style":2945},[12073],{"type":40,"value":3055},{"type":31,"tag":1215,"props":12075,"children":12077},{"class":1217,"line":12076},60,[12078],{"type":31,"tag":1215,"props":12079,"children":12080},{"emptyLinePlaceholder":13},[12081],{"type":40,"value":1233},{"type":31,"tag":1215,"props":12083,"children":12085},{"class":1217,"line":12084},61,[12086],{"type":31,"tag":1215,"props":12087,"children":12088},{"style":2924},[12089],{"type":40,"value":12090},"    \u002F\u002F 5. Événement domaine (4 lignes)\n",{"type":31,"tag":1215,"props":12092,"children":12094},{"class":1217,"line":12093},62,[12095,12099,12103,12108,12112,12117,12121,12126,12130,12134,12139,12143,12147,12151,12155,12159,12163,12167,12171],{"type":31,"tag":1215,"props":12096,"children":12097},{"style":4190},[12098],{"type":40,"value":4367},{"type":31,"tag":1215,"props":12100,"children":12101},{"style":3113},[12102],{"type":40,"value":76},{"type":31,"tag":1215,"props":12104,"children":12105},{"style":1250},[12106],{"type":40,"value":12107},"eventEmitter",{"type":31,"tag":1215,"props":12109,"children":12110},{"style":3113},[12111],{"type":40,"value":76},{"type":31,"tag":1215,"props":12113,"children":12114},{"style":2939},[12115],{"type":40,"value":12116},"emit",{"type":31,"tag":1215,"props":12118,"children":12119},{"style":1250},[12120],{"type":40,"value":3483},{"type":31,"tag":1215,"props":12122,"children":12123},{"style":4392},[12124],{"type":40,"value":12125},"'client.created'",{"type":31,"tag":1215,"props":12127,"children":12128},{"style":2945},[12129],{"type":40,"value":5843},{"type":31,"tag":1215,"props":12131,"children":12132},{"style":2945},[12133],{"type":40,"value":5616},{"type":31,"tag":1215,"props":12135,"children":12136},{"style":1250},[12137],{"type":40,"value":12138}," clientId",{"type":31,"tag":1215,"props":12140,"children":12141},{"style":3113},[12142],{"type":40,"value":2963},{"type":31,"tag":1215,"props":12144,"children":12145},{"style":1250},[12146],{"type":40,"value":4178},{"type":31,"tag":1215,"props":12148,"children":12149},{"style":3113},[12150],{"type":40,"value":76},{"type":31,"tag":1215,"props":12152,"children":12153},{"style":1250},[12154],{"type":40,"value":6048},{"type":31,"tag":1215,"props":12156,"children":12157},{"style":2945},[12158],{"type":40,"value":5843},{"type":31,"tag":1215,"props":12160,"children":12161},{"style":1250},[12162],{"type":40,"value":11020},{"type":31,"tag":1215,"props":12164,"children":12165},{"style":2945},[12166],{"type":40,"value":5455},{"type":31,"tag":1215,"props":12168,"children":12169},{"style":1250},[12170],{"type":40,"value":3002},{"type":31,"tag":1215,"props":12172,"children":12173},{"style":2945},[12174],{"type":40,"value":3055},{"type":31,"tag":1215,"props":12176,"children":12177},{"class":1217,"line":10177},[12178],{"type":31,"tag":1215,"props":12179,"children":12180},{"emptyLinePlaceholder":13},[12181],{"type":40,"value":1233},{"type":31,"tag":1215,"props":12183,"children":12184},{"class":1217,"line":5018},[12185,12189,12194,12198,12203,12207],{"type":31,"tag":1215,"props":12186,"children":12187},{"style":2933},[12188],{"type":40,"value":3632},{"type":31,"tag":1215,"props":12190,"children":12191},{"style":1250},[12192],{"type":40,"value":12193}," ClientDto",{"type":31,"tag":1215,"props":12195,"children":12196},{"style":3113},[12197],{"type":40,"value":76},{"type":31,"tag":1215,"props":12199,"children":12200},{"style":2939},[12201],{"type":40,"value":12202},"fromPrisma",{"type":31,"tag":1215,"props":12204,"children":12205},{"style":1250},[12206],{"type":40,"value":4424},{"type":31,"tag":1215,"props":12208,"children":12209},{"style":2945},[12210],{"type":40,"value":3055},{"type":31,"tag":1215,"props":12212,"children":12213},{"class":1217,"line":2766},[12214],{"type":31,"tag":1215,"props":12215,"children":12216},{"style":2945},[12217],{"type":40,"value":3710},{"type":31,"tag":1215,"props":12219,"children":12220},{"class":1217,"line":1859},[12221],{"type":31,"tag":1215,"props":12222,"children":12223},{"style":2945},[12224],{"type":40,"value":3160},{"type":31,"tag":32,"props":12226,"children":12227},{},[12228,12230,12236,12238,12244,12245,12251,12252,12258,12259,12265,12266,12272],{"type":40,"value":12229},"Le contre-pattern craft, c'est l'application du Single Responsibility Principle. Cette fonction devient un orchestrateur de 10 lignes qui appelle des helpers nommés : ",{"type":31,"tag":169,"props":12231,"children":12233},{"className":12232},[],[12234],{"type":40,"value":12235},"assertLeadIsConvertible",{"type":40,"value":12237},", ",{"type":31,"tag":169,"props":12239,"children":12241},{"className":12240},[],[12242],{"type":40,"value":12243},"createClientFromLead",{"type":40,"value":12237},{"type":31,"tag":169,"props":12246,"children":12248},{"className":12247},[],[12249],{"type":40,"value":12250},"markLeadAsConverted",{"type":40,"value":12237},{"type":31,"tag":169,"props":12253,"children":12255},{"className":12254},[],[12256],{"type":40,"value":12257},"notifyClientOnboarding",{"type":40,"value":12237},{"type":31,"tag":169,"props":12260,"children":12262},{"className":12261},[],[12263],{"type":40,"value":12264},"logConversionAudit",{"type":40,"value":12237},{"type":31,"tag":169,"props":12267,"children":12269},{"className":12268},[],[12270],{"type":40,"value":12271},"emitClientCreated",{"type":40,"value":12273},". Chacun est testable isolément. Chacun est lisible en 30 secondes.",{"type":31,"tag":32,"props":12275,"children":12276},{},[12277,12279,12284],{"type":40,"value":12278},"Le prompt que j'utilise : ",{"type":31,"tag":97,"props":12280,"children":12281},{},[12282],{"type":40,"value":12283},"\"Découpe ce use case en responsabilités nommées. Chaque helper doit tenir en moins de 20 lignes et avoir un seul motif de changement.\"",{"type":40,"value":12285}," Claude exécute proprement. Le problème est qu'il ne le fait jamais spontanément.",{"type":31,"tag":48,"props":12287,"children":12288},{},[],{"type":31,"tag":52,"props":12290,"children":12292},{"id":12291},"pattern-2-couplage-métierorm",[12293],{"type":40,"value":12294},"Pattern #2 : couplage métier\u002FORM",{"type":31,"tag":32,"props":12296,"children":12297},{},[12298,12300,12306],{"type":40,"value":12299},"Celui-là est plus insidieux. Claude injecte directement le ",{"type":31,"tag":169,"props":12301,"children":12303},{"className":12302},[],[12304],{"type":40,"value":12305},"PrismaClient",{"type":40,"value":12307}," dans les use cases métier. Le résultat : vos règles de gestion ne peuvent plus tourner sans une base Postgres, vos tests ne peuvent plus être unitaires, et changer de stratégie de persistence devient une refonte de plusieurs mois.",{"type":31,"tag":32,"props":12309,"children":12310},{},[12311,12313,12319],{"type":40,"value":12312},"Concrètement, j'ai vu ça dans crmcoaching sur le bounded context ",{"type":31,"tag":169,"props":12314,"children":12316},{"className":12315},[],[12317],{"type":40,"value":12318},"slot-hold",{"type":40,"value":3689},{"type":31,"tag":1206,"props":12321,"children":12323},{"className":2912,"code":12322,"language":2914,"meta":8,"style":8},"\u002F\u002F apps\u002Fapi\u002Fsrc\u002Fapplication\u002Fuse-cases\u002Fcheck-slot-availability.use-case.ts\n\u002F\u002F Couplage direct Prisma dans le use case (anti-pattern)\n\n@Injectable()\nexport class CheckSlotAvailabilityUseCase {\n  constructor(\n    private readonly prisma: PrismaClient, \u002F\u002F couplage direct infra\n  ) {}\n\n  async execute(slotId: string, coachId: string): Promise\u003Cboolean> {\n    const holds = await this.prisma.slotHold.count({\n      where: { slotId, status: 'ACTIVE', expiresAt: { gt: new Date() } },\n    });\n    const slot = await this.prisma.slot.findUnique({ where: { id: slotId } });\n    return slot != null && slot.coachId === coachId && holds === 0;\n  }\n}\n",[12324],{"type":31,"tag":169,"props":12325,"children":12326},{"__ignoreMap":8},[12327,12335,12343,12350,12365,12385,12396,12428,12439,12446,12519,12577,12661,12676,12774,12841,12848],{"type":31,"tag":1215,"props":12328,"children":12329},{"class":1217,"line":1218},[12330],{"type":31,"tag":1215,"props":12331,"children":12332},{"style":2924},[12333],{"type":40,"value":12334},"\u002F\u002F apps\u002Fapi\u002Fsrc\u002Fapplication\u002Fuse-cases\u002Fcheck-slot-availability.use-case.ts\n",{"type":31,"tag":1215,"props":12336,"children":12337},{"class":1217,"line":817},[12338],{"type":31,"tag":1215,"props":12339,"children":12340},{"style":2924},[12341],{"type":40,"value":12342},"\u002F\u002F Couplage direct Prisma dans le use case (anti-pattern)\n",{"type":31,"tag":1215,"props":12344,"children":12345},{"class":1217,"line":1236},[12346],{"type":31,"tag":1215,"props":12347,"children":12348},{"emptyLinePlaceholder":13},[12349],{"type":40,"value":1233},{"type":31,"tag":1215,"props":12351,"children":12352},{"class":1217,"line":1246},[12353,12357,12361],{"type":31,"tag":1215,"props":12354,"children":12355},{"style":3541},[12356],{"type":40,"value":3544},{"type":31,"tag":1215,"props":12358,"children":12359},{"style":2939},[12360],{"type":40,"value":3549},{"type":31,"tag":1215,"props":12362,"children":12363},{"style":3552},[12364],{"type":40,"value":3555},{"type":31,"tag":1215,"props":12366,"children":12367},{"class":1217,"line":1256},[12368,12372,12376,12381],{"type":31,"tag":1215,"props":12369,"children":12370},{"style":2933},[12371],{"type":40,"value":3455},{"type":31,"tag":1215,"props":12373,"children":12374},{"style":2933},[12375],{"type":40,"value":3567},{"type":31,"tag":1215,"props":12377,"children":12378},{"style":3463},[12379],{"type":40,"value":12380}," CheckSlotAvailabilityUseCase",{"type":31,"tag":1215,"props":12382,"children":12383},{"style":2945},[12384],{"type":40,"value":3015},{"type":31,"tag":1215,"props":12386,"children":12387},{"class":1217,"line":1264},[12388,12392],{"type":31,"tag":1215,"props":12389,"children":12390},{"style":2933},[12391],{"type":40,"value":3988},{"type":31,"tag":1215,"props":12393,"children":12394},{"style":2945},[12395],{"type":40,"value":2948},{"type":31,"tag":1215,"props":12397,"children":12398},{"class":1217,"line":1273},[12399,12403,12407,12411,12415,12419,12423],{"type":31,"tag":1215,"props":12400,"children":12401},{"style":2933},[12402],{"type":40,"value":4000},{"type":31,"tag":1215,"props":12404,"children":12405},{"style":2933},[12406],{"type":40,"value":4005},{"type":31,"tag":1215,"props":12408,"children":12409},{"style":2954},[12410],{"type":40,"value":10376},{"type":31,"tag":1215,"props":12412,"children":12413},{"style":2960},[12414],{"type":40,"value":2963},{"type":31,"tag":1215,"props":12416,"children":12417},{"style":3463},[12418],{"type":40,"value":10385},{"type":31,"tag":1215,"props":12420,"children":12421},{"style":2945},[12422],{"type":40,"value":5843},{"type":31,"tag":1215,"props":12424,"children":12425},{"style":2924},[12426],{"type":40,"value":12427}," \u002F\u002F couplage direct infra\n",{"type":31,"tag":1215,"props":12429,"children":12430},{"class":1217,"line":1282},[12431,12435],{"type":31,"tag":1215,"props":12432,"children":12433},{"style":2945},[12434],{"type":40,"value":4089},{"type":31,"tag":1215,"props":12436,"children":12437},{"style":2945},[12438],{"type":40,"value":4094},{"type":31,"tag":1215,"props":12440,"children":12441},{"class":1217,"line":1291},[12442],{"type":31,"tag":1215,"props":12443,"children":12444},{"emptyLinePlaceholder":13},[12445],{"type":40,"value":1233},{"type":31,"tag":1215,"props":12447,"children":12448},{"class":1217,"line":1300},[12449,12453,12457,12461,12466,12470,12474,12478,12482,12486,12490,12494,12498,12502,12506,12511,12515],{"type":31,"tag":1215,"props":12450,"children":12451},{"style":2933},[12452],{"type":40,"value":4109},{"type":31,"tag":1215,"props":12454,"children":12455},{"style":2939},[12456],{"type":40,"value":10506},{"type":31,"tag":1215,"props":12458,"children":12459},{"style":2945},[12460],{"type":40,"value":3483},{"type":31,"tag":1215,"props":12462,"children":12463},{"style":2954},[12464],{"type":40,"value":12465},"slotId",{"type":31,"tag":1215,"props":12467,"children":12468},{"style":2960},[12469],{"type":40,"value":2963},{"type":31,"tag":1215,"props":12471,"children":12472},{"style":2966},[12473],{"type":40,"value":3783},{"type":31,"tag":1215,"props":12475,"children":12476},{"style":2945},[12477],{"type":40,"value":5843},{"type":31,"tag":1215,"props":12479,"children":12480},{"style":2954},[12481],{"type":40,"value":10532},{"type":31,"tag":1215,"props":12483,"children":12484},{"style":2960},[12485],{"type":40,"value":2963},{"type":31,"tag":1215,"props":12487,"children":12488},{"style":2966},[12489],{"type":40,"value":3783},{"type":31,"tag":1215,"props":12491,"children":12492},{"style":2945},[12493],{"type":40,"value":3002},{"type":31,"tag":1215,"props":12495,"children":12496},{"style":2960},[12497],{"type":40,"value":2963},{"type":31,"tag":1215,"props":12499,"children":12500},{"style":3463},[12501],{"type":40,"value":4145},{"type":31,"tag":1215,"props":12503,"children":12504},{"style":4148},[12505],{"type":40,"value":4151},{"type":31,"tag":1215,"props":12507,"children":12508},{"style":2966},[12509],{"type":40,"value":12510},"boolean",{"type":31,"tag":1215,"props":12512,"children":12513},{"style":4148},[12514],{"type":40,"value":4161},{"type":31,"tag":1215,"props":12516,"children":12517},{"style":2945},[12518],{"type":40,"value":3015},{"type":31,"tag":1215,"props":12520,"children":12521},{"class":1217,"line":1309},[12522,12526,12531,12535,12539,12543,12547,12551,12555,12560,12564,12569,12573],{"type":31,"tag":1215,"props":12523,"children":12524},{"style":2933},[12525],{"type":40,"value":4173},{"type":31,"tag":1215,"props":12527,"children":12528},{"style":3026},[12529],{"type":40,"value":12530}," holds",{"type":31,"tag":1215,"props":12532,"children":12533},{"style":2960},[12534],{"type":40,"value":3034},{"type":31,"tag":1215,"props":12536,"children":12537},{"style":2933},[12538],{"type":40,"value":4187},{"type":31,"tag":1215,"props":12540,"children":12541},{"style":4190},[12542],{"type":40,"value":4193},{"type":31,"tag":1215,"props":12544,"children":12545},{"style":3113},[12546],{"type":40,"value":76},{"type":31,"tag":1215,"props":12548,"children":12549},{"style":1250},[12550],{"type":40,"value":10659},{"type":31,"tag":1215,"props":12552,"children":12553},{"style":3113},[12554],{"type":40,"value":76},{"type":31,"tag":1215,"props":12556,"children":12557},{"style":1250},[12558],{"type":40,"value":12559},"slotHold",{"type":31,"tag":1215,"props":12561,"children":12562},{"style":3113},[12563],{"type":40,"value":76},{"type":31,"tag":1215,"props":12565,"children":12566},{"style":2939},[12567],{"type":40,"value":12568},"count",{"type":31,"tag":1215,"props":12570,"children":12571},{"style":1250},[12572],{"type":40,"value":3483},{"type":31,"tag":1215,"props":12574,"children":12575},{"style":2945},[12576],{"type":40,"value":4257},{"type":31,"tag":1215,"props":12578,"children":12579},{"class":1217,"line":1317},[12580,12584,12588,12592,12597,12601,12605,12609,12613,12617,12622,12626,12630,12635,12639,12643,12647,12652,12656],{"type":31,"tag":1215,"props":12581,"children":12582},{"style":1250},[12583],{"type":40,"value":10982},{"type":31,"tag":1215,"props":12585,"children":12586},{"style":3113},[12587],{"type":40,"value":2963},{"type":31,"tag":1215,"props":12589,"children":12590},{"style":2945},[12591],{"type":40,"value":5616},{"type":31,"tag":1215,"props":12593,"children":12594},{"style":1250},[12595],{"type":40,"value":12596}," slotId",{"type":31,"tag":1215,"props":12598,"children":12599},{"style":2945},[12600],{"type":40,"value":5843},{"type":31,"tag":1215,"props":12602,"children":12603},{"style":1250},[12604],{"type":40,"value":11481},{"type":31,"tag":1215,"props":12606,"children":12607},{"style":3113},[12608],{"type":40,"value":2963},{"type":31,"tag":1215,"props":12610,"children":12611},{"style":4392},[12612],{"type":40,"value":11319},{"type":31,"tag":1215,"props":12614,"children":12615},{"style":2945},[12616],{"type":40,"value":5843},{"type":31,"tag":1215,"props":12618,"children":12619},{"style":1250},[12620],{"type":40,"value":12621}," expiresAt",{"type":31,"tag":1215,"props":12623,"children":12624},{"style":3113},[12625],{"type":40,"value":2963},{"type":31,"tag":1215,"props":12627,"children":12628},{"style":2945},[12629],{"type":40,"value":5616},{"type":31,"tag":1215,"props":12631,"children":12632},{"style":1250},[12633],{"type":40,"value":12634}," gt",{"type":31,"tag":1215,"props":12636,"children":12637},{"style":3113},[12638],{"type":40,"value":2963},{"type":31,"tag":1215,"props":12640,"children":12641},{"style":3809},[12642],{"type":40,"value":3812},{"type":31,"tag":1215,"props":12644,"children":12645},{"style":2939},[12646],{"type":40,"value":6135},{"type":31,"tag":1215,"props":12648,"children":12649},{"style":1250},[12650],{"type":40,"value":12651},"() ",{"type":31,"tag":1215,"props":12653,"children":12654},{"style":2945},[12655],{"type":40,"value":5455},{"type":31,"tag":1215,"props":12657,"children":12658},{"style":2945},[12659],{"type":40,"value":12660}," },\n",{"type":31,"tag":1215,"props":12662,"children":12663},{"class":1217,"line":1326},[12664,12668,12672],{"type":31,"tag":1215,"props":12665,"children":12666},{"style":2945},[12667],{"type":40,"value":5651},{"type":31,"tag":1215,"props":12669,"children":12670},{"style":1250},[12671],{"type":40,"value":3002},{"type":31,"tag":1215,"props":12673,"children":12674},{"style":2945},[12675],{"type":40,"value":3055},{"type":31,"tag":1215,"props":12677,"children":12678},{"class":1217,"line":1341},[12679,12683,12688,12692,12696,12700,12704,12708,12712,12717,12721,12725,12729,12733,12737,12741,12745,12749,12753,12758,12762,12766,12770],{"type":31,"tag":1215,"props":12680,"children":12681},{"style":2933},[12682],{"type":40,"value":4173},{"type":31,"tag":1215,"props":12684,"children":12685},{"style":3026},[12686],{"type":40,"value":12687}," slot",{"type":31,"tag":1215,"props":12689,"children":12690},{"style":2960},[12691],{"type":40,"value":3034},{"type":31,"tag":1215,"props":12693,"children":12694},{"style":2933},[12695],{"type":40,"value":4187},{"type":31,"tag":1215,"props":12697,"children":12698},{"style":4190},[12699],{"type":40,"value":4193},{"type":31,"tag":1215,"props":12701,"children":12702},{"style":3113},[12703],{"type":40,"value":76},{"type":31,"tag":1215,"props":12705,"children":12706},{"style":1250},[12707],{"type":40,"value":10659},{"type":31,"tag":1215,"props":12709,"children":12710},{"style":3113},[12711],{"type":40,"value":76},{"type":31,"tag":1215,"props":12713,"children":12714},{"style":1250},[12715],{"type":40,"value":12716},"slot",{"type":31,"tag":1215,"props":12718,"children":12719},{"style":3113},[12720],{"type":40,"value":76},{"type":31,"tag":1215,"props":12722,"children":12723},{"style":2939},[12724],{"type":40,"value":10676},{"type":31,"tag":1215,"props":12726,"children":12727},{"style":1250},[12728],{"type":40,"value":3483},{"type":31,"tag":1215,"props":12730,"children":12731},{"style":2945},[12732],{"type":40,"value":5534},{"type":31,"tag":1215,"props":12734,"children":12735},{"style":1250},[12736],{"type":40,"value":10689},{"type":31,"tag":1215,"props":12738,"children":12739},{"style":3113},[12740],{"type":40,"value":2963},{"type":31,"tag":1215,"props":12742,"children":12743},{"style":2945},[12744],{"type":40,"value":5616},{"type":31,"tag":1215,"props":12746,"children":12747},{"style":1250},[12748],{"type":40,"value":10702},{"type":31,"tag":1215,"props":12750,"children":12751},{"style":3113},[12752],{"type":40,"value":2963},{"type":31,"tag":1215,"props":12754,"children":12755},{"style":1250},[12756],{"type":40,"value":12757}," slotId ",{"type":31,"tag":1215,"props":12759,"children":12760},{"style":2945},[12761],{"type":40,"value":5455},{"type":31,"tag":1215,"props":12763,"children":12764},{"style":2945},[12765],{"type":40,"value":7476},{"type":31,"tag":1215,"props":12767,"children":12768},{"style":1250},[12769],{"type":40,"value":3002},{"type":31,"tag":1215,"props":12771,"children":12772},{"style":2945},[12773],{"type":40,"value":3055},{"type":31,"tag":1215,"props":12775,"children":12776},{"class":1217,"line":1355},[12777,12781,12786,12790,12794,12799,12803,12807,12811,12815,12819,12824,12829,12833,12837],{"type":31,"tag":1215,"props":12778,"children":12779},{"style":2933},[12780],{"type":40,"value":3632},{"type":31,"tag":1215,"props":12782,"children":12783},{"style":1250},[12784],{"type":40,"value":12785}," slot ",{"type":31,"tag":1215,"props":12787,"children":12788},{"style":2960},[12789],{"type":40,"value":8723},{"type":31,"tag":1215,"props":12791,"children":12792},{"style":2966},[12793],{"type":40,"value":7866},{"type":31,"tag":1215,"props":12795,"children":12796},{"style":2960},[12797],{"type":40,"value":12798}," &&",{"type":31,"tag":1215,"props":12800,"children":12801},{"style":1250},[12802],{"type":40,"value":12687},{"type":31,"tag":1215,"props":12804,"children":12805},{"style":3113},[12806],{"type":40,"value":76},{"type":31,"tag":1215,"props":12808,"children":12809},{"style":1250},[12810],{"type":40,"value":10814},{"type":31,"tag":1215,"props":12812,"children":12813},{"style":2960},[12814],{"type":40,"value":6415},{"type":31,"tag":1215,"props":12816,"children":12817},{"style":1250},[12818],{"type":40,"value":11020},{"type":31,"tag":1215,"props":12820,"children":12821},{"style":2960},[12822],{"type":40,"value":12823},"&&",{"type":31,"tag":1215,"props":12825,"children":12826},{"style":1250},[12827],{"type":40,"value":12828}," holds ",{"type":31,"tag":1215,"props":12830,"children":12831},{"style":2960},[12832],{"type":40,"value":6415},{"type":31,"tag":1215,"props":12834,"children":12835},{"style":3047},[12836],{"type":40,"value":3694},{"type":31,"tag":1215,"props":12838,"children":12839},{"style":2945},[12840],{"type":40,"value":3055},{"type":31,"tag":1215,"props":12842,"children":12843},{"class":1217,"line":1364},[12844],{"type":31,"tag":1215,"props":12845,"children":12846},{"style":2945},[12847],{"type":40,"value":3710},{"type":31,"tag":1215,"props":12849,"children":12850},{"class":1217,"line":1378},[12851],{"type":31,"tag":1215,"props":12852,"children":12853},{"style":2945},[12854],{"type":40,"value":3160},{"type":31,"tag":32,"props":12856,"children":12857},{},[12858,12860,12865,12867,12873,12875,12881,12883,12889,12890,12896,12898,12904],{"type":40,"value":12859},"Le contre-pattern, c'est l'",{"type":31,"tag":69,"props":12861,"children":12862},{"href":4922},[12863],{"type":40,"value":12864},"architecture hexagonale d'Alistair Cockburn",{"type":40,"value":12866}," (2005). La règle métier ne connaît pas Prisma. Elle prend un port ",{"type":31,"tag":169,"props":12868,"children":12870},{"className":12869},[],[12871],{"type":40,"value":12872},"ISlotHoldRepository",{"type":40,"value":12874}," qui expose ",{"type":31,"tag":169,"props":12876,"children":12878},{"className":12877},[],[12879],{"type":40,"value":12880},"countActiveHolds(slotId)",{"type":40,"value":12882}," et un port ",{"type":31,"tag":169,"props":12884,"children":12886},{"className":12885},[],[12887],{"type":40,"value":12888},"ISlotRepository",{"type":40,"value":12874},{"type":31,"tag":169,"props":12891,"children":12893},{"className":12892},[],[12894],{"type":40,"value":12895},"findById(slotId)",{"type":40,"value":12897},". L'implémentation Prisma vit dans ",{"type":31,"tag":169,"props":12899,"children":12901},{"className":12900},[],[12902],{"type":40,"value":12903},"infrastructure\u002F",{"type":40,"value":12905},". Les tests unitaires utilisent un fake repository en mémoire.",{"type":31,"tag":1206,"props":12907,"children":12909},{"className":2912,"code":12908,"language":2914,"meta":8,"style":8},"\u002F\u002F apps\u002Fapi\u002Fsrc\u002Fdomain\u002Fports\u002Fslot-hold.repository.port.ts\nexport interface ISlotHoldRepository {\n  countActiveHolds(slotId: string): Promise\u003Cnumber>;\n}\n\n\u002F\u002F apps\u002Fapi\u002Fsrc\u002Fapplication\u002Fuse-cases\u002Fcheck-slot-availability.use-case.ts\n@Injectable()\nexport class CheckSlotAvailabilityUseCase {\n  constructor(\n    private readonly slotRepo: ISlotRepository,\n    private readonly slotHoldRepo: ISlotHoldRepository,\n  ) {}\n\n  async execute(slotId: string, coachId: string): Promise\u003Cboolean> {\n    const slot = await this.slotRepo.findById(slotId);\n    if (slot == null || slot.coachId !== coachId) return false;\n    const activeHolds = await this.slotHoldRepo.countActiveHolds(slotId);\n    return activeHolds === 0;\n  }\n}\n",[12910],{"type":31,"tag":169,"props":12911,"children":12912},{"__ignoreMap":8},[12913,12921,12941,12994,13001,13008,13015,13030,13049,13060,13089,13117,13128,13135,13206,13255,13312,13362,13386,13393],{"type":31,"tag":1215,"props":12914,"children":12915},{"class":1217,"line":1218},[12916],{"type":31,"tag":1215,"props":12917,"children":12918},{"style":2924},[12919],{"type":40,"value":12920},"\u002F\u002F apps\u002Fapi\u002Fsrc\u002Fdomain\u002Fports\u002Fslot-hold.repository.port.ts\n",{"type":31,"tag":1215,"props":12922,"children":12923},{"class":1217,"line":817},[12924,12928,12932,12937],{"type":31,"tag":1215,"props":12925,"children":12926},{"style":2933},[12927],{"type":40,"value":3455},{"type":31,"tag":1215,"props":12929,"children":12930},{"style":2933},[12931],{"type":40,"value":3460},{"type":31,"tag":1215,"props":12933,"children":12934},{"style":3463},[12935],{"type":40,"value":12936}," ISlotHoldRepository",{"type":31,"tag":1215,"props":12938,"children":12939},{"style":2945},[12940],{"type":40,"value":3015},{"type":31,"tag":1215,"props":12942,"children":12943},{"class":1217,"line":1236},[12944,12949,12953,12957,12961,12965,12969,12973,12977,12981,12986,12990],{"type":31,"tag":1215,"props":12945,"children":12946},{"style":2939},[12947],{"type":40,"value":12948},"  countActiveHolds",{"type":31,"tag":1215,"props":12950,"children":12951},{"style":2945},[12952],{"type":40,"value":3483},{"type":31,"tag":1215,"props":12954,"children":12955},{"style":2954},[12956],{"type":40,"value":12465},{"type":31,"tag":1215,"props":12958,"children":12959},{"style":2960},[12960],{"type":40,"value":2963},{"type":31,"tag":1215,"props":12962,"children":12963},{"style":2966},[12964],{"type":40,"value":3783},{"type":31,"tag":1215,"props":12966,"children":12967},{"style":2945},[12968],{"type":40,"value":3002},{"type":31,"tag":1215,"props":12970,"children":12971},{"style":2960},[12972],{"type":40,"value":2963},{"type":31,"tag":1215,"props":12974,"children":12975},{"style":3463},[12976],{"type":40,"value":4145},{"type":31,"tag":1215,"props":12978,"children":12979},{"style":4148},[12980],{"type":40,"value":4151},{"type":31,"tag":1215,"props":12982,"children":12983},{"style":2966},[12984],{"type":40,"value":12985},"number",{"type":31,"tag":1215,"props":12987,"children":12988},{"style":4148},[12989],{"type":40,"value":4161},{"type":31,"tag":1215,"props":12991,"children":12992},{"style":2945},[12993],{"type":40,"value":3055},{"type":31,"tag":1215,"props":12995,"children":12996},{"class":1217,"line":1246},[12997],{"type":31,"tag":1215,"props":12998,"children":12999},{"style":2945},[13000],{"type":40,"value":3160},{"type":31,"tag":1215,"props":13002,"children":13003},{"class":1217,"line":1256},[13004],{"type":31,"tag":1215,"props":13005,"children":13006},{"emptyLinePlaceholder":13},[13007],{"type":40,"value":1233},{"type":31,"tag":1215,"props":13009,"children":13010},{"class":1217,"line":1264},[13011],{"type":31,"tag":1215,"props":13012,"children":13013},{"style":2924},[13014],{"type":40,"value":12334},{"type":31,"tag":1215,"props":13016,"children":13017},{"class":1217,"line":1273},[13018,13022,13026],{"type":31,"tag":1215,"props":13019,"children":13020},{"style":3541},[13021],{"type":40,"value":3544},{"type":31,"tag":1215,"props":13023,"children":13024},{"style":2939},[13025],{"type":40,"value":3549},{"type":31,"tag":1215,"props":13027,"children":13028},{"style":3552},[13029],{"type":40,"value":3555},{"type":31,"tag":1215,"props":13031,"children":13032},{"class":1217,"line":1282},[13033,13037,13041,13045],{"type":31,"tag":1215,"props":13034,"children":13035},{"style":2933},[13036],{"type":40,"value":3455},{"type":31,"tag":1215,"props":13038,"children":13039},{"style":2933},[13040],{"type":40,"value":3567},{"type":31,"tag":1215,"props":13042,"children":13043},{"style":3463},[13044],{"type":40,"value":12380},{"type":31,"tag":1215,"props":13046,"children":13047},{"style":2945},[13048],{"type":40,"value":3015},{"type":31,"tag":1215,"props":13050,"children":13051},{"class":1217,"line":1291},[13052,13056],{"type":31,"tag":1215,"props":13053,"children":13054},{"style":2933},[13055],{"type":40,"value":3988},{"type":31,"tag":1215,"props":13057,"children":13058},{"style":2945},[13059],{"type":40,"value":2948},{"type":31,"tag":1215,"props":13061,"children":13062},{"class":1217,"line":1300},[13063,13067,13071,13076,13080,13085],{"type":31,"tag":1215,"props":13064,"children":13065},{"style":2933},[13066],{"type":40,"value":4000},{"type":31,"tag":1215,"props":13068,"children":13069},{"style":2933},[13070],{"type":40,"value":4005},{"type":31,"tag":1215,"props":13072,"children":13073},{"style":2954},[13074],{"type":40,"value":13075}," slotRepo",{"type":31,"tag":1215,"props":13077,"children":13078},{"style":2960},[13079],{"type":40,"value":2963},{"type":31,"tag":1215,"props":13081,"children":13082},{"style":3463},[13083],{"type":40,"value":13084}," ISlotRepository",{"type":31,"tag":1215,"props":13086,"children":13087},{"style":2945},[13088],{"type":40,"value":2974},{"type":31,"tag":1215,"props":13090,"children":13091},{"class":1217,"line":1309},[13092,13096,13100,13105,13109,13113],{"type":31,"tag":1215,"props":13093,"children":13094},{"style":2933},[13095],{"type":40,"value":4000},{"type":31,"tag":1215,"props":13097,"children":13098},{"style":2933},[13099],{"type":40,"value":4005},{"type":31,"tag":1215,"props":13101,"children":13102},{"style":2954},[13103],{"type":40,"value":13104}," slotHoldRepo",{"type":31,"tag":1215,"props":13106,"children":13107},{"style":2960},[13108],{"type":40,"value":2963},{"type":31,"tag":1215,"props":13110,"children":13111},{"style":3463},[13112],{"type":40,"value":12936},{"type":31,"tag":1215,"props":13114,"children":13115},{"style":2945},[13116],{"type":40,"value":2974},{"type":31,"tag":1215,"props":13118,"children":13119},{"class":1217,"line":1317},[13120,13124],{"type":31,"tag":1215,"props":13121,"children":13122},{"style":2945},[13123],{"type":40,"value":4089},{"type":31,"tag":1215,"props":13125,"children":13126},{"style":2945},[13127],{"type":40,"value":4094},{"type":31,"tag":1215,"props":13129,"children":13130},{"class":1217,"line":1326},[13131],{"type":31,"tag":1215,"props":13132,"children":13133},{"emptyLinePlaceholder":13},[13134],{"type":40,"value":1233},{"type":31,"tag":1215,"props":13136,"children":13137},{"class":1217,"line":1341},[13138,13142,13146,13150,13154,13158,13162,13166,13170,13174,13178,13182,13186,13190,13194,13198,13202],{"type":31,"tag":1215,"props":13139,"children":13140},{"style":2933},[13141],{"type":40,"value":4109},{"type":31,"tag":1215,"props":13143,"children":13144},{"style":2939},[13145],{"type":40,"value":10506},{"type":31,"tag":1215,"props":13147,"children":13148},{"style":2945},[13149],{"type":40,"value":3483},{"type":31,"tag":1215,"props":13151,"children":13152},{"style":2954},[13153],{"type":40,"value":12465},{"type":31,"tag":1215,"props":13155,"children":13156},{"style":2960},[13157],{"type":40,"value":2963},{"type":31,"tag":1215,"props":13159,"children":13160},{"style":2966},[13161],{"type":40,"value":3783},{"type":31,"tag":1215,"props":13163,"children":13164},{"style":2945},[13165],{"type":40,"value":5843},{"type":31,"tag":1215,"props":13167,"children":13168},{"style":2954},[13169],{"type":40,"value":10532},{"type":31,"tag":1215,"props":13171,"children":13172},{"style":2960},[13173],{"type":40,"value":2963},{"type":31,"tag":1215,"props":13175,"children":13176},{"style":2966},[13177],{"type":40,"value":3783},{"type":31,"tag":1215,"props":13179,"children":13180},{"style":2945},[13181],{"type":40,"value":3002},{"type":31,"tag":1215,"props":13183,"children":13184},{"style":2960},[13185],{"type":40,"value":2963},{"type":31,"tag":1215,"props":13187,"children":13188},{"style":3463},[13189],{"type":40,"value":4145},{"type":31,"tag":1215,"props":13191,"children":13192},{"style":4148},[13193],{"type":40,"value":4151},{"type":31,"tag":1215,"props":13195,"children":13196},{"style":2966},[13197],{"type":40,"value":12510},{"type":31,"tag":1215,"props":13199,"children":13200},{"style":4148},[13201],{"type":40,"value":4161},{"type":31,"tag":1215,"props":13203,"children":13204},{"style":2945},[13205],{"type":40,"value":3015},{"type":31,"tag":1215,"props":13207,"children":13208},{"class":1217,"line":1355},[13209,13213,13217,13221,13225,13229,13233,13238,13242,13246,13251],{"type":31,"tag":1215,"props":13210,"children":13211},{"style":2933},[13212],{"type":40,"value":4173},{"type":31,"tag":1215,"props":13214,"children":13215},{"style":3026},[13216],{"type":40,"value":12687},{"type":31,"tag":1215,"props":13218,"children":13219},{"style":2960},[13220],{"type":40,"value":3034},{"type":31,"tag":1215,"props":13222,"children":13223},{"style":2933},[13224],{"type":40,"value":4187},{"type":31,"tag":1215,"props":13226,"children":13227},{"style":4190},[13228],{"type":40,"value":4193},{"type":31,"tag":1215,"props":13230,"children":13231},{"style":3113},[13232],{"type":40,"value":76},{"type":31,"tag":1215,"props":13234,"children":13235},{"style":1250},[13236],{"type":40,"value":13237},"slotRepo",{"type":31,"tag":1215,"props":13239,"children":13240},{"style":3113},[13241],{"type":40,"value":76},{"type":31,"tag":1215,"props":13243,"children":13244},{"style":2939},[13245],{"type":40,"value":4211},{"type":31,"tag":1215,"props":13247,"children":13248},{"style":1250},[13249],{"type":40,"value":13250},"(slotId)",{"type":31,"tag":1215,"props":13252,"children":13253},{"style":2945},[13254],{"type":40,"value":3055},{"type":31,"tag":1215,"props":13256,"children":13257},{"class":1217,"line":1364},[13258,13262,13267,13271,13275,13279,13283,13287,13291,13295,13299,13303,13308],{"type":31,"tag":1215,"props":13259,"children":13260},{"style":2933},[13261],{"type":40,"value":4237},{"type":31,"tag":1215,"props":13263,"children":13264},{"style":1250},[13265],{"type":40,"value":13266}," (slot ",{"type":31,"tag":1215,"props":13268,"children":13269},{"style":2960},[13270],{"type":40,"value":8630},{"type":31,"tag":1215,"props":13272,"children":13273},{"style":2966},[13274],{"type":40,"value":7866},{"type":31,"tag":1215,"props":13276,"children":13277},{"style":2960},[13278],{"type":40,"value":9350},{"type":31,"tag":1215,"props":13280,"children":13281},{"style":1250},[13282],{"type":40,"value":12687},{"type":31,"tag":1215,"props":13284,"children":13285},{"style":3113},[13286],{"type":40,"value":76},{"type":31,"tag":1215,"props":13288,"children":13289},{"style":1250},[13290],{"type":40,"value":10814},{"type":31,"tag":1215,"props":13292,"children":13293},{"style":2960},[13294],{"type":40,"value":10819},{"type":31,"tag":1215,"props":13296,"children":13297},{"style":1250},[13298],{"type":40,"value":10824},{"type":31,"tag":1215,"props":13300,"children":13301},{"style":2933},[13302],{"type":40,"value":8736},{"type":31,"tag":1215,"props":13304,"children":13305},{"style":3047},[13306],{"type":40,"value":13307}," false",{"type":31,"tag":1215,"props":13309,"children":13310},{"style":2945},[13311],{"type":40,"value":3055},{"type":31,"tag":1215,"props":13313,"children":13314},{"class":1217,"line":1378},[13315,13319,13324,13328,13332,13336,13340,13345,13349,13354,13358],{"type":31,"tag":1215,"props":13316,"children":13317},{"style":2933},[13318],{"type":40,"value":4173},{"type":31,"tag":1215,"props":13320,"children":13321},{"style":3026},[13322],{"type":40,"value":13323}," activeHolds",{"type":31,"tag":1215,"props":13325,"children":13326},{"style":2960},[13327],{"type":40,"value":3034},{"type":31,"tag":1215,"props":13329,"children":13330},{"style":2933},[13331],{"type":40,"value":4187},{"type":31,"tag":1215,"props":13333,"children":13334},{"style":4190},[13335],{"type":40,"value":4193},{"type":31,"tag":1215,"props":13337,"children":13338},{"style":3113},[13339],{"type":40,"value":76},{"type":31,"tag":1215,"props":13341,"children":13342},{"style":1250},[13343],{"type":40,"value":13344},"slotHoldRepo",{"type":31,"tag":1215,"props":13346,"children":13347},{"style":3113},[13348],{"type":40,"value":76},{"type":31,"tag":1215,"props":13350,"children":13351},{"style":2939},[13352],{"type":40,"value":13353},"countActiveHolds",{"type":31,"tag":1215,"props":13355,"children":13356},{"style":1250},[13357],{"type":40,"value":13250},{"type":31,"tag":1215,"props":13359,"children":13360},{"style":2945},[13361],{"type":40,"value":3055},{"type":31,"tag":1215,"props":13363,"children":13364},{"class":1217,"line":1387},[13365,13369,13374,13378,13382],{"type":31,"tag":1215,"props":13366,"children":13367},{"style":2933},[13368],{"type":40,"value":3632},{"type":31,"tag":1215,"props":13370,"children":13371},{"style":1250},[13372],{"type":40,"value":13373}," activeHolds ",{"type":31,"tag":1215,"props":13375,"children":13376},{"style":2960},[13377],{"type":40,"value":6415},{"type":31,"tag":1215,"props":13379,"children":13380},{"style":3047},[13381],{"type":40,"value":3694},{"type":31,"tag":1215,"props":13383,"children":13384},{"style":2945},[13385],{"type":40,"value":3055},{"type":31,"tag":1215,"props":13387,"children":13388},{"class":1217,"line":1401},[13389],{"type":31,"tag":1215,"props":13390,"children":13391},{"style":2945},[13392],{"type":40,"value":3710},{"type":31,"tag":1215,"props":13394,"children":13395},{"class":1217,"line":1410},[13396],{"type":31,"tag":1215,"props":13397,"children":13398},{"style":2945},[13399],{"type":40,"value":3160},{"type":31,"tag":32,"props":13401,"children":13402},{},[13403],{"type":40,"value":13404},"Sur crmcoaching, j'ai 47 use cases qui ne dépendent d'aucun module externe. Je peux faire tourner toute la logique métier en CI sans démarrer Postgres. Le jour où je voudrai migrer vers Drizzle, ce sera 2 jours de travail au lieu de plusieurs mois.",{"type":31,"tag":48,"props":13406,"children":13407},{},[],{"type":31,"tag":52,"props":13409,"children":13411},{"id":13410},"pattern-3-tests-qui-ne-testent-rien",[13412],{"type":40,"value":13413},"Pattern #3 : tests qui ne testent rien",{"type":31,"tag":32,"props":13415,"children":13416},{},[13417,13419,13425,13427,13432],{"type":40,"value":13418},"Celui-là me fait grincer des dents à chaque fois. Claude génère des tests qui ont l'air sérieux, avec des ",{"type":31,"tag":169,"props":13420,"children":13422},{"className":13421},[],[13423],{"type":40,"value":13424},"describe",{"type":40,"value":13426},", des ",{"type":31,"tag":169,"props":13428,"children":13430},{"className":13429},[],[13431],{"type":40,"value":5699},{"type":40,"value":13433},", des assertions, et qui ne testent en réalité aucun comportement métier. Exemple typique sur crmcoaching :",{"type":31,"tag":1206,"props":13435,"children":13437},{"className":2912,"code":13436,"language":2914,"meta":8,"style":8},"\u002F\u002F apps\u002Fapi\u002Fsrc\u002Fapplication\u002Fuse-cases\u002F__tests__\u002Fcreate-mentoring-checkout-intent.test.ts\n\u002F\u002F Test généré par Claude sans prompt de comportement\n\ndescribe('CreateMentoringCheckoutIntentUseCase', () => {\n  it('should call repository save', async () => {\n    const repo = { save: vi.fn().mockResolvedValue({ id: 'intent-1' }) };\n    const useCase = new CreateMentoringCheckoutIntentUseCase(repo as any);\n\n    await useCase.execute({ coachId: 'c1', clientId: 'cl1', offreId: 'o1' });\n\n    expect(repo.save).toHaveBeenCalledWith(\n      expect.objectContaining({ coachId: 'c1' }),\n    ); \u002F\u002F vérifie un appel, pas un comportement\n  });\n});\n",[13438],{"type":31,"tag":169,"props":13439,"children":13440},{"__ignoreMap":8},[13441,13449,13457,13464,13496,13533,13621,13668,13675,13763,13770,13804,13853,13869,13884],{"type":31,"tag":1215,"props":13442,"children":13443},{"class":1217,"line":1218},[13444],{"type":31,"tag":1215,"props":13445,"children":13446},{"style":2924},[13447],{"type":40,"value":13448},"\u002F\u002F apps\u002Fapi\u002Fsrc\u002Fapplication\u002Fuse-cases\u002F__tests__\u002Fcreate-mentoring-checkout-intent.test.ts\n",{"type":31,"tag":1215,"props":13450,"children":13451},{"class":1217,"line":817},[13452],{"type":31,"tag":1215,"props":13453,"children":13454},{"style":2924},[13455],{"type":40,"value":13456},"\u002F\u002F Test généré par Claude sans prompt de comportement\n",{"type":31,"tag":1215,"props":13458,"children":13459},{"class":1217,"line":1236},[13460],{"type":31,"tag":1215,"props":13461,"children":13462},{"emptyLinePlaceholder":13},[13463],{"type":40,"value":1233},{"type":31,"tag":1215,"props":13465,"children":13466},{"class":1217,"line":1246},[13467,13471,13475,13480,13484,13488,13492],{"type":31,"tag":1215,"props":13468,"children":13469},{"style":2939},[13470],{"type":40,"value":13424},{"type":31,"tag":1215,"props":13472,"children":13473},{"style":1250},[13474],{"type":40,"value":3483},{"type":31,"tag":1215,"props":13476,"children":13477},{"style":4392},[13478],{"type":40,"value":13479},"'CreateMentoringCheckoutIntentUseCase'",{"type":31,"tag":1215,"props":13481,"children":13482},{"style":2945},[13483],{"type":40,"value":5843},{"type":31,"tag":1215,"props":13485,"children":13486},{"style":2945},[13487],{"type":40,"value":5853},{"type":31,"tag":1215,"props":13489,"children":13490},{"style":2960},[13491],{"type":40,"value":5858},{"type":31,"tag":1215,"props":13493,"children":13494},{"style":2945},[13495],{"type":40,"value":3015},{"type":31,"tag":1215,"props":13497,"children":13498},{"class":1217,"line":1256},[13499,13504,13508,13513,13517,13521,13525,13529],{"type":31,"tag":1215,"props":13500,"children":13501},{"style":2939},[13502],{"type":40,"value":13503},"  it",{"type":31,"tag":1215,"props":13505,"children":13506},{"style":1250},[13507],{"type":40,"value":3483},{"type":31,"tag":1215,"props":13509,"children":13510},{"style":4392},[13511],{"type":40,"value":13512},"'should call repository save'",{"type":31,"tag":1215,"props":13514,"children":13515},{"style":2945},[13516],{"type":40,"value":5843},{"type":31,"tag":1215,"props":13518,"children":13519},{"style":2933},[13520],{"type":40,"value":5848},{"type":31,"tag":1215,"props":13522,"children":13523},{"style":2945},[13524],{"type":40,"value":5853},{"type":31,"tag":1215,"props":13526,"children":13527},{"style":2960},[13528],{"type":40,"value":5858},{"type":31,"tag":1215,"props":13530,"children":13531},{"style":2945},[13532],{"type":40,"value":3015},{"type":31,"tag":1215,"props":13534,"children":13535},{"class":1217,"line":1264},[13536,13540,13545,13549,13553,13558,13562,13566,13570,13575,13579,13583,13587,13591,13595,13599,13603,13608,13612,13616],{"type":31,"tag":1215,"props":13537,"children":13538},{"style":2933},[13539],{"type":40,"value":4173},{"type":31,"tag":1215,"props":13541,"children":13542},{"style":3026},[13543],{"type":40,"value":13544}," repo",{"type":31,"tag":1215,"props":13546,"children":13547},{"style":2960},[13548],{"type":40,"value":3034},{"type":31,"tag":1215,"props":13550,"children":13551},{"style":2945},[13552],{"type":40,"value":5616},{"type":31,"tag":1215,"props":13554,"children":13555},{"style":1250},[13556],{"type":40,"value":13557}," save",{"type":31,"tag":1215,"props":13559,"children":13560},{"style":3113},[13561],{"type":40,"value":2963},{"type":31,"tag":1215,"props":13563,"children":13564},{"style":1250},[13565],{"type":40,"value":7574},{"type":31,"tag":1215,"props":13567,"children":13568},{"style":3113},[13569],{"type":40,"value":76},{"type":31,"tag":1215,"props":13571,"children":13572},{"style":2939},[13573],{"type":40,"value":13574},"fn",{"type":31,"tag":1215,"props":13576,"children":13577},{"style":1250},[13578],{"type":40,"value":3821},{"type":31,"tag":1215,"props":13580,"children":13581},{"style":3113},[13582],{"type":40,"value":76},{"type":31,"tag":1215,"props":13584,"children":13585},{"style":2939},[13586],{"type":40,"value":6207},{"type":31,"tag":1215,"props":13588,"children":13589},{"style":1250},[13590],{"type":40,"value":3483},{"type":31,"tag":1215,"props":13592,"children":13593},{"style":2945},[13594],{"type":40,"value":5534},{"type":31,"tag":1215,"props":13596,"children":13597},{"style":1250},[13598],{"type":40,"value":10702},{"type":31,"tag":1215,"props":13600,"children":13601},{"style":3113},[13602],{"type":40,"value":2963},{"type":31,"tag":1215,"props":13604,"children":13605},{"style":4392},[13606],{"type":40,"value":13607}," 'intent-1'",{"type":31,"tag":1215,"props":13609,"children":13610},{"style":2945},[13611],{"type":40,"value":7476},{"type":31,"tag":1215,"props":13613,"children":13614},{"style":1250},[13615],{"type":40,"value":3140},{"type":31,"tag":1215,"props":13617,"children":13618},{"style":2945},[13619],{"type":40,"value":13620},"};\n",{"type":31,"tag":1215,"props":13622,"children":13623},{"class":1217,"line":1273},[13624,13628,13633,13637,13641,13646,13651,13655,13660,13664],{"type":31,"tag":1215,"props":13625,"children":13626},{"style":2933},[13627],{"type":40,"value":4173},{"type":31,"tag":1215,"props":13629,"children":13630},{"style":3026},[13631],{"type":40,"value":13632}," useCase",{"type":31,"tag":1215,"props":13634,"children":13635},{"style":2960},[13636],{"type":40,"value":3034},{"type":31,"tag":1215,"props":13638,"children":13639},{"style":3809},[13640],{"type":40,"value":3812},{"type":31,"tag":1215,"props":13642,"children":13643},{"style":2939},[13644],{"type":40,"value":13645}," CreateMentoringCheckoutIntentUseCase",{"type":31,"tag":1215,"props":13647,"children":13648},{"style":1250},[13649],{"type":40,"value":13650},"(repo ",{"type":31,"tag":1215,"props":13652,"children":13653},{"style":2933},[13654],{"type":40,"value":6904},{"type":31,"tag":1215,"props":13656,"children":13657},{"style":2966},[13658],{"type":40,"value":13659}," any",{"type":31,"tag":1215,"props":13661,"children":13662},{"style":1250},[13663],{"type":40,"value":3002},{"type":31,"tag":1215,"props":13665,"children":13666},{"style":2945},[13667],{"type":40,"value":3055},{"type":31,"tag":1215,"props":13669,"children":13670},{"class":1217,"line":1282},[13671],{"type":31,"tag":1215,"props":13672,"children":13673},{"emptyLinePlaceholder":13},[13674],{"type":40,"value":1233},{"type":31,"tag":1215,"props":13676,"children":13677},{"class":1217,"line":1291},[13678,13682,13686,13690,13695,13699,13703,13707,13711,13716,13720,13724,13728,13733,13737,13742,13746,13751,13755,13759],{"type":31,"tag":1215,"props":13679,"children":13680},{"style":2933},[13681],{"type":40,"value":4306},{"type":31,"tag":1215,"props":13683,"children":13684},{"style":1250},[13685],{"type":40,"value":13632},{"type":31,"tag":1215,"props":13687,"children":13688},{"style":3113},[13689],{"type":40,"value":76},{"type":31,"tag":1215,"props":13691,"children":13692},{"style":2939},[13693],{"type":40,"value":13694},"execute",{"type":31,"tag":1215,"props":13696,"children":13697},{"style":1250},[13698],{"type":40,"value":3483},{"type":31,"tag":1215,"props":13700,"children":13701},{"style":2945},[13702],{"type":40,"value":5534},{"type":31,"tag":1215,"props":13704,"children":13705},{"style":1250},[13706],{"type":40,"value":10532},{"type":31,"tag":1215,"props":13708,"children":13709},{"style":3113},[13710],{"type":40,"value":2963},{"type":31,"tag":1215,"props":13712,"children":13713},{"style":4392},[13714],{"type":40,"value":13715}," 'c1'",{"type":31,"tag":1215,"props":13717,"children":13718},{"style":2945},[13719],{"type":40,"value":5843},{"type":31,"tag":1215,"props":13721,"children":13722},{"style":1250},[13723],{"type":40,"value":12138},{"type":31,"tag":1215,"props":13725,"children":13726},{"style":3113},[13727],{"type":40,"value":2963},{"type":31,"tag":1215,"props":13729,"children":13730},{"style":4392},[13731],{"type":40,"value":13732}," 'cl1'",{"type":31,"tag":1215,"props":13734,"children":13735},{"style":2945},[13736],{"type":40,"value":5843},{"type":31,"tag":1215,"props":13738,"children":13739},{"style":1250},[13740],{"type":40,"value":13741}," offreId",{"type":31,"tag":1215,"props":13743,"children":13744},{"style":3113},[13745],{"type":40,"value":2963},{"type":31,"tag":1215,"props":13747,"children":13748},{"style":4392},[13749],{"type":40,"value":13750}," 'o1'",{"type":31,"tag":1215,"props":13752,"children":13753},{"style":2945},[13754],{"type":40,"value":7476},{"type":31,"tag":1215,"props":13756,"children":13757},{"style":1250},[13758],{"type":40,"value":3002},{"type":31,"tag":1215,"props":13760,"children":13761},{"style":2945},[13762],{"type":40,"value":3055},{"type":31,"tag":1215,"props":13764,"children":13765},{"class":1217,"line":1300},[13766],{"type":31,"tag":1215,"props":13767,"children":13768},{"emptyLinePlaceholder":13},[13769],{"type":40,"value":1233},{"type":31,"tag":1215,"props":13771,"children":13772},{"class":1217,"line":1309},[13773,13778,13783,13787,13791,13795,13800],{"type":31,"tag":1215,"props":13774,"children":13775},{"style":2939},[13776],{"type":40,"value":13777},"    expect",{"type":31,"tag":1215,"props":13779,"children":13780},{"style":1250},[13781],{"type":40,"value":13782},"(repo",{"type":31,"tag":1215,"props":13784,"children":13785},{"style":3113},[13786],{"type":40,"value":76},{"type":31,"tag":1215,"props":13788,"children":13789},{"style":1250},[13790],{"type":40,"value":8131},{"type":31,"tag":1215,"props":13792,"children":13793},{"style":3113},[13794],{"type":40,"value":76},{"type":31,"tag":1215,"props":13796,"children":13797},{"style":2939},[13798],{"type":40,"value":13799},"toHaveBeenCalledWith",{"type":31,"tag":1215,"props":13801,"children":13802},{"style":1250},[13803],{"type":40,"value":2948},{"type":31,"tag":1215,"props":13805,"children":13806},{"class":1217,"line":1317},[13807,13812,13816,13821,13825,13829,13833,13837,13841,13845,13849],{"type":31,"tag":1215,"props":13808,"children":13809},{"style":1250},[13810],{"type":40,"value":13811},"      expect",{"type":31,"tag":1215,"props":13813,"children":13814},{"style":3113},[13815],{"type":40,"value":76},{"type":31,"tag":1215,"props":13817,"children":13818},{"style":2939},[13819],{"type":40,"value":13820},"objectContaining",{"type":31,"tag":1215,"props":13822,"children":13823},{"style":1250},[13824],{"type":40,"value":3483},{"type":31,"tag":1215,"props":13826,"children":13827},{"style":2945},[13828],{"type":40,"value":5534},{"type":31,"tag":1215,"props":13830,"children":13831},{"style":1250},[13832],{"type":40,"value":10532},{"type":31,"tag":1215,"props":13834,"children":13835},{"style":3113},[13836],{"type":40,"value":2963},{"type":31,"tag":1215,"props":13838,"children":13839},{"style":4392},[13840],{"type":40,"value":13715},{"type":31,"tag":1215,"props":13842,"children":13843},{"style":2945},[13844],{"type":40,"value":7476},{"type":31,"tag":1215,"props":13846,"children":13847},{"style":1250},[13848],{"type":40,"value":3002},{"type":31,"tag":1215,"props":13850,"children":13851},{"style":2945},[13852],{"type":40,"value":2974},{"type":31,"tag":1215,"props":13854,"children":13855},{"class":1217,"line":1326},[13856,13860,13864],{"type":31,"tag":1215,"props":13857,"children":13858},{"style":1250},[13859],{"type":40,"value":9653},{"type":31,"tag":1215,"props":13861,"children":13862},{"style":2945},[13863],{"type":40,"value":9381},{"type":31,"tag":1215,"props":13865,"children":13866},{"style":2924},[13867],{"type":40,"value":13868}," \u002F\u002F vérifie un appel, pas un comportement\n",{"type":31,"tag":1215,"props":13870,"children":13871},{"class":1217,"line":1341},[13872,13876,13880],{"type":31,"tag":1215,"props":13873,"children":13874},{"style":2945},[13875],{"type":40,"value":6160},{"type":31,"tag":1215,"props":13877,"children":13878},{"style":1250},[13879],{"type":40,"value":3002},{"type":31,"tag":1215,"props":13881,"children":13882},{"style":2945},[13883],{"type":40,"value":3055},{"type":31,"tag":1215,"props":13885,"children":13886},{"class":1217,"line":1355},[13887,13891,13895],{"type":31,"tag":1215,"props":13888,"children":13889},{"style":2945},[13890],{"type":40,"value":5455},{"type":31,"tag":1215,"props":13892,"children":13893},{"style":1250},[13894],{"type":40,"value":3002},{"type":31,"tag":1215,"props":13896,"children":13897},{"style":2945},[13898],{"type":40,"value":3055},{"type":31,"tag":32,"props":13900,"children":13901},{},[13902],{"type":40,"value":13903},"Ce test ne dit rien. Il vérifie que la fonction appelle une autre fonction. C'est tautologique. Si vous changez l'implémentation pour utiliser un event store au lieu d'un repository, le test casse, sans qu'aucun comportement métier ait changé. C'est le pire des deux mondes : couplage fort à l'implémentation, zéro garantie de correction.",{"type":31,"tag":32,"props":13905,"children":13906},{},[13907,13909,13914],{"type":40,"value":13908},"Le contre-pattern, c'est ce que Dan North appelait ",{"type":31,"tag":97,"props":13910,"children":13911},{},[13912],{"type":40,"value":13913},"\"Tests as Specifications\"",{"type":40,"value":13915}," dans ses articles BDD fondateurs de 2006. Le test décrit un comportement observable :",{"type":31,"tag":1206,"props":13917,"children":13919},{"className":2912,"code":13918,"language":2914,"meta":8,"style":8},"describe('CreateMentoringCheckoutIntentUseCase', () => {\n  it('étant donné une offre active, quand on crée l\\'intent, alors l\\'intent contient le prix de l\\'offre', async () => {\n    \u002F\u002F Given\n    const offre = { id: 'o1', price: 150, currency: 'EUR', status: 'ACTIVE' };\n    const repo = new InMemoryCheckoutIntentRepository();\n    const offreRepo = new InMemoryOffreRepository([offre]);\n    const useCase = new CreateMentoringCheckoutIntentUseCase(repo, offreRepo);\n\n    \u002F\u002F When\n    const result = await useCase.execute({ coachId: 'c1', clientId: 'cl1', offreId: 'o1' });\n\n    \u002F\u002F Then\n    expect(result.amount).toBe(150);\n    expect(result.currency).toBe('EUR');\n    expect(result.status).toBe('PENDING');\n  });\n});\n",[13920],{"type":31,"tag":169,"props":13921,"children":13922},{"__ignoreMap":8},[13923,13954,14019,14027,14115,14147,14181,14221,14228,14236,14332,14339,14347,14393,14438,14483,14498],{"type":31,"tag":1215,"props":13924,"children":13925},{"class":1217,"line":1218},[13926,13930,13934,13938,13942,13946,13950],{"type":31,"tag":1215,"props":13927,"children":13928},{"style":2939},[13929],{"type":40,"value":13424},{"type":31,"tag":1215,"props":13931,"children":13932},{"style":1250},[13933],{"type":40,"value":3483},{"type":31,"tag":1215,"props":13935,"children":13936},{"style":4392},[13937],{"type":40,"value":13479},{"type":31,"tag":1215,"props":13939,"children":13940},{"style":2945},[13941],{"type":40,"value":5843},{"type":31,"tag":1215,"props":13943,"children":13944},{"style":2945},[13945],{"type":40,"value":5853},{"type":31,"tag":1215,"props":13947,"children":13948},{"style":2960},[13949],{"type":40,"value":5858},{"type":31,"tag":1215,"props":13951,"children":13952},{"style":2945},[13953],{"type":40,"value":3015},{"type":31,"tag":1215,"props":13955,"children":13956},{"class":1217,"line":817},[13957,13961,13965,13970,13976,13981,13985,13990,13994,13999,14003,14007,14011,14015],{"type":31,"tag":1215,"props":13958,"children":13959},{"style":2939},[13960],{"type":40,"value":13503},{"type":31,"tag":1215,"props":13962,"children":13963},{"style":1250},[13964],{"type":40,"value":3483},{"type":31,"tag":1215,"props":13966,"children":13967},{"style":4392},[13968],{"type":40,"value":13969},"'étant donné une offre active, quand on crée l",{"type":31,"tag":1215,"props":13971,"children":13973},{"style":13972},"--shiki-default:#F4B8E4;--shiki-dark:#79B8FF",[13974],{"type":40,"value":13975},"\\'",{"type":31,"tag":1215,"props":13977,"children":13978},{"style":4392},[13979],{"type":40,"value":13980},"intent, alors l",{"type":31,"tag":1215,"props":13982,"children":13983},{"style":13972},[13984],{"type":40,"value":13975},{"type":31,"tag":1215,"props":13986,"children":13987},{"style":4392},[13988],{"type":40,"value":13989},"intent contient le prix de l",{"type":31,"tag":1215,"props":13991,"children":13992},{"style":13972},[13993],{"type":40,"value":13975},{"type":31,"tag":1215,"props":13995,"children":13996},{"style":4392},[13997],{"type":40,"value":13998},"offre'",{"type":31,"tag":1215,"props":14000,"children":14001},{"style":2945},[14002],{"type":40,"value":5843},{"type":31,"tag":1215,"props":14004,"children":14005},{"style":2933},[14006],{"type":40,"value":5848},{"type":31,"tag":1215,"props":14008,"children":14009},{"style":2945},[14010],{"type":40,"value":5853},{"type":31,"tag":1215,"props":14012,"children":14013},{"style":2960},[14014],{"type":40,"value":5858},{"type":31,"tag":1215,"props":14016,"children":14017},{"style":2945},[14018],{"type":40,"value":3015},{"type":31,"tag":1215,"props":14020,"children":14021},{"class":1217,"line":1236},[14022],{"type":31,"tag":1215,"props":14023,"children":14024},{"style":2924},[14025],{"type":40,"value":14026},"    \u002F\u002F Given\n",{"type":31,"tag":1215,"props":14028,"children":14029},{"class":1217,"line":1246},[14030,14034,14039,14043,14047,14051,14055,14059,14063,14068,14072,14077,14081,14086,14090,14095,14099,14103,14107,14111],{"type":31,"tag":1215,"props":14031,"children":14032},{"style":2933},[14033],{"type":40,"value":4173},{"type":31,"tag":1215,"props":14035,"children":14036},{"style":3026},[14037],{"type":40,"value":14038}," offre",{"type":31,"tag":1215,"props":14040,"children":14041},{"style":2960},[14042],{"type":40,"value":3034},{"type":31,"tag":1215,"props":14044,"children":14045},{"style":2945},[14046],{"type":40,"value":5616},{"type":31,"tag":1215,"props":14048,"children":14049},{"style":1250},[14050],{"type":40,"value":10702},{"type":31,"tag":1215,"props":14052,"children":14053},{"style":3113},[14054],{"type":40,"value":2963},{"type":31,"tag":1215,"props":14056,"children":14057},{"style":4392},[14058],{"type":40,"value":13750},{"type":31,"tag":1215,"props":14060,"children":14061},{"style":2945},[14062],{"type":40,"value":5843},{"type":31,"tag":1215,"props":14064,"children":14065},{"style":1250},[14066],{"type":40,"value":14067}," price",{"type":31,"tag":1215,"props":14069,"children":14070},{"style":3113},[14071],{"type":40,"value":2963},{"type":31,"tag":1215,"props":14073,"children":14074},{"style":3047},[14075],{"type":40,"value":14076}," 150",{"type":31,"tag":1215,"props":14078,"children":14079},{"style":2945},[14080],{"type":40,"value":5843},{"type":31,"tag":1215,"props":14082,"children":14083},{"style":1250},[14084],{"type":40,"value":14085}," currency",{"type":31,"tag":1215,"props":14087,"children":14088},{"style":3113},[14089],{"type":40,"value":2963},{"type":31,"tag":1215,"props":14091,"children":14092},{"style":4392},[14093],{"type":40,"value":14094}," 'EUR'",{"type":31,"tag":1215,"props":14096,"children":14097},{"style":2945},[14098],{"type":40,"value":5843},{"type":31,"tag":1215,"props":14100,"children":14101},{"style":1250},[14102],{"type":40,"value":11481},{"type":31,"tag":1215,"props":14104,"children":14105},{"style":3113},[14106],{"type":40,"value":2963},{"type":31,"tag":1215,"props":14108,"children":14109},{"style":4392},[14110],{"type":40,"value":11319},{"type":31,"tag":1215,"props":14112,"children":14113},{"style":2945},[14114],{"type":40,"value":6936},{"type":31,"tag":1215,"props":14116,"children":14117},{"class":1217,"line":1256},[14118,14122,14126,14130,14134,14139,14143],{"type":31,"tag":1215,"props":14119,"children":14120},{"style":2933},[14121],{"type":40,"value":4173},{"type":31,"tag":1215,"props":14123,"children":14124},{"style":3026},[14125],{"type":40,"value":13544},{"type":31,"tag":1215,"props":14127,"children":14128},{"style":2960},[14129],{"type":40,"value":3034},{"type":31,"tag":1215,"props":14131,"children":14132},{"style":3809},[14133],{"type":40,"value":3812},{"type":31,"tag":1215,"props":14135,"children":14136},{"style":2939},[14137],{"type":40,"value":14138}," InMemoryCheckoutIntentRepository",{"type":31,"tag":1215,"props":14140,"children":14141},{"style":1250},[14142],{"type":40,"value":3821},{"type":31,"tag":1215,"props":14144,"children":14145},{"style":2945},[14146],{"type":40,"value":3055},{"type":31,"tag":1215,"props":14148,"children":14149},{"class":1217,"line":1264},[14150,14154,14159,14163,14167,14172,14177],{"type":31,"tag":1215,"props":14151,"children":14152},{"style":2933},[14153],{"type":40,"value":4173},{"type":31,"tag":1215,"props":14155,"children":14156},{"style":3026},[14157],{"type":40,"value":14158}," offreRepo",{"type":31,"tag":1215,"props":14160,"children":14161},{"style":2960},[14162],{"type":40,"value":3034},{"type":31,"tag":1215,"props":14164,"children":14165},{"style":3809},[14166],{"type":40,"value":3812},{"type":31,"tag":1215,"props":14168,"children":14169},{"style":2939},[14170],{"type":40,"value":14171}," InMemoryOffreRepository",{"type":31,"tag":1215,"props":14173,"children":14174},{"style":1250},[14175],{"type":40,"value":14176},"([offre])",{"type":31,"tag":1215,"props":14178,"children":14179},{"style":2945},[14180],{"type":40,"value":3055},{"type":31,"tag":1215,"props":14182,"children":14183},{"class":1217,"line":1273},[14184,14188,14192,14196,14200,14204,14208,14212,14217],{"type":31,"tag":1215,"props":14185,"children":14186},{"style":2933},[14187],{"type":40,"value":4173},{"type":31,"tag":1215,"props":14189,"children":14190},{"style":3026},[14191],{"type":40,"value":13632},{"type":31,"tag":1215,"props":14193,"children":14194},{"style":2960},[14195],{"type":40,"value":3034},{"type":31,"tag":1215,"props":14197,"children":14198},{"style":3809},[14199],{"type":40,"value":3812},{"type":31,"tag":1215,"props":14201,"children":14202},{"style":2939},[14203],{"type":40,"value":13645},{"type":31,"tag":1215,"props":14205,"children":14206},{"style":1250},[14207],{"type":40,"value":13782},{"type":31,"tag":1215,"props":14209,"children":14210},{"style":2945},[14211],{"type":40,"value":5843},{"type":31,"tag":1215,"props":14213,"children":14214},{"style":1250},[14215],{"type":40,"value":14216}," offreRepo)",{"type":31,"tag":1215,"props":14218,"children":14219},{"style":2945},[14220],{"type":40,"value":3055},{"type":31,"tag":1215,"props":14222,"children":14223},{"class":1217,"line":1282},[14224],{"type":31,"tag":1215,"props":14225,"children":14226},{"emptyLinePlaceholder":13},[14227],{"type":40,"value":1233},{"type":31,"tag":1215,"props":14229,"children":14230},{"class":1217,"line":1291},[14231],{"type":31,"tag":1215,"props":14232,"children":14233},{"style":2924},[14234],{"type":40,"value":14235},"    \u002F\u002F When\n",{"type":31,"tag":1215,"props":14237,"children":14238},{"class":1217,"line":1300},[14239,14243,14248,14252,14256,14260,14264,14268,14272,14276,14280,14284,14288,14292,14296,14300,14304,14308,14312,14316,14320,14324,14328],{"type":31,"tag":1215,"props":14240,"children":14241},{"style":2933},[14242],{"type":40,"value":4173},{"type":31,"tag":1215,"props":14244,"children":14245},{"style":3026},[14246],{"type":40,"value":14247}," result",{"type":31,"tag":1215,"props":14249,"children":14250},{"style":2960},[14251],{"type":40,"value":3034},{"type":31,"tag":1215,"props":14253,"children":14254},{"style":2933},[14255],{"type":40,"value":4187},{"type":31,"tag":1215,"props":14257,"children":14258},{"style":1250},[14259],{"type":40,"value":13632},{"type":31,"tag":1215,"props":14261,"children":14262},{"style":3113},[14263],{"type":40,"value":76},{"type":31,"tag":1215,"props":14265,"children":14266},{"style":2939},[14267],{"type":40,"value":13694},{"type":31,"tag":1215,"props":14269,"children":14270},{"style":1250},[14271],{"type":40,"value":3483},{"type":31,"tag":1215,"props":14273,"children":14274},{"style":2945},[14275],{"type":40,"value":5534},{"type":31,"tag":1215,"props":14277,"children":14278},{"style":1250},[14279],{"type":40,"value":10532},{"type":31,"tag":1215,"props":14281,"children":14282},{"style":3113},[14283],{"type":40,"value":2963},{"type":31,"tag":1215,"props":14285,"children":14286},{"style":4392},[14287],{"type":40,"value":13715},{"type":31,"tag":1215,"props":14289,"children":14290},{"style":2945},[14291],{"type":40,"value":5843},{"type":31,"tag":1215,"props":14293,"children":14294},{"style":1250},[14295],{"type":40,"value":12138},{"type":31,"tag":1215,"props":14297,"children":14298},{"style":3113},[14299],{"type":40,"value":2963},{"type":31,"tag":1215,"props":14301,"children":14302},{"style":4392},[14303],{"type":40,"value":13732},{"type":31,"tag":1215,"props":14305,"children":14306},{"style":2945},[14307],{"type":40,"value":5843},{"type":31,"tag":1215,"props":14309,"children":14310},{"style":1250},[14311],{"type":40,"value":13741},{"type":31,"tag":1215,"props":14313,"children":14314},{"style":3113},[14315],{"type":40,"value":2963},{"type":31,"tag":1215,"props":14317,"children":14318},{"style":4392},[14319],{"type":40,"value":13750},{"type":31,"tag":1215,"props":14321,"children":14322},{"style":2945},[14323],{"type":40,"value":7476},{"type":31,"tag":1215,"props":14325,"children":14326},{"style":1250},[14327],{"type":40,"value":3002},{"type":31,"tag":1215,"props":14329,"children":14330},{"style":2945},[14331],{"type":40,"value":3055},{"type":31,"tag":1215,"props":14333,"children":14334},{"class":1217,"line":1309},[14335],{"type":31,"tag":1215,"props":14336,"children":14337},{"emptyLinePlaceholder":13},[14338],{"type":40,"value":1233},{"type":31,"tag":1215,"props":14340,"children":14341},{"class":1217,"line":1317},[14342],{"type":31,"tag":1215,"props":14343,"children":14344},{"style":2924},[14345],{"type":40,"value":14346},"    \u002F\u002F Then\n",{"type":31,"tag":1215,"props":14348,"children":14349},{"class":1217,"line":1326},[14350,14354,14359,14363,14368,14372,14376,14380,14385,14389],{"type":31,"tag":1215,"props":14351,"children":14352},{"style":2939},[14353],{"type":40,"value":13777},{"type":31,"tag":1215,"props":14355,"children":14356},{"style":1250},[14357],{"type":40,"value":14358},"(result",{"type":31,"tag":1215,"props":14360,"children":14361},{"style":3113},[14362],{"type":40,"value":76},{"type":31,"tag":1215,"props":14364,"children":14365},{"style":1250},[14366],{"type":40,"value":14367},"amount)",{"type":31,"tag":1215,"props":14369,"children":14370},{"style":3113},[14371],{"type":40,"value":76},{"type":31,"tag":1215,"props":14373,"children":14374},{"style":2939},[14375],{"type":40,"value":7075},{"type":31,"tag":1215,"props":14377,"children":14378},{"style":1250},[14379],{"type":40,"value":3483},{"type":31,"tag":1215,"props":14381,"children":14382},{"style":3047},[14383],{"type":40,"value":14384},"150",{"type":31,"tag":1215,"props":14386,"children":14387},{"style":1250},[14388],{"type":40,"value":3002},{"type":31,"tag":1215,"props":14390,"children":14391},{"style":2945},[14392],{"type":40,"value":3055},{"type":31,"tag":1215,"props":14394,"children":14395},{"class":1217,"line":1341},[14396,14400,14404,14408,14413,14417,14421,14425,14430,14434],{"type":31,"tag":1215,"props":14397,"children":14398},{"style":2939},[14399],{"type":40,"value":13777},{"type":31,"tag":1215,"props":14401,"children":14402},{"style":1250},[14403],{"type":40,"value":14358},{"type":31,"tag":1215,"props":14405,"children":14406},{"style":3113},[14407],{"type":40,"value":76},{"type":31,"tag":1215,"props":14409,"children":14410},{"style":1250},[14411],{"type":40,"value":14412},"currency)",{"type":31,"tag":1215,"props":14414,"children":14415},{"style":3113},[14416],{"type":40,"value":76},{"type":31,"tag":1215,"props":14418,"children":14419},{"style":2939},[14420],{"type":40,"value":7075},{"type":31,"tag":1215,"props":14422,"children":14423},{"style":1250},[14424],{"type":40,"value":3483},{"type":31,"tag":1215,"props":14426,"children":14427},{"style":4392},[14428],{"type":40,"value":14429},"'EUR'",{"type":31,"tag":1215,"props":14431,"children":14432},{"style":1250},[14433],{"type":40,"value":3002},{"type":31,"tag":1215,"props":14435,"children":14436},{"style":2945},[14437],{"type":40,"value":3055},{"type":31,"tag":1215,"props":14439,"children":14440},{"class":1217,"line":1355},[14441,14445,14449,14453,14458,14462,14466,14470,14475,14479],{"type":31,"tag":1215,"props":14442,"children":14443},{"style":2939},[14444],{"type":40,"value":13777},{"type":31,"tag":1215,"props":14446,"children":14447},{"style":1250},[14448],{"type":40,"value":14358},{"type":31,"tag":1215,"props":14450,"children":14451},{"style":3113},[14452],{"type":40,"value":76},{"type":31,"tag":1215,"props":14454,"children":14455},{"style":1250},[14456],{"type":40,"value":14457},"status)",{"type":31,"tag":1215,"props":14459,"children":14460},{"style":3113},[14461],{"type":40,"value":76},{"type":31,"tag":1215,"props":14463,"children":14464},{"style":2939},[14465],{"type":40,"value":7075},{"type":31,"tag":1215,"props":14467,"children":14468},{"style":1250},[14469],{"type":40,"value":3483},{"type":31,"tag":1215,"props":14471,"children":14472},{"style":4392},[14473],{"type":40,"value":14474},"'PENDING'",{"type":31,"tag":1215,"props":14476,"children":14477},{"style":1250},[14478],{"type":40,"value":3002},{"type":31,"tag":1215,"props":14480,"children":14481},{"style":2945},[14482],{"type":40,"value":3055},{"type":31,"tag":1215,"props":14484,"children":14485},{"class":1217,"line":1364},[14486,14490,14494],{"type":31,"tag":1215,"props":14487,"children":14488},{"style":2945},[14489],{"type":40,"value":6160},{"type":31,"tag":1215,"props":14491,"children":14492},{"style":1250},[14493],{"type":40,"value":3002},{"type":31,"tag":1215,"props":14495,"children":14496},{"style":2945},[14497],{"type":40,"value":3055},{"type":31,"tag":1215,"props":14499,"children":14500},{"class":1217,"line":1378},[14501,14505,14509],{"type":31,"tag":1215,"props":14502,"children":14503},{"style":2945},[14504],{"type":40,"value":5455},{"type":31,"tag":1215,"props":14506,"children":14507},{"style":1250},[14508],{"type":40,"value":3002},{"type":31,"tag":1215,"props":14510,"children":14511},{"style":2945},[14512],{"type":40,"value":3055},{"type":31,"tag":32,"props":14514,"children":14515},{},[14516,14518,14524],{"type":40,"value":14517},"L'implémentation peut changer, le test reste valable. C'est exactement la philosophie défendue dans ",{"type":31,"tag":69,"props":14519,"children":14521},{"href":14520},"\u002Ffr\u002Fpratiques-agiles\u002Fadopter-behaviour-driven-development-bdd-guide-agile",[14522],{"type":40,"value":14523},"l'adoption du Behaviour-Driven Development",{"type":40,"value":14525}," et dans la Definition of Done bien posée.",{"type":31,"tag":32,"props":14527,"children":14528},{},[14529],{"type":40,"value":14530},"Sur crmcoaching, après avoir appliqué ce changement systématiquement, le ratio de tests \"behavior-level\" est passé de 15% à 70%. La couverture brute a parfois baissé. La couverture utile a triplé.",{"type":31,"tag":48,"props":14532,"children":14533},{},[],{"type":31,"tag":52,"props":14535,"children":14537},{"id":14536},"pattern-4-catch-all-silencieux",[14538],{"type":40,"value":14539},"Pattern #4 : catch-all silencieux",{"type":31,"tag":32,"props":14541,"children":14542},{},[14543],{"type":40,"value":14544},"C'est le pattern le plus dangereux, parce qu'il masque les bugs au lieu de les révéler. Claude écrit volontiers :",{"type":31,"tag":1206,"props":14546,"children":14548},{"className":2912,"code":14547,"language":2914,"meta":8,"style":8},"\u002F\u002F apps\u002Fapi\u002Fsrc\u002Finfrastructure\u002Fadapters\u002Fbrevo-notification.adapter.ts\n\nasync sendSlotConfirmation(clientEmail: string, slotId: string): Promise\u003Cvoid> {\n  try {\n    await this.brevoService.sendTransactionalEmail({\n      to: [{ email: clientEmail }],\n      templateId: 12,\n      params: { slotId },\n    });\n  } catch (e) {\n    console.error('notification failed', e);\n    \u002F\u002F la suite s'exécute comme si tout allait bien\n  }\n}\n",[14549],{"type":31,"tag":169,"props":14550,"children":14551},{"__ignoreMap":8},[14552,14560,14567,14608,14620,14655,14699,14718,14741,14756,14768,14806,14814,14821],{"type":31,"tag":1215,"props":14553,"children":14554},{"class":1217,"line":1218},[14555],{"type":31,"tag":1215,"props":14556,"children":14557},{"style":2924},[14558],{"type":40,"value":14559},"\u002F\u002F apps\u002Fapi\u002Fsrc\u002Finfrastructure\u002Fadapters\u002Fbrevo-notification.adapter.ts\n",{"type":31,"tag":1215,"props":14561,"children":14562},{"class":1217,"line":817},[14563],{"type":31,"tag":1215,"props":14564,"children":14565},{"emptyLinePlaceholder":13},[14566],{"type":40,"value":1233},{"type":31,"tag":1215,"props":14568,"children":14569},{"class":1217,"line":1236},[14570,14575,14580,14585,14589,14594,14599,14604],{"type":31,"tag":1215,"props":14571,"children":14572},{"style":1250},[14573],{"type":40,"value":14574},"async ",{"type":31,"tag":1215,"props":14576,"children":14577},{"style":2939},[14578],{"type":40,"value":14579},"sendSlotConfirmation",{"type":31,"tag":1215,"props":14581,"children":14582},{"style":1250},[14583],{"type":40,"value":14584},"(clientEmail: string",{"type":31,"tag":1215,"props":14586,"children":14587},{"style":2945},[14588],{"type":40,"value":5843},{"type":31,"tag":1215,"props":14590,"children":14591},{"style":1250},[14592],{"type":40,"value":14593}," slotId: string): ",{"type":31,"tag":1215,"props":14595,"children":14596},{"style":6254},[14597],{"type":40,"value":14598},"Promise",{"type":31,"tag":1215,"props":14600,"children":14601},{"style":2960},[14602],{"type":40,"value":14603},"\u003Cvoid>",{"type":31,"tag":1215,"props":14605,"children":14606},{"style":2945},[14607],{"type":40,"value":3015},{"type":31,"tag":1215,"props":14609,"children":14610},{"class":1217,"line":1246},[14611,14616],{"type":31,"tag":1215,"props":14612,"children":14613},{"style":1250},[14614],{"type":40,"value":14615},"  try ",{"type":31,"tag":1215,"props":14617,"children":14618},{"style":2945},[14619],{"type":40,"value":4257},{"type":31,"tag":1215,"props":14621,"children":14622},{"class":1217,"line":1256},[14623,14627,14631,14635,14639,14643,14647,14651],{"type":31,"tag":1215,"props":14624,"children":14625},{"style":2933},[14626],{"type":40,"value":4306},{"type":31,"tag":1215,"props":14628,"children":14629},{"style":4190},[14630],{"type":40,"value":4193},{"type":31,"tag":1215,"props":14632,"children":14633},{"style":3113},[14634],{"type":40,"value":76},{"type":31,"tag":1215,"props":14636,"children":14637},{"style":1250},[14638],{"type":40,"value":11594},{"type":31,"tag":1215,"props":14640,"children":14641},{"style":3113},[14642],{"type":40,"value":76},{"type":31,"tag":1215,"props":14644,"children":14645},{"style":2939},[14646],{"type":40,"value":5504},{"type":31,"tag":1215,"props":14648,"children":14649},{"style":1250},[14650],{"type":40,"value":3483},{"type":31,"tag":1215,"props":14652,"children":14653},{"style":2945},[14654],{"type":40,"value":4257},{"type":31,"tag":1215,"props":14656,"children":14657},{"class":1217,"line":1264},[14658,14662,14666,14670,14674,14678,14682,14687,14691,14695],{"type":31,"tag":1215,"props":14659,"children":14660},{"style":1250},[14661],{"type":40,"value":5520},{"type":31,"tag":1215,"props":14663,"children":14664},{"style":3113},[14665],{"type":40,"value":2963},{"type":31,"tag":1215,"props":14667,"children":14668},{"style":1250},[14669],{"type":40,"value":5529},{"type":31,"tag":1215,"props":14671,"children":14672},{"style":2945},[14673],{"type":40,"value":5534},{"type":31,"tag":1215,"props":14675,"children":14676},{"style":1250},[14677],{"type":40,"value":5539},{"type":31,"tag":1215,"props":14679,"children":14680},{"style":3113},[14681],{"type":40,"value":2963},{"type":31,"tag":1215,"props":14683,"children":14684},{"style":1250},[14685],{"type":40,"value":14686}," clientEmail ",{"type":31,"tag":1215,"props":14688,"children":14689},{"style":2945},[14690],{"type":40,"value":5455},{"type":31,"tag":1215,"props":14692,"children":14693},{"style":1250},[14694],{"type":40,"value":5565},{"type":31,"tag":1215,"props":14696,"children":14697},{"style":2945},[14698],{"type":40,"value":2974},{"type":31,"tag":1215,"props":14700,"children":14701},{"class":1217,"line":1273},[14702,14706,14710,14714],{"type":31,"tag":1215,"props":14703,"children":14704},{"style":1250},[14705],{"type":40,"value":5577},{"type":31,"tag":1215,"props":14707,"children":14708},{"style":3113},[14709],{"type":40,"value":2963},{"type":31,"tag":1215,"props":14711,"children":14712},{"style":3047},[14713],{"type":40,"value":3050},{"type":31,"tag":1215,"props":14715,"children":14716},{"style":2945},[14717],{"type":40,"value":2974},{"type":31,"tag":1215,"props":14719,"children":14720},{"class":1217,"line":1282},[14721,14725,14729,14733,14737],{"type":31,"tag":1215,"props":14722,"children":14723},{"style":1250},[14724],{"type":40,"value":5607},{"type":31,"tag":1215,"props":14726,"children":14727},{"style":3113},[14728],{"type":40,"value":2963},{"type":31,"tag":1215,"props":14730,"children":14731},{"style":2945},[14732],{"type":40,"value":5616},{"type":31,"tag":1215,"props":14734,"children":14735},{"style":1250},[14736],{"type":40,"value":12757},{"type":31,"tag":1215,"props":14738,"children":14739},{"style":2945},[14740],{"type":40,"value":5643},{"type":31,"tag":1215,"props":14742,"children":14743},{"class":1217,"line":1291},[14744,14748,14752],{"type":31,"tag":1215,"props":14745,"children":14746},{"style":2945},[14747],{"type":40,"value":5651},{"type":31,"tag":1215,"props":14749,"children":14750},{"style":1250},[14751],{"type":40,"value":3002},{"type":31,"tag":1215,"props":14753,"children":14754},{"style":2945},[14755],{"type":40,"value":3055},{"type":31,"tag":1215,"props":14757,"children":14758},{"class":1217,"line":1300},[14759,14763],{"type":31,"tag":1215,"props":14760,"children":14761},{"style":2945},[14762],{"type":40,"value":6160},{"type":31,"tag":1215,"props":14764,"children":14765},{"style":1250},[14766],{"type":40,"value":14767}," catch (e) {\n",{"type":31,"tag":1215,"props":14769,"children":14770},{"class":1217,"line":1309},[14771,14776,14780,14784,14789,14793,14798,14802],{"type":31,"tag":1215,"props":14772,"children":14773},{"style":1250},[14774],{"type":40,"value":14775},"    console.",{"type":31,"tag":1215,"props":14777,"children":14778},{"style":2939},[14779],{"type":40,"value":9568},{"type":31,"tag":1215,"props":14781,"children":14782},{"style":2945},[14783],{"type":40,"value":3483},{"type":31,"tag":1215,"props":14785,"children":14786},{"style":4392},[14787],{"type":40,"value":14788},"'notification failed'",{"type":31,"tag":1215,"props":14790,"children":14791},{"style":2945},[14792],{"type":40,"value":5843},{"type":31,"tag":1215,"props":14794,"children":14795},{"style":2954},[14796],{"type":40,"value":14797}," e",{"type":31,"tag":1215,"props":14799,"children":14800},{"style":2945},[14801],{"type":40,"value":3002},{"type":31,"tag":1215,"props":14803,"children":14804},{"style":1250},[14805],{"type":40,"value":3055},{"type":31,"tag":1215,"props":14807,"children":14808},{"class":1217,"line":1317},[14809],{"type":31,"tag":1215,"props":14810,"children":14811},{"style":2924},[14812],{"type":40,"value":14813},"    \u002F\u002F la suite s'exécute comme si tout allait bien\n",{"type":31,"tag":1215,"props":14815,"children":14816},{"class":1217,"line":1326},[14817],{"type":31,"tag":1215,"props":14818,"children":14819},{"style":2945},[14820],{"type":40,"value":3710},{"type":31,"tag":1215,"props":14822,"children":14823},{"class":1217,"line":1341},[14824],{"type":31,"tag":1215,"props":14825,"children":14826},{"style":1250},[14827],{"type":40,"value":3160},{"type":31,"tag":32,"props":14829,"children":14830},{},[14831,14833,14839],{"type":40,"value":14832},"Et la suite du code continue normalement. Le slot a-t-il été confirmé par email ? On ne sait pas. Le ",{"type":31,"tag":169,"props":14834,"children":14836},{"className":14835},[],[14837],{"type":40,"value":14838},"console.error",{"type":40,"value":14840}," est-il regardé par quelqu'un en prod ? Probablement pas. Le client voit \"Réservation confirmée\" alors que Brevo n'a rien envoyé.",{"type":31,"tag":32,"props":14842,"children":14843},{},[14844,14846,14852,14854,14860],{"type":40,"value":14845},"Le contre-pattern craft repose sur trois règles. D'abord, ne jamais avaler une exception sans politique explicite : soit on re-throw, soit on dégrade volontairement (tracé dans une métrique), soit on rollback. Ensuite, ne jamais utiliser ",{"type":31,"tag":169,"props":14847,"children":14849},{"className":14848},[],[14850],{"type":40,"value":14851},"catch (e)",{"type":40,"value":14853}," générique sans typer l'exception attendue : une erreur réseau Brevo ne se traite pas comme une ",{"type":31,"tag":169,"props":14855,"children":14857},{"className":14856},[],[14858],{"type":40,"value":14859},"DomainException",{"type":40,"value":14861},". Enfin, instrumenter chaque catch avec une métrique ou une alerte :",{"type":31,"tag":1206,"props":14863,"children":14865},{"className":2912,"code":14864,"language":2914,"meta":8,"style":8},"async sendSlotConfirmation(clientEmail: string, slotId: string): Promise\u003Cvoid> {\n  try {\n    await this.brevoService.sendTransactionalEmail({\n      to: [{ email: clientEmail }],\n      templateId: 12,\n      params: { slotId },\n    });\n  } catch (err) {\n    this.logger.error('brevo.slot_confirmation.failed', {\n      clientEmail,\n      slotId,\n      error: err instanceof Error ? err.message : String(err),\n    });\n    this.metricsService.increment('notification_failures_total', { type: 'slot_confirmation' });\n    throw new NotificationDeliveryException(`Confirmation slot ${slotId} non envoyée`);\n  }\n}\n",[14866],{"type":31,"tag":169,"props":14867,"children":14868},{"__ignoreMap":8},[14869,14904,14915,14950,14993,15012,15035,15050,15062,15091,15103,15115,15176,15188,15233,15280,15287],{"type":31,"tag":1215,"props":14870,"children":14871},{"class":1217,"line":1218},[14872,14876,14880,14884,14888,14892,14896,14900],{"type":31,"tag":1215,"props":14873,"children":14874},{"style":1250},[14875],{"type":40,"value":14574},{"type":31,"tag":1215,"props":14877,"children":14878},{"style":2939},[14879],{"type":40,"value":14579},{"type":31,"tag":1215,"props":14881,"children":14882},{"style":1250},[14883],{"type":40,"value":14584},{"type":31,"tag":1215,"props":14885,"children":14886},{"style":2945},[14887],{"type":40,"value":5843},{"type":31,"tag":1215,"props":14889,"children":14890},{"style":1250},[14891],{"type":40,"value":14593},{"type":31,"tag":1215,"props":14893,"children":14894},{"style":6254},[14895],{"type":40,"value":14598},{"type":31,"tag":1215,"props":14897,"children":14898},{"style":2960},[14899],{"type":40,"value":14603},{"type":31,"tag":1215,"props":14901,"children":14902},{"style":2945},[14903],{"type":40,"value":3015},{"type":31,"tag":1215,"props":14905,"children":14906},{"class":1217,"line":817},[14907,14911],{"type":31,"tag":1215,"props":14908,"children":14909},{"style":1250},[14910],{"type":40,"value":14615},{"type":31,"tag":1215,"props":14912,"children":14913},{"style":2945},[14914],{"type":40,"value":4257},{"type":31,"tag":1215,"props":14916,"children":14917},{"class":1217,"line":1236},[14918,14922,14926,14930,14934,14938,14942,14946],{"type":31,"tag":1215,"props":14919,"children":14920},{"style":2933},[14921],{"type":40,"value":4306},{"type":31,"tag":1215,"props":14923,"children":14924},{"style":4190},[14925],{"type":40,"value":4193},{"type":31,"tag":1215,"props":14927,"children":14928},{"style":3113},[14929],{"type":40,"value":76},{"type":31,"tag":1215,"props":14931,"children":14932},{"style":1250},[14933],{"type":40,"value":11594},{"type":31,"tag":1215,"props":14935,"children":14936},{"style":3113},[14937],{"type":40,"value":76},{"type":31,"tag":1215,"props":14939,"children":14940},{"style":2939},[14941],{"type":40,"value":5504},{"type":31,"tag":1215,"props":14943,"children":14944},{"style":1250},[14945],{"type":40,"value":3483},{"type":31,"tag":1215,"props":14947,"children":14948},{"style":2945},[14949],{"type":40,"value":4257},{"type":31,"tag":1215,"props":14951,"children":14952},{"class":1217,"line":1246},[14953,14957,14961,14965,14969,14973,14977,14981,14985,14989],{"type":31,"tag":1215,"props":14954,"children":14955},{"style":1250},[14956],{"type":40,"value":5520},{"type":31,"tag":1215,"props":14958,"children":14959},{"style":3113},[14960],{"type":40,"value":2963},{"type":31,"tag":1215,"props":14962,"children":14963},{"style":1250},[14964],{"type":40,"value":5529},{"type":31,"tag":1215,"props":14966,"children":14967},{"style":2945},[14968],{"type":40,"value":5534},{"type":31,"tag":1215,"props":14970,"children":14971},{"style":1250},[14972],{"type":40,"value":5539},{"type":31,"tag":1215,"props":14974,"children":14975},{"style":3113},[14976],{"type":40,"value":2963},{"type":31,"tag":1215,"props":14978,"children":14979},{"style":1250},[14980],{"type":40,"value":14686},{"type":31,"tag":1215,"props":14982,"children":14983},{"style":2945},[14984],{"type":40,"value":5455},{"type":31,"tag":1215,"props":14986,"children":14987},{"style":1250},[14988],{"type":40,"value":5565},{"type":31,"tag":1215,"props":14990,"children":14991},{"style":2945},[14992],{"type":40,"value":2974},{"type":31,"tag":1215,"props":14994,"children":14995},{"class":1217,"line":1256},[14996,15000,15004,15008],{"type":31,"tag":1215,"props":14997,"children":14998},{"style":1250},[14999],{"type":40,"value":5577},{"type":31,"tag":1215,"props":15001,"children":15002},{"style":3113},[15003],{"type":40,"value":2963},{"type":31,"tag":1215,"props":15005,"children":15006},{"style":3047},[15007],{"type":40,"value":3050},{"type":31,"tag":1215,"props":15009,"children":15010},{"style":2945},[15011],{"type":40,"value":2974},{"type":31,"tag":1215,"props":15013,"children":15014},{"class":1217,"line":1264},[15015,15019,15023,15027,15031],{"type":31,"tag":1215,"props":15016,"children":15017},{"style":1250},[15018],{"type":40,"value":5607},{"type":31,"tag":1215,"props":15020,"children":15021},{"style":3113},[15022],{"type":40,"value":2963},{"type":31,"tag":1215,"props":15024,"children":15025},{"style":2945},[15026],{"type":40,"value":5616},{"type":31,"tag":1215,"props":15028,"children":15029},{"style":1250},[15030],{"type":40,"value":12757},{"type":31,"tag":1215,"props":15032,"children":15033},{"style":2945},[15034],{"type":40,"value":5643},{"type":31,"tag":1215,"props":15036,"children":15037},{"class":1217,"line":1273},[15038,15042,15046],{"type":31,"tag":1215,"props":15039,"children":15040},{"style":2945},[15041],{"type":40,"value":5651},{"type":31,"tag":1215,"props":15043,"children":15044},{"style":1250},[15045],{"type":40,"value":3002},{"type":31,"tag":1215,"props":15047,"children":15048},{"style":2945},[15049],{"type":40,"value":3055},{"type":31,"tag":1215,"props":15051,"children":15052},{"class":1217,"line":1282},[15053,15057],{"type":31,"tag":1215,"props":15054,"children":15055},{"style":2945},[15056],{"type":40,"value":6160},{"type":31,"tag":1215,"props":15058,"children":15059},{"style":1250},[15060],{"type":40,"value":15061}," catch (err) {\n",{"type":31,"tag":1215,"props":15063,"children":15064},{"class":1217,"line":1291},[15065,15070,15074,15078,15083,15087],{"type":31,"tag":1215,"props":15066,"children":15067},{"style":1250},[15068],{"type":40,"value":15069},"    this.logger.",{"type":31,"tag":1215,"props":15071,"children":15072},{"style":2939},[15073],{"type":40,"value":9568},{"type":31,"tag":1215,"props":15075,"children":15076},{"style":2945},[15077],{"type":40,"value":3483},{"type":31,"tag":1215,"props":15079,"children":15080},{"style":4392},[15081],{"type":40,"value":15082},"'brevo.slot_confirmation.failed'",{"type":31,"tag":1215,"props":15084,"children":15085},{"style":2945},[15086],{"type":40,"value":5843},{"type":31,"tag":1215,"props":15088,"children":15089},{"style":2945},[15090],{"type":40,"value":3015},{"type":31,"tag":1215,"props":15092,"children":15093},{"class":1217,"line":1300},[15094,15099],{"type":31,"tag":1215,"props":15095,"children":15096},{"style":2954},[15097],{"type":40,"value":15098},"      clientEmail",{"type":31,"tag":1215,"props":15100,"children":15101},{"style":2945},[15102],{"type":40,"value":2974},{"type":31,"tag":1215,"props":15104,"children":15105},{"class":1217,"line":1309},[15106,15111],{"type":31,"tag":1215,"props":15107,"children":15108},{"style":2954},[15109],{"type":40,"value":15110},"      slotId",{"type":31,"tag":1215,"props":15112,"children":15113},{"style":2945},[15114],{"type":40,"value":2974},{"type":31,"tag":1215,"props":15116,"children":15117},{"class":1217,"line":1317},[15118,15123,15127,15131,15136,15140,15145,15149,15153,15158,15162,15167,15172],{"type":31,"tag":1215,"props":15119,"children":15120},{"style":6920},[15121],{"type":40,"value":15122},"      error",{"type":31,"tag":1215,"props":15124,"children":15125},{"style":2945},[15126],{"type":40,"value":2963},{"type":31,"tag":1215,"props":15128,"children":15129},{"style":2954},[15130],{"type":40,"value":6874},{"type":31,"tag":1215,"props":15132,"children":15133},{"style":2954},[15134],{"type":40,"value":15135}," instanceof",{"type":31,"tag":1215,"props":15137,"children":15138},{"style":2954},[15139],{"type":40,"value":5431},{"type":31,"tag":1215,"props":15141,"children":15142},{"style":1250},[15143],{"type":40,"value":15144}," ? ",{"type":31,"tag":1215,"props":15146,"children":15147},{"style":2954},[15148],{"type":40,"value":9146},{"type":31,"tag":1215,"props":15150,"children":15151},{"style":1250},[15152],{"type":40,"value":76},{"type":31,"tag":1215,"props":15154,"children":15155},{"style":6920},[15156],{"type":40,"value":15157},"message",{"type":31,"tag":1215,"props":15159,"children":15160},{"style":2945},[15161],{"type":40,"value":3689},{"type":31,"tag":1215,"props":15163,"children":15164},{"style":2954},[15165],{"type":40,"value":15166}," String",{"type":31,"tag":1215,"props":15168,"children":15169},{"style":1250},[15170],{"type":40,"value":15171},"(err)",{"type":31,"tag":1215,"props":15173,"children":15174},{"style":2945},[15175],{"type":40,"value":2974},{"type":31,"tag":1215,"props":15177,"children":15178},{"class":1217,"line":1326},[15179,15184],{"type":31,"tag":1215,"props":15180,"children":15181},{"style":2945},[15182],{"type":40,"value":15183},"    })",{"type":31,"tag":1215,"props":15185,"children":15186},{"style":1250},[15187],{"type":40,"value":3055},{"type":31,"tag":1215,"props":15189,"children":15190},{"class":1217,"line":1341},[15191,15196,15201,15205,15209,15215,15219,15224,15228],{"type":31,"tag":1215,"props":15192,"children":15193},{"style":1250},[15194],{"type":40,"value":15195},"    this.metricsService.increment(",{"type":31,"tag":1215,"props":15197,"children":15198},{"style":4392},[15199],{"type":40,"value":15200},"'notification_failures_total'",{"type":31,"tag":1215,"props":15202,"children":15203},{"style":2945},[15204],{"type":40,"value":5843},{"type":31,"tag":1215,"props":15206,"children":15207},{"style":2945},[15208],{"type":40,"value":5616},{"type":31,"tag":1215,"props":15210,"children":15212},{"style":15211},"--shiki-default:#C6D0F5;--shiki-dark:#B392F0",[15213],{"type":40,"value":15214}," type",{"type":31,"tag":1215,"props":15216,"children":15217},{"style":2945},[15218],{"type":40,"value":2963},{"type":31,"tag":1215,"props":15220,"children":15221},{"style":4392},[15222],{"type":40,"value":15223}," 'slot_confirmation'",{"type":31,"tag":1215,"props":15225,"children":15226},{"style":2945},[15227],{"type":40,"value":7476},{"type":31,"tag":1215,"props":15229,"children":15230},{"style":1250},[15231],{"type":40,"value":15232},");\n",{"type":31,"tag":1215,"props":15234,"children":15235},{"class":1217,"line":1355},[15236,15241,15246,15250,15255,15259,15263,15267,15272,15276],{"type":31,"tag":1215,"props":15237,"children":15238},{"style":1250},[15239],{"type":40,"value":15240},"    throw new ",{"type":31,"tag":1215,"props":15242,"children":15243},{"style":2939},[15244],{"type":40,"value":15245},"NotificationDeliveryException",{"type":31,"tag":1215,"props":15247,"children":15248},{"style":2945},[15249],{"type":40,"value":3483},{"type":31,"tag":1215,"props":15251,"children":15252},{"style":4392},[15253],{"type":40,"value":15254},"`Confirmation slot ",{"type":31,"tag":1215,"props":15256,"children":15257},{"style":5443},[15258],{"type":40,"value":5446},{"type":31,"tag":1215,"props":15260,"children":15261},{"style":1250},[15262],{"type":40,"value":12465},{"type":31,"tag":1215,"props":15264,"children":15265},{"style":5443},[15266],{"type":40,"value":5455},{"type":31,"tag":1215,"props":15268,"children":15269},{"style":4392},[15270],{"type":40,"value":15271}," non envoyée`",{"type":31,"tag":1215,"props":15273,"children":15274},{"style":2945},[15275],{"type":40,"value":3002},{"type":31,"tag":1215,"props":15277,"children":15278},{"style":1250},[15279],{"type":40,"value":3055},{"type":31,"tag":1215,"props":15281,"children":15282},{"class":1217,"line":1364},[15283],{"type":31,"tag":1215,"props":15284,"children":15285},{"style":2945},[15286],{"type":40,"value":3710},{"type":31,"tag":1215,"props":15288,"children":15289},{"class":1217,"line":1378},[15290],{"type":31,"tag":1215,"props":15291,"children":15292},{"style":1250},[15293],{"type":40,"value":3160},{"type":31,"tag":32,"props":15295,"children":15296},{},[15297,15299,15303,15305,15310],{"type":40,"value":15298},"Michael Nygard a documenté ce sujet en profondeur dans ",{"type":31,"tag":97,"props":15300,"children":15301},{},[15302],{"type":40,"value":790},{"type":40,"value":15304}," (2007). Les ",{"type":31,"tag":69,"props":15306,"children":15307},{"href":9751},[15308],{"type":40,"value":15309},"patterns de résilience type circuit breaker et retry",{"type":40,"value":15311}," n'ont de sens que si les erreurs sont visibles. Un catch-all silencieux casse toute la stratégie de résilience en amont.",{"type":31,"tag":48,"props":15313,"children":15314},{},[],{"type":31,"tag":52,"props":15316,"children":15318},{"id":15317},"pattern-5-dépendances-non-auditées",[15319],{"type":40,"value":15320},"Pattern #5 : dépendances non auditées",{"type":31,"tag":32,"props":15322,"children":15323},{},[15324,15326,15332,15334,15340,15342,15348,15350,15356],{"type":40,"value":15325},"Le dernier pattern est le plus invisible. Claude propose une dépendance pour résoudre un problème : \"Ajoute ",{"type":31,"tag":169,"props":15327,"children":15329},{"className":15328},[],[15330],{"type":40,"value":15331},"stripe",{"type":40,"value":15333}," pour les checkouts\", \"Utilise ",{"type":31,"tag":169,"props":15335,"children":15337},{"className":15336},[],[15338],{"type":40,"value":15339},"jose",{"type":40,"value":15341}," pour signer ce token\", \"Importe ",{"type":31,"tag":169,"props":15343,"children":15345},{"className":15344},[],[15346],{"type":40,"value":15347},"date-fns",{"type":40,"value":15349}," pour ce calcul de durée\". Le développeur lance ",{"type":31,"tag":169,"props":15351,"children":15353},{"className":15352},[],[15354],{"type":40,"value":15355},"pnpm add \u003Cpkg>",{"type":40,"value":15357},", ça compile, ça passe les tests, ça merge. Et l'artefact entre dans le repo, sans audit, sans justification.",{"type":31,"tag":32,"props":15359,"children":15360},{},[15361,15363,15367],{"type":40,"value":15362},"Le problème, ce sont les chiffres du Snyk Open Source Security Report 2024 : les attaques sur la supply chain ont augmenté de 156% en un an. Les écosystèmes les plus touchés incluent npm, PyPI et Maven Central, précisément ceux que Claude suggère le plus. Sur le cas xz-utils (CVE-2024-3094, mars 2024), une backdoor s'est glissée pendant 2 ans dans un package que tout le monde utilisait sans relire. Le sujet de fond est traité dans ",{"type":31,"tag":69,"props":15364,"children":15365},{"href":1508},[15366],{"type":40,"value":1511},{"type":40,"value":76},{"type":31,"tag":32,"props":15369,"children":15370},{},[15371,15373,15379,15381,15386],{"type":40,"value":15372},"Le contre-pattern craft repose sur 3 réflexes. D'abord, refuser toute nouvelle dépendance qui n'a pas été justifiée par un commentaire de PR (quelle alternative native Node ? quel score de vulnérabilités npm audit ? combien de mainteneurs actifs ?). Ensuite, mesurer la ",{"type":31,"tag":169,"props":15374,"children":15376},{"className":15375},[],[15377],{"type":40,"value":15378},"time-to-patch",{"type":40,"value":15380}," sur les CVE critiques (en dessous de 7 jours = sain). Enfin, automatiser la veille via ",{"type":31,"tag":69,"props":15382,"children":15383},{"href":364},[15384],{"type":40,"value":15385},"Dependabot configuré craft",{"type":40,"value":15387}," avec auto-merge sur les patches et review humaine sur les majors.",{"type":31,"tag":32,"props":15389,"children":15390},{},[15391,15393,15399],{"type":40,"value":15392},"Sur crmcoaching, j'audite chaque dépendance que Claude propose. Je vérifie le nombre de mainteneurs, la date du dernier commit, le score ",{"type":31,"tag":169,"props":15394,"children":15396},{"className":15395},[],[15397],{"type":40,"value":15398},"pnpm audit",{"type":40,"value":15400},". 30 secondes par dépendance. C'est ce qui fait la différence entre un repo sain et un repo qui exfiltre des données dans 2 ans.",{"type":31,"tag":48,"props":15402,"children":15403},{},[],{"type":31,"tag":52,"props":15405,"children":15407},{"id":15406},"les-5-garde-fous-craft-à-installer-dès-lundi",[15408],{"type":40,"value":15409},"Les 5 garde-fous craft à installer dès lundi",{"type":31,"tag":32,"props":15411,"children":15412},{},[15413],{"type":40,"value":15414},"Voici la grille condensée. Mettez-la en checklist GitHub PR template.",{"type":31,"tag":398,"props":15416,"children":15417},{},[15418,15438],{"type":31,"tag":402,"props":15419,"children":15420},{},[15421],{"type":31,"tag":406,"props":15422,"children":15423},{},[15424,15429,15434],{"type":31,"tag":410,"props":15425,"children":15426},{},[15427],{"type":40,"value":15428},"Pattern",{"type":31,"tag":410,"props":15430,"children":15431},{},[15432],{"type":40,"value":15433},"Signal en review",{"type":31,"tag":410,"props":15435,"children":15436},{},[15437],{"type":40,"value":424},{"type":31,"tag":426,"props":15439,"children":15440},{},[15441,15457,15496,15519,15541],{"type":31,"tag":406,"props":15442,"children":15443},{},[15444,15448,15453],{"type":31,"tag":433,"props":15445,"children":15446},{},[15447],{"type":40,"value":455},{"type":31,"tag":433,"props":15449,"children":15450},{},[15451],{"type":40,"value":15452},"Fonction > 40 lignes ou > 3 niveaux d'indentation",{"type":31,"tag":433,"props":15454,"children":15455},{},[15456],{"type":40,"value":465},{"type":31,"tag":406,"props":15458,"children":15459},{},[15460,15464,15486],{"type":31,"tag":433,"props":15461,"children":15462},{},[15463],{"type":40,"value":473},{"type":31,"tag":433,"props":15465,"children":15466},{},[15467,15472,15474,15480,15481],{"type":31,"tag":169,"props":15468,"children":15470},{"className":15469},[],[15471],{"type":40,"value":12305},{"type":40,"value":15473}," injecté dans ",{"type":31,"tag":169,"props":15475,"children":15477},{"className":15476},[],[15478],{"type":40,"value":15479},"application\u002F",{"type":40,"value":5791},{"type":31,"tag":169,"props":15482,"children":15484},{"className":15483},[],[15485],{"type":40,"value":484},{"type":31,"tag":433,"props":15487,"children":15488},{},[15489,15491],{"type":40,"value":15490},"Interface Repository (port), adapter Prisma en ",{"type":31,"tag":169,"props":15492,"children":15494},{"className":15493},[],[15495],{"type":40,"value":12903},{"type":31,"tag":406,"props":15497,"children":15498},{},[15499,15503,15514],{"type":31,"tag":433,"props":15500,"children":15501},{},[15502],{"type":40,"value":497},{"type":31,"tag":433,"props":15504,"children":15505},{},[15506,15512],{"type":31,"tag":169,"props":15507,"children":15509},{"className":15508},[],[15510],{"type":40,"value":15511},"expect(repo.save).toHaveBeenCalled()",{"type":40,"value":15513}," sans assertion comportementale",{"type":31,"tag":433,"props":15515,"children":15516},{},[15517],{"type":40,"value":15518},"Format Given\u002FWhen\u002FThen sur comportement observable",{"type":31,"tag":406,"props":15520,"children":15521},{},[15522,15526,15536],{"type":31,"tag":433,"props":15523,"children":15524},{},[15525],{"type":40,"value":520},{"type":31,"tag":433,"props":15527,"children":15528},{},[15529,15535],{"type":31,"tag":169,"props":15530,"children":15532},{"className":15531},[],[15533],{"type":40,"value":15534},"catch (e) { console.error(e) }",{"type":40,"value":531},{"type":31,"tag":433,"props":15537,"children":15538},{},[15539],{"type":40,"value":15540},"Re-throw typé, métrique, ou rollback",{"type":31,"tag":406,"props":15542,"children":15543},{},[15544,15549,15562],{"type":31,"tag":433,"props":15545,"children":15546},{},[15547],{"type":40,"value":15548},"Dépendance non auditée",{"type":31,"tag":433,"props":15550,"children":15551},{},[15552,15554,15560],{"type":40,"value":15553},"Entrée ",{"type":31,"tag":169,"props":15555,"children":15557},{"className":15556},[],[15558],{"type":40,"value":15559},"package.json",{"type":40,"value":15561}," sans commentaire de justification",{"type":31,"tag":433,"props":15563,"children":15564},{},[15565,15567,15572],{"type":40,"value":15566},"Audit 30 sec (",{"type":31,"tag":169,"props":15568,"children":15570},{"className":15569},[],[15571],{"type":40,"value":15398},{"type":40,"value":15573},") + Dependabot configuré craft",{"type":31,"tag":32,"props":15575,"children":15576},{},[15577],{"type":40,"value":15578},"Cette grille tient en 5 minutes par PR. Elle attrape 80% des patterns dangereux que j'ai vus en construisant crmcoaching et lors de mes accompagnements. Elle s'inscrit naturellement dans la philosophie Boy Scout Rule : on laisse le code un peu mieux qu'on l'a trouvé, à chaque PR.",{"type":31,"tag":48,"props":15580,"children":15581},{},[],{"type":31,"tag":229,"props":15583,"children":15585},{"cta":231,"href":232,"title":15584,"type":234},"Vous voulez voir ces patterns dans une PR Claude avant qu'ils ne deviennent de la dette ?",[15586],{"type":31,"tag":32,"props":15587,"children":15588},{},[15589],{"type":40,"value":15590},"Repérer une God Function ou un catch-all silencieux dans une suggestion de Claude, ça ne s'apprend pas en lisant un article : ça se travaille. En mentoring 1:1, je relis votre code avec vous, on rejoue vos prompts, et on installe ensemble le réflexe de review qui transforme Claude en exécutant discipliné. Vous repartez en voyant ce que le modèle ne voit pas.",{"type":31,"tag":48,"props":15592,"children":15593},{},[],{"type":31,"tag":52,"props":15595,"children":15596},{"id":1565},[15597],{"type":40,"value":1568},{"type":31,"tag":32,"props":15599,"children":15600},{},[15601],{"type":40,"value":15602},"Sur crmcoaching, après avoir installé cette grille et appliqué une vraie discipline de code review, voici les chiffres avant\u002Faprès mesurés sur 6 mois.",{"type":31,"tag":398,"props":15604,"children":15605},{},[15606,15625],{"type":31,"tag":402,"props":15607,"children":15608},{},[15609],{"type":31,"tag":406,"props":15610,"children":15611},{},[15612,15616,15621],{"type":31,"tag":410,"props":15613,"children":15614},{},[15615],{"type":40,"value":2217},{"type":31,"tag":410,"props":15617,"children":15618},{},[15619],{"type":40,"value":15620},"Avant grille",{"type":31,"tag":410,"props":15622,"children":15623},{},[15624],{"type":40,"value":4728},{"type":31,"tag":426,"props":15626,"children":15627},{},[15628,15646,15664,15681,15699],{"type":31,"tag":406,"props":15629,"children":15630},{},[15631,15636,15641],{"type":31,"tag":433,"props":15632,"children":15633},{},[15634],{"type":40,"value":15635},"Fonctions > 40 lignes dans le repo",{"type":31,"tag":433,"props":15637,"children":15638},{},[15639],{"type":40,"value":15640},"22% des fonctions",{"type":31,"tag":433,"props":15642,"children":15643},{},[15644],{"type":40,"value":15645},"4% des fonctions",{"type":31,"tag":406,"props":15647,"children":15648},{},[15649,15654,15659],{"type":31,"tag":433,"props":15650,"children":15651},{},[15652],{"type":40,"value":15653},"Couverture de tests behavior-level",{"type":31,"tag":433,"props":15655,"children":15656},{},[15657],{"type":40,"value":15658},"15%",{"type":31,"tag":433,"props":15660,"children":15661},{},[15662],{"type":40,"value":15663},"70%",{"type":31,"tag":406,"props":15665,"children":15666},{},[15667,15672,15677],{"type":31,"tag":433,"props":15668,"children":15669},{},[15670],{"type":40,"value":15671},"Incidents prod liés à exception swallowed",{"type":31,"tag":433,"props":15673,"children":15674},{},[15675],{"type":40,"value":15676},"4 par trimestre",{"type":31,"tag":433,"props":15678,"children":15679},{},[15680],{"type":40,"value":9887},{"type":31,"tag":406,"props":15682,"children":15683},{},[15684,15689,15694],{"type":31,"tag":433,"props":15685,"children":15686},{},[15687],{"type":40,"value":15688},"Time-to-patch CVE critique",{"type":31,"tag":433,"props":15690,"children":15691},{},[15692],{"type":40,"value":15693},"38 jours",{"type":31,"tag":433,"props":15695,"children":15696},{},[15697],{"type":40,"value":15698},"6 jours",{"type":31,"tag":406,"props":15700,"children":15701},{},[15702,15707,15712],{"type":31,"tag":433,"props":15703,"children":15704},{},[15705],{"type":40,"value":15706},"Heures consacrées au refactoring d'urgence",{"type":31,"tag":433,"props":15708,"children":15709},{},[15710],{"type":40,"value":15711},"12h\u002Fsemaine",{"type":31,"tag":433,"props":15713,"children":15714},{},[15715],{"type":40,"value":15716},"3h\u002Fsemaine",{"type":31,"tag":32,"props":15718,"children":15719},{},[15720,15722,15726],{"type":40,"value":15721},"Le gain est mesurable, et il est financier. Une heure de dev senior coûte entre 80 et 150 euros chargée. Économiser 9 heures par semaine, c'est une différence considérable sur l'année. Pour une équipe qui a peur de \"ralentir\" en imposant des règles de review, le calcul retourne le sens : c'est l'absence de règles qui ralentit, pas leur présence. C'est exactement l'angle que je détaille dans ",{"type":31,"tag":69,"props":15723,"children":15724},{"href":4622},[15725],{"type":40,"value":4625},{"type":40,"value":76},{"type":31,"tag":48,"props":15728,"children":15729},{},[],{"type":31,"tag":52,"props":15731,"children":15732},{"id":637},[15733],{"type":40,"value":640},{"type":31,"tag":32,"props":15735,"children":15736},{},[15737],{"type":40,"value":15738},"Ce que je veux que vous reteniez de cet article, c'est que Claude n'écrit pas de mauvais code par malveillance. Il écrit le code statistiquement moyen de son training data, et ce code moyen est plein d'anti-patterns silencieux que les équipes humaines apprenaient à éviter en 5 ans de mentoring. Sans grille explicite, ces patterns rentrent dans votre repo à 2 PR par jour, et au bout de 12 mois votre dette technique a triplé.",{"type":31,"tag":32,"props":15740,"children":15741},{},[15742],{"type":40,"value":15743},"La bonne nouvelle, c'est que Claude répond très bien à un prompt précis et à un contre-pattern nommé. Il n'a pas d'ego. Il ne défend pas ses choix. Si vous lui dites \"réécris ce use case en respectant le SRP, chaque helper de moins de 20 lignes\", il s'exécute proprement. Le craft, en 2026, consiste à savoir nommer les patterns qu'on attend, à les imposer en review, et à transformer Claude en exécutant discipliné plutôt qu'en générateur de moyenne statistique.",{"type":31,"tag":32,"props":15745,"children":15746},{},[15747],{"type":40,"value":15748},"Si en lisant ces lignes vous reconnaissez votre situation, vous avez deux choix. Vous pouvez laisser les 5 patterns s'installer un peu plus chaque jour. Ou vous pouvez commencer lundi matin, par une grille de 5 questions en review, et reprendre le contrôle de votre codebase.",{"type":31,"tag":32,"props":15750,"children":15751},{},[15752,15754,15759],{"type":40,"value":15753},"Pour suivre la suite des patterns craft que je documente chaque semaine, retrouvez-moi sur ",{"type":31,"tag":69,"props":15755,"children":15757},{"href":663,"rel":15756},[665],[15758],{"type":40,"value":668},{"type":40,"value":76},{"type":31,"tag":48,"props":15761,"children":15762},{},[],{"type":31,"tag":229,"props":15764,"children":15766},{"cta":673,"href":674,"title":15765,"type":676},"Ces 5 garde-fous ne sont qu'un début : il en existe 100",[15767],{"type":31,"tag":32,"props":15768,"children":15769},{},[15770],{"type":40,"value":15771},"La grille de review décrite ici neutralise 5 anti-patterns que Claude reproduit en silence. Elle fait partie d'un référentiel bien plus large : le Craft Bundle, les 100 pratiques craft que j'applique pour coder propre. Ce sont celles que l'IA ne vous apprendra jamais, parce qu'elle ne les a jamais vues tenir en production sur le long terme.",{"type":31,"tag":48,"props":15773,"children":15774},{},[],{"type":31,"tag":52,"props":15776,"children":15778},{"id":15777},"faq-sur-les-anti-patterns-claude-et-la-review-craft",[15779],{"type":40,"value":15780},"FAQ sur les anti-patterns Claude et la review craft",{"type":31,"tag":693,"props":15782,"children":15783},{},[15784,15789],{"type":31,"tag":697,"props":15785,"children":15786},{},[15787],{"type":40,"value":15788},"1. Comment introduire cette grille sans braquer une équipe qui aime Claude ?",{"type":31,"tag":32,"props":15790,"children":15791},{},[15792],{"type":40,"value":15793},"La meilleure approche que j'ai vue, c'est de présenter la grille non comme \"Claude écrit du mauvais code\" mais comme \"Claude écrit le code moyen, et notre standard est plus haut que la moyenne\". L'équipe ne se sent pas attaquée, elle se sent élevée. Combiné à une démo concrète sur leur propre repo (3 exemples de God Function trouvés en 5 minutes), l'adhésion vient en 2 sprints. C'est aussi ce que permet une vraie culture engineering de rituels bien posée.",{"type":31,"tag":693,"props":15795,"children":15796},{},[15797,15802],{"type":31,"tag":697,"props":15798,"children":15799},{},[15800],{"type":40,"value":15801},"2. Faut-il refuser Claude sur les use cases critiques (paiement, auth) ?",{"type":31,"tag":32,"props":15803,"children":15804},{},[15805,15807,15812],{"type":40,"value":15806},"Non, mais il faut prompter différemment. Sur du critique, je donne explicitement à Claude le contexte de concurrence, de criticité, et les patterns attendus. Exemple : ",{"type":31,"tag":97,"props":15808,"children":15809},{},[15810],{"type":40,"value":15811},"\"Ce use case gère un checkout Stripe sur un SaaS multi-tenant. Utilise une transaction Prisma, type les exceptions, ne swallow rien.\"",{"type":40,"value":15813}," Le résultat est radicalement meilleur.",{"type":31,"tag":693,"props":15815,"children":15816},{},[15817,15822],{"type":31,"tag":697,"props":15818,"children":15819},{},[15820],{"type":40,"value":15821},"3. Comment automatiser la détection de ces 5 patterns ?",{"type":31,"tag":32,"props":15823,"children":15824},{},[15825,15827,15833,15835,15841,15843,15848,15850,15855],{"type":40,"value":15826},"Partiellement. Biome détecte les fonctions trop longues (",{"type":31,"tag":169,"props":15828,"children":15830},{"className":15829},[],[15831],{"type":40,"value":15832},"noExcessiveLinesPerFunction",{"type":40,"value":15834},"), la complexité cognitive (",{"type":31,"tag":169,"props":15836,"children":15838},{"className":15837},[],[15839],{"type":40,"value":15840},"noExcessiveCognitiveComplexity",{"type":40,"value":15842},"), et les ",{"type":31,"tag":169,"props":15844,"children":15846},{"className":15845},[],[15847],{"type":40,"value":14851},{"type":40,"value":15849}," génériques. Les patterns \"tests vides\" et \"dépendances non auditées\" demandent une vigilance humaine ou des outils spécialisés (Vitest coverage + mutation testing pour les tests, ",{"type":31,"tag":169,"props":15851,"children":15853},{"className":15852},[],[15854],{"type":40,"value":15398},{"type":40,"value":15856}," + Dependabot pour les packages npm). L'arsenal complet est détaillé dans les outils d'analyse statique en 2026.",{"type":31,"tag":693,"props":15858,"children":15859},{},[15860,15865],{"type":31,"tag":697,"props":15861,"children":15862},{},[15863],{"type":40,"value":15864},"4. Le pattern God Function vient-il vraiment de Claude ou existait-il déjà ?",{"type":31,"tag":32,"props":15866,"children":15867},{},[15868],{"type":40,"value":15869},"Il existait déjà, mais Claude l'amplifie. Avant 2023, une fonction de 80 lignes existait dans 8% des PR humaines selon mes mesures. En 2025, elle existe dans 23% des PR contenant du code IA-généré non révisé. L'IA ne crée pas le pattern, elle le multiplie par 3.",{"type":31,"tag":693,"props":15871,"children":15872},{},[15873,15878],{"type":31,"tag":697,"props":15874,"children":15875},{},[15876],{"type":40,"value":15877},"5. Quelle taille maximum recommander pour une fonction ?",{"type":31,"tag":32,"props":15879,"children":15880},{},[15881,15883,15887,15889,15894],{"type":40,"value":15882},"Robert C. Martin disait dans ",{"type":31,"tag":97,"props":15884,"children":15885},{},[15886],{"type":40,"value":101},{"type":40,"value":15888}," : \"Functions should be small. Then they should be smaller than that.\" Concrètement, je m'impose 20 lignes maximum pour une fonction métier, 5 niveaux d'indentation maximum, 3 paramètres maximum (au-delà, c'est un objet). Ce sont des règles arbitraires, mais elles forcent à découper. Sur crmcoaching, 94% des fonctions respectent ces seuils, et Biome (",{"type":31,"tag":169,"props":15890,"children":15892},{"className":15891},[],[15893],{"type":40,"value":15832},{"type":40,"value":15895},") fait échouer le lint si on dépasse.",{"type":31,"tag":693,"props":15897,"children":15898},{},[15899,15904],{"type":31,"tag":697,"props":15900,"children":15901},{},[15902],{"type":40,"value":15903},"6. Les tests behavior-level ne ralentissent-ils pas la CI ?",{"type":31,"tag":32,"props":15905,"children":15906},{},[15907],{"type":40,"value":15908},"Au contraire. Les tests behavior-level sont souvent plus rapides que les tests unitaires couplés à l'implémentation, parce qu'ils ne re-testent pas la même chose à 5 endroits. Sur crmcoaching, la CI tourne en 4 minutes pour 1 200 tests behavior-level. Le facteur limitant n'est plus le temps, c'est la qualité du test, ce qui est aussi le sujet de fond traité dans la checklist pour tester du code généré par IA.",{"type":31,"tag":48,"props":15910,"children":15911},{},[],{"type":31,"tag":229,"props":15913,"children":15914},{"cta":806,"href":807,"title":808,"type":809},[15915],{"type":31,"tag":32,"props":15916,"children":15917},{},[15918],{"type":40,"value":15919},"L'EMA est l'outil que je propose au début de chaque mission. Il mesure la maturité de votre équipe sur plusieurs axes engineering : qualité du code, gouvernance IA, dette technique, code review. Quelques minutes pour identifier lequel des 5 patterns vous touche le plus, et où concentrer vos efforts en priorité.",{"type":31,"tag":1837,"props":15921,"children":15922},{},[15923],{"type":40,"value":1841},{"title":8,"searchDepth":817,"depth":817,"links":15925},[15926,15927,15928,15929,15930,15931,15932,15933,15934,15935],{"id":10209,"depth":817,"text":10212},{"id":10271,"depth":817,"text":10274},{"id":12291,"depth":817,"text":12294},{"id":13410,"depth":817,"text":13413},{"id":14536,"depth":817,"text":14539},{"id":15317,"depth":817,"text":15320},{"id":15406,"depth":817,"text":15409},{"id":1565,"depth":817,"text":1568},{"id":637,"depth":817,"text":640},{"id":15777,"depth":817,"text":15780},"content:fr:intelligence-artificielle:5-patterns-dangereux-claude.md","fr\u002Fintelligence-artificielle\u002F5-patterns-dangereux-claude.md","fr\u002Fintelligence-artificielle\u002F5-patterns-dangereux-claude",{"_path":135,"_dir":6,"_draft":7,"_partial":7,"_locale":8,"title":15940,"description":15941,"id":12093,"date":15942,"listed":13,"nocomments":7,"hidden":7,"categories":15943,"tags":15944,"cover":15946,"readingTime":15947,"body":15952,"_type":830,"_id":18321,"_source":832,"_file":18322,"_stem":18323,"_extension":835},"Le bug que Claude vous a fait coder vendredi dernier (sans que vous le sachiez)","Anatomie d'une race condition générée par IA dans le module slot-hold de crmcoaching, et la méthode craft pour la voir avant la prod.","2026-05-04",[6],[16,15945,10182,18,19],"race-condition","covers\u002Farticles\u002Fbug-claude-vendredi-dernier.jpg",{"text":15948,"minutes":15949,"time":15950,"words":15951},"12 min read",11.12,667200,2224,{"type":28,"children":15953,"toc":18312},[15954,15970,15975,15983,15986,15992,16003,16031,16050,16076,16079,16085,16097,16611,16637,16656,16667,16670,16679,16682,16688,16693,17212,17217,17385,17428,17445,17448,17454,17459,17476,17493,17524,17555,18022,18027,18030,18034,18039,18135,18147,18150,18159,18162,18166,18171,18176,18181,18193,18196,18202,18220,18233,18258,18271,18284,18297,18300,18308],{"type":31,"tag":32,"props":15955,"children":15956},{},[15957],{"type":31,"tag":36,"props":15958,"children":15959},{},[15960,15962,15968],{"type":40,"value":15961},"Un vendredi de février 2026, je relisais une PR que Claude venait d'écrire pour le module de réservation de créneau (slot-hold) de crmcoaching. 47 lignes, tests verts, tout avait l'air propre. Sauf qu'au beau milieu du use case ",{"type":31,"tag":169,"props":15963,"children":15965},{"className":15964},[],[15966],{"type":40,"value":15967},"holdSlot",{"type":40,"value":15969}," se cachait une race condition qui, en prod, aurait permis à deux clients de réserver le même créneau au même instant. Personne ne l'avait vue. Pas même moi, au premier regard.",{"type":31,"tag":32,"props":15971,"children":15972},{},[15973],{"type":40,"value":15974},"C'est ce genre de PR qui m'a fait changer ma méthode de review depuis que je développe crmcoaching avec Claude. Voici ce que j'ai appris à repérer, pourquoi Claude écrit ce genre de code, et la grille en 4 points que j'applique aujourd'hui sur chaque PR IA-générée.",{"type":31,"tag":32,"props":15976,"children":15977},{},[15978],{"type":31,"tag":2827,"props":15979,"children":15982},{"alt":15980,"src":15981},"Le module client de crmcoaching, que je développe avec Claude","\u002Fimages\u002Fcrm\u002Fclient.png",[],{"type":31,"tag":48,"props":15984,"children":15985},{},[],{"type":31,"tag":52,"props":15987,"children":15989},{"id":15988},"pourquoi-les-bugs-claude-passent-en-review",[15990],{"type":40,"value":15991},"Pourquoi les bugs Claude passent en review",{"type":31,"tag":32,"props":15993,"children":15994},{},[15995,15996,16001],{"type":40,"value":5058},{"type":31,"tag":69,"props":15997,"children":15999},{"href":2810,"rel":15998},[665],[16000],{"type":40,"value":2814},{"type":40,"value":16002},", les équipes qui s'appuient sur Claude voient le ratio de code \"moved or refactored\" baisser de 39%, et le code \"copied\" augmenter de 8 fois. Concrètement, le code IA-généré ressemble à du code propre, mais il transporte des patterns silencieux que la review humaine n'attrape plus.",{"type":31,"tag":32,"props":16004,"children":16005},{},[16006,16008,16013,16015,16021,16023,16029],{"type":40,"value":16007},"Le vendredi dont je parle, la PR portait sur ",{"type":31,"tag":169,"props":16009,"children":16011},{"className":16010},[],[16012],{"type":40,"value":15967},{"type":40,"value":16014}," : un use case NestJS qui lisait le statut du créneau en base, vérifiait qu'il était ",{"type":31,"tag":169,"props":16016,"children":16018},{"className":16017},[],[16019],{"type":40,"value":16020},"AVAILABLE",{"type":40,"value":16022},", puis écrivait ",{"type":31,"tag":169,"props":16024,"children":16026},{"className":16025},[],[16027],{"type":40,"value":16028},"HELD",{"type":40,"value":16030},". Tests verts parce que les tests unitaires mockaient le repository Prisma. Tests d'intégration verts parce qu'ils tournaient en séquentiel. La race condition était invisible jusqu'au jour où deux clients allaient soumettre leur réservation à 50 ms d'écart.",{"type":31,"tag":78,"props":16032,"children":16033},{},[16034],{"type":31,"tag":32,"props":16035,"children":16036},{},[16037,16042,16044,16048],{"type":31,"tag":36,"props":16038,"children":16039},{},[16040],{"type":40,"value":16041},"Ce que j'observe sur crmcoaching",{"type":40,"value":16043}," : je mesure régulièrement mon taux d'acceptation des suggestions Claude sur les PR du projet. Autour de 73% des suggestions sont acceptées telles quelles. Sur les PR que je retravaille, la majorité des retouches concernent le nommage ou le style. Jamais une race condition ou un problème de concurrence ne remonte spontanément. Le filtre humain ne voit plus ce genre de bug, exactement comme le décrit le ",{"type":31,"tag":69,"props":16045,"children":16046},{"href":71},[16047],{"type":40,"value":74},{"type":40,"value":16049}," que j'ai publié l'année dernière.",{"type":31,"tag":32,"props":16051,"children":16052},{},[16053,16055,16060,16062,16067,16069,16074],{"type":40,"value":16054},"C'est exactement ce que Bruce Schneier appelait ",{"type":31,"tag":97,"props":16056,"children":16057},{},[16058],{"type":40,"value":16059},"\"Security is a process, not a product\"",{"type":40,"value":16061}," dans son livre fondateur de 2000. La robustesse d'un ",{"type":31,"tag":169,"props":16063,"children":16065},{"className":16064},[],[16066],{"type":40,"value":15967},{"type":40,"value":16068}," n'est pas dans le code lui-même, elle est dans la discipline de review qui le précède. Si la discipline tombe, le code ne peut pas la remplacer. C'est la même logique qui anime ",{"type":31,"tag":69,"props":16070,"children":16071},{"href":113},[16072],{"type":40,"value":16073},"les principes de clean code et software craftsmanship",{"type":40,"value":16075}," que je rappelle systématiquement en mission.",{"type":31,"tag":48,"props":16077,"children":16078},{},[],{"type":31,"tag":52,"props":16080,"children":16082},{"id":16081},"anatomie-dune-race-condition-typique-générée-par-ia",[16083],{"type":40,"value":16084},"Anatomie d'une race condition typique générée par IA",{"type":31,"tag":32,"props":16086,"children":16087},{},[16088,16090,16095],{"type":40,"value":16089},"Reprenons le code que Claude m'avait produit pour ",{"type":31,"tag":169,"props":16091,"children":16093},{"className":16092},[],[16094],{"type":40,"value":15967},{"type":40,"value":16096},". Voici la version simplifiée qui illustre le pattern :",{"type":31,"tag":1206,"props":16098,"children":16100},{"className":2912,"code":16099,"language":2914,"meta":8,"style":8},"\u002F\u002F apps\u002Fapi\u002Fsrc\u002Fapplication\u002Fslot-hold\u002Fhold-slot.use-case.ts\n@Injectable()\nexport class HoldSlotUseCase {\n  constructor(private readonly prisma: PrismaService) {}\n\n  async execute(slotId: string, clientId: string): Promise\u003Cvoid> {\n    const slot = await this.prisma.slot.findUniqueOrThrow({\n      where: { id: slotId },\n    });\n\n    if (slot.status !== 'AVAILABLE') {\n      throw new Error('Slot not available');\n    }\n\n    await this.prisma.slot.update({\n      where: { id: slotId },\n      data: { status: 'HELD', heldByClientId: clientId },\n    });\n  }\n}\n",[16101],{"type":31,"tag":169,"props":16102,"children":16103},{"__ignoreMap":8},[16104,16112,16127,16147,16188,16195,16266,16322,16353,16368,16375,16412,16444,16451,16458,16501,16532,16582,16597,16604],{"type":31,"tag":1215,"props":16105,"children":16106},{"class":1217,"line":1218},[16107],{"type":31,"tag":1215,"props":16108,"children":16109},{"style":2924},[16110],{"type":40,"value":16111},"\u002F\u002F apps\u002Fapi\u002Fsrc\u002Fapplication\u002Fslot-hold\u002Fhold-slot.use-case.ts\n",{"type":31,"tag":1215,"props":16113,"children":16114},{"class":1217,"line":817},[16115,16119,16123],{"type":31,"tag":1215,"props":16116,"children":16117},{"style":3541},[16118],{"type":40,"value":3544},{"type":31,"tag":1215,"props":16120,"children":16121},{"style":2939},[16122],{"type":40,"value":3549},{"type":31,"tag":1215,"props":16124,"children":16125},{"style":3552},[16126],{"type":40,"value":3555},{"type":31,"tag":1215,"props":16128,"children":16129},{"class":1217,"line":1236},[16130,16134,16138,16143],{"type":31,"tag":1215,"props":16131,"children":16132},{"style":2933},[16133],{"type":40,"value":3455},{"type":31,"tag":1215,"props":16135,"children":16136},{"style":2933},[16137],{"type":40,"value":3567},{"type":31,"tag":1215,"props":16139,"children":16140},{"style":3463},[16141],{"type":40,"value":16142}," HoldSlotUseCase",{"type":31,"tag":1215,"props":16144,"children":16145},{"style":2945},[16146],{"type":40,"value":3015},{"type":31,"tag":1215,"props":16148,"children":16149},{"class":1217,"line":1246},[16150,16154,16158,16163,16167,16171,16175,16180,16184],{"type":31,"tag":1215,"props":16151,"children":16152},{"style":2933},[16153],{"type":40,"value":3988},{"type":31,"tag":1215,"props":16155,"children":16156},{"style":2945},[16157],{"type":40,"value":3483},{"type":31,"tag":1215,"props":16159,"children":16160},{"style":2933},[16161],{"type":40,"value":16162},"private",{"type":31,"tag":1215,"props":16164,"children":16165},{"style":2933},[16166],{"type":40,"value":4005},{"type":31,"tag":1215,"props":16168,"children":16169},{"style":2954},[16170],{"type":40,"value":10376},{"type":31,"tag":1215,"props":16172,"children":16173},{"style":2960},[16174],{"type":40,"value":2963},{"type":31,"tag":1215,"props":16176,"children":16177},{"style":3463},[16178],{"type":40,"value":16179}," PrismaService",{"type":31,"tag":1215,"props":16181,"children":16182},{"style":2945},[16183],{"type":40,"value":3002},{"type":31,"tag":1215,"props":16185,"children":16186},{"style":2945},[16187],{"type":40,"value":4094},{"type":31,"tag":1215,"props":16189,"children":16190},{"class":1217,"line":1256},[16191],{"type":31,"tag":1215,"props":16192,"children":16193},{"emptyLinePlaceholder":13},[16194],{"type":40,"value":1233},{"type":31,"tag":1215,"props":16196,"children":16197},{"class":1217,"line":1264},[16198,16202,16206,16210,16214,16218,16222,16226,16230,16234,16238,16242,16246,16250,16254,16258,16262],{"type":31,"tag":1215,"props":16199,"children":16200},{"style":2933},[16201],{"type":40,"value":4109},{"type":31,"tag":1215,"props":16203,"children":16204},{"style":2939},[16205],{"type":40,"value":10506},{"type":31,"tag":1215,"props":16207,"children":16208},{"style":2945},[16209],{"type":40,"value":3483},{"type":31,"tag":1215,"props":16211,"children":16212},{"style":2954},[16213],{"type":40,"value":12465},{"type":31,"tag":1215,"props":16215,"children":16216},{"style":2960},[16217],{"type":40,"value":2963},{"type":31,"tag":1215,"props":16219,"children":16220},{"style":2966},[16221],{"type":40,"value":3783},{"type":31,"tag":1215,"props":16223,"children":16224},{"style":2945},[16225],{"type":40,"value":5843},{"type":31,"tag":1215,"props":16227,"children":16228},{"style":2954},[16229],{"type":40,"value":12138},{"type":31,"tag":1215,"props":16231,"children":16232},{"style":2960},[16233],{"type":40,"value":2963},{"type":31,"tag":1215,"props":16235,"children":16236},{"style":2966},[16237],{"type":40,"value":3783},{"type":31,"tag":1215,"props":16239,"children":16240},{"style":2945},[16241],{"type":40,"value":3002},{"type":31,"tag":1215,"props":16243,"children":16244},{"style":2960},[16245],{"type":40,"value":2963},{"type":31,"tag":1215,"props":16247,"children":16248},{"style":3463},[16249],{"type":40,"value":4145},{"type":31,"tag":1215,"props":16251,"children":16252},{"style":4148},[16253],{"type":40,"value":4151},{"type":31,"tag":1215,"props":16255,"children":16256},{"style":2966},[16257],{"type":40,"value":5339},{"type":31,"tag":1215,"props":16259,"children":16260},{"style":4148},[16261],{"type":40,"value":4161},{"type":31,"tag":1215,"props":16263,"children":16264},{"style":2945},[16265],{"type":40,"value":3015},{"type":31,"tag":1215,"props":16267,"children":16268},{"class":1217,"line":1273},[16269,16273,16277,16281,16285,16289,16293,16297,16301,16305,16309,16314,16318],{"type":31,"tag":1215,"props":16270,"children":16271},{"style":2933},[16272],{"type":40,"value":4173},{"type":31,"tag":1215,"props":16274,"children":16275},{"style":3026},[16276],{"type":40,"value":12687},{"type":31,"tag":1215,"props":16278,"children":16279},{"style":2960},[16280],{"type":40,"value":3034},{"type":31,"tag":1215,"props":16282,"children":16283},{"style":2933},[16284],{"type":40,"value":4187},{"type":31,"tag":1215,"props":16286,"children":16287},{"style":4190},[16288],{"type":40,"value":4193},{"type":31,"tag":1215,"props":16290,"children":16291},{"style":3113},[16292],{"type":40,"value":76},{"type":31,"tag":1215,"props":16294,"children":16295},{"style":1250},[16296],{"type":40,"value":10659},{"type":31,"tag":1215,"props":16298,"children":16299},{"style":3113},[16300],{"type":40,"value":76},{"type":31,"tag":1215,"props":16302,"children":16303},{"style":1250},[16304],{"type":40,"value":12716},{"type":31,"tag":1215,"props":16306,"children":16307},{"style":3113},[16308],{"type":40,"value":76},{"type":31,"tag":1215,"props":16310,"children":16311},{"style":2939},[16312],{"type":40,"value":16313},"findUniqueOrThrow",{"type":31,"tag":1215,"props":16315,"children":16316},{"style":1250},[16317],{"type":40,"value":3483},{"type":31,"tag":1215,"props":16319,"children":16320},{"style":2945},[16321],{"type":40,"value":4257},{"type":31,"tag":1215,"props":16323,"children":16324},{"class":1217,"line":1282},[16325,16329,16333,16337,16341,16345,16349],{"type":31,"tag":1215,"props":16326,"children":16327},{"style":1250},[16328],{"type":40,"value":10982},{"type":31,"tag":1215,"props":16330,"children":16331},{"style":3113},[16332],{"type":40,"value":2963},{"type":31,"tag":1215,"props":16334,"children":16335},{"style":2945},[16336],{"type":40,"value":5616},{"type":31,"tag":1215,"props":16338,"children":16339},{"style":1250},[16340],{"type":40,"value":10702},{"type":31,"tag":1215,"props":16342,"children":16343},{"style":3113},[16344],{"type":40,"value":2963},{"type":31,"tag":1215,"props":16346,"children":16347},{"style":1250},[16348],{"type":40,"value":12757},{"type":31,"tag":1215,"props":16350,"children":16351},{"style":2945},[16352],{"type":40,"value":5643},{"type":31,"tag":1215,"props":16354,"children":16355},{"class":1217,"line":1291},[16356,16360,16364],{"type":31,"tag":1215,"props":16357,"children":16358},{"style":2945},[16359],{"type":40,"value":5651},{"type":31,"tag":1215,"props":16361,"children":16362},{"style":1250},[16363],{"type":40,"value":3002},{"type":31,"tag":1215,"props":16365,"children":16366},{"style":2945},[16367],{"type":40,"value":3055},{"type":31,"tag":1215,"props":16369,"children":16370},{"class":1217,"line":1300},[16371],{"type":31,"tag":1215,"props":16372,"children":16373},{"emptyLinePlaceholder":13},[16374],{"type":40,"value":1233},{"type":31,"tag":1215,"props":16376,"children":16377},{"class":1217,"line":1309},[16378,16382,16387,16391,16395,16399,16404,16408],{"type":31,"tag":1215,"props":16379,"children":16380},{"style":2933},[16381],{"type":40,"value":4237},{"type":31,"tag":1215,"props":16383,"children":16384},{"style":1250},[16385],{"type":40,"value":16386}," (slot",{"type":31,"tag":1215,"props":16388,"children":16389},{"style":3113},[16390],{"type":40,"value":76},{"type":31,"tag":1215,"props":16392,"children":16393},{"style":1250},[16394],{"type":40,"value":6410},{"type":31,"tag":1215,"props":16396,"children":16397},{"style":2960},[16398],{"type":40,"value":10819},{"type":31,"tag":1215,"props":16400,"children":16401},{"style":4392},[16402],{"type":40,"value":16403}," 'AVAILABLE'",{"type":31,"tag":1215,"props":16405,"children":16406},{"style":1250},[16407],{"type":40,"value":3140},{"type":31,"tag":1215,"props":16409,"children":16410},{"style":2945},[16411],{"type":40,"value":4257},{"type":31,"tag":1215,"props":16413,"children":16414},{"class":1217,"line":1317},[16415,16419,16423,16427,16431,16436,16440],{"type":31,"tag":1215,"props":16416,"children":16417},{"style":2933},[16418],{"type":40,"value":4265},{"type":31,"tag":1215,"props":16420,"children":16421},{"style":3809},[16422],{"type":40,"value":3812},{"type":31,"tag":1215,"props":16424,"children":16425},{"style":2939},[16426],{"type":40,"value":5431},{"type":31,"tag":1215,"props":16428,"children":16429},{"style":1250},[16430],{"type":40,"value":3483},{"type":31,"tag":1215,"props":16432,"children":16433},{"style":4392},[16434],{"type":40,"value":16435},"'Slot not available'",{"type":31,"tag":1215,"props":16437,"children":16438},{"style":1250},[16439],{"type":40,"value":3002},{"type":31,"tag":1215,"props":16441,"children":16442},{"style":2945},[16443],{"type":40,"value":3055},{"type":31,"tag":1215,"props":16445,"children":16446},{"class":1217,"line":1326},[16447],{"type":31,"tag":1215,"props":16448,"children":16449},{"style":2945},[16450],{"type":40,"value":4298},{"type":31,"tag":1215,"props":16452,"children":16453},{"class":1217,"line":1341},[16454],{"type":31,"tag":1215,"props":16455,"children":16456},{"emptyLinePlaceholder":13},[16457],{"type":40,"value":1233},{"type":31,"tag":1215,"props":16459,"children":16460},{"class":1217,"line":1355},[16461,16465,16469,16473,16477,16481,16485,16489,16493,16497],{"type":31,"tag":1215,"props":16462,"children":16463},{"style":2933},[16464],{"type":40,"value":4306},{"type":31,"tag":1215,"props":16466,"children":16467},{"style":4190},[16468],{"type":40,"value":4193},{"type":31,"tag":1215,"props":16470,"children":16471},{"style":3113},[16472],{"type":40,"value":76},{"type":31,"tag":1215,"props":16474,"children":16475},{"style":1250},[16476],{"type":40,"value":10659},{"type":31,"tag":1215,"props":16478,"children":16479},{"style":3113},[16480],{"type":40,"value":76},{"type":31,"tag":1215,"props":16482,"children":16483},{"style":1250},[16484],{"type":40,"value":12716},{"type":31,"tag":1215,"props":16486,"children":16487},{"style":3113},[16488],{"type":40,"value":76},{"type":31,"tag":1215,"props":16490,"children":16491},{"style":2939},[16492],{"type":40,"value":11422},{"type":31,"tag":1215,"props":16494,"children":16495},{"style":1250},[16496],{"type":40,"value":3483},{"type":31,"tag":1215,"props":16498,"children":16499},{"style":2945},[16500],{"type":40,"value":4257},{"type":31,"tag":1215,"props":16502,"children":16503},{"class":1217,"line":1364},[16504,16508,16512,16516,16520,16524,16528],{"type":31,"tag":1215,"props":16505,"children":16506},{"style":1250},[16507],{"type":40,"value":10982},{"type":31,"tag":1215,"props":16509,"children":16510},{"style":3113},[16511],{"type":40,"value":2963},{"type":31,"tag":1215,"props":16513,"children":16514},{"style":2945},[16515],{"type":40,"value":5616},{"type":31,"tag":1215,"props":16517,"children":16518},{"style":1250},[16519],{"type":40,"value":10702},{"type":31,"tag":1215,"props":16521,"children":16522},{"style":3113},[16523],{"type":40,"value":2963},{"type":31,"tag":1215,"props":16525,"children":16526},{"style":1250},[16527],{"type":40,"value":12757},{"type":31,"tag":1215,"props":16529,"children":16530},{"style":2945},[16531],{"type":40,"value":5643},{"type":31,"tag":1215,"props":16533,"children":16534},{"class":1217,"line":1378},[16535,16539,16543,16547,16551,16555,16560,16564,16569,16573,16578],{"type":31,"tag":1215,"props":16536,"children":16537},{"style":1250},[16538],{"type":40,"value":11167},{"type":31,"tag":1215,"props":16540,"children":16541},{"style":3113},[16542],{"type":40,"value":2963},{"type":31,"tag":1215,"props":16544,"children":16545},{"style":2945},[16546],{"type":40,"value":5616},{"type":31,"tag":1215,"props":16548,"children":16549},{"style":1250},[16550],{"type":40,"value":11481},{"type":31,"tag":1215,"props":16552,"children":16553},{"style":3113},[16554],{"type":40,"value":2963},{"type":31,"tag":1215,"props":16556,"children":16557},{"style":4392},[16558],{"type":40,"value":16559}," 'HELD'",{"type":31,"tag":1215,"props":16561,"children":16562},{"style":2945},[16563],{"type":40,"value":5843},{"type":31,"tag":1215,"props":16565,"children":16566},{"style":1250},[16567],{"type":40,"value":16568}," heldByClientId",{"type":31,"tag":1215,"props":16570,"children":16571},{"style":3113},[16572],{"type":40,"value":2963},{"type":31,"tag":1215,"props":16574,"children":16575},{"style":1250},[16576],{"type":40,"value":16577}," clientId ",{"type":31,"tag":1215,"props":16579,"children":16580},{"style":2945},[16581],{"type":40,"value":5643},{"type":31,"tag":1215,"props":16583,"children":16584},{"class":1217,"line":1387},[16585,16589,16593],{"type":31,"tag":1215,"props":16586,"children":16587},{"style":2945},[16588],{"type":40,"value":5651},{"type":31,"tag":1215,"props":16590,"children":16591},{"style":1250},[16592],{"type":40,"value":3002},{"type":31,"tag":1215,"props":16594,"children":16595},{"style":2945},[16596],{"type":40,"value":3055},{"type":31,"tag":1215,"props":16598,"children":16599},{"class":1217,"line":1401},[16600],{"type":31,"tag":1215,"props":16601,"children":16602},{"style":2945},[16603],{"type":40,"value":3710},{"type":31,"tag":1215,"props":16605,"children":16606},{"class":1217,"line":1410},[16607],{"type":31,"tag":1215,"props":16608,"children":16609},{"style":2945},[16610],{"type":40,"value":3160},{"type":31,"tag":32,"props":16612,"children":16613},{},[16614,16616,16622,16624,16629,16631,16636],{"type":40,"value":16615},"Le bug est à deux endroits. D'abord, la lecture de ",{"type":31,"tag":169,"props":16617,"children":16619},{"className":16618},[],[16620],{"type":40,"value":16621},"slot.status",{"type":40,"value":16623}," n'est pas verrouillée : deux requêtes parallèles peuvent toutes les deux lire ",{"type":31,"tag":169,"props":16625,"children":16627},{"className":16626},[],[16628],{"type":40,"value":16020},{"type":40,"value":16630}," et valider la condition indépendamment. Ensuite, l'écriture utilise la valeur lue en mémoire au lieu de demander à Postgres de faire le calcul atomique avec une condition. Résultat : deux clients se retrouvent avec le même créneau marqué ",{"type":31,"tag":169,"props":16632,"children":16634},{"className":16633},[],[16635],{"type":40,"value":16028},{"type":40,"value":76},{"type":31,"tag":32,"props":16638,"children":16639},{},[16640,16642,16647,16649,16654],{"type":40,"value":16641},"Pourquoi Claude écrit ce code-là ? Parce que c'est le pattern dominant dans son training data. Sur GitHub, des dizaines de milliers d'exemples Prisma montrent exactement cette séquence ",{"type":31,"tag":169,"props":16643,"children":16645},{"className":16644},[],[16646],{"type":40,"value":10676},{"type":40,"value":16648}," + ",{"type":31,"tag":169,"props":16650,"children":16652},{"className":16651},[],[16653],{"type":40,"value":11422},{"type":40,"value":16655},". Claude n'a pas conscience que dans un contexte de réservation, la concurrence est la norme et non l'exception. Il code comme un développeur junior bien intentionné qui n'a jamais vu de prod sous charge.",{"type":31,"tag":32,"props":16657,"children":16658},{},[16659,16661,16665],{"type":40,"value":16660},"C'est aussi ce que Martin Fowler a documenté dans ",{"type":31,"tag":97,"props":16662,"children":16663},{},[16664],{"type":40,"value":3882},{"type":40,"value":16666}," (2018) : le code qui marche dans un test unitaire n'est pas le code qui marche en production. La différence est dans les conditions d'exécution, et l'IA n'a pas accès à ces conditions au moment où elle génère. C'est exactement le genre de risque qui transforme un repo récent en legacy à évaluer urgemment à 12 mois si la discipline de review ne se met pas en place rapidement.",{"type":31,"tag":48,"props":16668,"children":16669},{},[],{"type":31,"tag":229,"props":16671,"children":16673},{"cta":231,"href":232,"title":16672,"type":234},"Vous voulez développer le réflexe qui attrape ces bugs avant la prod ?",[16674],{"type":31,"tag":32,"props":16675,"children":16676},{},[16677],{"type":40,"value":16678},"Repérer une race condition dans une PR Claude, ça ne s'apprend pas en lisant un article : ça se travaille. En mentoring 1:1, je relis votre code et vos PR IA-générées avec vous, et je vous transmets la grille de lecture que j'applique sur crmcoaching. En quelques sessions, vous arrêtez de relire les accolades et vous commencez à voir ce que le modèle ne voit pas.",{"type":31,"tag":48,"props":16680,"children":16681},{},[],{"type":31,"tag":52,"props":16683,"children":16685},{"id":16684},"le-fix-craft-transaction-atomique-et-garde-explicite",[16686],{"type":40,"value":16687},"Le fix craft : transaction atomique et garde explicite",{"type":31,"tag":32,"props":16689,"children":16690},{},[16691],{"type":40,"value":16692},"Voici la version que j'ai réécrite avec Claude, en lui donnant le bon contexte cette fois :",{"type":31,"tag":1206,"props":16694,"children":16696},{"className":2912,"code":16695,"language":2914,"meta":8,"style":8},"\u002F\u002F apps\u002Fapi\u002Fsrc\u002Fapplication\u002Fslot-hold\u002Fhold-slot.use-case.ts\n@Injectable()\nexport class HoldSlotUseCase {\n  constructor(private readonly prisma: PrismaService) {}\n\n  async execute(slotId: string, clientId: string): Promise\u003Cvoid> {\n    await this.prisma.$transaction(\n      async (tx) => {\n        const result = await tx.slot.updateMany({\n          where: { id: slotId, status: 'AVAILABLE' },\n          data: { status: 'HELD', heldByClientId: clientId },\n        });\n\n        if (result.count !== 1) {\n          throw new SlotAlreadyHeldError(slotId);\n        }\n      },\n      { isolationLevel: 'Serializable' },\n    );\n  }\n}\n",[16697],{"type":31,"tag":169,"props":16698,"children":16699},{"__ignoreMap":8},[16700,16707,16722,16741,16780,16787,16858,16890,16918,16967,17015,17063,17078,17085,17123,17147,17154,17161,17187,17198,17205],{"type":31,"tag":1215,"props":16701,"children":16702},{"class":1217,"line":1218},[16703],{"type":31,"tag":1215,"props":16704,"children":16705},{"style":2924},[16706],{"type":40,"value":16111},{"type":31,"tag":1215,"props":16708,"children":16709},{"class":1217,"line":817},[16710,16714,16718],{"type":31,"tag":1215,"props":16711,"children":16712},{"style":3541},[16713],{"type":40,"value":3544},{"type":31,"tag":1215,"props":16715,"children":16716},{"style":2939},[16717],{"type":40,"value":3549},{"type":31,"tag":1215,"props":16719,"children":16720},{"style":3552},[16721],{"type":40,"value":3555},{"type":31,"tag":1215,"props":16723,"children":16724},{"class":1217,"line":1236},[16725,16729,16733,16737],{"type":31,"tag":1215,"props":16726,"children":16727},{"style":2933},[16728],{"type":40,"value":3455},{"type":31,"tag":1215,"props":16730,"children":16731},{"style":2933},[16732],{"type":40,"value":3567},{"type":31,"tag":1215,"props":16734,"children":16735},{"style":3463},[16736],{"type":40,"value":16142},{"type":31,"tag":1215,"props":16738,"children":16739},{"style":2945},[16740],{"type":40,"value":3015},{"type":31,"tag":1215,"props":16742,"children":16743},{"class":1217,"line":1246},[16744,16748,16752,16756,16760,16764,16768,16772,16776],{"type":31,"tag":1215,"props":16745,"children":16746},{"style":2933},[16747],{"type":40,"value":3988},{"type":31,"tag":1215,"props":16749,"children":16750},{"style":2945},[16751],{"type":40,"value":3483},{"type":31,"tag":1215,"props":16753,"children":16754},{"style":2933},[16755],{"type":40,"value":16162},{"type":31,"tag":1215,"props":16757,"children":16758},{"style":2933},[16759],{"type":40,"value":4005},{"type":31,"tag":1215,"props":16761,"children":16762},{"style":2954},[16763],{"type":40,"value":10376},{"type":31,"tag":1215,"props":16765,"children":16766},{"style":2960},[16767],{"type":40,"value":2963},{"type":31,"tag":1215,"props":16769,"children":16770},{"style":3463},[16771],{"type":40,"value":16179},{"type":31,"tag":1215,"props":16773,"children":16774},{"style":2945},[16775],{"type":40,"value":3002},{"type":31,"tag":1215,"props":16777,"children":16778},{"style":2945},[16779],{"type":40,"value":4094},{"type":31,"tag":1215,"props":16781,"children":16782},{"class":1217,"line":1256},[16783],{"type":31,"tag":1215,"props":16784,"children":16785},{"emptyLinePlaceholder":13},[16786],{"type":40,"value":1233},{"type":31,"tag":1215,"props":16788,"children":16789},{"class":1217,"line":1264},[16790,16794,16798,16802,16806,16810,16814,16818,16822,16826,16830,16834,16838,16842,16846,16850,16854],{"type":31,"tag":1215,"props":16791,"children":16792},{"style":2933},[16793],{"type":40,"value":4109},{"type":31,"tag":1215,"props":16795,"children":16796},{"style":2939},[16797],{"type":40,"value":10506},{"type":31,"tag":1215,"props":16799,"children":16800},{"style":2945},[16801],{"type":40,"value":3483},{"type":31,"tag":1215,"props":16803,"children":16804},{"style":2954},[16805],{"type":40,"value":12465},{"type":31,"tag":1215,"props":16807,"children":16808},{"style":2960},[16809],{"type":40,"value":2963},{"type":31,"tag":1215,"props":16811,"children":16812},{"style":2966},[16813],{"type":40,"value":3783},{"type":31,"tag":1215,"props":16815,"children":16816},{"style":2945},[16817],{"type":40,"value":5843},{"type":31,"tag":1215,"props":16819,"children":16820},{"style":2954},[16821],{"type":40,"value":12138},{"type":31,"tag":1215,"props":16823,"children":16824},{"style":2960},[16825],{"type":40,"value":2963},{"type":31,"tag":1215,"props":16827,"children":16828},{"style":2966},[16829],{"type":40,"value":3783},{"type":31,"tag":1215,"props":16831,"children":16832},{"style":2945},[16833],{"type":40,"value":3002},{"type":31,"tag":1215,"props":16835,"children":16836},{"style":2960},[16837],{"type":40,"value":2963},{"type":31,"tag":1215,"props":16839,"children":16840},{"style":3463},[16841],{"type":40,"value":4145},{"type":31,"tag":1215,"props":16843,"children":16844},{"style":4148},[16845],{"type":40,"value":4151},{"type":31,"tag":1215,"props":16847,"children":16848},{"style":2966},[16849],{"type":40,"value":5339},{"type":31,"tag":1215,"props":16851,"children":16852},{"style":4148},[16853],{"type":40,"value":4161},{"type":31,"tag":1215,"props":16855,"children":16856},{"style":2945},[16857],{"type":40,"value":3015},{"type":31,"tag":1215,"props":16859,"children":16860},{"class":1217,"line":1273},[16861,16865,16869,16873,16877,16881,16886],{"type":31,"tag":1215,"props":16862,"children":16863},{"style":2933},[16864],{"type":40,"value":4306},{"type":31,"tag":1215,"props":16866,"children":16867},{"style":4190},[16868],{"type":40,"value":4193},{"type":31,"tag":1215,"props":16870,"children":16871},{"style":3113},[16872],{"type":40,"value":76},{"type":31,"tag":1215,"props":16874,"children":16875},{"style":1250},[16876],{"type":40,"value":10659},{"type":31,"tag":1215,"props":16878,"children":16879},{"style":3113},[16880],{"type":40,"value":76},{"type":31,"tag":1215,"props":16882,"children":16883},{"style":2939},[16884],{"type":40,"value":16885},"$transaction",{"type":31,"tag":1215,"props":16887,"children":16888},{"style":1250},[16889],{"type":40,"value":2948},{"type":31,"tag":1215,"props":16891,"children":16892},{"class":1217,"line":1282},[16893,16897,16901,16906,16910,16914],{"type":31,"tag":1215,"props":16894,"children":16895},{"style":2933},[16896],{"type":40,"value":8770},{"type":31,"tag":1215,"props":16898,"children":16899},{"style":2945},[16900],{"type":40,"value":4242},{"type":31,"tag":1215,"props":16902,"children":16903},{"style":2954},[16904],{"type":40,"value":16905},"tx",{"type":31,"tag":1215,"props":16907,"children":16908},{"style":2945},[16909],{"type":40,"value":3002},{"type":31,"tag":1215,"props":16911,"children":16912},{"style":2960},[16913],{"type":40,"value":5858},{"type":31,"tag":1215,"props":16915,"children":16916},{"style":2945},[16917],{"type":40,"value":3015},{"type":31,"tag":1215,"props":16919,"children":16920},{"class":1217,"line":1291},[16921,16925,16929,16933,16937,16942,16946,16950,16954,16959,16963],{"type":31,"tag":1215,"props":16922,"children":16923},{"style":2933},[16924],{"type":40,"value":8790},{"type":31,"tag":1215,"props":16926,"children":16927},{"style":3026},[16928],{"type":40,"value":14247},{"type":31,"tag":1215,"props":16930,"children":16931},{"style":2960},[16932],{"type":40,"value":3034},{"type":31,"tag":1215,"props":16934,"children":16935},{"style":2933},[16936],{"type":40,"value":4187},{"type":31,"tag":1215,"props":16938,"children":16939},{"style":1250},[16940],{"type":40,"value":16941}," tx",{"type":31,"tag":1215,"props":16943,"children":16944},{"style":3113},[16945],{"type":40,"value":76},{"type":31,"tag":1215,"props":16947,"children":16948},{"style":1250},[16949],{"type":40,"value":12716},{"type":31,"tag":1215,"props":16951,"children":16952},{"style":3113},[16953],{"type":40,"value":76},{"type":31,"tag":1215,"props":16955,"children":16956},{"style":2939},[16957],{"type":40,"value":16958},"updateMany",{"type":31,"tag":1215,"props":16960,"children":16961},{"style":1250},[16962],{"type":40,"value":3483},{"type":31,"tag":1215,"props":16964,"children":16965},{"style":2945},[16966],{"type":40,"value":4257},{"type":31,"tag":1215,"props":16968,"children":16969},{"class":1217,"line":1300},[16970,16975,16979,16983,16987,16991,16995,16999,17003,17007,17011],{"type":31,"tag":1215,"props":16971,"children":16972},{"style":1250},[16973],{"type":40,"value":16974},"          where",{"type":31,"tag":1215,"props":16976,"children":16977},{"style":3113},[16978],{"type":40,"value":2963},{"type":31,"tag":1215,"props":16980,"children":16981},{"style":2945},[16982],{"type":40,"value":5616},{"type":31,"tag":1215,"props":16984,"children":16985},{"style":1250},[16986],{"type":40,"value":10702},{"type":31,"tag":1215,"props":16988,"children":16989},{"style":3113},[16990],{"type":40,"value":2963},{"type":31,"tag":1215,"props":16992,"children":16993},{"style":1250},[16994],{"type":40,"value":12596},{"type":31,"tag":1215,"props":16996,"children":16997},{"style":2945},[16998],{"type":40,"value":5843},{"type":31,"tag":1215,"props":17000,"children":17001},{"style":1250},[17002],{"type":40,"value":11481},{"type":31,"tag":1215,"props":17004,"children":17005},{"style":3113},[17006],{"type":40,"value":2963},{"type":31,"tag":1215,"props":17008,"children":17009},{"style":4392},[17010],{"type":40,"value":16403},{"type":31,"tag":1215,"props":17012,"children":17013},{"style":2945},[17014],{"type":40,"value":12660},{"type":31,"tag":1215,"props":17016,"children":17017},{"class":1217,"line":1309},[17018,17023,17027,17031,17035,17039,17043,17047,17051,17055,17059],{"type":31,"tag":1215,"props":17019,"children":17020},{"style":1250},[17021],{"type":40,"value":17022},"          data",{"type":31,"tag":1215,"props":17024,"children":17025},{"style":3113},[17026],{"type":40,"value":2963},{"type":31,"tag":1215,"props":17028,"children":17029},{"style":2945},[17030],{"type":40,"value":5616},{"type":31,"tag":1215,"props":17032,"children":17033},{"style":1250},[17034],{"type":40,"value":11481},{"type":31,"tag":1215,"props":17036,"children":17037},{"style":3113},[17038],{"type":40,"value":2963},{"type":31,"tag":1215,"props":17040,"children":17041},{"style":4392},[17042],{"type":40,"value":16559},{"type":31,"tag":1215,"props":17044,"children":17045},{"style":2945},[17046],{"type":40,"value":5843},{"type":31,"tag":1215,"props":17048,"children":17049},{"style":1250},[17050],{"type":40,"value":16568},{"type":31,"tag":1215,"props":17052,"children":17053},{"style":3113},[17054],{"type":40,"value":2963},{"type":31,"tag":1215,"props":17056,"children":17057},{"style":1250},[17058],{"type":40,"value":16577},{"type":31,"tag":1215,"props":17060,"children":17061},{"style":2945},[17062],{"type":40,"value":5643},{"type":31,"tag":1215,"props":17064,"children":17065},{"class":1217,"line":1317},[17066,17070,17074],{"type":31,"tag":1215,"props":17067,"children":17068},{"style":2945},[17069],{"type":40,"value":9132},{"type":31,"tag":1215,"props":17071,"children":17072},{"style":1250},[17073],{"type":40,"value":3002},{"type":31,"tag":1215,"props":17075,"children":17076},{"style":2945},[17077],{"type":40,"value":3055},{"type":31,"tag":1215,"props":17079,"children":17080},{"class":1217,"line":1326},[17081],{"type":31,"tag":1215,"props":17082,"children":17083},{"emptyLinePlaceholder":13},[17084],{"type":40,"value":1233},{"type":31,"tag":1215,"props":17086,"children":17087},{"class":1217,"line":1341},[17088,17093,17098,17102,17107,17111,17115,17119],{"type":31,"tag":1215,"props":17089,"children":17090},{"style":2933},[17091],{"type":40,"value":17092},"        if",{"type":31,"tag":1215,"props":17094,"children":17095},{"style":1250},[17096],{"type":40,"value":17097}," (result",{"type":31,"tag":1215,"props":17099,"children":17100},{"style":3113},[17101],{"type":40,"value":76},{"type":31,"tag":1215,"props":17103,"children":17104},{"style":1250},[17105],{"type":40,"value":17106},"count ",{"type":31,"tag":1215,"props":17108,"children":17109},{"style":2960},[17110],{"type":40,"value":10819},{"type":31,"tag":1215,"props":17112,"children":17113},{"style":3047},[17114],{"type":40,"value":6829},{"type":31,"tag":1215,"props":17116,"children":17117},{"style":1250},[17118],{"type":40,"value":3140},{"type":31,"tag":1215,"props":17120,"children":17121},{"style":2945},[17122],{"type":40,"value":4257},{"type":31,"tag":1215,"props":17124,"children":17125},{"class":1217,"line":1355},[17126,17130,17134,17139,17143],{"type":31,"tag":1215,"props":17127,"children":17128},{"style":2933},[17129],{"type":40,"value":9395},{"type":31,"tag":1215,"props":17131,"children":17132},{"style":3809},[17133],{"type":40,"value":3812},{"type":31,"tag":1215,"props":17135,"children":17136},{"style":2939},[17137],{"type":40,"value":17138}," SlotAlreadyHeldError",{"type":31,"tag":1215,"props":17140,"children":17141},{"style":1250},[17142],{"type":40,"value":13250},{"type":31,"tag":1215,"props":17144,"children":17145},{"style":2945},[17146],{"type":40,"value":3055},{"type":31,"tag":1215,"props":17148,"children":17149},{"class":1217,"line":1364},[17150],{"type":31,"tag":1215,"props":17151,"children":17152},{"style":2945},[17153],{"type":40,"value":9447},{"type":31,"tag":1215,"props":17155,"children":17156},{"class":1217,"line":1378},[17157],{"type":31,"tag":1215,"props":17158,"children":17159},{"style":2945},[17160],{"type":40,"value":9473},{"type":31,"tag":1215,"props":17162,"children":17163},{"class":1217,"line":1387},[17164,17169,17174,17178,17183],{"type":31,"tag":1215,"props":17165,"children":17166},{"style":2945},[17167],{"type":40,"value":17168},"      {",{"type":31,"tag":1215,"props":17170,"children":17171},{"style":1250},[17172],{"type":40,"value":17173}," isolationLevel",{"type":31,"tag":1215,"props":17175,"children":17176},{"style":3113},[17177],{"type":40,"value":2963},{"type":31,"tag":1215,"props":17179,"children":17180},{"style":4392},[17181],{"type":40,"value":17182}," 'Serializable'",{"type":31,"tag":1215,"props":17184,"children":17185},{"style":2945},[17186],{"type":40,"value":12660},{"type":31,"tag":1215,"props":17188,"children":17189},{"class":1217,"line":1401},[17190,17194],{"type":31,"tag":1215,"props":17191,"children":17192},{"style":1250},[17193],{"type":40,"value":9653},{"type":31,"tag":1215,"props":17195,"children":17196},{"style":2945},[17197],{"type":40,"value":3055},{"type":31,"tag":1215,"props":17199,"children":17200},{"class":1217,"line":1410},[17201],{"type":31,"tag":1215,"props":17202,"children":17203},{"style":2945},[17204],{"type":40,"value":3710},{"type":31,"tag":1215,"props":17206,"children":17207},{"class":1217,"line":1419},[17208],{"type":31,"tag":1215,"props":17209,"children":17210},{"style":2945},[17211],{"type":40,"value":3160},{"type":31,"tag":32,"props":17213,"children":17214},{},[17215],{"type":40,"value":17216},"avec l'exception typée dans le domaine :",{"type":31,"tag":1206,"props":17218,"children":17220},{"className":2912,"code":17219,"language":2914,"meta":8,"style":8},"\u002F\u002F apps\u002Fapi\u002Fsrc\u002Fdomain\u002Fslot-hold\u002Fslot-already-held.error.ts\nexport class SlotAlreadyHeldError extends Error {\n  constructor(public readonly slotId: string) {\n    super(`Slot ${slotId} is no longer available`);\n    this.name = 'SlotAlreadyHeldError';\n  }\n}\n",[17221],{"type":31,"tag":169,"props":17222,"children":17223},{"__ignoreMap":8},[17224,17232,17260,17300,17342,17371,17378],{"type":31,"tag":1215,"props":17225,"children":17226},{"class":1217,"line":1218},[17227],{"type":31,"tag":1215,"props":17228,"children":17229},{"style":2924},[17230],{"type":40,"value":17231},"\u002F\u002F apps\u002Fapi\u002Fsrc\u002Fdomain\u002Fslot-hold\u002Fslot-already-held.error.ts\n",{"type":31,"tag":1215,"props":17233,"children":17234},{"class":1217,"line":817},[17235,17239,17243,17247,17252,17256],{"type":31,"tag":1215,"props":17236,"children":17237},{"style":2933},[17238],{"type":40,"value":3455},{"type":31,"tag":1215,"props":17240,"children":17241},{"style":2933},[17242],{"type":40,"value":3567},{"type":31,"tag":1215,"props":17244,"children":17245},{"style":3463},[17246],{"type":40,"value":17138},{"type":31,"tag":1215,"props":17248,"children":17249},{"style":2933},[17250],{"type":40,"value":17251}," extends",{"type":31,"tag":1215,"props":17253,"children":17254},{"style":3463},[17255],{"type":40,"value":5431},{"type":31,"tag":1215,"props":17257,"children":17258},{"style":2945},[17259],{"type":40,"value":3015},{"type":31,"tag":1215,"props":17261,"children":17262},{"class":1217,"line":1236},[17263,17267,17271,17276,17280,17284,17288,17292,17296],{"type":31,"tag":1215,"props":17264,"children":17265},{"style":2933},[17266],{"type":40,"value":3988},{"type":31,"tag":1215,"props":17268,"children":17269},{"style":2945},[17270],{"type":40,"value":3483},{"type":31,"tag":1215,"props":17272,"children":17273},{"style":2933},[17274],{"type":40,"value":17275},"public",{"type":31,"tag":1215,"props":17277,"children":17278},{"style":2933},[17279],{"type":40,"value":4005},{"type":31,"tag":1215,"props":17281,"children":17282},{"style":2954},[17283],{"type":40,"value":12596},{"type":31,"tag":1215,"props":17285,"children":17286},{"style":2960},[17287],{"type":40,"value":2963},{"type":31,"tag":1215,"props":17289,"children":17290},{"style":2966},[17291],{"type":40,"value":3783},{"type":31,"tag":1215,"props":17293,"children":17294},{"style":2945},[17295],{"type":40,"value":3002},{"type":31,"tag":1215,"props":17297,"children":17298},{"style":2945},[17299],{"type":40,"value":3015},{"type":31,"tag":1215,"props":17301,"children":17302},{"class":1217,"line":1246},[17303,17308,17312,17317,17321,17325,17329,17334,17338],{"type":31,"tag":1215,"props":17304,"children":17305},{"style":2966},[17306],{"type":40,"value":17307},"    super",{"type":31,"tag":1215,"props":17309,"children":17310},{"style":1250},[17311],{"type":40,"value":3483},{"type":31,"tag":1215,"props":17313,"children":17314},{"style":4392},[17315],{"type":40,"value":17316},"`Slot ",{"type":31,"tag":1215,"props":17318,"children":17319},{"style":5443},[17320],{"type":40,"value":5446},{"type":31,"tag":1215,"props":17322,"children":17323},{"style":1250},[17324],{"type":40,"value":12465},{"type":31,"tag":1215,"props":17326,"children":17327},{"style":5443},[17328],{"type":40,"value":5455},{"type":31,"tag":1215,"props":17330,"children":17331},{"style":4392},[17332],{"type":40,"value":17333}," is no longer available`",{"type":31,"tag":1215,"props":17335,"children":17336},{"style":1250},[17337],{"type":40,"value":3002},{"type":31,"tag":1215,"props":17339,"children":17340},{"style":2945},[17341],{"type":40,"value":3055},{"type":31,"tag":1215,"props":17343,"children":17344},{"class":1217,"line":1256},[17345,17349,17353,17358,17362,17367],{"type":31,"tag":1215,"props":17346,"children":17347},{"style":4190},[17348],{"type":40,"value":4367},{"type":31,"tag":1215,"props":17350,"children":17351},{"style":3113},[17352],{"type":40,"value":76},{"type":31,"tag":1215,"props":17354,"children":17355},{"style":1250},[17356],{"type":40,"value":17357},"name ",{"type":31,"tag":1215,"props":17359,"children":17360},{"style":2960},[17361],{"type":40,"value":6748},{"type":31,"tag":1215,"props":17363,"children":17364},{"style":4392},[17365],{"type":40,"value":17366}," 'SlotAlreadyHeldError'",{"type":31,"tag":1215,"props":17368,"children":17369},{"style":2945},[17370],{"type":40,"value":3055},{"type":31,"tag":1215,"props":17372,"children":17373},{"class":1217,"line":1264},[17374],{"type":31,"tag":1215,"props":17375,"children":17376},{"style":2945},[17377],{"type":40,"value":3710},{"type":31,"tag":1215,"props":17379,"children":17380},{"class":1217,"line":1273},[17381],{"type":31,"tag":1215,"props":17382,"children":17383},{"style":2945},[17384],{"type":40,"value":3160},{"type":31,"tag":32,"props":17386,"children":17387},{},[17388,17390,17396,17397,17403,17405,17410,17412,17418,17420,17426],{"type":40,"value":17389},"Trois changements comptent. D'abord le recours à ",{"type":31,"tag":169,"props":17391,"children":17393},{"className":17392},[],[17394],{"type":40,"value":17395},"prisma.$transaction",{"type":40,"value":5693},{"type":31,"tag":169,"props":17398,"children":17400},{"className":17399},[],[17401],{"type":40,"value":17402},"isolationLevel: 'Serializable'",{"type":40,"value":17404}," qui rend l'opération atomique : soit tout passe, soit rien ne passe, et Postgres empêche deux transactions concurrentes de lire le même état incohérent. Ensuite l'",{"type":31,"tag":169,"props":17406,"children":17408},{"className":17407},[],[17409],{"type":40,"value":16958},{"type":40,"value":17411}," conditionnel (avec ",{"type":31,"tag":169,"props":17413,"children":17415},{"className":17414},[],[17416],{"type":40,"value":17417},"where: { status: 'AVAILABLE' }",{"type":40,"value":17419},") qui fait vérifier la condition par Postgres au moment de l'écriture, pas par la mémoire Node.js au moment de la lecture. Enfin l'exception typée ",{"type":31,"tag":169,"props":17421,"children":17423},{"className":17422},[],[17424],{"type":40,"value":17425},"SlotAlreadyHeldError",{"type":40,"value":17427}," qui porte le contexte nécessaire aux couches supérieures.",{"type":31,"tag":32,"props":17429,"children":17430},{},[17431,17433,17438,17440,17444],{"type":40,"value":17432},"Quand je prompte Claude pour ce fix, je ne dis pas \"ajoute une transaction\". Je dis : ",{"type":31,"tag":97,"props":17434,"children":17435},{},[17436],{"type":40,"value":17437},"\"Ce use case sera appelé en concurrence. Deux clients peuvent réserver le même créneau à 10 ms d'écart. Réécris-le pour qu'il reste correct sous cette contrainte, en utilisant les primitives Prisma adaptées.\"",{"type":40,"value":17439}," Le résultat est radicalement meilleur. Claude n'invente pas, il applique. Le craft consiste à lui donner le bon contexte pour qu'il applique le bon pattern, comme le formalise une vraie ",{"type":31,"tag":69,"props":17441,"children":17442},{"href":1154},[17443],{"type":40,"value":1157},{"type":40,"value":76},{"type":31,"tag":48,"props":17446,"children":17447},{},[],{"type":31,"tag":52,"props":17449,"children":17451},{"id":17450},"les-4-garde-fous-à-appliquer-dès-lundi",[17452],{"type":40,"value":17453},"Les 4 garde-fous à appliquer dès lundi",{"type":31,"tag":32,"props":17455,"children":17456},{},[17457],{"type":40,"value":17458},"Voici les 4 points que je passe systématiquement en review sur chaque PR Claude qui touche à de l'état partagé (DB, cache, fichier, queue).",{"type":31,"tag":32,"props":17460,"children":17461},{},[17462,17467,17469,17474],{"type":31,"tag":36,"props":17463,"children":17464},{},[17465],{"type":40,"value":17466},"Garde-fou 1 : la concurrence est-elle nommée explicitement ?",{"type":40,"value":17468}," Pour toute fonction qui lit puis écrit, je me pose la question : ",{"type":31,"tag":97,"props":17470,"children":17471},{},[17472],{"type":40,"value":17473},"\"Ce use case peut-il être appelé par 2 requêtes simultanées ? Si oui, que se passe-t-il ?\"",{"type":40,"value":17475}," Si la réponse est \"je ne sais pas\", la PR retourne en draft.",{"type":31,"tag":32,"props":17477,"children":17478},{},[17479,17484,17486,17491],{"type":31,"tag":36,"props":17480,"children":17481},{},[17482],{"type":40,"value":17483},"Garde-fou 2 : les écritures qui dépendent de lectures passent-elles par une transaction ?",{"type":40,"value":17485}," Pas de read-modify-write hors ",{"type":31,"tag":169,"props":17487,"children":17489},{"className":17488},[],[17490],{"type":40,"value":17395},{"type":40,"value":17492},". C'est la règle. Si Claude n'a pas utilisé une transaction avec un niveau d'isolation explicite, c'est un signal qu'il a écrit le pattern naïf.",{"type":31,"tag":32,"props":17494,"children":17495},{},[17496,17501,17502,17508,17510,17516,17518,17522],{"type":31,"tag":36,"props":17497,"children":17498},{},[17499],{"type":40,"value":17500},"Garde-fou 3 : les exceptions sont-elles typées et porteuses de contexte ?",{"type":40,"value":4661},{"type":31,"tag":169,"props":17503,"children":17505},{"className":17504},[],[17506],{"type":40,"value":17507},"throw new Error('Slot not available')",{"type":40,"value":17509}," est insuffisant. ",{"type":31,"tag":169,"props":17511,"children":17513},{"className":17512},[],[17514],{"type":40,"value":17515},"throw new SlotAlreadyHeldError(slotId)",{"type":40,"value":17517}," permet à la couche supérieure de prendre des décisions différenciées, et à l'observabilité de remonter les bons signaux. Ce détail est ce qui sépare le code junior du code senior, et c'est aussi ce qui permet de construire les ",{"type":31,"tag":69,"props":17519,"children":17520},{"href":9751},[17521],{"type":40,"value":15309},{"type":40,"value":17523}," en aval.",{"type":31,"tag":32,"props":17525,"children":17526},{},[17527,17532,17534,17539,17541,17546,17548,17553],{"type":31,"tag":36,"props":17528,"children":17529},{},[17530],{"type":40,"value":17531},"Garde-fou 4 : un test de concurrence existe-t-il ?",{"type":40,"value":17533}," Pour toute méthode critique, j'exige un test Vitest qui soumet le use case en parallèle via ",{"type":31,"tag":169,"props":17535,"children":17537},{"className":17536},[],[17538],{"type":40,"value":5111},{"type":40,"value":17540}," sur le même créneau, et qui vérifie que l'état final est cohérent (un seul ",{"type":31,"tag":169,"props":17542,"children":17544},{"className":17543},[],[17545],{"type":40,"value":16028},{"type":40,"value":17547},", une seule erreur ",{"type":31,"tag":169,"props":17549,"children":17551},{"className":17550},[],[17552],{"type":40,"value":17425},{"type":40,"value":17554},"). Si ce test n'existe pas, Claude ne l'a pas écrit. C'est à la review de l'exiger.",{"type":31,"tag":1206,"props":17556,"children":17558},{"className":2912,"code":17557,"language":2914,"meta":8,"style":8},"\u002F\u002F Exemple de test de concurrence Vitest\nit('ne doit accorder le créneau qu'à un seul client en concurrence', async () => {\n  const slotId = 'slot-test-id';\n  const results = await Promise.allSettled([\n    holdSlotUseCase.execute(slotId, 'client-A'),\n    holdSlotUseCase.execute(slotId, 'client-B'),\n  ]);\n\n  const fulfilled = results.filter((r) => r.status === 'fulfilled');\n  const rejected = results.filter((r) => r.status === 'rejected');\n\n  expect(fulfilled).toHaveLength(1);\n  expect(rejected).toHaveLength(1);\n  expect((rejected[0] as PromiseRejectedResult).reason).toBeInstanceOf(SlotAlreadyHeldError);\n});\n",[17559],{"type":31,"tag":169,"props":17560,"children":17561},{"__ignoreMap":8},[17562,17570,17601,17622,17655,17693,17729,17737,17744,17813,17881,17888,17921,17953,18010],{"type":31,"tag":1215,"props":17563,"children":17564},{"class":1217,"line":1218},[17565],{"type":31,"tag":1215,"props":17566,"children":17567},{"style":2924},[17568],{"type":40,"value":17569},"\u002F\u002F Exemple de test de concurrence Vitest\n",{"type":31,"tag":1215,"props":17571,"children":17572},{"class":1217,"line":817},[17573,17577,17581,17586,17591,17596],{"type":31,"tag":1215,"props":17574,"children":17575},{"style":2939},[17576],{"type":40,"value":5829},{"type":31,"tag":1215,"props":17578,"children":17579},{"style":1250},[17580],{"type":40,"value":3483},{"type":31,"tag":1215,"props":17582,"children":17583},{"style":4392},[17584],{"type":40,"value":17585},"'ne doit accorder le créneau qu'",{"type":31,"tag":1215,"props":17587,"children":17588},{"style":1250},[17589],{"type":40,"value":17590},"à un seul client en concurrence",{"type":31,"tag":1215,"props":17592,"children":17593},{"style":4392},[17594],{"type":40,"value":17595},"', async () => ",{"type":31,"tag":1215,"props":17597,"children":17599},{"style":17598},"--shiki-default:#A6D189;--shiki-default-font-style:inherit;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic",[17600],{"type":40,"value":4257},{"type":31,"tag":1215,"props":17602,"children":17603},{"class":1217,"line":1236},[17604,17609,17613,17618],{"type":31,"tag":1215,"props":17605,"children":17606},{"style":1250},[17607],{"type":40,"value":17608},"  const slotId ",{"type":31,"tag":1215,"props":17610,"children":17611},{"style":2960},[17612],{"type":40,"value":6748},{"type":31,"tag":1215,"props":17614,"children":17615},{"style":4392},[17616],{"type":40,"value":17617}," 'slot-test-id'",{"type":31,"tag":1215,"props":17619,"children":17620},{"style":1250},[17621],{"type":40,"value":3055},{"type":31,"tag":1215,"props":17623,"children":17624},{"class":1217,"line":1246},[17625,17630,17634,17638,17642,17646,17650],{"type":31,"tag":1215,"props":17626,"children":17627},{"style":1250},[17628],{"type":40,"value":17629},"  const results ",{"type":31,"tag":1215,"props":17631,"children":17632},{"style":2960},[17633],{"type":40,"value":6748},{"type":31,"tag":1215,"props":17635,"children":17636},{"style":2933},[17637],{"type":40,"value":4187},{"type":31,"tag":1215,"props":17639,"children":17640},{"style":6254},[17641],{"type":40,"value":4145},{"type":31,"tag":1215,"props":17643,"children":17644},{"style":3113},[17645],{"type":40,"value":76},{"type":31,"tag":1215,"props":17647,"children":17648},{"style":2939},[17649],{"type":40,"value":6265},{"type":31,"tag":1215,"props":17651,"children":17652},{"style":1250},[17653],{"type":40,"value":17654},"([\n",{"type":31,"tag":1215,"props":17656,"children":17657},{"class":1217,"line":1256},[17658,17663,17667,17671,17676,17680,17685,17689],{"type":31,"tag":1215,"props":17659,"children":17660},{"style":1250},[17661],{"type":40,"value":17662},"    holdSlotUseCase",{"type":31,"tag":1215,"props":17664,"children":17665},{"style":3113},[17666],{"type":40,"value":76},{"type":31,"tag":1215,"props":17668,"children":17669},{"style":2939},[17670],{"type":40,"value":13694},{"type":31,"tag":1215,"props":17672,"children":17673},{"style":1250},[17674],{"type":40,"value":17675},"(slotId",{"type":31,"tag":1215,"props":17677,"children":17678},{"style":2945},[17679],{"type":40,"value":5843},{"type":31,"tag":1215,"props":17681,"children":17682},{"style":4392},[17683],{"type":40,"value":17684}," 'client-A'",{"type":31,"tag":1215,"props":17686,"children":17687},{"style":1250},[17688],{"type":40,"value":3002},{"type":31,"tag":1215,"props":17690,"children":17691},{"style":2945},[17692],{"type":40,"value":2974},{"type":31,"tag":1215,"props":17694,"children":17695},{"class":1217,"line":1264},[17696,17700,17704,17708,17712,17716,17721,17725],{"type":31,"tag":1215,"props":17697,"children":17698},{"style":1250},[17699],{"type":40,"value":17662},{"type":31,"tag":1215,"props":17701,"children":17702},{"style":3113},[17703],{"type":40,"value":76},{"type":31,"tag":1215,"props":17705,"children":17706},{"style":2939},[17707],{"type":40,"value":13694},{"type":31,"tag":1215,"props":17709,"children":17710},{"style":1250},[17711],{"type":40,"value":17675},{"type":31,"tag":1215,"props":17713,"children":17714},{"style":2945},[17715],{"type":40,"value":5843},{"type":31,"tag":1215,"props":17717,"children":17718},{"style":4392},[17719],{"type":40,"value":17720}," 'client-B'",{"type":31,"tag":1215,"props":17722,"children":17723},{"style":1250},[17724],{"type":40,"value":3002},{"type":31,"tag":1215,"props":17726,"children":17727},{"style":2945},[17728],{"type":40,"value":2974},{"type":31,"tag":1215,"props":17730,"children":17731},{"class":1217,"line":1273},[17732],{"type":31,"tag":1215,"props":17733,"children":17734},{"style":1250},[17735],{"type":40,"value":17736},"  ]);\n",{"type":31,"tag":1215,"props":17738,"children":17739},{"class":1217,"line":1282},[17740],{"type":31,"tag":1215,"props":17741,"children":17742},{"emptyLinePlaceholder":13},[17743],{"type":40,"value":1233},{"type":31,"tag":1215,"props":17745,"children":17746},{"class":1217,"line":1291},[17747,17752,17756,17760,17764,17768,17772,17776,17780,17784,17788,17792,17796,17800,17804,17809],{"type":31,"tag":1215,"props":17748,"children":17749},{"style":1250},[17750],{"type":40,"value":17751},"  const fulfilled ",{"type":31,"tag":1215,"props":17753,"children":17754},{"style":2960},[17755],{"type":40,"value":6748},{"type":31,"tag":1215,"props":17757,"children":17758},{"style":1250},[17759],{"type":40,"value":6243},{"type":31,"tag":1215,"props":17761,"children":17762},{"style":3113},[17763],{"type":40,"value":76},{"type":31,"tag":1215,"props":17765,"children":17766},{"style":2939},[17767],{"type":40,"value":6375},{"type":31,"tag":1215,"props":17769,"children":17770},{"style":1250},[17771],{"type":40,"value":3483},{"type":31,"tag":1215,"props":17773,"children":17774},{"style":2945},[17775],{"type":40,"value":3483},{"type":31,"tag":1215,"props":17777,"children":17778},{"style":2954},[17779],{"type":40,"value":6388},{"type":31,"tag":1215,"props":17781,"children":17782},{"style":2945},[17783],{"type":40,"value":3002},{"type":31,"tag":1215,"props":17785,"children":17786},{"style":2960},[17787],{"type":40,"value":5858},{"type":31,"tag":1215,"props":17789,"children":17790},{"style":1250},[17791],{"type":40,"value":6401},{"type":31,"tag":1215,"props":17793,"children":17794},{"style":3113},[17795],{"type":40,"value":76},{"type":31,"tag":1215,"props":17797,"children":17798},{"style":1250},[17799],{"type":40,"value":6410},{"type":31,"tag":1215,"props":17801,"children":17802},{"style":2960},[17803],{"type":40,"value":6415},{"type":31,"tag":1215,"props":17805,"children":17806},{"style":4392},[17807],{"type":40,"value":17808}," 'fulfilled'",{"type":31,"tag":1215,"props":17810,"children":17811},{"style":1250},[17812],{"type":40,"value":15232},{"type":31,"tag":1215,"props":17814,"children":17815},{"class":1217,"line":1300},[17816,17821,17825,17829,17833,17837,17841,17845,17849,17853,17857,17861,17865,17869,17873,17877],{"type":31,"tag":1215,"props":17817,"children":17818},{"style":1250},[17819],{"type":40,"value":17820},"  const rejected ",{"type":31,"tag":1215,"props":17822,"children":17823},{"style":2960},[17824],{"type":40,"value":6748},{"type":31,"tag":1215,"props":17826,"children":17827},{"style":1250},[17828],{"type":40,"value":6243},{"type":31,"tag":1215,"props":17830,"children":17831},{"style":3113},[17832],{"type":40,"value":76},{"type":31,"tag":1215,"props":17834,"children":17835},{"style":2939},[17836],{"type":40,"value":6375},{"type":31,"tag":1215,"props":17838,"children":17839},{"style":1250},[17840],{"type":40,"value":3483},{"type":31,"tag":1215,"props":17842,"children":17843},{"style":2945},[17844],{"type":40,"value":3483},{"type":31,"tag":1215,"props":17846,"children":17847},{"style":2954},[17848],{"type":40,"value":6388},{"type":31,"tag":1215,"props":17850,"children":17851},{"style":2945},[17852],{"type":40,"value":3002},{"type":31,"tag":1215,"props":17854,"children":17855},{"style":2960},[17856],{"type":40,"value":5858},{"type":31,"tag":1215,"props":17858,"children":17859},{"style":1250},[17860],{"type":40,"value":6401},{"type":31,"tag":1215,"props":17862,"children":17863},{"style":3113},[17864],{"type":40,"value":76},{"type":31,"tag":1215,"props":17866,"children":17867},{"style":1250},[17868],{"type":40,"value":6410},{"type":31,"tag":1215,"props":17870,"children":17871},{"style":2960},[17872],{"type":40,"value":6415},{"type":31,"tag":1215,"props":17874,"children":17875},{"style":4392},[17876],{"type":40,"value":6420},{"type":31,"tag":1215,"props":17878,"children":17879},{"style":1250},[17880],{"type":40,"value":15232},{"type":31,"tag":1215,"props":17882,"children":17883},{"class":1217,"line":1309},[17884],{"type":31,"tag":1215,"props":17885,"children":17886},{"emptyLinePlaceholder":13},[17887],{"type":40,"value":1233},{"type":31,"tag":1215,"props":17889,"children":17890},{"class":1217,"line":1317},[17891,17895,17900,17904,17908,17912,17917],{"type":31,"tag":1215,"props":17892,"children":17893},{"style":2939},[17894],{"type":40,"value":6436},{"type":31,"tag":1215,"props":17896,"children":17897},{"style":1250},[17898],{"type":40,"value":17899},"(fulfilled)",{"type":31,"tag":1215,"props":17901,"children":17902},{"style":3113},[17903],{"type":40,"value":76},{"type":31,"tag":1215,"props":17905,"children":17906},{"style":2939},[17907],{"type":40,"value":6450},{"type":31,"tag":1215,"props":17909,"children":17910},{"style":1250},[17911],{"type":40,"value":3483},{"type":31,"tag":1215,"props":17913,"children":17914},{"style":3047},[17915],{"type":40,"value":17916},"1",{"type":31,"tag":1215,"props":17918,"children":17919},{"style":1250},[17920],{"type":40,"value":15232},{"type":31,"tag":1215,"props":17922,"children":17923},{"class":1217,"line":1326},[17924,17928,17933,17937,17941,17945,17949],{"type":31,"tag":1215,"props":17925,"children":17926},{"style":2939},[17927],{"type":40,"value":6436},{"type":31,"tag":1215,"props":17929,"children":17930},{"style":1250},[17931],{"type":40,"value":17932},"(rejected)",{"type":31,"tag":1215,"props":17934,"children":17935},{"style":3113},[17936],{"type":40,"value":76},{"type":31,"tag":1215,"props":17938,"children":17939},{"style":2939},[17940],{"type":40,"value":6450},{"type":31,"tag":1215,"props":17942,"children":17943},{"style":1250},[17944],{"type":40,"value":3483},{"type":31,"tag":1215,"props":17946,"children":17947},{"style":3047},[17948],{"type":40,"value":17916},{"type":31,"tag":1215,"props":17950,"children":17951},{"style":1250},[17952],{"type":40,"value":15232},{"type":31,"tag":1215,"props":17954,"children":17955},{"class":1217,"line":1341},[17956,17960,17965,17969,17974,17978,17983,17987,17991,17996,18000,18005],{"type":31,"tag":1215,"props":17957,"children":17958},{"style":2939},[17959],{"type":40,"value":6436},{"type":31,"tag":1215,"props":17961,"children":17962},{"style":1250},[17963],{"type":40,"value":17964},"((rejected[",{"type":31,"tag":1215,"props":17966,"children":17967},{"style":3047},[17968],{"type":40,"value":6459},{"type":31,"tag":1215,"props":17970,"children":17971},{"style":1250},[17972],{"type":40,"value":17973},"] ",{"type":31,"tag":1215,"props":17975,"children":17976},{"style":2933},[17977],{"type":40,"value":6904},{"type":31,"tag":1215,"props":17979,"children":17980},{"style":3463},[17981],{"type":40,"value":17982}," PromiseRejectedResult",{"type":31,"tag":1215,"props":17984,"children":17985},{"style":1250},[17986],{"type":40,"value":3002},{"type":31,"tag":1215,"props":17988,"children":17989},{"style":3113},[17990],{"type":40,"value":76},{"type":31,"tag":1215,"props":17992,"children":17993},{"style":1250},[17994],{"type":40,"value":17995},"reason)",{"type":31,"tag":1215,"props":17997,"children":17998},{"style":3113},[17999],{"type":40,"value":76},{"type":31,"tag":1215,"props":18001,"children":18002},{"style":2939},[18003],{"type":40,"value":18004},"toBeInstanceOf",{"type":31,"tag":1215,"props":18006,"children":18007},{"style":1250},[18008],{"type":40,"value":18009},"(SlotAlreadyHeldError);\n",{"type":31,"tag":1215,"props":18011,"children":18012},{"class":1217,"line":1355},[18013,18018],{"type":31,"tag":1215,"props":18014,"children":18015},{"style":1250},[18016],{"type":40,"value":18017},"})",{"type":31,"tag":1215,"props":18019,"children":18020},{"style":2945},[18021],{"type":40,"value":3055},{"type":31,"tag":32,"props":18023,"children":18024},{},[18025],{"type":40,"value":18026},"Ces 4 garde-fous tiennent en 2 minutes par PR. Ils attrapent 90% des bugs de concurrence que j'ai vus passer en prod sur des codebases IA-heavy. Le coût d'application est bas. Le coût d'omission peut, lui, se chiffrer en heures de débogage sur un incident un vendredi soir.",{"type":31,"tag":48,"props":18028,"children":18029},{},[],{"type":31,"tag":52,"props":18031,"children":18032},{"id":1565},[18033],{"type":40,"value":1568},{"type":31,"tag":32,"props":18035,"children":18036},{},[18037],{"type":40,"value":18038},"Sur les 3 dernières missions où j'ai déployé cette grille de review, voici les chiffres avant\u002Faprès mesurés sur 4 mois.",{"type":31,"tag":398,"props":18040,"children":18041},{},[18042,18061],{"type":31,"tag":402,"props":18043,"children":18044},{},[18045],{"type":31,"tag":406,"props":18046,"children":18047},{},[18048,18052,18056],{"type":31,"tag":410,"props":18049,"children":18050},{},[18051],{"type":40,"value":2217},{"type":31,"tag":410,"props":18053,"children":18054},{},[18055],{"type":40,"value":15620},{"type":31,"tag":410,"props":18057,"children":18058},{},[18059],{"type":40,"value":18060},"Après 4 mois",{"type":31,"tag":426,"props":18062,"children":18063},{},[18064,18082,18100,18118],{"type":31,"tag":406,"props":18065,"children":18066},{},[18067,18072,18077],{"type":31,"tag":433,"props":18068,"children":18069},{},[18070],{"type":40,"value":18071},"Bugs de concurrence trouvés en review",{"type":31,"tag":433,"props":18073,"children":18074},{},[18075],{"type":40,"value":18076},"2 par mois en moyenne",{"type":31,"tag":433,"props":18078,"children":18079},{},[18080],{"type":40,"value":18081},"11 par mois en moyenne",{"type":31,"tag":406,"props":18083,"children":18084},{},[18085,18090,18095],{"type":31,"tag":433,"props":18086,"children":18087},{},[18088],{"type":40,"value":18089},"Bugs de concurrence remontés depuis la prod",{"type":31,"tag":433,"props":18091,"children":18092},{},[18093],{"type":40,"value":18094},"5 par trimestre",{"type":31,"tag":433,"props":18096,"children":18097},{},[18098],{"type":40,"value":18099},"1 par trimestre",{"type":31,"tag":406,"props":18101,"children":18102},{},[18103,18108,18113],{"type":31,"tag":433,"props":18104,"children":18105},{},[18106],{"type":40,"value":18107},"Temps moyen de review d'une PR Claude",{"type":31,"tag":433,"props":18109,"children":18110},{},[18111],{"type":40,"value":18112},"8 minutes",{"type":31,"tag":433,"props":18114,"children":18115},{},[18116],{"type":40,"value":18117},"11 minutes",{"type":31,"tag":406,"props":18119,"children":18120},{},[18121,18126,18130],{"type":31,"tag":433,"props":18122,"children":18123},{},[18124],{"type":40,"value":18125},"Confiance équipe dans le code IA-généré (sondage 1-10)",{"type":31,"tag":433,"props":18127,"children":18128},{},[18129],{"type":40,"value":2320},{"type":31,"tag":433,"props":18131,"children":18132},{},[18133],{"type":40,"value":18134},"8,1",{"type":31,"tag":32,"props":18136,"children":18137},{},[18138,18140,18145],{"type":40,"value":18139},"Le coût est de 3 minutes supplémentaires par PR. Le gain est de 4 bugs de prod évités par trimestre. Sur une équipe de 5 développeurs avec 60 PR par mois, c'est environ 3 heures par semaine investies pour éviter 8 à 12 incidents par an. Le rapport est nettement favorable. C'est aussi ce qu'une ",{"type":31,"tag":69,"props":18141,"children":18142},{"href":148},[18143],{"type":40,"value":18144},"revue de code structurée et appliquée à l'ère IA",{"type":40,"value":18146}," permet d'enclencher : pas un contrôle qualité figé, mais un filet adaptatif qui suit là où l'IA produit ses angles morts.",{"type":31,"tag":48,"props":18148,"children":18149},{},[],{"type":31,"tag":229,"props":18151,"children":18153},{"cta":673,"href":674,"title":18152,"type":676},"Ces 4 garde-fous ne sont qu'un début : il en existe 100",[18154],{"type":31,"tag":32,"props":18155,"children":18156},{},[18157],{"type":40,"value":18158},"La grille de review concurrence décrite ici fait partie d'un référentiel plus large : le Craft Bundle, les 100 pratiques craft que j'applique pour écrire du code propre et robuste, celles que l'IA ne vous apprendra jamais parce qu'elle ne les a jamais vues tourner en prod. De quoi transformer vos réflexes, une pratique à la fois.",{"type":31,"tag":48,"props":18160,"children":18161},{},[],{"type":31,"tag":52,"props":18163,"children":18164},{"id":637},[18165],{"type":40,"value":640},{"type":31,"tag":32,"props":18167,"children":18168},{},[18169],{"type":40,"value":18170},"Ce que je veux que vous reteniez de cet article, c'est que Claude n'écrit pas de bugs au sens où on l'entendait avant. Il écrit du code qui passe les tests qu'on lui demande de passer, dans les conditions qu'on lui décrit. Quand ces conditions ne couvrent pas la concurrence, la latence réseau, les partial failures, le code est silencieusement faux. Le bug est là, mais il dort.",{"type":31,"tag":32,"props":18172,"children":18173},{},[18174],{"type":40,"value":18175},"Votre rôle de senior, en 2026, n'est pas de relire les accolades. C'est de poser à chaque PR Claude les 4 questions que l'IA ne se pose pas elle-même. Si vous tenez cette discipline, vous transformez Claude d'un junior risqué en un copilote fiable. Si vous la lâchez, vous accumulez de la dette qui se révèlera un vendredi soir, quand il ne faudra surtout pas qu'elle se révèle.",{"type":31,"tag":32,"props":18177,"children":18178},{},[18179],{"type":40,"value":18180},"Si en lisant ces lignes vous reconnaissez votre situation, vous avez deux choix. Vous pouvez attendre votre prochain incident pour réagir. Ou vous pouvez commencer lundi matin, par une grille de review en 4 points, et apprendre à voir ce que Claude vous fait coder sans que vous le sachiez.",{"type":31,"tag":32,"props":18182,"children":18183},{},[18184,18186,18191],{"type":40,"value":18185},"Pour la suite des patterns craft + IA en format court, retrouvez-moi sur ",{"type":31,"tag":69,"props":18187,"children":18189},{"href":663,"rel":18188},[665],[18190],{"type":40,"value":668},{"type":40,"value":18192},", où je publie chaque semaine les pièges que je vois revenir en mission.",{"type":31,"tag":48,"props":18194,"children":18195},{},[],{"type":31,"tag":52,"props":18197,"children":18199},{"id":18198},"faq-sur-les-bugs-claude-et-la-review-ia-générée",[18200],{"type":40,"value":18201},"FAQ sur les bugs Claude et la review IA-générée",{"type":31,"tag":693,"props":18203,"children":18204},{},[18205,18210],{"type":31,"tag":697,"props":18206,"children":18207},{},[18208],{"type":40,"value":18209},"1. Comment justifier auprès de mon équipe le temps supplémentaire de review ?",{"type":31,"tag":32,"props":18211,"children":18212},{},[18213,18215,18219],{"type":40,"value":18214},"Le calcul est simple. 3 minutes de review supplémentaires par PR, sur 60 PR par mois, c'est 3 heures par mois. Un seul incident de race condition en prod sur une fonctionnalité critique peut coûter entre 5 000 et 50 000 euros en remédiation, perte de confiance client et heures de war room. Le ROI est calculable et défendable devant n'importe quel CTO, et c'est exactement l'angle que je détaille dans ",{"type":31,"tag":69,"props":18216,"children":18217},{"href":4622},[18218],{"type":40,"value":4625},{"type":40,"value":76},{"type":31,"tag":693,"props":18221,"children":18222},{},[18223,18228],{"type":31,"tag":697,"props":18224,"children":18225},{},[18226],{"type":40,"value":18227},"2. Faut-il refuser systématiquement les PR Claude qui touchent à l'état partagé ?",{"type":31,"tag":32,"props":18229,"children":18230},{},[18231],{"type":40,"value":18232},"Non, ce serait contre-productif et casserait la vélocité. Ce qu'il faut, c'est instrumenter la review avec les 4 garde-fous décrits ici, et exiger explicitement que Claude soit re-prompté avec le contexte de concurrence quand il manque. Le test : si vous ne savez pas répondre à \"que se passe-t-il si 2 appels simultanés\", la PR retourne en draft.",{"type":31,"tag":693,"props":18234,"children":18235},{},[18236,18241],{"type":31,"tag":697,"props":18237,"children":18238},{},[18239],{"type":40,"value":18240},"3. Les tests automatisés ne devraient-ils pas attraper ces bugs ?",{"type":31,"tag":32,"props":18242,"children":18243},{},[18244,18246,18251,18253,18257],{"type":40,"value":18245},"En théorie oui, en pratique non. Les tests unitaires mockent la DB, donc ils ne voient pas la concurrence. Les tests d'intégration tournent en séquentiel par défaut, donc ils ne déclenchent pas la race. Il faut écrire des tests de concurrence explicites (via ",{"type":31,"tag":169,"props":18247,"children":18249},{"className":18248},[],[18250],{"type":40,"value":5111},{"type":40,"value":18252}," sur la même ressource en Vitest), et ces tests, Claude ne les écrit jamais spontanément. C'est aussi ce que pointe ",{"type":31,"tag":69,"props":18254,"children":18255},{"href":219},[18256],{"type":40,"value":5070},{"type":40,"value":76},{"type":31,"tag":693,"props":18259,"children":18260},{},[18261,18266],{"type":31,"tag":697,"props":18262,"children":18263},{},[18264],{"type":40,"value":18265},"4. Quels autres patterns dangereux faut-il surveiller dans le code Claude ?",{"type":31,"tag":32,"props":18267,"children":18268},{},[18269],{"type":40,"value":18270},"J'en ai documenté 5 récurrents : la God Function, le couplage métier\u002FORM, les tests qui ne testent rien, le catch-all silencieux et les dépendances non auditées. Chacun mérite un traitement spécifique en review. Le sujet est aussi lié aux vulnérabilités de sécurité dans le code LLM-généré pour les patterns les plus sensibles.",{"type":31,"tag":693,"props":18272,"children":18273},{},[18274,18279],{"type":31,"tag":697,"props":18275,"children":18276},{},[18277],{"type":40,"value":18278},"5. Comment former mon équipe à voir ces bugs sans alourdir la charge cognitive ?",{"type":31,"tag":32,"props":18280,"children":18281},{},[18282],{"type":40,"value":18283},"La meilleure méthode que j'ai vue fonctionner, c'est de transformer la grille en 4 garde-fous en checklist GitHub PR template, visible à chaque review. Les juniors la suivent par discipline, les seniors la suivent par réflexe, et au bout de 3 mois tout le monde l'a intégrée sans s'en rendre compte. Le rituel s'inscrit dans une culture engineering de rituels craft qui transforme les bonnes intentions en habitudes durables.",{"type":31,"tag":693,"props":18285,"children":18286},{},[18287,18292],{"type":31,"tag":697,"props":18288,"children":18289},{},[18290],{"type":40,"value":18291},"6. Est-ce que ce problème va disparaître avec les prochaines versions de Claude ?",{"type":31,"tag":32,"props":18293,"children":18294},{},[18295],{"type":40,"value":18296},"Non, et c'est important de le comprendre. Le problème n'est pas dans la qualité du modèle, il est dans l'absence de contexte d'exécution au moment de la génération. Tant que Claude génère du code sans observer la prod réelle (charge, concurrence, latence), il continuera à produire des patterns qui passent les tests et plantent sous contrainte. L'amélioration viendra des outils d'exécution autour de l'IA, pas du modèle lui-même.",{"type":31,"tag":48,"props":18298,"children":18299},{},[],{"type":31,"tag":229,"props":18301,"children":18302},{"cta":806,"href":807,"title":808,"type":809},[18303],{"type":31,"tag":32,"props":18304,"children":18305},{},[18306],{"type":40,"value":18307},"L'EMA est l'outil que je propose au début de chaque mission. Il mesure la maturité de votre équipe sur plusieurs axes engineering, dont la code review, la gouvernance IA et la résilience prod. Quelques minutes pour identifier où votre filet de review craque le plus, et où concentrer vos efforts en priorité.",{"type":31,"tag":1837,"props":18309,"children":18310},{},[18311],{"type":40,"value":1841},{"title":8,"searchDepth":817,"depth":817,"links":18313},[18314,18315,18316,18317,18318,18319,18320],{"id":15988,"depth":817,"text":15991},{"id":16081,"depth":817,"text":16084},{"id":16684,"depth":817,"text":16687},{"id":17450,"depth":817,"text":17453},{"id":1565,"depth":817,"text":1568},{"id":637,"depth":817,"text":640},{"id":18198,"depth":817,"text":18201},"content:fr:intelligence-artificielle:bug-claude-vendredi-dernier.md","fr\u002Fintelligence-artificielle\u002Fbug-claude-vendredi-dernier.md","fr\u002Fintelligence-artificielle\u002Fbug-claude-vendredi-dernier",{"_path":364,"_dir":2770,"_draft":7,"_partial":7,"_locale":8,"title":18325,"description":18326,"id":12084,"date":18327,"listed":13,"nocomments":7,"hidden":7,"categories":18328,"tags":18329,"cover":18333,"readingTime":18334,"body":18339,"_type":830,"_id":19682,"_source":832,"_file":19683,"_stem":19684,"_extension":835},"Dependabot : configurer son coéquipier silencieux contre la dette de dépendances","Dependabot bien configuré vous évite des nuits à patcher la prochaine CVE critique. Guide craft : 7 tips, config YAML annotée, auto-merge intelligent.","2026-05-01",[2770],[18330,18331,2770,18332,19],"dependabot","supply-chain","sécurité","covers\u002Farticles\u002Fdependabot-craft-gestion-dependances.jpg",{"text":18335,"minutes":18336,"time":18337,"words":18338},"11 min read",10.825,649500,2165,{"type":28,"children":18340,"toc":19668},[18341,18349,18369,18372,18378,18383,18397,18402,18422,18433,18436,18442,18447,18532,18537,18540,18549,18552,18558,18563,18576,18589,19090,19139,19174,19180,19192,19232,19244,19250,19262,19267,19270,19276,19281,19297,19307,19339,19342,19346,19351,19445,19450,19455,19458,19462,19467,19472,19484,19493,19496,19502,19515,19528,19576,19627,19640,19653,19656,19664],{"type":31,"tag":32,"props":18342,"children":18343},{},[18344],{"type":31,"tag":36,"props":18345,"children":18346},{},[18347],{"type":40,"value":18348},"En décembre 2021, Log4Shell (CVE-2021-44228, score CVSS 10 sur 10) a tenu l'industrie entière debout tout un week-end. Une faille critique dans une librairie que presque personne n'avait choisie explicitement, et qui se cachait pourtant dans des dizaines de milliers de projets. Ce soir-là, d'innombrables équipes ont découvert la même chose : elles ne savaient pas ce qu'il y avait vraiment dans leur arbre de dépendances. C'est exactement ce genre de nuit que Dependabot bien configuré vous évite.",{"type":31,"tag":32,"props":18350,"children":18351},{},[18352,18354,18359,18361,18367],{"type":40,"value":18353},"Depuis, je n'ouvre plus un projet sans ce réflexe. Sur crmcoaching, le SaaS que je développe seul avec Claude, l'un des premiers fichiers que je versionne après le ",{"type":31,"tag":169,"props":18355,"children":18357},{"className":18356},[],[18358],{"type":40,"value":15559},{"type":40,"value":18360},", c'est ",{"type":31,"tag":169,"props":18362,"children":18364},{"className":18363},[],[18365],{"type":40,"value":18366},"dependabot.yml",{"type":40,"value":18368}," : versionné, audité, pensé. Pas l'activation par défaut que la majorité des projets laissent en l'état, mais une vraie configuration craft. Voici ce que j'applique au quotidien sur crmcoaching, ce que je vois rater en mission, et la méthode complète.",{"type":31,"tag":48,"props":18370,"children":18371},{},[],{"type":31,"tag":52,"props":18373,"children":18375},{"id":18374},"pourquoi-vos-dépendances-pourrissent-plus-vite-en-2026",[18376],{"type":40,"value":18377},"Pourquoi vos dépendances pourrissent plus vite en 2026",{"type":31,"tag":32,"props":18379,"children":18380},{},[18381],{"type":40,"value":18382},"Le constat est chiffré, et il est sévère.",{"type":31,"tag":32,"props":18384,"children":18385},{},[18386,18388,18395],{"type":40,"value":18387},"Selon le ",{"type":31,"tag":69,"props":18389,"children":18392},{"href":18390,"rel":18391},"https:\u002F\u002Fsnyk.io\u002Freports\u002Fopen-source-security\u002F",[665],[18393],{"type":40,"value":18394},"Snyk Open Source Security Report 2024",{"type":40,"value":18396},", les vulnérabilités de supply chain ont progressé de 156% entre 2023 et 2024. Sur la même période, GitClear documente que les équipes utilisant un assistant IA comme Claude ajoutent en moyenne 40% de dépendances en plus par projet. Le ratio est clair : on consomme plus, on audite moins.",{"type":31,"tag":32,"props":18398,"children":18399},{},[18400],{"type":40,"value":18401},"Je l'ai vu sur crmcoaching même. La séquence est toujours la même : je prompte Claude pour résoudre un problème, il me propose une lib externe (souvent une qu'il connaît depuis son training data, donc pas la version courante), je l'accepte parce qu'elle marche, et la dépendance entre dans le repo. Six mois plus tard, cette même lib a une CVE critique. Sans filet de sécurité, personne ne le sait, surtout quand on développe seul.",{"type":31,"tag":78,"props":18403,"children":18404},{},[18405],{"type":31,"tag":32,"props":18406,"children":18407},{},[18408,18412,18414,18420],{"type":31,"tag":36,"props":18409,"children":18410},{},[18411],{"type":40,"value":2847},{"type":40,"value":18413}," : un ",{"type":31,"tag":169,"props":18415,"children":18417},{"className":18416},[],[18418],{"type":40,"value":18419},"pnpm list --depth=Infinity",{"type":40,"value":18421}," sur crmcoaching listait plus de 1 000 dépendances transitives, pour une application que je connais pourtant ligne par ligne. La quasi-totalité n'avait jamais été ajoutée volontairement : elles sont arrivées dans les bagages de quelques dépendances directes. Et pourtant, chacune d'elles peut, demain matin, ouvrir une faille sur le paiement ou l'authentification de mes utilisateurs.",{"type":31,"tag":32,"props":18423,"children":18424},{},[18425,18427,18431],{"type":40,"value":18426},"Bruce Schneier a écrit que ",{"type":31,"tag":97,"props":18428,"children":18429},{},[18430],{"type":40,"value":16059},{"type":40,"value":18432},". Dependabot n'est pas un produit que vous activez et que vous oubliez : c'est le squelette d'un processus craft de gestion de dépendances. Sans le squelette, vous codez en aveugle.",{"type":31,"tag":48,"props":18434,"children":18435},{},[],{"type":31,"tag":52,"props":18437,"children":18439},{"id":18438},"les-4-signaux-que-votre-gestion-de-dépendances-dérive",[18440],{"type":40,"value":18441},"Les 4 signaux que votre gestion de dépendances dérive",{"type":31,"tag":32,"props":18443,"children":18444},{},[18445],{"type":40,"value":18446},"Demain matin, ouvrez votre repo principal et vérifiez ces 4 signaux. Si vous en cochez 2 ou plus, vous avez un problème.",{"type":31,"tag":2378,"props":18448,"children":18449},{},[18450,18475,18485,18500],{"type":31,"tag":2382,"props":18451,"children":18452},{},[18453,18473],{"type":31,"tag":36,"props":18454,"children":18455},{},[18456,18458,18463,18465,18471],{"type":40,"value":18457},"Signal 1 : votre dernier audit de dépendances (",{"type":31,"tag":169,"props":18459,"children":18461},{"className":18460},[],[18462],{"type":40,"value":15398},{"type":40,"value":18464}," côté Node, ",{"type":31,"tag":169,"props":18466,"children":18468},{"className":18467},[],[18469],{"type":40,"value":18470},"mvn dependency-check:check",{"type":40,"value":18472}," côté Java, ou équivalent) date de plus de 30 jours.",{"type":40,"value":18474}," Concrètement, vous ne savez pas combien de vulnérabilités critiques dorment dans votre arbre de dépendances. Mauvais signe.",{"type":31,"tag":2382,"props":18476,"children":18477},{},[18478,18483],{"type":31,"tag":36,"props":18479,"children":18480},{},[18481],{"type":40,"value":18482},"Signal 2 : vos PR Dependabot s'accumulent en mode \"à traiter plus tard\".",{"type":40,"value":18484}," Si vous voyez plus de 10 PR ouvertes depuis plus de 14 jours, votre filet de sécurité est devenu un cimetière.",{"type":31,"tag":2382,"props":18486,"children":18487},{},[18488,18498],{"type":31,"tag":36,"props":18489,"children":18490},{},[18491,18493],{"type":40,"value":18492},"Signal 3 : vous ne savez pas quelle est votre ",{"type":31,"tag":169,"props":18494,"children":18496},{"className":18495},[],[18497],{"type":40,"value":15378},{"type":40,"value":18499}," sur les CVE critiques. C'est le délai entre disclosure d'une CVE et son fix en prod. En dessous de 7 jours, vous êtes sain. Au-dessus de 30 jours, vous jouez à la roulette russe.",{"type":31,"tag":2382,"props":18501,"children":18502},{},[18503,18523,18525,18530],{"type":31,"tag":36,"props":18504,"children":18505},{},[18506,18508,18514,18516,18521],{"type":40,"value":18507},"Signal 4 : vous avez des entrées ",{"type":31,"tag":169,"props":18509,"children":18511},{"className":18510},[],[18512],{"type":40,"value":18513},"ignore",{"type":40,"value":18515}," dans votre ",{"type":31,"tag":169,"props":18517,"children":18519},{"className":18518},[],[18520],{"type":40,"value":18366},{"type":40,"value":18522}," qui datent de plus de 6 mois.",{"type":40,"value":18524}," Un ",{"type":31,"tag":169,"props":18526,"children":18528},{"className":18527},[],[18529],{"type":40,"value":18513},{"type":40,"value":18531}," est une dette que vous reportez. Si vous ne savez plus pourquoi vous l'avez mis, vous avez déjà perdu.",{"type":31,"tag":32,"props":18533,"children":18534},{},[18535],{"type":40,"value":18536},"Ces signaux ne sont pas théoriques. Je les ai vus, dans cet ordre, dans la majorité des équipes que j'ai accompagnées sur 2024 et 2025. Le 8ème signal arrive toujours : c'est l'incident.",{"type":31,"tag":48,"props":18538,"children":18539},{},[],{"type":31,"tag":229,"props":18541,"children":18543},{"cta":231,"href":232,"title":18542,"type":234},"Vous voulez acquérir le réflexe d'auditer chaque dépendance avant qu'elle entre dans votre repo ?",[18544],{"type":31,"tag":32,"props":18545,"children":18546},{},[18547],{"type":40,"value":18548},"Savoir lire un arbre de dépendances, flairer la lib que Claude propose en retard de 18 mois, juger quand un bump major mérite un ADR : ça ne se lit pas, ça se travaille. En mentoring 1:1, on relit votre vraie config Dependabot ensemble, je vous montre les arbitrages que je fais sur mes propres projets, et vous repartez avec le jugement qui transforme une alerte en décision sereine.",{"type":31,"tag":48,"props":18550,"children":18551},{},[],{"type":31,"tag":52,"props":18553,"children":18555},{"id":18554},"comment-je-configure-dependabot-étape-par-étape",[18556],{"type":40,"value":18557},"Comment je configure Dependabot, étape par étape",{"type":31,"tag":32,"props":18559,"children":18560},{},[18561],{"type":40,"value":18562},"Voici la méthode que j'applique sur crmcoaching, et que je redéploie ensuite quand j'arrive en mission dans une équipe qui n'a pas (ou peu) de gestion de dépendances. Pas une checklist générique, mais ce que je fais vraiment, dans l'ordre.",{"type":31,"tag":5774,"props":18564,"children":18566},{"id":18565},"étape-1-le-dependabotyml-craft",[18567,18569,18574],{"type":40,"value":18568},"Étape 1 : le ",{"type":31,"tag":169,"props":18570,"children":18572},{"className":18571},[],[18573],{"type":40,"value":18366},{"type":40,"value":18575}," craft",{"type":31,"tag":32,"props":18577,"children":18578},{},[18579,18581,18587],{"type":40,"value":18580},"Je crée (ou je refais) le fichier ",{"type":31,"tag":169,"props":18582,"children":18584},{"className":18583},[],[18585],{"type":40,"value":18586},".github\u002Fdependabot.yml",{"type":40,"value":18588},". Voici la version courte de ma configuration de référence, celle que j'utilise sur crmcoaching (TypeScript, géré avec pnpm) et que je décline ensuite en mission sur des codebases Java\u002FMaven :",{"type":31,"tag":1206,"props":18590,"children":18594},{"className":18591,"code":18592,"language":18593,"meta":8,"style":8},"language-yaml shiki shiki-themes catppuccin-frappe github-dark","version: 2\nupdates:\n  - package-ecosystem: \"npm\" # gère aussi pnpm et yarn\n    directory: \"\u002F\"\n    schedule:\n      interval: \"weekly\"\n      day: \"monday\"\n      time: \"09:00\"\n    open-pull-requests-limit: 5\n    groups:\n      minor-and-patch:\n        patterns: [\"*\"]\n        update-types: [\"minor\", \"patch\"]\n    labels: [\"dependencies\", \"auto-review\"]\n    reviewers: [\"kamanga\"]\n    ignore:\n      - dependency-name: \"next\"\n        update-types: [\"version-update:semver-major\"]\n\n  - package-ecosystem: \"docker\"\n    directory: \"\u002F\"\n    schedule:\n      interval: \"weekly\"\n\n  - package-ecosystem: \"github-actions\"\n    directory: \"\u002F\"\n    schedule:\n      interval: \"weekly\"\n","yaml",[18595],{"type":31,"tag":169,"props":18596,"children":18597},{"__ignoreMap":8},[18598,18616,18629,18656,18673,18685,18702,18719,18736,18753,18765,18777,18803,18837,18871,18896,18908,18930,18954,18961,18981,18996,19007,19022,19029,19049,19064,19075],{"type":31,"tag":1215,"props":18599,"children":18600},{"class":1217,"line":1218},[18601,18607,18611],{"type":31,"tag":1215,"props":18602,"children":18604},{"style":18603},"--shiki-default:#8CAAEE;--shiki-dark:#85E89D",[18605],{"type":40,"value":18606},"version",{"type":31,"tag":1215,"props":18608,"children":18609},{"style":3113},[18610],{"type":40,"value":2963},{"type":31,"tag":1215,"props":18612,"children":18613},{"style":3047},[18614],{"type":40,"value":18615}," 2\n",{"type":31,"tag":1215,"props":18617,"children":18618},{"class":1217,"line":817},[18619,18624],{"type":31,"tag":1215,"props":18620,"children":18621},{"style":18603},[18622],{"type":40,"value":18623},"updates",{"type":31,"tag":1215,"props":18625,"children":18626},{"style":3113},[18627],{"type":40,"value":18628},":\n",{"type":31,"tag":1215,"props":18630,"children":18631},{"class":1217,"line":1236},[18632,18637,18642,18646,18651],{"type":31,"tag":1215,"props":18633,"children":18634},{"style":2945},[18635],{"type":40,"value":18636},"  -",{"type":31,"tag":1215,"props":18638,"children":18639},{"style":18603},[18640],{"type":40,"value":18641}," package-ecosystem",{"type":31,"tag":1215,"props":18643,"children":18644},{"style":3113},[18645],{"type":40,"value":2963},{"type":31,"tag":1215,"props":18647,"children":18648},{"style":4392},[18649],{"type":40,"value":18650}," \"npm\"",{"type":31,"tag":1215,"props":18652,"children":18653},{"style":2924},[18654],{"type":40,"value":18655}," # gère aussi pnpm et yarn\n",{"type":31,"tag":1215,"props":18657,"children":18658},{"class":1217,"line":1246},[18659,18664,18668],{"type":31,"tag":1215,"props":18660,"children":18661},{"style":18603},[18662],{"type":40,"value":18663},"    directory",{"type":31,"tag":1215,"props":18665,"children":18666},{"style":3113},[18667],{"type":40,"value":2963},{"type":31,"tag":1215,"props":18669,"children":18670},{"style":4392},[18671],{"type":40,"value":18672}," \"\u002F\"\n",{"type":31,"tag":1215,"props":18674,"children":18675},{"class":1217,"line":1256},[18676,18681],{"type":31,"tag":1215,"props":18677,"children":18678},{"style":18603},[18679],{"type":40,"value":18680},"    schedule",{"type":31,"tag":1215,"props":18682,"children":18683},{"style":3113},[18684],{"type":40,"value":18628},{"type":31,"tag":1215,"props":18686,"children":18687},{"class":1217,"line":1264},[18688,18693,18697],{"type":31,"tag":1215,"props":18689,"children":18690},{"style":18603},[18691],{"type":40,"value":18692},"      interval",{"type":31,"tag":1215,"props":18694,"children":18695},{"style":3113},[18696],{"type":40,"value":2963},{"type":31,"tag":1215,"props":18698,"children":18699},{"style":4392},[18700],{"type":40,"value":18701}," \"weekly\"\n",{"type":31,"tag":1215,"props":18703,"children":18704},{"class":1217,"line":1273},[18705,18710,18714],{"type":31,"tag":1215,"props":18706,"children":18707},{"style":18603},[18708],{"type":40,"value":18709},"      day",{"type":31,"tag":1215,"props":18711,"children":18712},{"style":3113},[18713],{"type":40,"value":2963},{"type":31,"tag":1215,"props":18715,"children":18716},{"style":4392},[18717],{"type":40,"value":18718}," \"monday\"\n",{"type":31,"tag":1215,"props":18720,"children":18721},{"class":1217,"line":1282},[18722,18727,18731],{"type":31,"tag":1215,"props":18723,"children":18724},{"style":18603},[18725],{"type":40,"value":18726},"      time",{"type":31,"tag":1215,"props":18728,"children":18729},{"style":3113},[18730],{"type":40,"value":2963},{"type":31,"tag":1215,"props":18732,"children":18733},{"style":4392},[18734],{"type":40,"value":18735}," \"09:00\"\n",{"type":31,"tag":1215,"props":18737,"children":18738},{"class":1217,"line":1291},[18739,18744,18748],{"type":31,"tag":1215,"props":18740,"children":18741},{"style":18603},[18742],{"type":40,"value":18743},"    open-pull-requests-limit",{"type":31,"tag":1215,"props":18745,"children":18746},{"style":3113},[18747],{"type":40,"value":2963},{"type":31,"tag":1215,"props":18749,"children":18750},{"style":3047},[18751],{"type":40,"value":18752}," 5\n",{"type":31,"tag":1215,"props":18754,"children":18755},{"class":1217,"line":1300},[18756,18761],{"type":31,"tag":1215,"props":18757,"children":18758},{"style":18603},[18759],{"type":40,"value":18760},"    groups",{"type":31,"tag":1215,"props":18762,"children":18763},{"style":3113},[18764],{"type":40,"value":18628},{"type":31,"tag":1215,"props":18766,"children":18767},{"class":1217,"line":1309},[18768,18773],{"type":31,"tag":1215,"props":18769,"children":18770},{"style":18603},[18771],{"type":40,"value":18772},"      minor-and-patch",{"type":31,"tag":1215,"props":18774,"children":18775},{"style":3113},[18776],{"type":40,"value":18628},{"type":31,"tag":1215,"props":18778,"children":18779},{"class":1217,"line":1317},[18780,18785,18789,18793,18798],{"type":31,"tag":1215,"props":18781,"children":18782},{"style":18603},[18783],{"type":40,"value":18784},"        patterns",{"type":31,"tag":1215,"props":18786,"children":18787},{"style":3113},[18788],{"type":40,"value":2963},{"type":31,"tag":1215,"props":18790,"children":18791},{"style":2945},[18792],{"type":40,"value":5529},{"type":31,"tag":1215,"props":18794,"children":18795},{"style":4392},[18796],{"type":40,"value":18797},"\"*\"",{"type":31,"tag":1215,"props":18799,"children":18800},{"style":2945},[18801],{"type":40,"value":18802},"]\n",{"type":31,"tag":1215,"props":18804,"children":18805},{"class":1217,"line":1326},[18806,18811,18815,18819,18824,18828,18833],{"type":31,"tag":1215,"props":18807,"children":18808},{"style":18603},[18809],{"type":40,"value":18810},"        update-types",{"type":31,"tag":1215,"props":18812,"children":18813},{"style":3113},[18814],{"type":40,"value":2963},{"type":31,"tag":1215,"props":18816,"children":18817},{"style":2945},[18818],{"type":40,"value":5529},{"type":31,"tag":1215,"props":18820,"children":18821},{"style":4392},[18822],{"type":40,"value":18823},"\"minor\"",{"type":31,"tag":1215,"props":18825,"children":18826},{"style":2945},[18827],{"type":40,"value":5843},{"type":31,"tag":1215,"props":18829,"children":18830},{"style":4392},[18831],{"type":40,"value":18832}," \"patch\"",{"type":31,"tag":1215,"props":18834,"children":18835},{"style":2945},[18836],{"type":40,"value":18802},{"type":31,"tag":1215,"props":18838,"children":18839},{"class":1217,"line":1341},[18840,18845,18849,18853,18858,18862,18867],{"type":31,"tag":1215,"props":18841,"children":18842},{"style":18603},[18843],{"type":40,"value":18844},"    labels",{"type":31,"tag":1215,"props":18846,"children":18847},{"style":3113},[18848],{"type":40,"value":2963},{"type":31,"tag":1215,"props":18850,"children":18851},{"style":2945},[18852],{"type":40,"value":5529},{"type":31,"tag":1215,"props":18854,"children":18855},{"style":4392},[18856],{"type":40,"value":18857},"\"dependencies\"",{"type":31,"tag":1215,"props":18859,"children":18860},{"style":2945},[18861],{"type":40,"value":5843},{"type":31,"tag":1215,"props":18863,"children":18864},{"style":4392},[18865],{"type":40,"value":18866}," \"auto-review\"",{"type":31,"tag":1215,"props":18868,"children":18869},{"style":2945},[18870],{"type":40,"value":18802},{"type":31,"tag":1215,"props":18872,"children":18873},{"class":1217,"line":1355},[18874,18879,18883,18887,18892],{"type":31,"tag":1215,"props":18875,"children":18876},{"style":18603},[18877],{"type":40,"value":18878},"    reviewers",{"type":31,"tag":1215,"props":18880,"children":18881},{"style":3113},[18882],{"type":40,"value":2963},{"type":31,"tag":1215,"props":18884,"children":18885},{"style":2945},[18886],{"type":40,"value":5529},{"type":31,"tag":1215,"props":18888,"children":18889},{"style":4392},[18890],{"type":40,"value":18891},"\"kamanga\"",{"type":31,"tag":1215,"props":18893,"children":18894},{"style":2945},[18895],{"type":40,"value":18802},{"type":31,"tag":1215,"props":18897,"children":18898},{"class":1217,"line":1364},[18899,18904],{"type":31,"tag":1215,"props":18900,"children":18901},{"style":18603},[18902],{"type":40,"value":18903},"    ignore",{"type":31,"tag":1215,"props":18905,"children":18906},{"style":3113},[18907],{"type":40,"value":18628},{"type":31,"tag":1215,"props":18909,"children":18910},{"class":1217,"line":1378},[18911,18916,18921,18925],{"type":31,"tag":1215,"props":18912,"children":18913},{"style":2945},[18914],{"type":40,"value":18915},"      -",{"type":31,"tag":1215,"props":18917,"children":18918},{"style":18603},[18919],{"type":40,"value":18920}," dependency-name",{"type":31,"tag":1215,"props":18922,"children":18923},{"style":3113},[18924],{"type":40,"value":2963},{"type":31,"tag":1215,"props":18926,"children":18927},{"style":4392},[18928],{"type":40,"value":18929}," \"next\"\n",{"type":31,"tag":1215,"props":18931,"children":18932},{"class":1217,"line":1387},[18933,18937,18941,18945,18950],{"type":31,"tag":1215,"props":18934,"children":18935},{"style":18603},[18936],{"type":40,"value":18810},{"type":31,"tag":1215,"props":18938,"children":18939},{"style":3113},[18940],{"type":40,"value":2963},{"type":31,"tag":1215,"props":18942,"children":18943},{"style":2945},[18944],{"type":40,"value":5529},{"type":31,"tag":1215,"props":18946,"children":18947},{"style":4392},[18948],{"type":40,"value":18949},"\"version-update:semver-major\"",{"type":31,"tag":1215,"props":18951,"children":18952},{"style":2945},[18953],{"type":40,"value":18802},{"type":31,"tag":1215,"props":18955,"children":18956},{"class":1217,"line":1401},[18957],{"type":31,"tag":1215,"props":18958,"children":18959},{"emptyLinePlaceholder":13},[18960],{"type":40,"value":1233},{"type":31,"tag":1215,"props":18962,"children":18963},{"class":1217,"line":1410},[18964,18968,18972,18976],{"type":31,"tag":1215,"props":18965,"children":18966},{"style":2945},[18967],{"type":40,"value":18636},{"type":31,"tag":1215,"props":18969,"children":18970},{"style":18603},[18971],{"type":40,"value":18641},{"type":31,"tag":1215,"props":18973,"children":18974},{"style":3113},[18975],{"type":40,"value":2963},{"type":31,"tag":1215,"props":18977,"children":18978},{"style":4392},[18979],{"type":40,"value":18980}," \"docker\"\n",{"type":31,"tag":1215,"props":18982,"children":18983},{"class":1217,"line":1419},[18984,18988,18992],{"type":31,"tag":1215,"props":18985,"children":18986},{"style":18603},[18987],{"type":40,"value":18663},{"type":31,"tag":1215,"props":18989,"children":18990},{"style":3113},[18991],{"type":40,"value":2963},{"type":31,"tag":1215,"props":18993,"children":18994},{"style":4392},[18995],{"type":40,"value":18672},{"type":31,"tag":1215,"props":18997,"children":18998},{"class":1217,"line":1427},[18999,19003],{"type":31,"tag":1215,"props":19000,"children":19001},{"style":18603},[19002],{"type":40,"value":18680},{"type":31,"tag":1215,"props":19004,"children":19005},{"style":3113},[19006],{"type":40,"value":18628},{"type":31,"tag":1215,"props":19008,"children":19009},{"class":1217,"line":1436},[19010,19014,19018],{"type":31,"tag":1215,"props":19011,"children":19012},{"style":18603},[19013],{"type":40,"value":18692},{"type":31,"tag":1215,"props":19015,"children":19016},{"style":3113},[19017],{"type":40,"value":2963},{"type":31,"tag":1215,"props":19019,"children":19020},{"style":4392},[19021],{"type":40,"value":18701},{"type":31,"tag":1215,"props":19023,"children":19024},{"class":1217,"line":1450},[19025],{"type":31,"tag":1215,"props":19026,"children":19027},{"emptyLinePlaceholder":13},[19028],{"type":40,"value":1233},{"type":31,"tag":1215,"props":19030,"children":19031},{"class":1217,"line":1463},[19032,19036,19040,19044],{"type":31,"tag":1215,"props":19033,"children":19034},{"style":2945},[19035],{"type":40,"value":18636},{"type":31,"tag":1215,"props":19037,"children":19038},{"style":18603},[19039],{"type":40,"value":18641},{"type":31,"tag":1215,"props":19041,"children":19042},{"style":3113},[19043],{"type":40,"value":2963},{"type":31,"tag":1215,"props":19045,"children":19046},{"style":4392},[19047],{"type":40,"value":19048}," \"github-actions\"\n",{"type":31,"tag":1215,"props":19050,"children":19051},{"class":1217,"line":1476},[19052,19056,19060],{"type":31,"tag":1215,"props":19053,"children":19054},{"style":18603},[19055],{"type":40,"value":18663},{"type":31,"tag":1215,"props":19057,"children":19058},{"style":3113},[19059],{"type":40,"value":2963},{"type":31,"tag":1215,"props":19061,"children":19062},{"style":4392},[19063],{"type":40,"value":18672},{"type":31,"tag":1215,"props":19065,"children":19066},{"class":1217,"line":1490},[19067,19071],{"type":31,"tag":1215,"props":19068,"children":19069},{"style":18603},[19070],{"type":40,"value":18680},{"type":31,"tag":1215,"props":19072,"children":19073},{"style":3113},[19074],{"type":40,"value":18628},{"type":31,"tag":1215,"props":19076,"children":19077},{"class":1217,"line":8904},[19078,19082,19086],{"type":31,"tag":1215,"props":19079,"children":19080},{"style":18603},[19081],{"type":40,"value":18692},{"type":31,"tag":1215,"props":19083,"children":19084},{"style":3113},[19085],{"type":40,"value":2963},{"type":31,"tag":1215,"props":19087,"children":19088},{"style":4392},[19089],{"type":40,"value":18701},{"type":31,"tag":32,"props":19091,"children":19092},{},[19093,19095,19101,19103,19109,19111,19116,19118,19123,19125,19130,19132,19137],{"type":40,"value":19094},"Trois choses comptent dans cette configuration. D'abord le ",{"type":31,"tag":169,"props":19096,"children":19098},{"className":19097},[],[19099],{"type":40,"value":19100},"groups",{"type":40,"value":19102}," qui rassemble minor et patch en une seule PR : c'est ce qui fait passer un repo de 30 PR par semaine à 3. Ensuite le ",{"type":31,"tag":169,"props":19104,"children":19106},{"className":19105},[],[19107],{"type":40,"value":19108},"open-pull-requests-limit: 5",{"type":40,"value":19110}," qui force le réalisme, parce que personne ne traite 15 PR Dependabot en parallèle. Enfin le ",{"type":31,"tag":169,"props":19112,"children":19114},{"className":19113},[],[19115],{"type":40,"value":18513},{"type":40,"value":19117}," ciblé sur les majors risquées (ici les montées majeures de Next.js, que je veux préparer avec un ADR et une fenêtre de migration avant d'accepter), avec une règle stricte : tout ",{"type":31,"tag":169,"props":19119,"children":19121},{"className":19120},[],[19122],{"type":40,"value":18513},{"type":40,"value":19124}," doit être documenté dans un commentaire qui dit ",{"type":31,"tag":97,"props":19126,"children":19127},{},[19128],{"type":40,"value":19129},"pourquoi",{"type":40,"value":19131}," il existe et ",{"type":31,"tag":97,"props":19133,"children":19134},{},[19135],{"type":40,"value":19136},"jusqu'à quand",{"type":40,"value":19138}," il reste valide.",{"type":31,"tag":32,"props":19140,"children":19141},{},[19142,19144,19150,19152,19158,19160,19165,19167,19172],{"type":40,"value":19143},"En mission sur une codebase Java\u002FMaven, le quotidien du secteur bancaire, je remplace simplement ",{"type":31,"tag":169,"props":19145,"children":19147},{"className":19146},[],[19148],{"type":40,"value":19149},"npm",{"type":40,"value":19151}," par ",{"type":31,"tag":169,"props":19153,"children":19155},{"className":19154},[],[19156],{"type":40,"value":19157},"maven",{"type":40,"value":19159}," et l'",{"type":31,"tag":169,"props":19161,"children":19163},{"className":19162},[],[19164],{"type":40,"value":18513},{"type":40,"value":19166}," Next.js par un ",{"type":31,"tag":169,"props":19168,"children":19170},{"className":19169},[],[19171],{"type":40,"value":18513},{"type":40,"value":19173}," sur un bump de Spring Boot que je veux préparer avec un ADR. Le reste de la structure (groups, limit, schedule, labels) ne change pas. C'est ce qui fait qu'une fois la discipline acquise sur un repo, vous la portez en 10 minutes sur le suivant, quel que soit l'écosystème.",{"type":31,"tag":5774,"props":19175,"children":19177},{"id":19176},"étape-2-le-pipeline-dauto-merge-intelligent",[19178],{"type":40,"value":19179},"Étape 2 : le pipeline d'auto-merge intelligent",{"type":31,"tag":32,"props":19181,"children":19182},{},[19183,19185,19191],{"type":40,"value":19184},"Dependabot seul, c'est 50% du travail. Sans auto-merge, vous gardez la charge mentale de chaque PR. Voici ce que j'ajoute systématiquement, sous forme de GitHub Action ",{"type":31,"tag":169,"props":19186,"children":19188},{"className":19187},[],[19189],{"type":40,"value":19190},".github\u002Fworkflows\u002Fdependabot-auto-merge.yml",{"type":40,"value":3689},{"type":31,"tag":2378,"props":19193,"children":19194},{},[19195,19205,19215],{"type":31,"tag":2382,"props":19196,"children":19197},{},[19198,19203],{"type":31,"tag":36,"props":19199,"children":19200},{},[19201],{"type":40,"value":19202},"Patch",{"type":40,"value":19204}," : auto-merge si la CI est verte. Pas de discussion. Une mise à jour de patch ne casse rien, sauf si la CI dit le contraire.",{"type":31,"tag":2382,"props":19206,"children":19207},{},[19208,19213],{"type":31,"tag":36,"props":19209,"children":19210},{},[19211],{"type":40,"value":19212},"Minor",{"type":40,"value":19214}," : review humaine obligatoire, mais rapide. 5 minutes pour lire le changelog et valider.",{"type":31,"tag":2382,"props":19216,"children":19217},{},[19218,19223,19225,19230],{"type":31,"tag":36,"props":19219,"children":19220},{},[19221],{"type":40,"value":19222},"Major",{"type":40,"value":19224}," : ADR obligatoire. Pas de bump de major sans une ",{"type":31,"tag":69,"props":19226,"children":19227},{"href":329},[19228],{"type":40,"value":19229},"décision documentée par ADR",{"type":40,"value":19231}," qui dit pourquoi, quel impact, et quel plan de rollback.",{"type":31,"tag":32,"props":19233,"children":19234},{},[19235,19237,19242],{"type":40,"value":19236},"Cette discipline tient parce qu'elle est asymétrique : on automatise le bas risque, on humanise le haut risque. C'est exactement ce que Martin Fowler décrit dans son article ",{"type":31,"tag":97,"props":19238,"children":19239},{},[19240],{"type":40,"value":19241},"ContinuousIntegration",{"type":40,"value":19243}," : automatiser ce qui doit l'être pour libérer du temps pour ce qui ne peut pas l'être.",{"type":31,"tag":5774,"props":19245,"children":19247},{"id":19246},"étape-3-la-métrique-qui-compte-vraiment",[19248],{"type":40,"value":19249},"Étape 3 : la métrique qui compte vraiment",{"type":31,"tag":32,"props":19251,"children":19252},{},[19253,19255,19260],{"type":40,"value":19254},"J'instaure une seule métrique de suivi : la ",{"type":31,"tag":169,"props":19256,"children":19258},{"className":19257},[],[19259],{"type":40,"value":15378},{"type":40,"value":19261}," sur les CVE critiques. Pas le nombre de PR mergées, pas le pourcentage de dépendances à jour. Juste : combien de temps entre la disclosure d'une CVE critique et son fix en prod.",{"type":31,"tag":32,"props":19263,"children":19264},{},[19265],{"type":40,"value":19266},"Sur crmcoaching, je suis à 3 jours en moyenne entre la disclosure d'une CVE critique et son fix en prod. Au démarrage, sans discipline, j'étais à plusieurs semaines. En mission, je vois régulièrement des équipes partir de 40 jours et plus. La baisse n'est jamais linéaire, elle vient des trois leviers ci-dessus combinés.",{"type":31,"tag":48,"props":19268,"children":19269},{},[],{"type":31,"tag":52,"props":19271,"children":19273},{"id":19272},"les-3-erreurs-à-éviter",[19274],{"type":40,"value":19275},"Les 3 erreurs à éviter",{"type":31,"tag":32,"props":19277,"children":19278},{},[19279],{"type":40,"value":19280},"Toutes les équipes que j'ai accompagnées sur ce sujet sont passées par au moins une de ces trois erreurs. Évitez-vous le détour.",{"type":31,"tag":32,"props":19282,"children":19283},{},[19284,19295],{"type":31,"tag":36,"props":19285,"children":19286},{},[19287,19289,19294],{"type":40,"value":19288},"Erreur 1 : laisser Dependabot ouvrir 30 PR par semaine sans ",{"type":31,"tag":169,"props":19290,"children":19292},{"className":19291},[],[19293],{"type":40,"value":19100},{"type":40,"value":76},{"type":40,"value":19296}," Le bruit tue le signal. Les développeurs commencent à fermer les PR sans lire. C'est pire que pas de Dependabot du tout, parce que vous avez l'illusion d'être protégé.",{"type":31,"tag":32,"props":19298,"children":19299},{},[19300,19305],{"type":31,"tag":36,"props":19301,"children":19302},{},[19303],{"type":40,"value":19304},"Erreur 2 : auto-merger les minor et les major.",{"type":40,"value":19306}," C'est tentant, ça soulage la charge mentale, mais une seule mise à jour cassante en prod détruit toute la confiance dans le pipeline. Auto-merge sur le patch uniquement, sauf si vous avez une couverture de tests acceptance-level qui couvre vos vrais comportements métier.",{"type":31,"tag":32,"props":19308,"children":19309},{},[19310,19315,19317,19322,19324,19330,19332,19337],{"type":31,"tag":36,"props":19311,"children":19312},{},[19313],{"type":40,"value":19314},"Erreur 3 : oublier les dépendances transitives.",{"type":40,"value":19316}," Dependabot scanne ce qui est dans votre ",{"type":31,"tag":169,"props":19318,"children":19320},{"className":19319},[],[19321],{"type":40,"value":15559},{"type":40,"value":19323}," ou votre ",{"type":31,"tag":169,"props":19325,"children":19327},{"className":19326},[],[19328],{"type":40,"value":19329},"pom.xml",{"type":40,"value":19331},". Mais les vulnérabilités se planquent souvent 3 ou 4 niveaux en dessous, dans des libs que vous n'avez jamais nommément ajoutées. C'est là que ",{"type":31,"tag":69,"props":19333,"children":19334},{"href":1508},[19335],{"type":40,"value":19336},"les alertes de sécurité dédiées au code IA-généré",{"type":40,"value":19338}," complètent l'arsenal, pas en concurrent mais en complément.",{"type":31,"tag":48,"props":19340,"children":19341},{},[],{"type":31,"tag":52,"props":19343,"children":19344},{"id":1565},[19345],{"type":40,"value":1568},{"type":31,"tag":32,"props":19347,"children":19348},{},[19349],{"type":40,"value":19350},"Sur les 4 dernières missions où j'ai déployé cette discipline, voici les chiffres avant\u002Faprès, mesurés sur 6 mois.",{"type":31,"tag":398,"props":19352,"children":19353},{},[19354,19372],{"type":31,"tag":402,"props":19355,"children":19356},{},[19357],{"type":31,"tag":406,"props":19358,"children":19359},{},[19360,19364,19368],{"type":31,"tag":410,"props":19361,"children":19362},{},[19363],{"type":40,"value":2217},{"type":31,"tag":410,"props":19365,"children":19366},{},[19367],{"type":40,"value":9861},{"type":31,"tag":410,"props":19369,"children":19370},{},[19371],{"type":40,"value":4728},{"type":31,"tag":426,"props":19373,"children":19374},{},[19375,19392,19410,19427],{"type":31,"tag":406,"props":19376,"children":19377},{},[19378,19382,19387],{"type":31,"tag":433,"props":19379,"children":19380},{},[19381],{"type":40,"value":15688},{"type":31,"tag":433,"props":19383,"children":19384},{},[19385],{"type":40,"value":19386},"32 jours en moyenne",{"type":31,"tag":433,"props":19388,"children":19389},{},[19390],{"type":40,"value":19391},"6 jours en moyenne",{"type":31,"tag":406,"props":19393,"children":19394},{},[19395,19400,19405],{"type":31,"tag":433,"props":19396,"children":19397},{},[19398],{"type":40,"value":19399},"Nombre de PR Dependabot ouvertes plus de 14 jours",{"type":31,"tag":433,"props":19401,"children":19402},{},[19403],{"type":40,"value":19404},"18 en moyenne",{"type":31,"tag":433,"props":19406,"children":19407},{},[19408],{"type":40,"value":19409},"2 en moyenne",{"type":31,"tag":406,"props":19411,"children":19412},{},[19413,19418,19423],{"type":31,"tag":433,"props":19414,"children":19415},{},[19416],{"type":40,"value":19417},"Incidents prod liés à une CVE supply chain",{"type":31,"tag":433,"props":19419,"children":19420},{},[19421],{"type":40,"value":19422},"2 par an",{"type":31,"tag":433,"props":19424,"children":19425},{},[19426],{"type":40,"value":6459},{"type":31,"tag":406,"props":19428,"children":19429},{},[19430,19435,19440],{"type":31,"tag":433,"props":19431,"children":19432},{},[19433],{"type":40,"value":19434},"Heures dev consacrées à la gestion des dépendances",{"type":31,"tag":433,"props":19436,"children":19437},{},[19438],{"type":40,"value":19439},"6h par semaine",{"type":31,"tag":433,"props":19441,"children":19442},{},[19443],{"type":40,"value":19444},"1h30 par semaine",{"type":31,"tag":32,"props":19446,"children":19447},{},[19448],{"type":40,"value":19449},"Le gain n'est pas que technique, il est économique. Une heure de dev senior coûte entre 80 et 150 euros chargée. Économiser 4 heures et demie par semaine, c'est 18 000 à 35 000 euros par an. Pour une équipe de 5 développeurs, cette discipline financière paie son installation en 3 semaines.",{"type":31,"tag":32,"props":19451,"children":19452},{},[19453],{"type":40,"value":19454},"C'est ce que je dis aux CTOs qui hésitent : Dependabot n'est pas un sujet d'engineering, c'est un sujet d'allocation de capital.",{"type":31,"tag":48,"props":19456,"children":19457},{},[],{"type":31,"tag":52,"props":19459,"children":19460},{"id":637},[19461],{"type":40,"value":640},{"type":31,"tag":32,"props":19463,"children":19464},{},[19465],{"type":40,"value":19466},"Ce que je veux que vous reteniez de cet article, c'est que la gestion de dépendances n'est pas une tâche d'hygiène que vous traitez quand vous aurez le temps. C'est un processus craft, au même titre que la code review ou les tests. Et comme tout processus craft, il a besoin d'un squelette automatisé pour tenir dans la durée.",{"type":31,"tag":32,"props":19468,"children":19469},{},[19470],{"type":40,"value":19471},"L'IA accélère la production de code. Elle accélère aussi la production de dette de dépendances. Dependabot est un des rares outils où l'automatisation ne remplace pas le jugement : elle libère du temps pour l'exercer là où il compte vraiment, sur les majors, les CVE critiques, les choix architecturaux. C'est exactement ce que je cherche dans une bonne discipline craft.",{"type":31,"tag":32,"props":19473,"children":19474},{},[19475,19477,19482],{"type":40,"value":19476},"Si en lisant ces lignes vous avez reconnu votre situation, vous avez deux choix. Vous pouvez attendre votre prochain Log4Shell. Ou vous pouvez commencer lundi matin, par un seul fichier ",{"type":31,"tag":169,"props":19478,"children":19480},{"className":19479},[],[19481],{"type":40,"value":18366},{"type":40,"value":19483}," versionné, et bâtir la suite.",{"type":31,"tag":229,"props":19485,"children":19487},{"cta":673,"href":674,"title":19486,"type":676},"La gestion de dépendances n'est qu'une des 100 pratiques qui font un code de senior",[19488],{"type":31,"tag":32,"props":19489,"children":19490},{},[19491],{"type":40,"value":19492},"Configurer Dependabot en craft, c'est une seule pratique parmi celles qui séparent un dev qui subit sa dette d'un dev qui la pilote. Le Craft Bundle réunit les 100 pratiques que j'applique pour coder propre, de la gestion de dépendances aux ADR en passant par la review : exactement celles que l'IA ne vous apprendra jamais, parce qu'elle ne les a jamais vues tenir en prod.",{"type":31,"tag":48,"props":19494,"children":19495},{},[],{"type":31,"tag":52,"props":19497,"children":19499},{"id":19498},"faq-sur-dependabot-et-la-gestion-de-dépendances",[19500],{"type":40,"value":19501},"FAQ sur Dependabot et la gestion de dépendances",{"type":31,"tag":693,"props":19503,"children":19504},{},[19505,19510],{"type":31,"tag":697,"props":19506,"children":19507},{},[19508],{"type":40,"value":19509},"1. Dependabot ou Renovate : lequel choisir ?",{"type":31,"tag":32,"props":19511,"children":19512},{},[19513],{"type":40,"value":19514},"Dependabot est gratuit, intégré nativement à GitHub, et largement suffisant pour la majorité des équipes. Renovate est plus puissant (règles plus fines, plus d'écosystèmes, monorepo natif) mais demande plus de temps de configuration. Mon conseil : commencez par Dependabot. Si au bout de 6 mois vous trouvez ses limites (vous le saurez), passez à Renovate. Mais ne commencez pas par Renovate, vous perdrez 2 semaines avant la première PR utile.",{"type":31,"tag":693,"props":19516,"children":19517},{},[19518,19523],{"type":31,"tag":697,"props":19519,"children":19520},{},[19521],{"type":40,"value":19522},"2. Faut-il auto-merger les patch updates ?",{"type":31,"tag":32,"props":19524,"children":19525},{},[19526],{"type":40,"value":19527},"Oui, à une condition : votre CI doit être verte, complète, et fiable. Si votre couverture de tests est faible (moins de 60% sur les chemins critiques), n'auto-mergez rien. Renforcez d'abord les tests, puis activez l'auto-merge sur patch uniquement. Jamais sur minor ou major sans review humaine.",{"type":31,"tag":693,"props":19529,"children":19530},{},[19531,19536],{"type":31,"tag":697,"props":19532,"children":19533},{},[19534],{"type":40,"value":19535},"3. Comment gérer les dépendances qu'on ne peut pas mettre à jour (lock sur une vieille version) ?",{"type":31,"tag":32,"props":19537,"children":19538},{},[19539,19541,19546,19548,19553,19555,19560,19562,19567,19569,19575],{"type":40,"value":19540},"Utilisez le bloc ",{"type":31,"tag":169,"props":19542,"children":19544},{"className":19543},[],[19545],{"type":40,"value":18513},{"type":40,"value":19547}," du ",{"type":31,"tag":169,"props":19549,"children":19551},{"className":19550},[],[19552],{"type":40,"value":18366},{"type":40,"value":19554},", mais avec une règle stricte : tout ",{"type":31,"tag":169,"props":19556,"children":19558},{"className":19557},[],[19559],{"type":40,"value":18513},{"type":40,"value":19561}," doit être documenté (commentaire YAML expliquant pourquoi et jusqu'à quand) et révisé tous les 3 mois. Un ",{"type":31,"tag":169,"props":19563,"children":19565},{"className":19564},[],[19566],{"type":40,"value":18513},{"type":40,"value":19568}," non documenté devient une dette technique cachée. Vous remplacez un problème de sécurité par un problème d'oubli, ce qui est exactement le mécanisme décrit dans cet article sur ",{"type":31,"tag":69,"props":19570,"children":19572},{"href":19571},"\u002Ffr\u002Fdette-technique\u002Flegacy-code-evaluer-risque",[19573],{"type":40,"value":19574},"l'évaluation du risque legacy",{"type":40,"value":76},{"type":31,"tag":693,"props":19577,"children":19578},{},[19579,19584],{"type":31,"tag":697,"props":19580,"children":19581},{},[19582],{"type":40,"value":19583},"4. Dependabot crée trop de PR, comment réduire le bruit ?",{"type":31,"tag":32,"props":19585,"children":19586},{},[19587,19589,19594,19596,19601,19603,19609,19611,19617,19619,19625],{"type":40,"value":19588},"Trois leviers, dans cet ordre. Premièrement, regroupez les minor et patch via la clé ",{"type":31,"tag":169,"props":19590,"children":19592},{"className":19591},[],[19593],{"type":40,"value":19100},{"type":40,"value":19595},". C'est ce qui divise le bruit par 10. Deuxièmement, limitez avec ",{"type":31,"tag":169,"props":19597,"children":19599},{"className":19598},[],[19600],{"type":40,"value":19108},{"type":40,"value":19602},". Troisièmement, espacez le ",{"type":31,"tag":169,"props":19604,"children":19606},{"className":19605},[],[19607],{"type":40,"value":19608},"schedule",{"type":40,"value":19610}," à ",{"type":31,"tag":169,"props":19612,"children":19614},{"className":19613},[],[19615],{"type":40,"value":19616},"weekly",{"type":40,"value":19618}," plutôt que ",{"type":31,"tag":169,"props":19620,"children":19622},{"className":19621},[],[19623],{"type":40,"value":19624},"daily",{"type":40,"value":19626},". Avec ces trois réglages, vous passez de 30 PR par semaine à 3 ou 4.",{"type":31,"tag":693,"props":19628,"children":19629},{},[19630,19635],{"type":31,"tag":697,"props":19631,"children":19632},{},[19633],{"type":40,"value":19634},"5. Et le code IA-généré qui pull des dépendances obsolètes ?",{"type":31,"tag":32,"props":19636,"children":19637},{},[19638],{"type":40,"value":19639},"C'est exactement le scénario contre lequel Dependabot protège. Claude pulle des libs qu'il connaît depuis son training data, souvent en retard de 12 à 18 mois sur la version courante. Dependabot rattrape ces écarts à la PR suivante. La discipline craft à ajouter : auditer chaque dépendance que Claude propose avant de l'accepter dans le code. C'est un réflexe à 30 secondes qui évite des heures de remédiation.",{"type":31,"tag":693,"props":19641,"children":19642},{},[19643,19648],{"type":31,"tag":697,"props":19644,"children":19645},{},[19646],{"type":40,"value":19647},"6. Quel est le ROI réel d'une bonne configuration Dependabot ?",{"type":31,"tag":32,"props":19649,"children":19650},{},[19651],{"type":40,"value":19652},"Sur les 4 missions où j'ai mesuré, le ROI se situe entre 18 000 et 35 000 euros par an pour une équipe de 5 développeurs. Le calcul est simple : 4 à 5 heures de dev économisées par semaine, à un coût chargé de 80 à 150 euros l'heure. Sans compter les incidents évités, dont le coût unitaire peut dépasser 50 000 euros pour une CVE critique en prod.",{"type":31,"tag":48,"props":19654,"children":19655},{},[],{"type":31,"tag":229,"props":19657,"children":19658},{"cta":806,"href":807,"title":808,"type":809},[19659],{"type":31,"tag":32,"props":19660,"children":19661},{},[19662],{"type":40,"value":19663},"L'EMA est l'outil que je propose au début de chaque mission. Il mesure la maturité de votre équipe sur plusieurs axes engineering, dont la dette technique, le delivery, la gouvernance IA et l'architecture. La gestion de dépendances en est un sous-axe. Quelques minutes pour avoir une vision claire de votre passif engineering, et savoir où concentrer vos efforts en priorité.",{"type":31,"tag":1837,"props":19665,"children":19666},{},[19667],{"type":40,"value":1841},{"title":8,"searchDepth":817,"depth":817,"links":19669},[19670,19671,19672,19678,19679,19680,19681],{"id":18374,"depth":817,"text":18377},{"id":18438,"depth":817,"text":18441},{"id":18554,"depth":817,"text":18557,"children":19673},[19674,19676,19677],{"id":18565,"depth":1236,"text":19675},"Étape 1 : le dependabot.yml craft",{"id":19176,"depth":1236,"text":19179},{"id":19246,"depth":1236,"text":19249},{"id":19272,"depth":817,"text":19275},{"id":1565,"depth":817,"text":1568},{"id":637,"depth":817,"text":640},{"id":19498,"depth":817,"text":19501},"content:fr:dette-technique:dependabot-craft-gestion-dependances.md","fr\u002Fdette-technique\u002Fdependabot-craft-gestion-dependances.md","fr\u002Fdette-technique\u002Fdependabot-craft-gestion-dependances",1783466530478]