[{"data":1,"prerenderedAt":3592},["ShallowReactive",2],{"search-api":-1,"listing-tag-refactoring-page-1":3},[4,539,1373,2876],{"_path":5,"_dir":6,"_draft":7,"_partial":7,"_locale":8,"title":9,"description":10,"id":11,"date":12,"listed":13,"nocomments":7,"hidden":7,"categories":14,"tags":15,"cover":19,"readingTime":20,"body":25,"_type":533,"_id":534,"_source":535,"_file":536,"_stem":537,"_extension":538},"\u002Ffr\u002Fdette-technique\u002Ftests-integration-legacy-pieges","dette-technique",false,"","Tests d'intégration : les pièges à éviter dans un legacy","Ajouter des tests d'intégration à un legacy est souvent plus difficile qu'écrire from scratch. Les 4 pièges classiques et la séquence qui fonctionne.",21,"2026-02-20",true,[6],[16,17,18],"tests","refactoring","qualite","covers\u002Farticles\u002Ftests-integration-legacy.jpg",{"text":21,"minutes":22,"time":23,"words":24},"8 min read",7.95,477000,1590,{"type":26,"children":27,"toc":523},"root",[28,36,45,59,63,70,75,102,107,110,116,121,131,148,151,157,162,171,187,201,204,217,220,226,231,240,265,281,286,289,295,300,309,329,334,337,343,415,420,432,435,441,456,469,482,495,508,511],{"type":29,"tag":30,"props":31,"children":32},"element","p",{},[33],{"type":34,"value":35},"text","J'accompagnais une équipe dans un opérateur télécoms : des ingénieurs sérieux, déterminés à \"enfin écrire des tests sur le legacy\". Ils avaient décidé d'attaquer directement le moteur de facturation : 18 000 lignes de code, 30 dépendances, et une logique métier partiellement documentée. Quatre semaines plus tard, ils avaient produit 3 tests unitaires instables et une équipe découragée. Les tests ne compilaient pas parce que les dépendances ne pouvaient pas être initialisées dans un contexte de test. La motivation avait plongé. On a repris à zéro, mais différemment.",{"type":29,"tag":30,"props":37,"children":38},{},[39],{"type":29,"tag":40,"props":41,"children":42},"strong",{},[43],{"type":34,"value":44},"Décider d'ajouter des tests d'intégration à un legacy est une bonne décision. Mal exécutée, c'est une décision qui peut paralyser l'équipe pendant 3 mois pour un résultat décevant. Les pièges sont prévisibles et évitables.",{"type":29,"tag":30,"props":46,"children":47},{},[48,50,57],{"type":34,"value":49},"Les tests unitaires sur le legacy ont une limite bien connue : ils testent des unités isolées mais ne testent pas le comportement d'ensemble. Pour un filet de sécurité au niveau de l'équipe, assurez-vous que votre ",{"type":29,"tag":51,"props":52,"children":54},"a",{"href":53},"\u002Ffr\u002Fdette-technique\u002Fdefinition-of-done-qualite",[55],{"type":34,"value":56},"Definition of Done",{"type":34,"value":58}," exige des tests d'intégration sur les chemins critiques.. Un système peut avoir 70% de couverture unitaire et tomber en production parce que l'intégration entre composants n'est pas testée. Mais ajouter des tests d'intégration à un legacy n'est pas une opération simple. Le legacy n'a généralement pas été conçu pour être testé.",{"type":29,"tag":60,"props":61,"children":62},"hr",{},[],{"type":29,"tag":64,"props":65,"children":67},"h2",{"id":66},"pourquoi-les-tests-unitaires-seuls-ne-suffisent-pas",[68],{"type":34,"value":69},"Pourquoi les tests unitaires seuls ne suffisent pas",{"type":29,"tag":30,"props":71,"children":72},{},[73],{"type":34,"value":74},"Un test unitaire isole une fonction et vérifie son comportement en isolation. C'est nécessaire. Mais c'est insuffisant pour détecter les bugs qui apparaissent à la jonction entre composants.",{"type":29,"tag":30,"props":76,"children":77},{},[78,83,85,92,94,100],{"type":29,"tag":40,"props":79,"children":80},{},[81],{"type":34,"value":82},"Exemple concret",{"type":34,"value":84}," : deux classes parfaitement testées en isolation, l'une qui sérialise une date en ",{"type":29,"tag":86,"props":87,"children":89},"code",{"className":88},[],[90],{"type":34,"value":91},"MM\u002FDD\u002FYYYY",{"type":34,"value":93}," et l'autre qui la désérialise en attendant ",{"type":29,"tag":86,"props":95,"children":97},{"className":96},[],[98],{"type":34,"value":99},"YYYY-MM-DD",{"type":34,"value":101},". Les tests unitaires passent. Le système plante en prod.",{"type":29,"tag":30,"props":103,"children":104},{},[105],{"type":34,"value":106},"Les tests d'intégration testent les interactions : entre services, entre couches, entre la couche application et la base de données. Ils ont un coût d'exécution plus élevé mais une valeur de détection différente et complémentaire. Sur un legacy, les bugs les plus coûteux sont presque toujours des bugs d'intégration, pas des bugs unitaires. J'ai rarement vu un incident de prod majeur causé par un bug unitaire. J'en ai vu des dizaines causés par des bugs d'intégration silencieux.",{"type":29,"tag":60,"props":108,"children":109},{},[],{"type":29,"tag":64,"props":111,"children":113},{"id":112},"piège-1-tester-sans-comprendre-le-comportement-attendu",[114],{"type":34,"value":115},"Piège 1 : Tester sans comprendre le comportement attendu",{"type":29,"tag":30,"props":117,"children":118},{},[119],{"type":34,"value":120},"La première erreur est de commencer à écrire des tests sans savoir ce que le système est censé faire. Sur un legacy réel, le comportement attendu est souvent partiellement documenté ou connu uniquement par des personnes qui ont quitté l'entreprise.",{"type":29,"tag":30,"props":122,"children":123},{},[124,129],{"type":29,"tag":40,"props":125,"children":126},{},[127],{"type":34,"value":128},"Ce qui arrive",{"type":34,"value":130}," : l'équipe écrit des tests qui vérifient le comportement actuel, y compris les bugs existants. Quand un vrai bug est corrigé, les tests cassent. L'équipe corrige les tests pour \"faire passer le build\" au lieu de corriger le comportement. Les tests deviennent un obstacle plutôt qu'un filet de sécurité.",{"type":29,"tag":30,"props":132,"children":133},{},[134,139,141,146],{"type":29,"tag":40,"props":135,"children":136},{},[137],{"type":34,"value":138},"La sortie",{"type":34,"value":140}," : avant d'écrire le premier test d'intégration, j'organise une session de 2 à 3 heures avec un expert métier pour définir les ",{"type":29,"tag":40,"props":142,"children":143},{},[144],{"type":34,"value":145},"comportements de référence",{"type":34,"value":147}," : quels sont les scénarios nominaux et les scénarios d'erreur qui doivent être garantis ? Cette session produit les spécifications des tests, pas l'inverse.",{"type":29,"tag":60,"props":149,"children":150},{},[],{"type":29,"tag":64,"props":152,"children":154},{"id":153},"piège-2-commencer-par-le-code-le-plus-complexe",[155],{"type":34,"value":156},"Piège 2 : Commencer par le code le plus complexe",{"type":29,"tag":30,"props":158,"children":159},{},[160],{"type":34,"value":161},"L'instinct technique pousse à attaquer les modules les plus risqués en premier. C'est une mauvaise stratégie, je l'ai apprise à mes dépens dans mes premiers accompagnements.",{"type":29,"tag":30,"props":163,"children":164},{},[165,169],{"type":29,"tag":40,"props":166,"children":167},{},[168],{"type":34,"value":128},{"type":34,"value":170}," : l'équipe passe 3 semaines à essayer de tester le module central du legacy, celui avec 10 000 lignes de code, 30 dépendances implicites, et une logique métier que personne ne comprend entièrement. Les tests ne compilent pas. L'équipe est bloquée et découragée.",{"type":29,"tag":30,"props":172,"children":173},{},[174,178,180,185],{"type":29,"tag":40,"props":175,"children":176},{},[177],{"type":34,"value":138},{"type":34,"value":179}," : commencer par les modules ",{"type":29,"tag":40,"props":181,"children":182},{},[183],{"type":34,"value":184},"en bordure",{"type":34,"value":186}," du système, c'est-à-dire les points d'entrée et de sortie. Un test d'intégration qui vérifie qu'une API renvoie le bon status code pour une requête valide est simple à écrire et immédiatement utile. Une fois les tests de bordure en place, progresser vers le centre.",{"type":29,"tag":188,"props":189,"children":190},"blockquote",{},[191],{"type":29,"tag":30,"props":192,"children":193},{},[194,199],{"type":29,"tag":40,"props":195,"children":196},{},[197],{"type":34,"value":198},"Retour sur l'équipe télécoms",{"type":34,"value":200}," : après l'échec initial sur le moteur de facturation, j'ai proposé de pivoter vers les tests de bordure, à savoir les API REST qui alimentent le moteur. En 2 semaines, 15 tests d'intégration fonctionnels. Ce succès rapide a relancé la motivation et donné une base solide pour progresser vers le centre progressivement.",{"type":29,"tag":60,"props":202,"children":203},{},[],{"type":29,"tag":205,"props":206,"children":211},"cta",{"cta":207,"href":208,"title":209,"type":210},"Coder comme un senior →","https:\u002F\u002Fapp.kamanga.fr\u002Fforms\u002Fmentoring","Vous voulez savoir par où attaquer un legacy sans tests, et tenir la séquence jusqu'au bout ?","call",[212],{"type":29,"tag":30,"props":213,"children":214},{},[215],{"type":34,"value":216},"Choisir le bon point d'entrée, écrire un characterization test qui tient, savoir quand pivoter quand un module résiste : ça ne s'apprend pas en lisant un article, ça se travaille sur votre vrai code. En mentoring 1:1, on prend vos modules legacy et on construit ensemble la séquence de tests qui vous redonne confiance. Vous repartez avec le réflexe, pas juste avec la théorie.",{"type":29,"tag":60,"props":218,"children":219},{},[],{"type":29,"tag":64,"props":221,"children":223},{"id":222},"piège-3-créer-une-dépendance-à-la-base-de-données-de-production",[224],{"type":34,"value":225},"Piège 3 : Créer une dépendance à la base de données de production",{"type":29,"tag":30,"props":227,"children":228},{},[229],{"type":34,"value":230},"Pour tester l'intégration, il faut des données. L'approche la plus simple est de pointer sur la base de données de production (ou une copie) dans les tests. C'est une très mauvaise idée.",{"type":29,"tag":30,"props":232,"children":233},{},[234,238],{"type":29,"tag":40,"props":235,"children":236},{},[237],{"type":34,"value":128},{"type":34,"value":239}," :",{"type":29,"tag":241,"props":242,"children":243},"ul",{},[244,250,255,260],{"type":29,"tag":245,"props":246,"children":247},"li",{},[248],{"type":34,"value":249},"Les tests sont non-déterministes : ils dépendent de l'état des données, qui change",{"type":29,"tag":245,"props":251,"children":252},{},[253],{"type":34,"value":254},"Les tests ralentissent avec le volume de données",{"type":29,"tag":245,"props":256,"children":257},{},[258],{"type":34,"value":259},"Des tests mal écrits peuvent modifier des données réelles",{"type":29,"tag":245,"props":261,"children":262},{},[263],{"type":34,"value":264},"La base de prod ne peut pas être réinitialisée entre les tests",{"type":29,"tag":30,"props":266,"children":267},{},[268,272,274,279],{"type":29,"tag":40,"props":269,"children":270},{},[271],{"type":34,"value":138},{"type":34,"value":273}," : utiliser une base de test dédiée, initialisée à chaque exécution de test avec un jeu de données connu et contrôlé. Pour les bases relationnelles, les ",{"type":29,"tag":40,"props":275,"children":276},{},[277],{"type":34,"value":278},"test containers",{"type":34,"value":280}," (Testcontainers Java, Python, Node.js) permettent de démarrer une vraie base de données dans Docker pour chaque suite de tests : propre, isolée, et détruite après.",{"type":29,"tag":30,"props":282,"children":283},{},[284],{"type":34,"value":285},"Pour les systèmes avec des données volumineuses, créer un dataset de test représentatif mais minimal (100 à 1 000 enregistrements couvrant tous les scénarios) plutôt que de copier des millions de lignes de prod.",{"type":29,"tag":60,"props":287,"children":288},{},[],{"type":29,"tag":64,"props":290,"children":292},{"id":291},"piège-4-ignorer-les-tests-de-régression-comportementale",[293],{"type":34,"value":294},"Piège 4 : Ignorer les tests de régression comportementale",{"type":29,"tag":30,"props":296,"children":297},{},[298],{"type":34,"value":299},"Le legacy a un comportement actuel. Ce comportement, même imparfait, est probablement attendu par les utilisateurs. Des tests d'intégration qui ne couvrent pas ce comportement actuel permettent des régressions silencieuses.",{"type":29,"tag":30,"props":301,"children":302},{},[303,307],{"type":29,"tag":40,"props":304,"children":305},{},[306],{"type":34,"value":128},{"type":34,"value":308}," : l'équipe écrit de nouveaux tests d'intégration basés sur la documentation (incomplète). Des comportements non-documentés mais attendus par les utilisateurs sont cassés lors d'un refactoring. L'équipe l'apprend en production.",{"type":29,"tag":30,"props":310,"children":311},{},[312,316,318,327],{"type":29,"tag":40,"props":313,"children":314},{},[315],{"type":34,"value":138},{"type":34,"value":317}," : les ",{"type":29,"tag":40,"props":319,"children":320},{},[321],{"type":29,"tag":51,"props":322,"children":324},{"href":323},"\u002Ffr\u002Fdette-technique\u002Flegacy-code-evaluer-risque",[325],{"type":34,"value":326},"characterization tests",{"type":34,"value":328}," d'abord. Avant d'écrire des tests qui vérifient le comportement attendu, écrire des tests qui documentent le comportement actuel. C'est le concept de Michael Feathers dans \"Working Effectively with Legacy Code\" : un characterization test capture ce que le système fait réellement, pas ce qu'il devrait faire.",{"type":29,"tag":30,"props":330,"children":331},{},[332],{"type":34,"value":333},"Ces tests ont une propriété précieuse : si un refactoring casse un characterization test, il faut une décision explicite pour valider que le changement de comportement est intentionnel. C'est le filet de sécurité que je rends obligatoire avant toute modification significative d'un legacy.",{"type":29,"tag":60,"props":335,"children":336},{},[],{"type":29,"tag":64,"props":338,"children":340},{"id":339},"la-séquence-qui-fonctionne",[341],{"type":34,"value":342},"La séquence qui fonctionne",{"type":29,"tag":344,"props":345,"children":346},"ol",{},[347,357,367,385,395,405],{"type":29,"tag":245,"props":348,"children":349},{},[350,355],{"type":29,"tag":40,"props":351,"children":352},{},[353],{"type":34,"value":354},"Session de définition des comportements de référence",{"type":34,"value":356}," (2-3h avec expert métier) → liste des scénarios à garantir",{"type":29,"tag":245,"props":358,"children":359},{},[360,365],{"type":29,"tag":40,"props":361,"children":362},{},[363],{"type":34,"value":364},"Audit des dépendances",{"type":34,"value":366}," (1 jour) → cartographie des intégrations externes, DB, services tiers",{"type":29,"tag":245,"props":368,"children":369},{},[370,375,377,383],{"type":29,"tag":40,"props":371,"children":372},{},[373],{"type":34,"value":374},"Infrastructure de test",{"type":34,"value":376}," (2-3 jours) → test containers, fixtures de données, ",{"type":29,"tag":51,"props":378,"children":380},{"href":379},"\u002Ffr\u002Fpratiques-agiles\u002Fcontinuous-integration-fondamentaux",[381],{"type":34,"value":382},"CI",{"type":34,"value":384}," configurée",{"type":29,"tag":245,"props":386,"children":387},{},[388,393],{"type":29,"tag":40,"props":389,"children":390},{},[391],{"type":34,"value":392},"Characterization tests sur les chemins critiques",{"type":34,"value":394}," (1-2 semaines) → documentation du comportement actuel",{"type":29,"tag":245,"props":396,"children":397},{},[398,403],{"type":29,"tag":40,"props":399,"children":400},{},[401],{"type":34,"value":402},"Tests d'intégration de bordure",{"type":34,"value":404}," (2-3 semaines) → points d'entrée et de sortie du système",{"type":29,"tag":245,"props":406,"children":407},{},[408,413],{"type":29,"tag":40,"props":409,"children":410},{},[411],{"type":34,"value":412},"Progression vers le centre",{"type":34,"value":414}," (continu) → module par module, en priorisant par criticité",{"type":29,"tag":30,"props":416,"children":417},{},[418],{"type":34,"value":419},"Cette séquence n'est pas rapide : comptez 6 à 10 semaines pour une base solide. Mais elle produit des tests qui tiennent dans le temps et qui augmentent réellement la confiance de l'équipe.",{"type":29,"tag":205,"props":421,"children":426},{"cta":422,"href":423,"title":424,"type":425},"Les 100 pratiques que l'IA n'enseigne pas →","https:\u002F\u002Fkamanga.fr\u002Freferentiel-craft","Tester un legacy proprement n'est qu'une des 100 pratiques qui font un dev senior","product",[427],{"type":29,"tag":30,"props":428,"children":429},{},[430],{"type":34,"value":431},"La séquence décrite ici (comportements de référence, characterization tests, bordure avant le centre) fait partie d'un référentiel plus large : le Craft Bundle, les 100 pratiques que j'applique pour coder propre et garder un legacy sous contrôle. Ce sont celles que l'IA ne vous apprendra jamais, parce qu'elle ne les a jamais vues éviter un incident en prod.",{"type":29,"tag":60,"props":433,"children":434},{},[],{"type":29,"tag":64,"props":436,"children":438},{"id":437},"faq-sur-les-tests-dintégration-en-legacy",[439],{"type":34,"value":440},"FAQ sur les tests d'intégration en legacy",{"type":29,"tag":442,"props":443,"children":444},"details",{},[445,451],{"type":29,"tag":446,"props":447,"children":448},"summary",{},[449],{"type":34,"value":450},"1. Quelle est la différence entre un test d'intégration et un test end-to-end ?",{"type":29,"tag":30,"props":452,"children":453},{},[454],{"type":34,"value":455},"Un test d'intégration vérifie l'interaction entre deux ou plusieurs composants internes du système (ex : service + base de données). Un test end-to-end vérifie un scénario utilisateur complet, de l'interface jusqu'à la persistance. Sur un legacy, je commence par les tests d'intégration : ils sont plus rapides, plus stables, et plus faciles à diagnostiquer que les tests E2E.",{"type":29,"tag":442,"props":457,"children":458},{},[459,464],{"type":29,"tag":446,"props":460,"children":461},{},[462],{"type":34,"value":463},"2. Combien de temps faut-il pour avoir une suite de tests d'intégration utile sur un legacy ?",{"type":29,"tag":30,"props":465,"children":466},{},[467],{"type":34,"value":468},"Pour un legacy de taille moyenne (50 000 à 200 000 lignes de code), comptez 6 à 10 semaines pour avoir une suite couvrant les 10 à 15 scénarios critiques. C'est un investissement qui se rentabilise dès le premier refactoring majeur : une régression évitée sur un module critique vaut facilement 10 fois le coût de la suite de tests.",{"type":29,"tag":442,"props":470,"children":471},{},[472,477],{"type":29,"tag":446,"props":473,"children":474},{},[475],{"type":34,"value":476},"3. Faut-il des développeurs dédiés aux tests ou tous les développeurs participent ?",{"type":29,"tag":30,"props":478,"children":479},{},[480],{"type":34,"value":481},"Tous les développeurs participent. Les tests d'intégration ne sont pas une spécialité, ils sont une responsabilité de l'équipe. En pratique, je désigne un \"test champion\" par équipe pour maintenir l'infrastructure et les standards, mais chaque développeur qui modifie un module doit ajouter ou mettre à jour les tests correspondants.",{"type":29,"tag":442,"props":483,"children":484},{},[485,490],{"type":29,"tag":446,"props":486,"children":487},{},[488],{"type":34,"value":489},"4. Comment gérer les tests qui tombent de façon intermittente (flaky tests) ?",{"type":29,"tag":30,"props":491,"children":492},{},[493],{"type":34,"value":494},"Les flaky tests sont le cancer des suites de tests : ils réduisent la confiance dans tous les tests et poussent les équipes à ignorer les échecs. Ma règle est simple : un flaky test est soit corrigé dans les 48h, soit désactivé avec un ticket de suivi. Un test désactivé qui n'a pas de ticket actif dans le backlog est supprimé. La discipline sur les flaky tests conditionne la confiance dans toute la suite.",{"type":29,"tag":442,"props":496,"children":497},{},[498,503],{"type":29,"tag":446,"props":499,"children":500},{},[501],{"type":34,"value":502},"5. Les test containers ralentissent-ils la CI de manière inacceptable ?",{"type":29,"tag":30,"props":504,"children":505},{},[506],{"type":34,"value":507},"Sur un pipeline bien configuré, non. Les test containers démarrent en 5 à 15 secondes pour une base PostgreSQL ou MySQL. En parallélisant les suites de tests et en utilisant un cache Docker sur la CI, l'impact sur le temps total est marginal. Le vrai coût est le premier setup : 1 à 2 jours pour configurer l'infrastructure. Après ça, chaque nouvelle suite de tests bénéficie de l'infrastructure existante.",{"type":29,"tag":60,"props":509,"children":510},{},[],{"type":29,"tag":205,"props":512,"children":517},{"cta":513,"href":514,"title":515,"type":516},"Accéder à l'assessment gratuit →","\u002Fmes-ressources","Ressource gratuite : Engineering Maturity Self-Assessment","resource",[518],{"type":29,"tag":30,"props":519,"children":520},{},[521],{"type":34,"value":522},"L'assessment inclut une évaluation complète de vos pratiques de tests : couverture, types de tests, intégration dans la CI. Identifiez vos angles morts et priorisez les améliorations à fort impact.",{"title":8,"searchDepth":524,"depth":524,"links":525},2,[526,527,528,529,530,531,532],{"id":66,"depth":524,"text":69},{"id":112,"depth":524,"text":115},{"id":153,"depth":524,"text":156},{"id":222,"depth":524,"text":225},{"id":291,"depth":524,"text":294},{"id":339,"depth":524,"text":342},{"id":437,"depth":524,"text":440},"markdown","content:fr:dette-technique:tests-integration-legacy-pieges.md","content","fr\u002Fdette-technique\u002Ftests-integration-legacy-pieges.md","fr\u002Fdette-technique\u002Ftests-integration-legacy-pieges","md",{"_path":540,"_dir":6,"_draft":7,"_partial":7,"_locale":8,"title":541,"description":542,"id":543,"date":544,"listed":13,"nocomments":7,"hidden":7,"categories":545,"tags":546,"cover":549,"readingTime":550,"body":554,"_type":533,"_id":1370,"_source":535,"_file":1371,"_stem":1372,"_extension":538},"\u002Ffr\u002Fdette-technique\u002Fprogramme-refactoring-approuve-business","Comment créer un programme de refactoring approuvé par le business","Le refactoring refusé par le business n'est pas un problème technique : c'est un problème de présentation. Construire le business case qui obtient le feu vert.",16,"2026-02-09",[6],[17,547,548],"metriques","leadership","covers\u002Farticles\u002Frefactoring-business-case.jpg",{"text":21,"minutes":551,"time":552,"words":553},7.255,435300,1451,{"type":26,"children":555,"toc":1360},[556,561,569,574,577,583,606,609,615,632,637,642,665,674,705,710,720,723,729,744,749,767,777,786,915,925,934,937,946,949,955,960,969,978,996,1005,1023,1032,1050,1060,1069,1072,1078,1083,1144,1154,1157,1163,1171,1174,1180,1279,1288,1291,1297,1310,1323,1336,1349,1352],{"type":29,"tag":30,"props":557,"children":558},{},[559],{"type":34,"value":560},"Il y a quelques années, j'accompagnais le CTO d'une société de gestion d'épargne, avec une équipe de 20 développeurs. Il venait de se faire refuser pour la troisième fois un programme de refactoring sur leur module de calcul de rendement. Sa présentation était techniquement parfaite : complexité cyclomatique, taux de duplication, nombre de hotspots SonarQube. Le CPO l'avait écouté poliment puis avait dit : \"Je comprends que c'est important pour toi, mais on a la roadmap à tenir.\" Ce n'était pas un refus du business face à la technique. C'était un professionnel qui ne voyait pas son problème dans ce que l'autre lui présentait.",{"type":29,"tag":30,"props":562,"children":563},{},[564],{"type":29,"tag":40,"props":565,"children":566},{},[567],{"type":34,"value":568},"\"On a besoin de 3 mois pour refactoriser le core.\" Réponse du CPO : \"Pas maintenant, on a la roadmap feature à tenir.\" Cette conversation se joue dans 80% des entreprises tech. Et dans 80% des cas, c'est l'équipe technique qui présente mal, pas le business qui refuse mal.",{"type":29,"tag":30,"props":570,"children":571},{},[572],{"type":34,"value":573},"Le business ne comprend pas \"refactoring\". Il comprend \"coût\", \"risque\", \"délai\", et \"avantage concurrentiel\". Tant que vous présentez un projet technique dans un langage technique, vous demandez au business de vous faire confiance sur un sujet qu'il ne maîtrise pas. Ce n'est pas raisonnable, et ce n'est pas sa faute.",{"type":29,"tag":60,"props":575,"children":576},{},[],{"type":29,"tag":64,"props":578,"children":580},{"id":579},"avant-de-commencer-les-prérequis",[581],{"type":34,"value":582},"Avant de commencer : les prérequis",{"type":29,"tag":241,"props":584,"children":585},{},[586,596,601],{"type":29,"tag":245,"props":587,"children":588},{},[589,591],{"type":34,"value":590},"Vous avez identifié le périmètre du refactoring (module, couche, ou composant spécifique, si ce n'est pas encore fait, commencez par ",{"type":29,"tag":51,"props":592,"children":593},{"href":323},[594],{"type":34,"value":595},"évaluer le risque du code legacy",{"type":29,"tag":245,"props":597,"children":598},{},[599],{"type":34,"value":600},"Vous avez une estimation grossière de l'effort technique (order of magnitude : semaines, pas jours)",{"type":29,"tag":245,"props":602,"children":603},{},[604],{"type":34,"value":605},"Vous avez accès aux métriques de base : lead time, taux de bugs, temps passé en maintenance",{"type":29,"tag":60,"props":607,"children":608},{},[],{"type":29,"tag":64,"props":610,"children":612},{"id":611},"étape-1-quantifier-le-coût-actuel-pas-leffort-de-refactoring",[613],{"type":34,"value":614},"Étape 1 : Quantifier le coût actuel, pas l'effort de refactoring",{"type":29,"tag":30,"props":616,"children":617},{},[618,623,625,630],{"type":29,"tag":40,"props":619,"children":620},{},[621],{"type":34,"value":622},"Durée estimée",{"type":34,"value":624}," : 4 heures\n",{"type":29,"tag":40,"props":626,"children":627},{},[628],{"type":34,"value":629},"Qui",{"type":34,"value":631}," : Tech Lead + 1 développeur senior",{"type":29,"tag":30,"props":633,"children":634},{},[635],{"type":34,"value":636},"C'est l'étape que la plupart des équipes sautent. Elles présentent l'effort du refactoring sans jamais quantifier le coût de l'inaction.",{"type":29,"tag":30,"props":638,"children":639},{},[640],{"type":34,"value":641},"Questions à chiffrer :",{"type":29,"tag":241,"props":643,"children":644},{},[645,650,655,660],{"type":29,"tag":245,"props":646,"children":647},{},[648],{"type":34,"value":649},"Quel pourcentage du temps de l'équipe est consacré à la maintenance de ce module ?",{"type":29,"tag":245,"props":651,"children":652},{},[653],{"type":34,"value":654},"Combien d'incidents de prod ce module a-t-il causés les 6 derniers mois ?",{"type":29,"tag":245,"props":656,"children":657},{},[658],{"type":34,"value":659},"Quel est le lead time pour une modification simple dans ce module vs un module sain ?",{"type":29,"tag":245,"props":661,"children":662},{},[663],{"type":34,"value":664},"Combien de développeurs ont quitté l'équipe en citant \"la dette technique\" dans leur entretien de sortie ?",{"type":29,"tag":30,"props":666,"children":667},{},[668,673],{"type":29,"tag":40,"props":669,"children":670},{},[671],{"type":34,"value":672},"Calcul type",{"type":34,"value":239},{"type":29,"tag":241,"props":675,"children":676},{},[677,687,697],{"type":29,"tag":245,"props":678,"children":679},{},[680,682],{"type":34,"value":681},"Équipe de 20 développeurs × 40% d'absorption sur ce module × 450€\u002Fjour × 220 jours = ",{"type":29,"tag":40,"props":683,"children":684},{},[685],{"type":34,"value":686},"792 000€\u002Fan",{"type":29,"tag":245,"props":688,"children":689},{},[690,692],{"type":34,"value":691},"3 incidents P1 × 80 000€ d'impact moyen = ",{"type":29,"tag":40,"props":693,"children":694},{},[695],{"type":34,"value":696},"240 000€\u002Fan",{"type":29,"tag":245,"props":698,"children":699},{},[700],{"type":29,"tag":40,"props":701,"children":702},{},[703],{"type":34,"value":704},"Coût total de l'inaction : ~1 000 000€\u002Fan",{"type":29,"tag":30,"props":706,"children":707},{},[708],{"type":34,"value":709},"Ce chiffre est le fondement du business case. Sans lui, vous demandez un investissement sans montrer le retour. Avec lui, vous parlez la même langue que votre interlocuteur.",{"type":29,"tag":30,"props":711,"children":712},{},[713,718],{"type":29,"tag":40,"props":714,"children":715},{},[716],{"type":34,"value":717},"Résultat attendu",{"type":34,"value":719}," : un tableur avec les coûts actuels quantifiés, prêt à être présenté.",{"type":29,"tag":60,"props":721,"children":722},{},[],{"type":29,"tag":64,"props":724,"children":726},{"id":725},"étape-2-construire-le-roi-en-langage-business",[727],{"type":34,"value":728},"Étape 2 : Construire le ROI en langage business",{"type":29,"tag":30,"props":730,"children":731},{},[732,736,738,742],{"type":29,"tag":40,"props":733,"children":734},{},[735],{"type":34,"value":622},{"type":34,"value":737}," : 3 heures\n",{"type":29,"tag":40,"props":739,"children":740},{},[741],{"type":34,"value":629},{"type":34,"value":743}," : CTO + Tech Lead",{"type":29,"tag":30,"props":745,"children":746},{},[747],{"type":34,"value":748},"Le ROI d'un programme de refactoring a deux composantes :",{"type":29,"tag":30,"props":750,"children":751},{},[752,757,759,765],{"type":29,"tag":40,"props":753,"children":754},{},[755],{"type":34,"value":756},"Composante 1 : Réduction des coûts",{"type":34,"value":758}," : si le refactoring réduit l'",{"type":29,"tag":51,"props":760,"children":762},{"href":761},"\u002Ffr\u002Fdette-technique\u002Fintroduction-maturite-engineering-5-niveaux",[763],{"type":34,"value":764},"absorption de 40% à 20%",{"type":34,"value":766},", vous récupérez 50% du coût de l'inaction. Sur l'exemple précédent : 500 000€\u002Fan récupérés.",{"type":29,"tag":30,"props":768,"children":769},{},[770,775],{"type":29,"tag":40,"props":771,"children":772},{},[773],{"type":34,"value":774},"Composante 2 : Accélération business",{"type":34,"value":776}," : le lead time divisé par 2 ou 3 signifie que les features arrivent plus vite sur le marché. Chiffrez-le en termes de features livrées par trimestre ou de time-to-market sur vos initiatives stratégiques.",{"type":29,"tag":30,"props":778,"children":779},{},[780,785],{"type":29,"tag":40,"props":781,"children":782},{},[783],{"type":34,"value":784},"Format de présentation pour le board",{"type":34,"value":239},{"type":29,"tag":787,"props":788,"children":789},"table",{},[790,812],{"type":29,"tag":791,"props":792,"children":793},"thead",{},[794],{"type":29,"tag":795,"props":796,"children":797},"tr",{},[798,802,807],{"type":29,"tag":799,"props":800,"children":801},"th",{},[],{"type":29,"tag":799,"props":803,"children":804},{},[805],{"type":34,"value":806},"Scénario actuel",{"type":29,"tag":799,"props":808,"children":809},{},[810],{"type":34,"value":811},"Après refactoring",{"type":29,"tag":813,"props":814,"children":815},"tbody",{},[816,835,857,875,893],{"type":29,"tag":795,"props":817,"children":818},{},[819,825,830],{"type":29,"tag":820,"props":821,"children":822},"td",{},[823],{"type":34,"value":824},"Absorption maintenance",{"type":29,"tag":820,"props":826,"children":827},{},[828],{"type":34,"value":829},"40%",{"type":29,"tag":820,"props":831,"children":832},{},[833],{"type":34,"value":834},"20%",{"type":29,"tag":795,"props":836,"children":837},{},[838,847,852],{"type":29,"tag":820,"props":839,"children":840},{},[841],{"type":29,"tag":51,"props":842,"children":844},{"href":843},"\u002Ffr\u002Fpratiques-agiles\u002Freduire-work-in-progress-velocite",[845],{"type":34,"value":846},"Lead time moyen",{"type":29,"tag":820,"props":848,"children":849},{},[850],{"type":34,"value":851},"4 semaines",{"type":29,"tag":820,"props":853,"children":854},{},[855],{"type":34,"value":856},"2 semaines",{"type":29,"tag":795,"props":858,"children":859},{},[860,865,870],{"type":29,"tag":820,"props":861,"children":862},{},[863],{"type":34,"value":864},"Incidents P1\u002Fan",{"type":29,"tag":820,"props":866,"children":867},{},[868],{"type":34,"value":869},"6",{"type":29,"tag":820,"props":871,"children":872},{},[873],{"type":34,"value":874},"1-2",{"type":29,"tag":795,"props":876,"children":877},{},[878,883,888],{"type":29,"tag":820,"props":879,"children":880},{},[881],{"type":34,"value":882},"Coût annuel estimé",{"type":29,"tag":820,"props":884,"children":885},{},[886],{"type":34,"value":887},"1 000 000€",{"type":29,"tag":820,"props":889,"children":890},{},[891],{"type":34,"value":892},"400 000€",{"type":29,"tag":795,"props":894,"children":895},{},[896,904,907],{"type":29,"tag":820,"props":897,"children":898},{},[899],{"type":29,"tag":40,"props":900,"children":901},{},[902],{"type":34,"value":903},"Économie annuelle",{"type":29,"tag":820,"props":905,"children":906},{},[],{"type":29,"tag":820,"props":908,"children":909},{},[910],{"type":29,"tag":40,"props":911,"children":912},{},[913],{"type":34,"value":914},"600 000€",{"type":29,"tag":30,"props":916,"children":917},{},[918,920],{"type":34,"value":919},"Investissement programme : 200 000€ (3 mois, 4 développeurs). ",{"type":29,"tag":40,"props":921,"children":922},{},[923],{"type":34,"value":924},"ROI : 3 mois.",{"type":29,"tag":30,"props":926,"children":927},{},[928,932],{"type":29,"tag":40,"props":929,"children":930},{},[931],{"type":34,"value":717},{"type":34,"value":933}," : un slide ou un tableau de 1 page qui montre le ROI clairement.",{"type":29,"tag":60,"props":935,"children":936},{},[],{"type":29,"tag":205,"props":938,"children":940},{"cta":207,"href":208,"title":939,"type":210},"Vous voulez savoir refactoriser sans tout casser, et le justifier ?",[941],{"type":29,"tag":30,"props":942,"children":943},{},[944],{"type":34,"value":945},"Découper un refactoring en étapes sûres, garder le code livrable à chaque commit, savoir où poser la limite : ça ne s'apprend pas dans un slide de ROI, ça se travaille sur du vrai code. En mentoring 1:1, je relis votre code avec vous et on s'entraîne à transformer un module legacy par petits pas que vous savez défendre. Vous montez en niveau sur la pratique qui rend vos refactorings crédibles.",{"type":29,"tag":60,"props":947,"children":948},{},[],{"type":29,"tag":64,"props":950,"children":952},{"id":951},"étape-3-proposer-un-plan-en-3-phases-avec-quick-wins",[953],{"type":34,"value":954},"Étape 3 : Proposer un plan en 3 phases avec quick wins",{"type":29,"tag":30,"props":956,"children":957},{},[958],{"type":34,"value":959},"Le business est rassuré par les quick wins. Un programme de 6 mois qui ne livre rien de visible pendant 6 mois est un programme à haut risque perçu.",{"type":29,"tag":30,"props":961,"children":962},{},[963,968],{"type":29,"tag":40,"props":964,"children":965},{},[966],{"type":34,"value":967},"Structure recommandée en 3 phases",{"type":34,"value":239},{"type":29,"tag":30,"props":970,"children":971},{},[972,977],{"type":29,"tag":40,"props":973,"children":974},{},[975],{"type":34,"value":976},"Phase 1 (4-6 semaines) : Stabilisation et quick wins",{"type":34,"value":239},{"type":29,"tag":241,"props":979,"children":980},{},[981,986,991],{"type":29,"tag":245,"props":982,"children":983},{},[984],{"type":34,"value":985},"Objectif : réduire les incidents de prod immédiats",{"type":29,"tag":245,"props":987,"children":988},{},[989],{"type":34,"value":990},"Livrable visible : réduction du taux d'incidents de 50%",{"type":29,"tag":245,"props":992,"children":993},{},[994],{"type":34,"value":995},"Investissement : 20% de la capacité de l'équipe",{"type":29,"tag":30,"props":997,"children":998},{},[999,1004],{"type":29,"tag":40,"props":1000,"children":1001},{},[1002],{"type":34,"value":1003},"Phase 2 (6-8 semaines) : Refactoring structurel",{"type":34,"value":239},{"type":29,"tag":241,"props":1006,"children":1007},{},[1008,1013,1018],{"type":29,"tag":245,"props":1009,"children":1010},{},[1011],{"type":34,"value":1012},"Objectif : réduire l'absorption de la dette",{"type":29,"tag":245,"props":1014,"children":1015},{},[1016],{"type":34,"value":1017},"Livrable visible : le lead time sur le module cible commence à baisser",{"type":29,"tag":245,"props":1019,"children":1020},{},[1021],{"type":34,"value":1022},"Investissement : 30-40% de la capacité",{"type":29,"tag":30,"props":1024,"children":1025},{},[1026,1031],{"type":29,"tag":40,"props":1027,"children":1028},{},[1029],{"type":34,"value":1030},"Phase 3 (4-6 semaines) : Consolidation et transfert",{"type":34,"value":239},{"type":29,"tag":241,"props":1033,"children":1034},{},[1035,1040,1045],{"type":29,"tag":245,"props":1036,"children":1037},{},[1038],{"type":34,"value":1039},"Objectif : documenter, tester, former l'équipe sur les nouveaux patterns",{"type":29,"tag":245,"props":1041,"children":1042},{},[1043],{"type":34,"value":1044},"Livrable visible : les autres équipes peuvent modifier le module sans accompagnement",{"type":29,"tag":245,"props":1046,"children":1047},{},[1048],{"type":34,"value":1049},"Investissement : 20% de la capacité",{"type":29,"tag":30,"props":1051,"children":1052},{},[1053,1058],{"type":29,"tag":40,"props":1054,"children":1055},{},[1056],{"type":34,"value":1057},"L'argument clé",{"type":34,"value":1059}," : les features continuent de sortir pendant tout le programme. Ce n'est pas un arrêt, c'est une réallocation partielle et temporaire. C'est précisément ce que la théorie des contraintes de Goldratt préconise : traiter le goulot d'étranglement en maintenant le flux global.",{"type":29,"tag":30,"props":1061,"children":1062},{},[1063,1067],{"type":29,"tag":40,"props":1064,"children":1065},{},[1066],{"type":34,"value":717},{"type":34,"value":1068}," : un plan de 3 phases avec les livrables visibles à chaque étape.",{"type":29,"tag":60,"props":1070,"children":1071},{},[],{"type":29,"tag":64,"props":1073,"children":1075},{"id":1074},"étape-4-le-pitch-de-10-minutes-qui-obtient-le-budget",[1076],{"type":34,"value":1077},"Étape 4 : Le pitch de 10 minutes qui obtient le budget",{"type":29,"tag":30,"props":1079,"children":1080},{},[1081],{"type":34,"value":1082},"Structure du pitch :",{"type":29,"tag":344,"props":1084,"children":1085},{},[1086,1096,1106,1116,1126],{"type":29,"tag":245,"props":1087,"children":1088},{},[1089,1094],{"type":29,"tag":40,"props":1090,"children":1091},{},[1092],{"type":34,"value":1093},"L'enjeu",{"type":34,"value":1095}," (2 min) : \"Notre module X nous coûte 1M€\u002Fan. Voici les chiffres.\"",{"type":29,"tag":245,"props":1097,"children":1098},{},[1099,1104],{"type":29,"tag":40,"props":1100,"children":1101},{},[1102],{"type":34,"value":1103},"Le diagnostic",{"type":34,"value":1105}," (2 min) : \"Voici pourquoi il coûte autant et pourquoi ça va empirer.\"",{"type":29,"tag":245,"props":1107,"children":1108},{},[1109,1114],{"type":29,"tag":40,"props":1110,"children":1111},{},[1112],{"type":34,"value":1113},"La solution",{"type":34,"value":1115}," (2 min) : \"Un programme de 3 mois, 3 phases, qui continue à livrer des features.\"",{"type":29,"tag":245,"props":1117,"children":1118},{},[1119,1124],{"type":29,"tag":40,"props":1120,"children":1121},{},[1122],{"type":34,"value":1123},"Le ROI",{"type":34,"value":1125}," (2 min) : \"Investissement : 200K€. Économie annuelle : 600K€. Retour en 3 mois.\"",{"type":29,"tag":245,"props":1127,"children":1128},{},[1129,1134,1136,1142],{"type":29,"tag":40,"props":1130,"children":1131},{},[1132],{"type":34,"value":1133},"La décision demandée",{"type":34,"value":1135}," (2 min) : \"On a besoin de votre feu vert pour allouer 30% de la capacité de l'équipe pendant 3 mois, à partir du ",{"type":29,"tag":1137,"props":1138,"children":1139},"span",{},[1140],{"type":34,"value":1141},"date",{"type":34,"value":1143},".\"",{"type":29,"tag":30,"props":1145,"children":1146},{},[1147,1152],{"type":29,"tag":40,"props":1148,"children":1149},{},[1150],{"type":34,"value":1151},"Ce qu'il ne faut surtout pas dire",{"type":34,"value":1153}," : \"On a besoin de payer la dette technique.\" C'est une phrase technique sans signification financière. Remplacez-la par : \"On a besoin de réduire notre coût opérationnel de 600K€\u002Fan.\" Ce n'est pas du spin, c'est la même réalité exprimée dans la langue de votre interlocuteur.",{"type":29,"tag":60,"props":1155,"children":1156},{},[],{"type":29,"tag":64,"props":1158,"children":1160},{"id":1159},"le-piège-à-éviter",[1161],{"type":34,"value":1162},"Le piège à éviter",{"type":29,"tag":188,"props":1164,"children":1165},{},[1166],{"type":29,"tag":30,"props":1167,"children":1168},{},[1169],{"type":34,"value":1170},"Ne promettre jamais une réduction de 100% de la dette. Je promets des améliorations mesurables sur des métriques spécifiques. Un programme qui promet \"tout refactoriser\" ne sera jamais terminé et perdra la confiance du business. Un programme qui promet \"réduire les incidents P1 de 6 à 2 par an sur le module X en 3 mois\" est vérifiable et crédible.",{"type":29,"tag":60,"props":1172,"children":1173},{},[],{"type":29,"tag":64,"props":1175,"children":1177},{"id":1176},"en-résumé",[1178],{"type":34,"value":1179},"En résumé",{"type":29,"tag":787,"props":1181,"children":1182},{},[1183,1204],{"type":29,"tag":791,"props":1184,"children":1185},{},[1186],{"type":29,"tag":795,"props":1187,"children":1188},{},[1189,1194,1199],{"type":29,"tag":799,"props":1190,"children":1191},{},[1192],{"type":34,"value":1193},"Étape",{"type":29,"tag":799,"props":1195,"children":1196},{},[1197],{"type":34,"value":1198},"Action",{"type":29,"tag":799,"props":1200,"children":1201},{},[1202],{"type":34,"value":1203},"Résultat",{"type":29,"tag":813,"props":1205,"children":1206},{},[1207,1225,1243,1261],{"type":29,"tag":795,"props":1208,"children":1209},{},[1210,1215,1220],{"type":29,"tag":820,"props":1211,"children":1212},{},[1213],{"type":34,"value":1214},"1",{"type":29,"tag":820,"props":1216,"children":1217},{},[1218],{"type":34,"value":1219},"Quantifier le coût actuel de l'inaction",{"type":29,"tag":820,"props":1221,"children":1222},{},[1223],{"type":34,"value":1224},"Chiffres prêts pour le business case",{"type":29,"tag":795,"props":1226,"children":1227},{},[1228,1233,1238],{"type":29,"tag":820,"props":1229,"children":1230},{},[1231],{"type":34,"value":1232},"2",{"type":29,"tag":820,"props":1234,"children":1235},{},[1236],{"type":34,"value":1237},"Construire le ROI en langage financier",{"type":29,"tag":820,"props":1239,"children":1240},{},[1241],{"type":34,"value":1242},"Slide de ROI en 1 page",{"type":29,"tag":795,"props":1244,"children":1245},{},[1246,1251,1256],{"type":29,"tag":820,"props":1247,"children":1248},{},[1249],{"type":34,"value":1250},"3",{"type":29,"tag":820,"props":1252,"children":1253},{},[1254],{"type":34,"value":1255},"Plan en 3 phases avec quick wins visibles",{"type":29,"tag":820,"props":1257,"children":1258},{},[1259],{"type":34,"value":1260},"Programme crédible sans arrêt des features",{"type":29,"tag":795,"props":1262,"children":1263},{},[1264,1269,1274],{"type":29,"tag":820,"props":1265,"children":1266},{},[1267],{"type":34,"value":1268},"4",{"type":29,"tag":820,"props":1270,"children":1271},{},[1272],{"type":34,"value":1273},"Pitch de 10 minutes structuré",{"type":29,"tag":820,"props":1275,"children":1276},{},[1277],{"type":34,"value":1278},"Feu vert et budget alloué",{"type":29,"tag":205,"props":1280,"children":1282},{"cta":422,"href":423,"title":1281,"type":425},"Refactoriser proprement n'est qu'une des 100 pratiques d'un dev senior",[1283],{"type":29,"tag":30,"props":1284,"children":1285},{},[1286],{"type":34,"value":1287},"Cet article montre comment structurer et défendre un refactoring, mais le savoir-faire technique qui le rend possible est bien plus large. Le Craft Bundle réunit les 100 pratiques que j'applique au quotidien pour coder propre, garder un module modifiable et éviter qu'il redevienne un coût d'un million d'euros par an. Ce sont les pratiques que l'IA ne vous apprendra jamais, parce qu'elle ne les a jamais vues tenir en production.",{"type":29,"tag":60,"props":1289,"children":1290},{},[],{"type":29,"tag":64,"props":1292,"children":1294},{"id":1293},"faq-sur-le-business-case-du-refactoring",[1295],{"type":34,"value":1296},"FAQ sur le business case du refactoring",{"type":29,"tag":442,"props":1298,"children":1299},{},[1300,1305],{"type":29,"tag":446,"props":1301,"children":1302},{},[1303],{"type":34,"value":1304},"1. Que faire si le business accepte mais réduit le budget de moitié ?",{"type":29,"tag":30,"props":1306,"children":1307},{},[1308],{"type":34,"value":1309},"Ne pas accepter un programme sous-dimensionné qui ne peut pas atteindre ses objectifs. C'est pire qu'un refus : il consomme de la capacité sans produire de résultats, et détruit la crédibilité pour les prochaines demandes. Il vaut mieux replanifier sur un périmètre plus restreint (un module au lieu de trois) avec le budget disponible, et livrer des résultats mesurables avant de demander la suite.",{"type":29,"tag":442,"props":1311,"children":1312},{},[1313,1318],{"type":29,"tag":446,"props":1314,"children":1315},{},[1316],{"type":34,"value":1317},"2. Comment mesurer l'impact du programme une fois démarré ?",{"type":29,"tag":30,"props":1319,"children":1320},{},[1321],{"type":34,"value":1322},"Je définis les métriques de succès avant de commencer : lead time cible, taux d'incidents cible, absorption de maintenance cible. Je mesure ces métriques à J0, J30, J60, J90. Un tableau de bord visible par le business tous les 15 jours. La transparence sur les progrès maintient le soutien et prévient les remises en question en cours de route.",{"type":29,"tag":442,"props":1324,"children":1325},{},[1326,1331],{"type":29,"tag":446,"props":1327,"children":1328},{},[1329],{"type":34,"value":1330},"3. Le business peut-il comprendre les concepts techniques comme la \"dette technique\" ?",{"type":29,"tag":30,"props":1332,"children":1333},{},[1334],{"type":34,"value":1335},"Oui, mais pas dans le vocabulaire technique. L'analogie financière fonctionne très bien : \"La dette technique est exactement comme une dette financière. Elle a un principal (le travail à faire pour la rembourser) et des intérêts (le surcoût de travail que vous payez chaque jour parce qu'elle existe). Nous payons actuellement 1M€\u002Fan d'intérêts. Ce programme rembourse une partie du principal et réduit les intérêts de 60%.\"",{"type":29,"tag":442,"props":1337,"children":1338},{},[1339,1344],{"type":29,"tag":446,"props":1340,"children":1341},{},[1342],{"type":34,"value":1343},"4. Comment gérer la pression de maintenir la roadmap feature pendant le programme ?",{"type":29,"tag":30,"props":1345,"children":1346},{},[1347],{"type":34,"value":1348},"Je négocie explicitement le \"budget technique\" avant de commencer : X% de la capacité de l'équipe est allouée au programme pendant Y semaines. Ce n'est pas du temps volé aux features, c'est un investissement planifié. Je présente chaque sprint les features livrées ET l'avancement du programme. La co-existence est possible à condition que les règles soient claires dès le départ.",{"type":29,"tag":60,"props":1350,"children":1351},{},[],{"type":29,"tag":205,"props":1353,"children":1354},{"cta":513,"href":514,"title":515,"type":516},[1355],{"type":29,"tag":30,"props":1356,"children":1357},{},[1358],{"type":34,"value":1359},"L'assessment inclut une section dédiée à la gestion de la dette technique et vous aide à quantifier votre niveau d'absorption actuel : le premier chiffre dont vous avez besoin pour votre business case.",{"title":8,"searchDepth":524,"depth":524,"links":1361},[1362,1363,1364,1365,1366,1367,1368,1369],{"id":579,"depth":524,"text":582},{"id":611,"depth":524,"text":614},{"id":725,"depth":524,"text":728},{"id":951,"depth":524,"text":954},{"id":1074,"depth":524,"text":1077},{"id":1159,"depth":524,"text":1162},{"id":1176,"depth":524,"text":1179},{"id":1293,"depth":524,"text":1296},"content:fr:dette-technique:programme-refactoring-approuve-business.md","fr\u002Fdette-technique\u002Fprogramme-refactoring-approuve-business.md","fr\u002Fdette-technique\u002Fprogramme-refactoring-approuve-business",{"_path":1374,"_dir":1375,"_draft":7,"_partial":7,"_locale":8,"title":1376,"description":1377,"id":1378,"date":1379,"listed":13,"nocomments":7,"hidden":7,"categories":1380,"tags":1381,"cover":1383,"readingTime":1384,"body":1388,"_type":533,"_id":2873,"_source":535,"_file":2874,"_stem":2875,"_extension":538},"\u002Ffr\u002Farchitecture-craft\u002Fcouplage-temporel-code-asynchrone","architecture-craft","Le couplage temporel : la dette cachée dans votre code asynchrone","Le couplage temporel est invisible dans les revues de code et catastrophique en production. Comment l'identifier, le mesurer et l'éliminer progressivement.",15,"2026-02-06",[1375],[17,1382],"architecture","covers\u002Farticles\u002Fcouplage-temporel-asynchrone.jpg",{"text":21,"minutes":1385,"time":1386,"words":1387},7.79,467400,1558,{"type":26,"children":1389,"toc":2857},[1390,1395,1400,1405,1410,1413,1419,1431,1436,1445,1595,1607,1610,1616,1623,1628,1638,1643,1649,1654,2205,2211,2216,2229,2238,2241,2247,2265,2288,2298,2316,2319,2325,2331,2336,2676,2682,2694,2699,2705,2710,2726,2729,2735,2745,2757,2760,2769,2772,2778,2791,2812,2825,2838,2841,2851],{"type":29,"tag":30,"props":1391,"children":1392},{},[1393],{"type":34,"value":1394},"Un vendredi soir, 22h. J'étais en astreinte pour une plateforme de paiement dans le secteur bancaire. La chaîne de traitement des virements tombait de façon intermittente depuis 48 heures : 2 à 3 fois par jour, toujours \"résolue\" par un retry manuel ou une correction en base. Le bug semblait se résoudre de lui-même.",{"type":29,"tag":30,"props":1396,"children":1397},{},[1398],{"type":34,"value":1399},"Trois heures plus tard, j'avais trouvé : une contrainte d'ordre implicite entre deux services asynchrones. L'un devait recevoir un événement \"CompteValidé\" avant de traiter un événement \"VirementInitié\". Lors des pics de charge, les deux événements arrivaient dans le désordre. Le service traitait le virement sur un compte qui n'existait pas encore dans son état local.",{"type":29,"tag":30,"props":1401,"children":1402},{},[1403],{"type":34,"value":1404},"Rien dans le code ne signalait cette contrainte. Elle était documentée dans un commentaire Confluence que personne ne lisait plus.",{"type":29,"tag":30,"props":1406,"children":1407},{},[1408],{"type":34,"value":1409},"Ce problème avait un nom : le couplage temporel. Et il était présent depuis 18 mois.",{"type":29,"tag":60,"props":1411,"children":1412},{},[],{"type":29,"tag":64,"props":1414,"children":1416},{"id":1415},"ce-quest-vraiment-le-couplage-temporel",[1417],{"type":34,"value":1418},"Ce qu'est vraiment le couplage temporel",{"type":29,"tag":30,"props":1420,"children":1421},{},[1422,1424,1429],{"type":34,"value":1423},"Le couplage temporel existe quand deux opérations ",{"type":29,"tag":40,"props":1425,"children":1426},{},[1427],{"type":34,"value":1428},"doivent se dérouler dans un ordre spécifique ou dans une fenêtre temporelle précise",{"type":34,"value":1430},", et que cette contrainte n'est pas rendue explicite dans le code.",{"type":29,"tag":30,"props":1432,"children":1433},{},[1434],{"type":34,"value":1435},"C'est une contrainte implicite : rien dans le code ne vous dit que A doit se terminer avant B. Vous le savez parce que vous avez écrit le code, ou parce que vous avez lu la documentation. Le prochain développeur ne le saura peut-être pas.",{"type":29,"tag":30,"props":1437,"children":1438},{},[1439,1444],{"type":29,"tag":40,"props":1440,"children":1441},{},[1442],{"type":34,"value":1443},"Exemple basique",{"type":34,"value":239},{"type":29,"tag":1446,"props":1447,"children":1451},"pre",{"className":1448,"code":1449,"language":1450,"meta":8,"style":8},"language-javascript shiki shiki-themes catppuccin-frappe github-dark","\u002F\u002F Couplage temporel implicite :\nawait initializeDatabase();   \u002F\u002F doit s'exécuter en premier\nawait loadConfiguration();    \u002F\u002F dépend de la DB\nawait startHttpServer();       \u002F\u002F dépend de la configuration\n\n\u002F\u002F Si initializeDatabase() échoue silencieusement,\n\u002F\u002F loadConfiguration() peut \"fonctionner\" avec des données partielles\n\u002F\u002F et startHttpServer() démarrera avec une configuration corrompue.\n\u002F\u002F Le bug se manifeste plus tard, dans une feature spécifique.\n","javascript",[1452],{"type":29,"tag":86,"props":1453,"children":1454},{"__ignoreMap":8},[1455,1466,1498,1524,1550,1559,1568,1577,1586],{"type":29,"tag":1137,"props":1456,"children":1459},{"class":1457,"line":1458},"line",1,[1460],{"type":29,"tag":1137,"props":1461,"children":1463},{"style":1462},"--shiki-default:#737994;--shiki-default-font-style:italic;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit",[1464],{"type":34,"value":1465},"\u002F\u002F Couplage temporel implicite :\n",{"type":29,"tag":1137,"props":1467,"children":1468},{"class":1457,"line":524},[1469,1475,1481,1487,1493],{"type":29,"tag":1137,"props":1470,"children":1472},{"style":1471},"--shiki-default:#CA9EE6;--shiki-dark:#F97583",[1473],{"type":34,"value":1474},"await",{"type":29,"tag":1137,"props":1476,"children":1478},{"style":1477},"--shiki-default:#8CAAEE;--shiki-default-font-style:italic;--shiki-dark:#B392F0;--shiki-dark-font-style:inherit",[1479],{"type":34,"value":1480}," initializeDatabase",{"type":29,"tag":1137,"props":1482,"children":1484},{"style":1483},"--shiki-default:#C6D0F5;--shiki-dark:#E1E4E8",[1485],{"type":34,"value":1486},"()",{"type":29,"tag":1137,"props":1488,"children":1490},{"style":1489},"--shiki-default:#949CBB;--shiki-dark:#E1E4E8",[1491],{"type":34,"value":1492},";",{"type":29,"tag":1137,"props":1494,"children":1495},{"style":1462},[1496],{"type":34,"value":1497},"   \u002F\u002F doit s'exécuter en premier\n",{"type":29,"tag":1137,"props":1499,"children":1501},{"class":1457,"line":1500},3,[1502,1506,1511,1515,1519],{"type":29,"tag":1137,"props":1503,"children":1504},{"style":1471},[1505],{"type":34,"value":1474},{"type":29,"tag":1137,"props":1507,"children":1508},{"style":1477},[1509],{"type":34,"value":1510}," loadConfiguration",{"type":29,"tag":1137,"props":1512,"children":1513},{"style":1483},[1514],{"type":34,"value":1486},{"type":29,"tag":1137,"props":1516,"children":1517},{"style":1489},[1518],{"type":34,"value":1492},{"type":29,"tag":1137,"props":1520,"children":1521},{"style":1462},[1522],{"type":34,"value":1523},"    \u002F\u002F dépend de la DB\n",{"type":29,"tag":1137,"props":1525,"children":1527},{"class":1457,"line":1526},4,[1528,1532,1537,1541,1545],{"type":29,"tag":1137,"props":1529,"children":1530},{"style":1471},[1531],{"type":34,"value":1474},{"type":29,"tag":1137,"props":1533,"children":1534},{"style":1477},[1535],{"type":34,"value":1536}," startHttpServer",{"type":29,"tag":1137,"props":1538,"children":1539},{"style":1483},[1540],{"type":34,"value":1486},{"type":29,"tag":1137,"props":1542,"children":1543},{"style":1489},[1544],{"type":34,"value":1492},{"type":29,"tag":1137,"props":1546,"children":1547},{"style":1462},[1548],{"type":34,"value":1549},"       \u002F\u002F dépend de la configuration\n",{"type":29,"tag":1137,"props":1551,"children":1553},{"class":1457,"line":1552},5,[1554],{"type":29,"tag":1137,"props":1555,"children":1556},{"emptyLinePlaceholder":13},[1557],{"type":34,"value":1558},"\n",{"type":29,"tag":1137,"props":1560,"children":1562},{"class":1457,"line":1561},6,[1563],{"type":29,"tag":1137,"props":1564,"children":1565},{"style":1462},[1566],{"type":34,"value":1567},"\u002F\u002F Si initializeDatabase() échoue silencieusement,\n",{"type":29,"tag":1137,"props":1569,"children":1571},{"class":1457,"line":1570},7,[1572],{"type":29,"tag":1137,"props":1573,"children":1574},{"style":1462},[1575],{"type":34,"value":1576},"\u002F\u002F loadConfiguration() peut \"fonctionner\" avec des données partielles\n",{"type":29,"tag":1137,"props":1578,"children":1580},{"class":1457,"line":1579},8,[1581],{"type":29,"tag":1137,"props":1582,"children":1583},{"style":1462},[1584],{"type":34,"value":1585},"\u002F\u002F et startHttpServer() démarrera avec une configuration corrompue.\n",{"type":29,"tag":1137,"props":1587,"children":1589},{"class":1457,"line":1588},9,[1590],{"type":29,"tag":1137,"props":1591,"children":1592},{"style":1462},[1593],{"type":34,"value":1594},"\u002F\u002F Le bug se manifeste plus tard, dans une feature spécifique.\n",{"type":29,"tag":30,"props":1596,"children":1597},{},[1598,1600,1605],{"type":34,"value":1599},"Selon les données que j'observe dans mes missions, ",{"type":29,"tag":40,"props":1601,"children":1602},{},[1603],{"type":34,"value":1604},"les incidents intermittents en production représentent 40 à 60% du temps de debugging des équipes senior",{"type":34,"value":1606},", et le couplage temporel en est l'une des causes les plus fréquentes et les moins visibles.",{"type":29,"tag":60,"props":1608,"children":1609},{},[],{"type":29,"tag":64,"props":1611,"children":1613},{"id":1612},"les-3-formes-les-plus-courantes",[1614],{"type":34,"value":1615},"Les 3 formes les plus courantes",{"type":29,"tag":1617,"props":1618,"children":1620},"h3",{"id":1619},"forme-1-la-séquence-obligatoire-non-documentée",[1621],{"type":34,"value":1622},"Forme 1 : La séquence obligatoire non documentée",{"type":29,"tag":30,"props":1624,"children":1625},{},[1626],{"type":34,"value":1627},"Des opérations qui doivent être exécutées dans un ordre précis, sans que cet ordre soit rendu explicite ni protégé par du code.",{"type":29,"tag":30,"props":1629,"children":1630},{},[1631,1636],{"type":29,"tag":40,"props":1632,"children":1633},{},[1634],{"type":34,"value":1635},"Exemple",{"type":34,"value":1637}," : un service qui doit recevoir un événement \"UserCreated\" avant de pouvoir traiter un événement \"UserProfileUpdated\". Si les deux arrivent simultanément (race condition), le traitement échoue ou produit un état incohérent.",{"type":29,"tag":30,"props":1639,"children":1640},{},[1641],{"type":34,"value":1642},"Ce type d'incident coûte en moyenne 2 à 5 heures de debugging pour un développeur senior, et laisse un résidu de méfiance dans le système. L'équipe commence à \"monitorer manuellement\" ce flux, ce qui est un signal fort que la contrainte n'est pas correctement encodée.",{"type":29,"tag":1617,"props":1644,"children":1646},{"id":1645},"forme-2-les-appels-asynchrones-séquentiels-inutiles",[1647],{"type":34,"value":1648},"Forme 2 : Les appels asynchrones séquentiels inutiles",{"type":29,"tag":30,"props":1650,"children":1651},{},[1652],{"type":34,"value":1653},"Des appels asynchrones imbriqués où chaque niveau dépend du précédent, même quand ce n'est pas nécessaire. Même avec async\u002Fawait, le pattern peut rester présent sous une forme différente.",{"type":29,"tag":1446,"props":1655,"children":1657},{"className":1448,"code":1656,"language":1450,"meta":8,"style":8},"\u002F\u002F ❌ Couplage temporel implicite même avec async\u002Fawait\nasync function processOrder(orderId) {\n    const order = await getOrder(orderId);\n    const customer = await getCustomer(order.customerId); \u002F\u002F dépend de order\n    const inventory = await checkInventory(order.items);  \u002F\u002F pourrait être parallèle !\n    const payment = await processPayment(customer, order);\n    await sendConfirmation(customer, order);\n}\n\n\u002F\u002F ✅ Parallélisation explicite pour les opérations indépendantes\nasync function processOrder(orderId) {\n    const order = await getOrder(orderId);\n    const [customer, inventory] = await Promise.all([\n        getCustomer(order.customerId),\n        checkInventory(order.items)    \u002F\u002F pas de dépendance sur customer\n    ]);\n    const payment = await processPayment(customer, order);\n    await sendConfirmation(customer, order);\n}\n",[1658],{"type":29,"tag":86,"props":1659,"children":1660},{"__ignoreMap":8},[1661,1669,1708,1748,1798,1845,1889,1918,1926,1933,1942,1974,2006,2065,2091,2117,2129,2169,2197],{"type":29,"tag":1137,"props":1662,"children":1663},{"class":1457,"line":1458},[1664],{"type":29,"tag":1137,"props":1665,"children":1666},{"style":1462},[1667],{"type":34,"value":1668},"\u002F\u002F ❌ Couplage temporel implicite même avec async\u002Fawait\n",{"type":29,"tag":1137,"props":1670,"children":1671},{"class":1457,"line":524},[1672,1677,1682,1687,1692,1698,1703],{"type":29,"tag":1137,"props":1673,"children":1674},{"style":1471},[1675],{"type":34,"value":1676},"async",{"type":29,"tag":1137,"props":1678,"children":1679},{"style":1471},[1680],{"type":34,"value":1681}," function",{"type":29,"tag":1137,"props":1683,"children":1684},{"style":1477},[1685],{"type":34,"value":1686}," processOrder",{"type":29,"tag":1137,"props":1688,"children":1689},{"style":1489},[1690],{"type":34,"value":1691},"(",{"type":29,"tag":1137,"props":1693,"children":1695},{"style":1694},"--shiki-default:#EA999C;--shiki-default-font-style:italic;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit",[1696],{"type":34,"value":1697},"orderId",{"type":29,"tag":1137,"props":1699,"children":1700},{"style":1489},[1701],{"type":34,"value":1702},")",{"type":29,"tag":1137,"props":1704,"children":1705},{"style":1489},[1706],{"type":34,"value":1707}," {\n",{"type":29,"tag":1137,"props":1709,"children":1710},{"class":1457,"line":1500},[1711,1716,1722,1728,1733,1738,1743],{"type":29,"tag":1137,"props":1712,"children":1713},{"style":1471},[1714],{"type":34,"value":1715},"    const",{"type":29,"tag":1137,"props":1717,"children":1719},{"style":1718},"--shiki-default:#C6D0F5;--shiki-dark:#79B8FF",[1720],{"type":34,"value":1721}," order",{"type":29,"tag":1137,"props":1723,"children":1725},{"style":1724},"--shiki-default:#81C8BE;--shiki-dark:#F97583",[1726],{"type":34,"value":1727}," =",{"type":29,"tag":1137,"props":1729,"children":1730},{"style":1471},[1731],{"type":34,"value":1732}," await",{"type":29,"tag":1137,"props":1734,"children":1735},{"style":1477},[1736],{"type":34,"value":1737}," getOrder",{"type":29,"tag":1137,"props":1739,"children":1740},{"style":1483},[1741],{"type":34,"value":1742},"(orderId)",{"type":29,"tag":1137,"props":1744,"children":1745},{"style":1489},[1746],{"type":34,"value":1747},";\n",{"type":29,"tag":1137,"props":1749,"children":1750},{"class":1457,"line":1526},[1751,1755,1760,1764,1768,1773,1778,1784,1789,1793],{"type":29,"tag":1137,"props":1752,"children":1753},{"style":1471},[1754],{"type":34,"value":1715},{"type":29,"tag":1137,"props":1756,"children":1757},{"style":1718},[1758],{"type":34,"value":1759}," customer",{"type":29,"tag":1137,"props":1761,"children":1762},{"style":1724},[1763],{"type":34,"value":1727},{"type":29,"tag":1137,"props":1765,"children":1766},{"style":1471},[1767],{"type":34,"value":1732},{"type":29,"tag":1137,"props":1769,"children":1770},{"style":1477},[1771],{"type":34,"value":1772}," getCustomer",{"type":29,"tag":1137,"props":1774,"children":1775},{"style":1483},[1776],{"type":34,"value":1777},"(order",{"type":29,"tag":1137,"props":1779,"children":1781},{"style":1780},"--shiki-default:#81C8BE;--shiki-dark:#E1E4E8",[1782],{"type":34,"value":1783},".",{"type":29,"tag":1137,"props":1785,"children":1786},{"style":1483},[1787],{"type":34,"value":1788},"customerId)",{"type":29,"tag":1137,"props":1790,"children":1791},{"style":1489},[1792],{"type":34,"value":1492},{"type":29,"tag":1137,"props":1794,"children":1795},{"style":1462},[1796],{"type":34,"value":1797}," \u002F\u002F dépend de order\n",{"type":29,"tag":1137,"props":1799,"children":1800},{"class":1457,"line":1552},[1801,1805,1810,1814,1818,1823,1827,1831,1836,1840],{"type":29,"tag":1137,"props":1802,"children":1803},{"style":1471},[1804],{"type":34,"value":1715},{"type":29,"tag":1137,"props":1806,"children":1807},{"style":1718},[1808],{"type":34,"value":1809}," inventory",{"type":29,"tag":1137,"props":1811,"children":1812},{"style":1724},[1813],{"type":34,"value":1727},{"type":29,"tag":1137,"props":1815,"children":1816},{"style":1471},[1817],{"type":34,"value":1732},{"type":29,"tag":1137,"props":1819,"children":1820},{"style":1477},[1821],{"type":34,"value":1822}," checkInventory",{"type":29,"tag":1137,"props":1824,"children":1825},{"style":1483},[1826],{"type":34,"value":1777},{"type":29,"tag":1137,"props":1828,"children":1829},{"style":1780},[1830],{"type":34,"value":1783},{"type":29,"tag":1137,"props":1832,"children":1833},{"style":1483},[1834],{"type":34,"value":1835},"items)",{"type":29,"tag":1137,"props":1837,"children":1838},{"style":1489},[1839],{"type":34,"value":1492},{"type":29,"tag":1137,"props":1841,"children":1842},{"style":1462},[1843],{"type":34,"value":1844},"  \u002F\u002F pourrait être parallèle !\n",{"type":29,"tag":1137,"props":1846,"children":1847},{"class":1457,"line":1561},[1848,1852,1857,1861,1865,1870,1875,1880,1885],{"type":29,"tag":1137,"props":1849,"children":1850},{"style":1471},[1851],{"type":34,"value":1715},{"type":29,"tag":1137,"props":1853,"children":1854},{"style":1718},[1855],{"type":34,"value":1856}," payment",{"type":29,"tag":1137,"props":1858,"children":1859},{"style":1724},[1860],{"type":34,"value":1727},{"type":29,"tag":1137,"props":1862,"children":1863},{"style":1471},[1864],{"type":34,"value":1732},{"type":29,"tag":1137,"props":1866,"children":1867},{"style":1477},[1868],{"type":34,"value":1869}," processPayment",{"type":29,"tag":1137,"props":1871,"children":1872},{"style":1483},[1873],{"type":34,"value":1874},"(customer",{"type":29,"tag":1137,"props":1876,"children":1877},{"style":1489},[1878],{"type":34,"value":1879},",",{"type":29,"tag":1137,"props":1881,"children":1882},{"style":1483},[1883],{"type":34,"value":1884}," order)",{"type":29,"tag":1137,"props":1886,"children":1887},{"style":1489},[1888],{"type":34,"value":1747},{"type":29,"tag":1137,"props":1890,"children":1891},{"class":1457,"line":1570},[1892,1897,1902,1906,1910,1914],{"type":29,"tag":1137,"props":1893,"children":1894},{"style":1471},[1895],{"type":34,"value":1896},"    await",{"type":29,"tag":1137,"props":1898,"children":1899},{"style":1477},[1900],{"type":34,"value":1901}," sendConfirmation",{"type":29,"tag":1137,"props":1903,"children":1904},{"style":1483},[1905],{"type":34,"value":1874},{"type":29,"tag":1137,"props":1907,"children":1908},{"style":1489},[1909],{"type":34,"value":1879},{"type":29,"tag":1137,"props":1911,"children":1912},{"style":1483},[1913],{"type":34,"value":1884},{"type":29,"tag":1137,"props":1915,"children":1916},{"style":1489},[1917],{"type":34,"value":1747},{"type":29,"tag":1137,"props":1919,"children":1920},{"class":1457,"line":1579},[1921],{"type":29,"tag":1137,"props":1922,"children":1923},{"style":1489},[1924],{"type":34,"value":1925},"}\n",{"type":29,"tag":1137,"props":1927,"children":1928},{"class":1457,"line":1588},[1929],{"type":29,"tag":1137,"props":1930,"children":1931},{"emptyLinePlaceholder":13},[1932],{"type":34,"value":1558},{"type":29,"tag":1137,"props":1934,"children":1936},{"class":1457,"line":1935},10,[1937],{"type":29,"tag":1137,"props":1938,"children":1939},{"style":1462},[1940],{"type":34,"value":1941},"\u002F\u002F ✅ Parallélisation explicite pour les opérations indépendantes\n",{"type":29,"tag":1137,"props":1943,"children":1945},{"class":1457,"line":1944},11,[1946,1950,1954,1958,1962,1966,1970],{"type":29,"tag":1137,"props":1947,"children":1948},{"style":1471},[1949],{"type":34,"value":1676},{"type":29,"tag":1137,"props":1951,"children":1952},{"style":1471},[1953],{"type":34,"value":1681},{"type":29,"tag":1137,"props":1955,"children":1956},{"style":1477},[1957],{"type":34,"value":1686},{"type":29,"tag":1137,"props":1959,"children":1960},{"style":1489},[1961],{"type":34,"value":1691},{"type":29,"tag":1137,"props":1963,"children":1964},{"style":1694},[1965],{"type":34,"value":1697},{"type":29,"tag":1137,"props":1967,"children":1968},{"style":1489},[1969],{"type":34,"value":1702},{"type":29,"tag":1137,"props":1971,"children":1972},{"style":1489},[1973],{"type":34,"value":1707},{"type":29,"tag":1137,"props":1975,"children":1977},{"class":1457,"line":1976},12,[1978,1982,1986,1990,1994,1998,2002],{"type":29,"tag":1137,"props":1979,"children":1980},{"style":1471},[1981],{"type":34,"value":1715},{"type":29,"tag":1137,"props":1983,"children":1984},{"style":1718},[1985],{"type":34,"value":1721},{"type":29,"tag":1137,"props":1987,"children":1988},{"style":1724},[1989],{"type":34,"value":1727},{"type":29,"tag":1137,"props":1991,"children":1992},{"style":1471},[1993],{"type":34,"value":1732},{"type":29,"tag":1137,"props":1995,"children":1996},{"style":1477},[1997],{"type":34,"value":1737},{"type":29,"tag":1137,"props":1999,"children":2000},{"style":1483},[2001],{"type":34,"value":1742},{"type":29,"tag":1137,"props":2003,"children":2004},{"style":1489},[2005],{"type":34,"value":1747},{"type":29,"tag":1137,"props":2007,"children":2009},{"class":1457,"line":2008},13,[2010,2014,2019,2024,2028,2032,2037,2041,2045,2051,2055,2060],{"type":29,"tag":1137,"props":2011,"children":2012},{"style":1471},[2013],{"type":34,"value":1715},{"type":29,"tag":1137,"props":2015,"children":2016},{"style":1489},[2017],{"type":34,"value":2018}," [",{"type":29,"tag":1137,"props":2020,"children":2021},{"style":1718},[2022],{"type":34,"value":2023},"customer",{"type":29,"tag":1137,"props":2025,"children":2026},{"style":1489},[2027],{"type":34,"value":1879},{"type":29,"tag":1137,"props":2029,"children":2030},{"style":1718},[2031],{"type":34,"value":1809},{"type":29,"tag":1137,"props":2033,"children":2034},{"style":1489},[2035],{"type":34,"value":2036},"]",{"type":29,"tag":1137,"props":2038,"children":2039},{"style":1724},[2040],{"type":34,"value":1727},{"type":29,"tag":1137,"props":2042,"children":2043},{"style":1471},[2044],{"type":34,"value":1732},{"type":29,"tag":1137,"props":2046,"children":2048},{"style":2047},"--shiki-default:#E5C890;--shiki-default-font-style:italic;--shiki-dark:#79B8FF;--shiki-dark-font-style:inherit",[2049],{"type":34,"value":2050}," Promise",{"type":29,"tag":1137,"props":2052,"children":2053},{"style":1780},[2054],{"type":34,"value":1783},{"type":29,"tag":1137,"props":2056,"children":2057},{"style":1477},[2058],{"type":34,"value":2059},"all",{"type":29,"tag":1137,"props":2061,"children":2062},{"style":1483},[2063],{"type":34,"value":2064},"([\n",{"type":29,"tag":1137,"props":2066,"children":2068},{"class":1457,"line":2067},14,[2069,2074,2078,2082,2086],{"type":29,"tag":1137,"props":2070,"children":2071},{"style":1477},[2072],{"type":34,"value":2073},"        getCustomer",{"type":29,"tag":1137,"props":2075,"children":2076},{"style":1483},[2077],{"type":34,"value":1777},{"type":29,"tag":1137,"props":2079,"children":2080},{"style":1780},[2081],{"type":34,"value":1783},{"type":29,"tag":1137,"props":2083,"children":2084},{"style":1483},[2085],{"type":34,"value":1788},{"type":29,"tag":1137,"props":2087,"children":2088},{"style":1489},[2089],{"type":34,"value":2090},",\n",{"type":29,"tag":1137,"props":2092,"children":2093},{"class":1457,"line":1378},[2094,2099,2103,2107,2112],{"type":29,"tag":1137,"props":2095,"children":2096},{"style":1477},[2097],{"type":34,"value":2098},"        checkInventory",{"type":29,"tag":1137,"props":2100,"children":2101},{"style":1483},[2102],{"type":34,"value":1777},{"type":29,"tag":1137,"props":2104,"children":2105},{"style":1780},[2106],{"type":34,"value":1783},{"type":29,"tag":1137,"props":2108,"children":2109},{"style":1483},[2110],{"type":34,"value":2111},"items)    ",{"type":29,"tag":1137,"props":2113,"children":2114},{"style":1462},[2115],{"type":34,"value":2116},"\u002F\u002F pas de dépendance sur customer\n",{"type":29,"tag":1137,"props":2118,"children":2119},{"class":1457,"line":543},[2120,2125],{"type":29,"tag":1137,"props":2121,"children":2122},{"style":1483},[2123],{"type":34,"value":2124},"    ])",{"type":29,"tag":1137,"props":2126,"children":2127},{"style":1489},[2128],{"type":34,"value":1747},{"type":29,"tag":1137,"props":2130,"children":2132},{"class":1457,"line":2131},17,[2133,2137,2141,2145,2149,2153,2157,2161,2165],{"type":29,"tag":1137,"props":2134,"children":2135},{"style":1471},[2136],{"type":34,"value":1715},{"type":29,"tag":1137,"props":2138,"children":2139},{"style":1718},[2140],{"type":34,"value":1856},{"type":29,"tag":1137,"props":2142,"children":2143},{"style":1724},[2144],{"type":34,"value":1727},{"type":29,"tag":1137,"props":2146,"children":2147},{"style":1471},[2148],{"type":34,"value":1732},{"type":29,"tag":1137,"props":2150,"children":2151},{"style":1477},[2152],{"type":34,"value":1869},{"type":29,"tag":1137,"props":2154,"children":2155},{"style":1483},[2156],{"type":34,"value":1874},{"type":29,"tag":1137,"props":2158,"children":2159},{"style":1489},[2160],{"type":34,"value":1879},{"type":29,"tag":1137,"props":2162,"children":2163},{"style":1483},[2164],{"type":34,"value":1884},{"type":29,"tag":1137,"props":2166,"children":2167},{"style":1489},[2168],{"type":34,"value":1747},{"type":29,"tag":1137,"props":2170,"children":2172},{"class":1457,"line":2171},18,[2173,2177,2181,2185,2189,2193],{"type":29,"tag":1137,"props":2174,"children":2175},{"style":1471},[2176],{"type":34,"value":1896},{"type":29,"tag":1137,"props":2178,"children":2179},{"style":1477},[2180],{"type":34,"value":1901},{"type":29,"tag":1137,"props":2182,"children":2183},{"style":1483},[2184],{"type":34,"value":1874},{"type":29,"tag":1137,"props":2186,"children":2187},{"style":1489},[2188],{"type":34,"value":1879},{"type":29,"tag":1137,"props":2190,"children":2191},{"style":1483},[2192],{"type":34,"value":1884},{"type":29,"tag":1137,"props":2194,"children":2195},{"style":1489},[2196],{"type":34,"value":1747},{"type":29,"tag":1137,"props":2198,"children":2200},{"class":1457,"line":2199},19,[2201],{"type":29,"tag":1137,"props":2202,"children":2203},{"style":1489},[2204],{"type":34,"value":1925},{"type":29,"tag":1617,"props":2206,"children":2208},{"id":2207},"forme-3-les-timeouts-implicites-ou-absents",[2209],{"type":34,"value":2210},"Forme 3 : Les timeouts implicites ou absents",{"type":29,"tag":30,"props":2212,"children":2213},{},[2214],{"type":34,"value":2215},"Des appels vers des services externes (API, base de données, message broker) sans timeout défini. Le comportement par défaut de la plupart des clients HTTP est d'attendre indéfiniment, ou avec un timeout système de plusieurs minutes.",{"type":29,"tag":30,"props":2217,"children":2218},{},[2219,2221,2227],{"type":34,"value":2220},"Ce qui se passe ensuite est prévisible : un service tiers commence à répondre lentement. Votre service attend. Les connexions s'accumulent. Le pool de connexions s'épuise. Votre service commence à rejeter des requêtes. La cascade se propage vers les services upstream. C'est précisément le scénario que les ",{"type":29,"tag":51,"props":2222,"children":2224},{"href":2223},"\u002Ffr\u002Farchitecture-craft\u002Fpatterns-resilience-circuit-breaker-retry",[2225],{"type":34,"value":2226},"patterns de résilience comme le circuit breaker et le retry",{"type":34,"value":2228}," sont conçus pour prévenir.",{"type":29,"tag":205,"props":2230,"children":2232},{"cta":207,"href":208,"title":2231,"type":210},"Vous voulez acquérir le réflexe qui repère un couplage temporel avant qu'il parte en prod ?",[2233],{"type":29,"tag":30,"props":2234,"children":2235},{},[2236],{"type":34,"value":2237},"Voir une séquence asynchrone implicite dans votre propre code, ça ne s'apprend pas en lisant un article : ça se travaille. En mentoring 1:1, je relis votre code avec vous, on rend les contraintes d'ordre explicites, on introduit idempotence et timeouts là où il faut. Vous repartez avec le réflexe d'écrire de l'asynchrone qui ne casse pas sous la charge.",{"type":29,"tag":60,"props":2239,"children":2240},{},[],{"type":29,"tag":64,"props":2242,"children":2244},{"id":2243},"comment-le-détecter-dans-votre-code",[2245],{"type":34,"value":2246},"Comment le détecter dans votre code",{"type":29,"tag":30,"props":2248,"children":2249},{},[2250,2255,2257,2263],{"type":29,"tag":40,"props":2251,"children":2252},{},[2253],{"type":34,"value":2254},"Dans le code",{"type":34,"value":2256},", je cherche ces patterns comme indicateurs potentiels, et les ",{"type":29,"tag":51,"props":2258,"children":2260},{"href":2259},"\u002Ffr\u002Fdette-technique\u002Foutils-analyse-statique-2026",[2261],{"type":34,"value":2262},"outils d'analyse statique",{"type":34,"value":2264}," permettent d'automatiser une partie de cette détection à grande échelle :",{"type":29,"tag":241,"props":2266,"children":2267},{},[2268,2273,2278,2283],{"type":29,"tag":245,"props":2269,"children":2270},{},[2271],{"type":34,"value":2272},"Appels asynchrones séquentiels qui pourraient être parallèles : vérifiez si chacun dépend vraiment du précédent",{"type":29,"tag":245,"props":2274,"children":2275},{},[2276],{"type":34,"value":2277},"Absence de timeout sur les appels vers des services externes",{"type":29,"tag":245,"props":2279,"children":2280},{},[2281],{"type":34,"value":2282},"Commentaires du type \"\u002F\u002F doit être appelé après X\" : la contrainte est documentée mais non protégée par du code",{"type":29,"tag":245,"props":2284,"children":2285},{},[2286],{"type":34,"value":2287},"Messages ou événements qui doivent être traités dans un ordre précis sans mécanisme de garantie",{"type":29,"tag":30,"props":2289,"children":2290},{},[2291,2296],{"type":29,"tag":40,"props":2292,"children":2293},{},[2294],{"type":34,"value":2295},"En production",{"type":34,"value":2297},", les signaux sont différents :",{"type":29,"tag":241,"props":2299,"children":2300},{},[2301,2306,2311],{"type":29,"tag":245,"props":2302,"children":2303},{},[2304],{"type":34,"value":2305},"Incidents intermittents qui se résolvent avec un retry → indicateur fort de race condition ou de contrainte temporelle non respectée",{"type":29,"tag":245,"props":2307,"children":2308},{},[2309],{"type":34,"value":2310},"Latence qui augmente linéairement avec la charge → indicateur d'opérations séquentielles qui pourraient être parallèles",{"type":29,"tag":245,"props":2312,"children":2313},{},[2314],{"type":34,"value":2315},"Timeouts qui se propagent en cascade → indicateur d'absence de timeout et de circuit breaker",{"type":29,"tag":60,"props":2317,"children":2318},{},[],{"type":29,"tag":64,"props":2320,"children":2322},{"id":2321},"les-3-corrections-prioritaires",[2323],{"type":34,"value":2324},"Les 3 corrections prioritaires",{"type":29,"tag":1617,"props":2326,"children":2328},{"id":2327},"idempotence-opérations-retriables-sans-effet-de-bord",[2329],{"type":34,"value":2330},"Idempotence : opérations retriables sans effet de bord",{"type":29,"tag":30,"props":2332,"children":2333},{},[2334],{"type":34,"value":2335},"Chaque opération qui peut échouer et être retentée doit être idempotente : la ré-exécuter avec le même input produit le même résultat, sans effets de bord additionnels.",{"type":29,"tag":1446,"props":2337,"children":2341},{"className":2338,"code":2339,"language":2340,"meta":8,"style":8},"language-java shiki shiki-themes catppuccin-frappe github-dark","\u002F\u002F ❌ Non-idempotent : chaque appel crée un enregistrement\npublic void createOrder(OrderCommand cmd) {\n    Order order = new Order(cmd);\n    orderRepository.save(order);  \u002F\u002F crée un nouveau record à chaque appel\n}\n\n\u002F\u002F ✅ Idempotent : même résultat quel que soit le nombre d'appels\npublic void createOrder(OrderCommand cmd) {\n    if (orderRepository.existsById(cmd.getIdempotencyKey())) {\n        return;  \u002F\u002F déjà traité\n    }\n    Order order = new Order(cmd);\n    orderRepository.save(order);\n}\n","java",[2342],{"type":29,"tag":86,"props":2343,"children":2344},{"__ignoreMap":8},[2345,2353,2394,2436,2472,2479,2486,2494,2529,2582,2599,2607,2642,2669],{"type":29,"tag":1137,"props":2346,"children":2347},{"class":1457,"line":1458},[2348],{"type":29,"tag":1137,"props":2349,"children":2350},{"style":1462},[2351],{"type":34,"value":2352},"\u002F\u002F ❌ Non-idempotent : chaque appel crée un enregistrement\n",{"type":29,"tag":1137,"props":2354,"children":2355},{"class":1457,"line":524},[2356,2361,2366,2371,2375,2381,2386,2390],{"type":29,"tag":1137,"props":2357,"children":2358},{"style":1471},[2359],{"type":34,"value":2360},"public",{"type":29,"tag":1137,"props":2362,"children":2363},{"style":1471},[2364],{"type":34,"value":2365}," void",{"type":29,"tag":1137,"props":2367,"children":2368},{"style":1477},[2369],{"type":34,"value":2370}," createOrder",{"type":29,"tag":1137,"props":2372,"children":2373},{"style":1489},[2374],{"type":34,"value":1691},{"type":29,"tag":1137,"props":2376,"children":2378},{"style":2377},"--shiki-default:#CA9EE6;--shiki-dark:#E1E4E8",[2379],{"type":34,"value":2380},"OrderCommand",{"type":29,"tag":1137,"props":2382,"children":2383},{"style":1483},[2384],{"type":34,"value":2385}," cmd",{"type":29,"tag":1137,"props":2387,"children":2388},{"style":1489},[2389],{"type":34,"value":1702},{"type":29,"tag":1137,"props":2391,"children":2392},{"style":1489},[2393],{"type":34,"value":1707},{"type":29,"tag":1137,"props":2395,"children":2396},{"class":1457,"line":1500},[2397,2402,2407,2412,2417,2422,2426,2431],{"type":29,"tag":1137,"props":2398,"children":2399},{"style":2377},[2400],{"type":34,"value":2401},"    Order",{"type":29,"tag":1137,"props":2403,"children":2404},{"style":1483},[2405],{"type":34,"value":2406}," order ",{"type":29,"tag":1137,"props":2408,"children":2409},{"style":1724},[2410],{"type":34,"value":2411},"=",{"type":29,"tag":1137,"props":2413,"children":2414},{"style":1471},[2415],{"type":34,"value":2416}," new",{"type":29,"tag":1137,"props":2418,"children":2419},{"style":1477},[2420],{"type":34,"value":2421}," Order",{"type":29,"tag":1137,"props":2423,"children":2424},{"style":1489},[2425],{"type":34,"value":1691},{"type":29,"tag":1137,"props":2427,"children":2428},{"style":1483},[2429],{"type":34,"value":2430},"cmd",{"type":29,"tag":1137,"props":2432,"children":2433},{"style":1489},[2434],{"type":34,"value":2435},");\n",{"type":29,"tag":1137,"props":2437,"children":2438},{"class":1457,"line":1526},[2439,2444,2448,2453,2457,2462,2467],{"type":29,"tag":1137,"props":2440,"children":2441},{"style":1483},[2442],{"type":34,"value":2443},"    orderRepository",{"type":29,"tag":1137,"props":2445,"children":2446},{"style":1489},[2447],{"type":34,"value":1783},{"type":29,"tag":1137,"props":2449,"children":2450},{"style":1477},[2451],{"type":34,"value":2452},"save",{"type":29,"tag":1137,"props":2454,"children":2455},{"style":1489},[2456],{"type":34,"value":1691},{"type":29,"tag":1137,"props":2458,"children":2459},{"style":1483},[2460],{"type":34,"value":2461},"order",{"type":29,"tag":1137,"props":2463,"children":2464},{"style":1489},[2465],{"type":34,"value":2466},");",{"type":29,"tag":1137,"props":2468,"children":2469},{"style":1462},[2470],{"type":34,"value":2471},"  \u002F\u002F crée un nouveau record à chaque appel\n",{"type":29,"tag":1137,"props":2473,"children":2474},{"class":1457,"line":1552},[2475],{"type":29,"tag":1137,"props":2476,"children":2477},{"style":1489},[2478],{"type":34,"value":1925},{"type":29,"tag":1137,"props":2480,"children":2481},{"class":1457,"line":1561},[2482],{"type":29,"tag":1137,"props":2483,"children":2484},{"emptyLinePlaceholder":13},[2485],{"type":34,"value":1558},{"type":29,"tag":1137,"props":2487,"children":2488},{"class":1457,"line":1570},[2489],{"type":29,"tag":1137,"props":2490,"children":2491},{"style":1462},[2492],{"type":34,"value":2493},"\u002F\u002F ✅ Idempotent : même résultat quel que soit le nombre d'appels\n",{"type":29,"tag":1137,"props":2495,"children":2496},{"class":1457,"line":1579},[2497,2501,2505,2509,2513,2517,2521,2525],{"type":29,"tag":1137,"props":2498,"children":2499},{"style":1471},[2500],{"type":34,"value":2360},{"type":29,"tag":1137,"props":2502,"children":2503},{"style":1471},[2504],{"type":34,"value":2365},{"type":29,"tag":1137,"props":2506,"children":2507},{"style":1477},[2508],{"type":34,"value":2370},{"type":29,"tag":1137,"props":2510,"children":2511},{"style":1489},[2512],{"type":34,"value":1691},{"type":29,"tag":1137,"props":2514,"children":2515},{"style":2377},[2516],{"type":34,"value":2380},{"type":29,"tag":1137,"props":2518,"children":2519},{"style":1483},[2520],{"type":34,"value":2385},{"type":29,"tag":1137,"props":2522,"children":2523},{"style":1489},[2524],{"type":34,"value":1702},{"type":29,"tag":1137,"props":2526,"children":2527},{"style":1489},[2528],{"type":34,"value":1707},{"type":29,"tag":1137,"props":2530,"children":2531},{"class":1457,"line":1588},[2532,2537,2542,2547,2551,2556,2560,2564,2568,2573,2578],{"type":29,"tag":1137,"props":2533,"children":2534},{"style":1471},[2535],{"type":34,"value":2536},"    if",{"type":29,"tag":1137,"props":2538,"children":2539},{"style":1489},[2540],{"type":34,"value":2541}," (",{"type":29,"tag":1137,"props":2543,"children":2544},{"style":1483},[2545],{"type":34,"value":2546},"orderRepository",{"type":29,"tag":1137,"props":2548,"children":2549},{"style":1489},[2550],{"type":34,"value":1783},{"type":29,"tag":1137,"props":2552,"children":2553},{"style":1477},[2554],{"type":34,"value":2555},"existsById",{"type":29,"tag":1137,"props":2557,"children":2558},{"style":1489},[2559],{"type":34,"value":1691},{"type":29,"tag":1137,"props":2561,"children":2562},{"style":1483},[2563],{"type":34,"value":2430},{"type":29,"tag":1137,"props":2565,"children":2566},{"style":1489},[2567],{"type":34,"value":1783},{"type":29,"tag":1137,"props":2569,"children":2570},{"style":1477},[2571],{"type":34,"value":2572},"getIdempotencyKey",{"type":29,"tag":1137,"props":2574,"children":2575},{"style":1489},[2576],{"type":34,"value":2577},"()))",{"type":29,"tag":1137,"props":2579,"children":2580},{"style":1489},[2581],{"type":34,"value":1707},{"type":29,"tag":1137,"props":2583,"children":2584},{"class":1457,"line":1935},[2585,2590,2594],{"type":29,"tag":1137,"props":2586,"children":2587},{"style":1471},[2588],{"type":34,"value":2589},"        return",{"type":29,"tag":1137,"props":2591,"children":2592},{"style":1489},[2593],{"type":34,"value":1492},{"type":29,"tag":1137,"props":2595,"children":2596},{"style":1462},[2597],{"type":34,"value":2598},"  \u002F\u002F déjà traité\n",{"type":29,"tag":1137,"props":2600,"children":2601},{"class":1457,"line":1944},[2602],{"type":29,"tag":1137,"props":2603,"children":2604},{"style":1489},[2605],{"type":34,"value":2606},"    }\n",{"type":29,"tag":1137,"props":2608,"children":2609},{"class":1457,"line":1976},[2610,2614,2618,2622,2626,2630,2634,2638],{"type":29,"tag":1137,"props":2611,"children":2612},{"style":2377},[2613],{"type":34,"value":2401},{"type":29,"tag":1137,"props":2615,"children":2616},{"style":1483},[2617],{"type":34,"value":2406},{"type":29,"tag":1137,"props":2619,"children":2620},{"style":1724},[2621],{"type":34,"value":2411},{"type":29,"tag":1137,"props":2623,"children":2624},{"style":1471},[2625],{"type":34,"value":2416},{"type":29,"tag":1137,"props":2627,"children":2628},{"style":1477},[2629],{"type":34,"value":2421},{"type":29,"tag":1137,"props":2631,"children":2632},{"style":1489},[2633],{"type":34,"value":1691},{"type":29,"tag":1137,"props":2635,"children":2636},{"style":1483},[2637],{"type":34,"value":2430},{"type":29,"tag":1137,"props":2639,"children":2640},{"style":1489},[2641],{"type":34,"value":2435},{"type":29,"tag":1137,"props":2643,"children":2644},{"class":1457,"line":2008},[2645,2649,2653,2657,2661,2665],{"type":29,"tag":1137,"props":2646,"children":2647},{"style":1483},[2648],{"type":34,"value":2443},{"type":29,"tag":1137,"props":2650,"children":2651},{"style":1489},[2652],{"type":34,"value":1783},{"type":29,"tag":1137,"props":2654,"children":2655},{"style":1477},[2656],{"type":34,"value":2452},{"type":29,"tag":1137,"props":2658,"children":2659},{"style":1489},[2660],{"type":34,"value":1691},{"type":29,"tag":1137,"props":2662,"children":2663},{"style":1483},[2664],{"type":34,"value":2461},{"type":29,"tag":1137,"props":2666,"children":2667},{"style":1489},[2668],{"type":34,"value":2435},{"type":29,"tag":1137,"props":2670,"children":2671},{"class":1457,"line":2067},[2672],{"type":29,"tag":1137,"props":2673,"children":2674},{"style":1489},[2675],{"type":34,"value":1925},{"type":29,"tag":1617,"props":2677,"children":2679},{"id":2678},"sagas-orchestration-explicite-des-séquences-longues",[2680],{"type":34,"value":2681},"Sagas : orchestration explicite des séquences longues",{"type":29,"tag":30,"props":2683,"children":2684},{},[2685,2687,2692],{"type":34,"value":2686},"Pour les transactions distribuées qui s'étendent sur plusieurs services, le pattern Saga, décrit par Vernon Vaughn dans \"Implementing Domain-Driven Design\", rend la séquence explicite et gère les compensations en cas d'échec partiel. Réduire ce couplage est aussi un objectif à inscrire dans un ",{"type":29,"tag":51,"props":2688,"children":2689},{"href":540},[2690],{"type":34,"value":2691},"programme de refactoring validé par le business",{"type":34,"value":2693},", pour que ces améliorations soient planifiées et financées correctement.",{"type":29,"tag":30,"props":2695,"children":2696},{},[2697],{"type":34,"value":2698},"Chorégraphié ou orchestré, le pattern Saga transforme une contrainte implicite d'ordre en protocole explicite avec gestion des cas d'échec.",{"type":29,"tag":1617,"props":2700,"children":2702},{"id":2701},"timeouts-explicites-sur-tous-les-appels-externes",[2703],{"type":34,"value":2704},"Timeouts explicites sur tous les appels externes",{"type":29,"tag":30,"props":2706,"children":2707},{},[2708],{"type":34,"value":2709},"Tout appel vers un service externe doit avoir un timeout défini par le code, pas par la valeur par défaut du client HTTP. Ce seul changement élimine la propagation en cascade lors des dégradations de services.",{"type":29,"tag":188,"props":2711,"children":2712},{},[2713],{"type":29,"tag":30,"props":2714,"children":2715},{},[2716,2718,2724],{"type":34,"value":2717},"Dans la plateforme bancaire que j'évoquais en ouverture, l'introduction de l'idempotence sur le service de virement a résolu le problème définitivement en 3 jours de développement. Le bug était présent depuis 18 mois. 18 mois de corrections manuelles, de runbooks, de post-mortems, résolus par un ",{"type":29,"tag":86,"props":2719,"children":2721},{"className":2720},[],[2722],{"type":34,"value":2723},"idempotencyKey",{"type":34,"value":2725}," et une vérification de doublon.",{"type":29,"tag":60,"props":2727,"children":2728},{},[],{"type":29,"tag":64,"props":2730,"children":2732},{"id":2731},"la-règle-dor",[2733],{"type":34,"value":2734},"La règle d'or",{"type":29,"tag":30,"props":2736,"children":2737},{},[2738,2743],{"type":29,"tag":40,"props":2739,"children":2740},{},[2741],{"type":34,"value":2742},"Chaque opération asynchrone doit être idempotente et retriable.",{"type":34,"value":2744}," Si elle ne l'est pas, la contrainte temporelle qui en découle doit être rendue explicite et protégée par du code, pas par de la documentation.",{"type":29,"tag":30,"props":2746,"children":2747},{},[2748,2750,2755],{"type":34,"value":2749},"L'introduction de l'idempotence et des timeouts explicites sur les 5 à 10 flux les plus critiques réduit de ",{"type":29,"tag":40,"props":2751,"children":2752},{},[2753],{"type":34,"value":2754},"70 à 80%",{"type":34,"value":2756}," le risque associé au couplage temporel existant dans un système.",{"type":29,"tag":60,"props":2758,"children":2759},{},[],{"type":29,"tag":205,"props":2761,"children":2763},{"cta":422,"href":423,"title":2762,"type":425},"Maîtriser le couplage temporel n'est qu'une pratique : il en existe 100",[2764],{"type":29,"tag":30,"props":2765,"children":2766},{},[2767],{"type":34,"value":2768},"Rendre explicite une contrainte d'ordre, c'est une des pratiques craft qui séparent le code qui tient en prod du code qui tombe au premier pic de charge. Le Craft Bundle réunit les 100 pratiques que j'applique pour coder propre, celles que l'IA ne vous apprendra jamais parce qu'elle ne les a jamais vues casser un vendredi soir en astreinte.",{"type":29,"tag":60,"props":2770,"children":2771},{},[],{"type":29,"tag":64,"props":2773,"children":2775},{"id":2774},"faq-sur-le-couplage-temporel",[2776],{"type":34,"value":2777},"FAQ sur le couplage temporel",{"type":29,"tag":442,"props":2779,"children":2780},{},[2781,2786],{"type":29,"tag":446,"props":2782,"children":2783},{},[2784],{"type":34,"value":2785},"1. Quelle est la différence entre couplage temporel et couplage spatial ?",{"type":29,"tag":30,"props":2787,"children":2788},{},[2789],{"type":34,"value":2790},"Le couplage spatial (structural coupling) concerne la dépendance directe entre deux composants : A connaît et appelle B. Le couplage temporel concerne la dépendance sur l'ordre ou le timing : A doit s'exécuter avant B, ou A et B doivent s'exécuter dans la même fenêtre temporelle. Les deux sont des formes de couplage, mais le temporel est plus difficile à détecter car il n'apparaît pas dans les dépendances statiques du code. C'est précisément ce qui le rend dangereux.",{"type":29,"tag":442,"props":2792,"children":2793},{},[2794,2799],{"type":29,"tag":446,"props":2795,"children":2796},{},[2797],{"type":34,"value":2798},"2. Comment tester les problèmes de couplage temporel ?",{"type":29,"tag":30,"props":2800,"children":2801},{},[2802,2804,2810],{"type":34,"value":2803},"Le chaos engineering est la méthode la plus efficace : injecter des délais aléatoires sur les appels vers les services dépendants et observer si le système se comporte correctement. Les outils comme Chaos Monkey, Gremlin, ou Pumba permettent d'introduire des latences contrôlées en environnement de staging. Pour commencer sans outil spécifique : injecter un ",{"type":29,"tag":86,"props":2805,"children":2807},{"className":2806},[],[2808],{"type":34,"value":2809},"sleep(aléatoire)",{"type":34,"value":2811}," en environnement de test et vérifier que les timeouts et les garanties d'ordre fonctionnent comme attendu.",{"type":29,"tag":442,"props":2813,"children":2814},{},[2815,2820],{"type":29,"tag":446,"props":2816,"children":2817},{},[2818],{"type":34,"value":2819},"3. L'idempotence est-elle possible sur tous les types d'opérations ?",{"type":29,"tag":30,"props":2821,"children":2822},{},[2823],{"type":34,"value":2824},"Presque toujours, avec la bonne conception. La clé est l'idempotency key : un identifiant unique de la requête que le client génère et que le serveur utilise pour détecter les doublons. Les opérations financières, les envois d'emails, et les modifications d'état peuvent toutes être rendues idempotentes avec ce pattern. La seule limitation réelle concerne les opérations dont le résultat dépend de l'heure exacte, mais celles-ci peuvent être gérées avec un mécanisme de verrou temporel.",{"type":29,"tag":442,"props":2826,"children":2827},{},[2828,2833],{"type":29,"tag":446,"props":2829,"children":2830},{},[2831],{"type":34,"value":2832},"4. Quelle est la différence entre couplage temporel et race condition ?",{"type":29,"tag":30,"props":2834,"children":2835},{},[2836],{"type":34,"value":2837},"Une race condition est un cas particulier de couplage temporel : deux opérations concurrentes accèdent au même état partagé et le résultat dépend de l'ordre d'exécution. Le couplage temporel est plus large : il inclut aussi les séquences non-concurrentes qui doivent se dérouler dans un ordre précis. Toutes les race conditions sont des couplages temporels, mais tous les couplages temporels ne sont pas des race conditions.",{"type":29,"tag":60,"props":2839,"children":2840},{},[],{"type":29,"tag":205,"props":2842,"children":2845},{"cta":2843,"href":2844,"title":515,"type":516},"Faire mon auto-évaluation →","\u002Fema",[2846],{"type":29,"tag":30,"props":2847,"children":2848},{},[2849],{"type":34,"value":2850},"L'Engineering Maturity Self-Assessment couvre le domaine Architecture & Résilience : évaluez votre niveau sur la gestion du couplage, les patterns asynchrones, et la robustesse de vos services. Score et recommandations en 10 minutes.",{"type":29,"tag":2852,"props":2853,"children":2854},"style",{},[2855],{"type":34,"value":2856},"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":524,"depth":524,"links":2858},[2859,2860,2865,2866,2871,2872],{"id":1415,"depth":524,"text":1418},{"id":1612,"depth":524,"text":1615,"children":2861},[2862,2863,2864],{"id":1619,"depth":1500,"text":1622},{"id":1645,"depth":1500,"text":1648},{"id":2207,"depth":1500,"text":2210},{"id":2243,"depth":524,"text":2246},{"id":2321,"depth":524,"text":2324,"children":2867},[2868,2869,2870],{"id":2327,"depth":1500,"text":2330},{"id":2678,"depth":1500,"text":2681},{"id":2701,"depth":1500,"text":2704},{"id":2731,"depth":524,"text":2734},{"id":2774,"depth":524,"text":2777},"content:fr:architecture-craft:couplage-temporel-code-asynchrone.md","fr\u002Farchitecture-craft\u002Fcouplage-temporel-code-asynchrone.md","fr\u002Farchitecture-craft\u002Fcouplage-temporel-code-asynchrone",{"_path":323,"_dir":6,"_draft":7,"_partial":7,"_locale":8,"title":2877,"description":2878,"id":1561,"date":2879,"listed":13,"nocomments":7,"hidden":7,"categories":2880,"tags":2881,"cover":2882,"readingTime":2883,"body":2888,"_type":533,"_id":3589,"_source":535,"_file":3590,"_stem":3591,"_extension":538},"Legacy code : comment évaluer le risque avant d'intervenir","Modifier du code legacy sans évaluation préalable est la cause #1 des régressions. Les 4 dimensions de risque à évaluer avant de toucher à la moindre ligne.","2026-01-16",[6],[17],"covers\u002Farticles\u002Flegacy-code-risque.jpg",{"text":2884,"minutes":2885,"time":2886,"words":2887},"9 min read",8.315,498900,1663,{"type":26,"children":2889,"toc":3580},[2890,2895,2903,2908,2911,2917,2922,2934,2946,2951,2954,2960,2965,2974,2992,3001,3019,3039,3042,3048,3053,3061,3079,3088,3106,3119,3122,3131,3134,3140,3145,3153,3178,3187,3205,3215,3218,3224,3229,3237,3255,3264,3297,3310,3313,3319,3324,3446,3455,3473,3478,3487,3490,3496,3509,3522,3535,3548,3569,3572],{"type":29,"tag":30,"props":2891,"children":2892},{},[2893],{"type":34,"value":2894},"J'étais dans une grande banque de réseau, avec une équipe de 35 développeurs. Un tech lead expérimenté m'annonce qu'il va \"faire un petit refactoring de 2 jours\" sur un module de calcul de frais. Deux semaines plus tard, la release était bloquée. Le module contenait une règle fiscale codée dans une condition sans commentaire, que personne ne connaissait plus. La personne qui l'avait écrite avait quitté l'entreprise trois ans plus tôt. Ce refactoring \"anodin\" avait cassé silencieusement un calcul utilisé sur 40 000 comptes clients.",{"type":29,"tag":30,"props":2896,"children":2897},{},[2898],{"type":29,"tag":40,"props":2899,"children":2900},{},[2901],{"type":34,"value":2902},"Modifier du code legacy sans évaluation préalable est la cause numéro un des régressions inattendues. Non pas parce que les développeurs sont imprudents, mais parce que le code legacy contient des comportements implicites que personne ne connaît plus.",{"type":29,"tag":30,"props":2904,"children":2905},{},[2906],{"type":34,"value":2907},"J'ai vu des équipes bloquer des releases entières pour des \"petits refactorings de 2 jours\". J'en ai vu d'autres refuser de toucher à des modules critiques pendant des années, laissant la dette s'accumuler jusqu'à ce que la maintenance devienne insoutenable. Entre la témérité et la paralysie, il existe une voie structurée : l'évaluation de risque en 4 dimensions.",{"type":29,"tag":60,"props":2909,"children":2910},{},[],{"type":29,"tag":64,"props":2912,"children":2914},{"id":2913},"pourquoi-le-code-legacy-est-un-terrain-miné",[2915],{"type":34,"value":2916},"Pourquoi le code legacy est un terrain miné",{"type":29,"tag":30,"props":2918,"children":2919},{},[2920],{"type":34,"value":2921},"Le danger du code legacy n'est pas sa complexité syntaxique. C'est l'accumulation de comportements implicites : des règles métier encodées dans des conditions sans commentaire, des effets de bord non documentés, des dépendances cachées entre modules qui semblent indépendants.",{"type":29,"tag":30,"props":2923,"children":2924},{},[2925,2927,2932],{"type":34,"value":2926},"Une étude de Cambridge University a estimé que les développeurs passent ",{"type":29,"tag":40,"props":2928,"children":2929},{},[2930],{"type":34,"value":2931},"50% de leur temps",{"type":34,"value":2933}," à comprendre du code existant avant de le modifier. Sur du code legacy, ce chiffre peut atteindre 70-80%.",{"type":29,"tag":30,"props":2935,"children":2936},{},[2937,2939,2944],{"type":34,"value":2938},"Le coût d'une régression sur un module critique va bien au-delà du temps de correction : il inclut le rollback, la communication de crise, le test de non-régression élargi, et la perte de confiance en l'équipe. Dans le secteur financier, une régression sur un moteur de calcul peut déclencher un incident réglementaire aux conséquences disproportionnées. J'ai accompagné une équipe Crédit Agricole où un incident de ce type avait mobilisé 12 personnes pendant 4 jours (voir comment ",{"type":29,"tag":51,"props":2940,"children":2941},{"href":540},[2942],{"type":34,"value":2943},"obtenir le budget pour traiter ces risques",{"type":34,"value":2945},"), soit 48 jours-homme pour corriger ce qu'une évaluation de risque préalable aurait évité.",{"type":29,"tag":30,"props":2947,"children":2948},{},[2949],{"type":34,"value":2950},"La bonne nouvelle : le risque d'intervention sur le legacy est évaluable. Il repose sur 4 dimensions.",{"type":29,"tag":60,"props":2952,"children":2953},{},[],{"type":29,"tag":64,"props":2955,"children":2957},{"id":2956},"dimension-1-couverture-de-tests-ou-son-absence",[2958],{"type":34,"value":2959},"Dimension 1 : Couverture de tests (ou son absence)",{"type":29,"tag":30,"props":2961,"children":2962},{},[2963],{"type":34,"value":2964},"C'est la dimension la plus visible et la plus impactante. Un module bien couvert en tests est un module sur lequel on peut intervenir avec confiance : si on casse quelque chose, les tests le disent immédiatement.",{"type":29,"tag":30,"props":2966,"children":2967},{},[2968,2973],{"type":29,"tag":40,"props":2969,"children":2970},{},[2971],{"type":34,"value":2972},"Comment évaluer",{"type":34,"value":239},{"type":29,"tag":241,"props":2975,"children":2976},{},[2977,2982,2987],{"type":29,"tag":245,"props":2978,"children":2979},{},[2980],{"type":34,"value":2981},"Calculer la couverture de tests du module cible (outils : JaCoCo, Istanbul, Coverage.py)",{"type":29,"tag":245,"props":2983,"children":2984},{},[2985],{"type":34,"value":2986},"Distinguer couverture de lignes (insuffisante) et couverture de branches (significative)",{"type":29,"tag":245,"props":2988,"children":2989},{},[2990],{"type":34,"value":2991},"Identifier les tests d'intégration qui couvrent le comportement end-to-end, pas seulement les tests unitaires",{"type":29,"tag":30,"props":2993,"children":2994},{},[2995,3000],{"type":29,"tag":40,"props":2996,"children":2997},{},[2998],{"type":34,"value":2999},"Seuils de risque",{"type":34,"value":239},{"type":29,"tag":241,"props":3002,"children":3003},{},[3004,3009,3014],{"type":29,"tag":245,"props":3005,"children":3006},{},[3007],{"type":34,"value":3008},"Couverture > 70% avec tests d'intégration → risque faible",{"type":29,"tag":245,"props":3010,"children":3011},{},[3012],{"type":34,"value":3013},"Couverture 30-70% → risque modéré, ajouter des tests de caractérisation avant d'intervenir",{"type":29,"tag":245,"props":3015,"children":3016},{},[3017],{"type":34,"value":3018},"Couverture \u003C 30% → risque élevé, écrire des characterization tests avant tout refactoring",{"type":29,"tag":188,"props":3020,"children":3021},{},[3022],{"type":29,"tag":30,"props":3023,"children":3024},{},[3025,3030,3032,3037],{"type":29,"tag":40,"props":3026,"children":3027},{},[3028],{"type":34,"value":3029},"La règle de Michael Feathers",{"type":34,"value":3031}," dans \"Working Effectively with Legacy Code\" : avant de modifier du legacy sans tests, écrire un \"",{"type":29,"tag":51,"props":3033,"children":3034},{"href":5},[3035],{"type":34,"value":3036},"characterization test",{"type":34,"value":3038},"\" : un test qui documente le comportement actuel, même s'il semble bizarre. Ce test devient votre filet de sécurité. Ce livre reste la référence absolue sur le sujet, 20 ans après sa publication.",{"type":29,"tag":60,"props":3040,"children":3041},{},[],{"type":29,"tag":64,"props":3043,"children":3045},{"id":3044},"dimension-2-compréhension-métier-du-code",[3046],{"type":34,"value":3047},"Dimension 2 : Compréhension métier du code",{"type":29,"tag":30,"props":3049,"children":3050},{},[3051],{"type":34,"value":3052},"Un module peut avoir une couverture de tests à 80% et rester dangereux si personne dans l'équipe ne comprend ce qu'il fait sur le plan métier.",{"type":29,"tag":30,"props":3054,"children":3055},{},[3056,3060],{"type":29,"tag":40,"props":3057,"children":3058},{},[3059],{"type":34,"value":2972},{"type":34,"value":239},{"type":29,"tag":241,"props":3062,"children":3063},{},[3064,3069,3074],{"type":29,"tag":245,"props":3065,"children":3066},{},[3067],{"type":34,"value":3068},"Identifier la ou les personnes capables d'expliquer la règle métier implémentée",{"type":29,"tag":245,"props":3070,"children":3071},{},[3072],{"type":34,"value":3073},"Vérifier si la documentation métier (spécifications, tickets d'origine) existe et est accessible",{"type":29,"tag":245,"props":3075,"children":3076},{},[3077],{"type":34,"value":3078},"Estimer le \"bus factor\" : si cette personne part, le module devient-il opaque ?",{"type":29,"tag":30,"props":3080,"children":3081},{},[3082,3087],{"type":29,"tag":40,"props":3083,"children":3084},{},[3085],{"type":34,"value":3086},"Questions concrètes à poser",{"type":34,"value":239},{"type":29,"tag":241,"props":3089,"children":3090},{},[3091,3096,3101],{"type":29,"tag":245,"props":3092,"children":3093},{},[3094],{"type":34,"value":3095},"\"Pouvez-vous m'expliquer ce que ce module fait en 3 phrases, sans regarder le code ?\"",{"type":29,"tag":245,"props":3097,"children":3098},{},[3099],{"type":34,"value":3100},"\"Existe-t-il des cas limites non évidents dans cette règle métier ?\"",{"type":29,"tag":245,"props":3102,"children":3103},{},[3104],{"type":34,"value":3105},"\"Y a-t-il eu des incidents passés sur ce module dont la cause était une incompréhension métier ?\"",{"type":29,"tag":188,"props":3107,"children":3108},{},[3109],{"type":29,"tag":30,"props":3110,"children":3111},{},[3112,3117],{"type":29,"tag":40,"props":3113,"children":3114},{},[3115],{"type":34,"value":3116},"Ce que j'ai vécu dans une société d'assurance vie",{"type":34,"value":3118}," : un module de calcul de prime avait une couverture de tests à 65%. L'équipe estimait le risque modéré. Mais lors de l'entretien, il s'est avéré que l'unique développeur qui comprenait les règles fiscales avait quitté l'entreprise 18 mois plus tôt. Le risque réel était élevé. Nous avons passé 3 jours à documenter le comportement avant d'intervenir. Ce délai a semblé coûteux sur le moment, mais il nous a évité une régression sur 80 000 contrats.",{"type":29,"tag":60,"props":3120,"children":3121},{},[],{"type":29,"tag":205,"props":3123,"children":3125},{"cta":207,"href":208,"title":3124,"type":210},"Vous voulez acquérir le réflexe d'évaluer un module legacy avant d'y toucher ?",[3126],{"type":29,"tag":30,"props":3127,"children":3128},{},[3129],{"type":34,"value":3130},"Lire la couverture de tests, repérer le couplage caché, sentir quand un module métier est plus dangereux qu'il n'en a l'air : ça ne se résume pas à une checklist, ça se travaille sur du vrai code. En mentoring 1:1, on prend votre legacy, on déroule les 4 dimensions ensemble, et je vous transmets la lecture du risque que j'applique avant chaque refactoring. Vous repartez capable de cadrer une intervention seul, sans paralysie ni témérité.",{"type":29,"tag":60,"props":3132,"children":3133},{},[],{"type":29,"tag":64,"props":3135,"children":3137},{"id":3136},"dimension-3-couplage-et-surface-dimpact",[3138],{"type":34,"value":3139},"Dimension 3 : Couplage et surface d'impact",{"type":29,"tag":30,"props":3141,"children":3142},{},[3143],{"type":34,"value":3144},"Un module peut être simple à comprendre et bien testé, et quand même risqué à modifier s'il est fortement couplé à d'autres modules. Chaque modification peut déclencher des effets en cascade.",{"type":29,"tag":30,"props":3146,"children":3147},{},[3148,3152],{"type":29,"tag":40,"props":3149,"children":3150},{},[3151],{"type":34,"value":2972},{"type":34,"value":239},{"type":29,"tag":241,"props":3154,"children":3155},{},[3156,3168,3173],{"type":29,"tag":245,"props":3157,"children":3158},{},[3159,3161,3167],{"type":34,"value":3160},"Identifier les dépendances entrantes et sortantes du module (outils : Structure101, Sonargraph, ",{"type":29,"tag":86,"props":3162,"children":3164},{"className":3163},[],[3165],{"type":34,"value":3166},"git log --follow",{"type":34,"value":1702},{"type":29,"tag":245,"props":3169,"children":3170},{},[3171],{"type":34,"value":3172},"Cartographier les modules qui appellent le module cible",{"type":29,"tag":245,"props":3174,"children":3175},{},[3176],{"type":34,"value":3177},"Évaluer le couplage de données : le module partage-t-il des structures de données ou une base avec d'autres modules ?",{"type":29,"tag":30,"props":3179,"children":3180},{},[3181,3186],{"type":29,"tag":40,"props":3182,"children":3183},{},[3184],{"type":34,"value":3185},"Indicateurs de couplage à risque",{"type":34,"value":239},{"type":29,"tag":241,"props":3188,"children":3189},{},[3190,3195,3200],{"type":29,"tag":245,"props":3191,"children":3192},{},[3193],{"type":34,"value":3194},"Plus de 5 modules différents qui appellent directement le module cible",{"type":29,"tag":245,"props":3196,"children":3197},{},[3198],{"type":34,"value":3199},"Modification de schéma de base de données partagée",{"type":29,"tag":245,"props":3201,"children":3202},{},[3203],{"type":34,"value":3204},"Présence de \"shotgun surgery\" : un changement logique qui nécessite de modifier 8+ fichiers",{"type":29,"tag":30,"props":3206,"children":3207},{},[3208,3213],{"type":29,"tag":40,"props":3209,"children":3210},{},[3211],{"type":34,"value":3212},"La règle empirique",{"type":34,"value":3214}," : pour chaque module entrant dans le scope de refactoring, je vérifie les 3 niveaux de dépendances (direct, indirect, transitif). La surface d'impact réelle est souvent 3 à 5 fois plus large que l'estimation initiale.",{"type":29,"tag":60,"props":3216,"children":3217},{},[],{"type":29,"tag":64,"props":3219,"children":3221},{"id":3220},"dimension-4-criticité-business-du-module",[3222],{"type":34,"value":3223},"Dimension 4 : Criticité business du module",{"type":29,"tag":30,"props":3225,"children":3226},{},[3227],{"type":34,"value":3228},"Deux modules à risque technique équivalent n'ont pas le même risque réel si l'un gère les notifications email et l'autre les virements bancaires.",{"type":29,"tag":30,"props":3230,"children":3231},{},[3232,3236],{"type":29,"tag":40,"props":3233,"children":3234},{},[3235],{"type":34,"value":2972},{"type":34,"value":239},{"type":29,"tag":241,"props":3238,"children":3239},{},[3240,3245,3250],{"type":29,"tag":245,"props":3241,"children":3242},{},[3243],{"type":34,"value":3244},"Identifier la criticité business : quel processus métier ce module supporte-t-il ?",{"type":29,"tag":245,"props":3246,"children":3247},{},[3248],{"type":34,"value":3249},"Évaluer l'impact d'une régression : financier, réglementaire, réputationnel",{"type":29,"tag":245,"props":3251,"children":3252},{},[3253],{"type":34,"value":3254},"Vérifier le SLA : ce module a-t-il un SLA de disponibilité ou de performance ?",{"type":29,"tag":30,"props":3256,"children":3257},{},[3258,3263],{"type":29,"tag":40,"props":3259,"children":3260},{},[3261],{"type":34,"value":3262},"Niveaux de criticité",{"type":34,"value":239},{"type":29,"tag":241,"props":3265,"children":3266},{},[3267,3277,3287],{"type":29,"tag":245,"props":3268,"children":3269},{},[3270,3275],{"type":29,"tag":40,"props":3271,"children":3272},{},[3273],{"type":34,"value":3274},"Critique",{"type":34,"value":3276}," : module dans le chemin de traitement principal, impact direct sur le revenu ou la conformité réglementaire",{"type":29,"tag":245,"props":3278,"children":3279},{},[3280,3285],{"type":29,"tag":40,"props":3281,"children":3282},{},[3283],{"type":34,"value":3284},"Important",{"type":34,"value":3286}," : module utilisé quotidiennement, régression visible par les utilisateurs mais non-bloquante",{"type":29,"tag":245,"props":3288,"children":3289},{},[3290,3295],{"type":29,"tag":40,"props":3291,"children":3292},{},[3293],{"type":34,"value":3294},"Standard",{"type":34,"value":3296}," : module d'administration, batch non-temps-réel, fonctionnalité non-critique",{"type":29,"tag":188,"props":3298,"children":3299},{},[3300],{"type":29,"tag":30,"props":3301,"children":3302},{},[3303,3308],{"type":29,"tag":40,"props":3304,"children":3305},{},[3306],{"type":34,"value":3307},"Règle",{"type":34,"value":3309}," : la criticité business multiplie le risque technique. Un module à risque technique modéré + criticité haute = risque réel élevé. Je refuse de commencer un refactoring sans avoir établi ce mapping clairement.",{"type":29,"tag":60,"props":3311,"children":3312},{},[],{"type":29,"tag":64,"props":3314,"children":3316},{"id":3315},"la-matrice-de-risque-et-comment-lutiliser",[3317],{"type":34,"value":3318},"La matrice de risque et comment l'utiliser",{"type":29,"tag":30,"props":3320,"children":3321},{},[3322],{"type":34,"value":3323},"Je combine les 4 dimensions dans une matrice simple :",{"type":29,"tag":787,"props":3325,"children":3326},{},[3327,3353],{"type":29,"tag":791,"props":3328,"children":3329},{},[3330],{"type":29,"tag":795,"props":3331,"children":3332},{},[3333,3338,3343,3348],{"type":29,"tag":799,"props":3334,"children":3335},{},[3336],{"type":34,"value":3337},"Dimension",{"type":29,"tag":799,"props":3339,"children":3340},{},[3341],{"type":34,"value":3342},"Score 1 (faible)",{"type":29,"tag":799,"props":3344,"children":3345},{},[3346],{"type":34,"value":3347},"Score 2 (modéré)",{"type":29,"tag":799,"props":3349,"children":3350},{},[3351],{"type":34,"value":3352},"Score 3 (élevé)",{"type":29,"tag":813,"props":3354,"children":3355},{},[3356,3379,3402,3425],{"type":29,"tag":795,"props":3357,"children":3358},{},[3359,3364,3369,3374],{"type":29,"tag":820,"props":3360,"children":3361},{},[3362],{"type":34,"value":3363},"Couverture tests",{"type":29,"tag":820,"props":3365,"children":3366},{},[3367],{"type":34,"value":3368},"> 70%",{"type":29,"tag":820,"props":3370,"children":3371},{},[3372],{"type":34,"value":3373},"30-70%",{"type":29,"tag":820,"props":3375,"children":3376},{},[3377],{"type":34,"value":3378},"\u003C 30%",{"type":29,"tag":795,"props":3380,"children":3381},{},[3382,3387,3392,3397],{"type":29,"tag":820,"props":3383,"children":3384},{},[3385],{"type":34,"value":3386},"Compréhension métier",{"type":29,"tag":820,"props":3388,"children":3389},{},[3390],{"type":34,"value":3391},"Bien documenté",{"type":29,"tag":820,"props":3393,"children":3394},{},[3395],{"type":34,"value":3396},"Documenté partiellement",{"type":29,"tag":820,"props":3398,"children":3399},{},[3400],{"type":34,"value":3401},"Non documenté",{"type":29,"tag":795,"props":3403,"children":3404},{},[3405,3410,3415,3420],{"type":29,"tag":820,"props":3406,"children":3407},{},[3408],{"type":34,"value":3409},"Couplage",{"type":29,"tag":820,"props":3411,"children":3412},{},[3413],{"type":34,"value":3414},"\u003C 3 dépendances",{"type":29,"tag":820,"props":3416,"children":3417},{},[3418],{"type":34,"value":3419},"3-7 dépendances",{"type":29,"tag":820,"props":3421,"children":3422},{},[3423],{"type":34,"value":3424},"> 7 dépendances",{"type":29,"tag":795,"props":3426,"children":3427},{},[3428,3433,3437,3442],{"type":29,"tag":820,"props":3429,"children":3430},{},[3431],{"type":34,"value":3432},"Criticité business",{"type":29,"tag":820,"props":3434,"children":3435},{},[3436],{"type":34,"value":3294},{"type":29,"tag":820,"props":3438,"children":3439},{},[3440],{"type":34,"value":3441},"Importante",{"type":29,"tag":820,"props":3443,"children":3444},{},[3445],{"type":34,"value":3274},{"type":29,"tag":30,"props":3447,"children":3448},{},[3449,3454],{"type":29,"tag":40,"props":3450,"children":3451},{},[3452],{"type":34,"value":3453},"Score total",{"type":34,"value":239},{"type":29,"tag":241,"props":3456,"children":3457},{},[3458,3463,3468],{"type":29,"tag":245,"props":3459,"children":3460},{},[3461],{"type":34,"value":3462},"4-6 points → Intervention directe possible",{"type":29,"tag":245,"props":3464,"children":3465},{},[3466],{"type":34,"value":3467},"7-9 points → Sécuriser avant d'intervenir (tests de caractérisation, documentation)",{"type":29,"tag":245,"props":3469,"children":3470},{},[3471],{"type":34,"value":3472},"10-12 points → Planifier une phase de préparation de 2-4 semaines avant toute modification",{"type":29,"tag":30,"props":3474,"children":3475},{},[3476],{"type":34,"value":3477},"Ce n'est pas un outil de blocage, c'est un outil de planification. Un module à 10-12 points peut et doit être refactorisé. Mais la séquence d'intervention change radicalement.",{"type":29,"tag":205,"props":3479,"children":3481},{"cta":422,"href":423,"title":3480,"type":425},"Évaluer le risque legacy est une pratique parmi 100 autres",[3482],{"type":29,"tag":30,"props":3483,"children":3484},{},[3485],{"type":34,"value":3486},"La matrice de risque en 4 dimensions décrite ici, c'est une des pratiques craft que j'applique au quotidien pour intervenir sur du legacy sans tout casser. Le Craft Bundle réunit les 100 pratiques que je mobilise pour coder propre, des characterization tests à la gestion du couplage. Ce sont celles que l'IA ne vous apprendra jamais, parce qu'elle ne les a jamais vues sauver une release en production.",{"type":29,"tag":60,"props":3488,"children":3489},{},[],{"type":29,"tag":64,"props":3491,"children":3493},{"id":3492},"faq-sur-lévaluation-du-risque-legacy",[3494],{"type":34,"value":3495},"FAQ sur l'évaluation du risque legacy",{"type":29,"tag":442,"props":3497,"children":3498},{},[3499,3504],{"type":29,"tag":446,"props":3500,"children":3501},{},[3502],{"type":34,"value":3503},"1. Combien de temps faut-il pour évaluer le risque d'un module legacy ?",{"type":29,"tag":30,"props":3505,"children":3506},{},[3507],{"type":34,"value":3508},"Pour un module de taille moyenne (5 000 à 20 000 lignes de code), l'évaluation des 4 dimensions prend 2 à 4 heures en impliquant un tech lead et un développeur qui connaît le contexte métier. Pour un module critique avec des dépendances complexes, comptez une demi-journée. C'est un investissement rentable : une régression sur un module critique peut coûter 10 à 100 fois ce temps en incident management.",{"type":29,"tag":442,"props":3510,"children":3511},{},[3512,3517],{"type":29,"tag":446,"props":3513,"children":3514},{},[3515],{"type":34,"value":3516},"2. Qu'est-ce qu'un characterization test et comment l'écrire ?",{"type":29,"tag":30,"props":3518,"children":3519},{},[3520],{"type":34,"value":3521},"Un characterization test documente le comportement actuel d'un module, qu'il soit correct ou non. On appelle la fonction avec des inputs réels, on observe l'output, et on l'encode comme valeur attendue dans le test. L'objectif n'est pas de vérifier que le comportement est juste, c'est de détecter si une modification le change. C'est le filet de sécurité minimal avant toute intervention sur du legacy non testé, tel que Michael Feathers l'a formalisé.",{"type":29,"tag":442,"props":3523,"children":3524},{},[3525,3530],{"type":29,"tag":446,"props":3526,"children":3527},{},[3528],{"type":34,"value":3529},"3. Comment gérer un module legacy sans aucun test et sans documentation ?",{"type":29,"tag":30,"props":3531,"children":3532},{},[3533],{"type":34,"value":3534},"La séquence que je recommande : (1) Identifier un expert métier capable d'expliquer ce que le module est censé faire. (2) Écrire des characterization tests basés sur des exemples de données réelles de production. (3) Organiser une session de \"live coding\" où un développeur senior lit le code à voix haute pendant qu'un autre prend des notes sur les règles implicites découvertes. (4) Documenter les règles dans des commentaires proches du code avant toute modification. Ce processus prend 1 à 3 jours mais réduit le risque de 70-80%.",{"type":29,"tag":442,"props":3536,"children":3537},{},[3538,3543],{"type":29,"tag":446,"props":3539,"children":3540},{},[3541],{"type":34,"value":3542},"4. Quand est-ce qu'il vaut mieux réécrire que refactoriser ?",{"type":29,"tag":30,"props":3544,"children":3545},{},[3546],{"type":34,"value":3547},"La réécriture est justifiée quand : (1) le couplage est si fort qu'il est impossible de modifier une partie sans toucher à l'ensemble, (2) la technologie sous-jacente est en fin de vie et ne peut pas être migrée progressivement, (3) la logique métier est si obscure qu'elle ne peut pas être testée avant d'être comprise. Mais la réécriture from scratch a un taux d'échec élevé : je préfère la migration progressive par le pattern \"strangler fig\" de Martin Fowler : remplacer le legacy morceau par morceau tout en maintenant le système en production.",{"type":29,"tag":442,"props":3549,"children":3550},{},[3551,3556],{"type":29,"tag":446,"props":3552,"children":3553},{},[3554],{"type":34,"value":3555},"5. Comment convaincre l'équipe de faire une évaluation de risque systématique ?",{"type":29,"tag":30,"props":3557,"children":3558},{},[3559,3561,3567],{"type":34,"value":3560},"La meilleure approche est de partager un post-mortem d'une régression passée en montrant qu'une évaluation préalable l'aurait évitée. Ensuite, intégrer l'évaluation dans la ",{"type":29,"tag":51,"props":3562,"children":3564},{"href":3563},"\u002Ffr\u002Fpratiques-agiles\u002Fdefinition-of-ready-bugs-sprint",[3565],{"type":34,"value":3566},"Definition of Ready",{"type":34,"value":3568}," des stories de refactoring : \"une story de refactoring legacy est prête quand la matrice de risque a été remplie\". Une fois que l'équipe a utilisé l'outil 2-3 fois et évité des régressions, le réflexe s'installe naturellement.",{"type":29,"tag":60,"props":3570,"children":3571},{},[],{"type":29,"tag":205,"props":3573,"children":3574},{"cta":513,"href":514,"title":515,"type":516},[3575],{"type":29,"tag":30,"props":3576,"children":3577},{},[3578],{"type":34,"value":3579},"30 questions sur 5 dimensions de maturité engineering, incluant une section dédiée aux pratiques sur le code legacy et la gestion de la dette technique. Score automatique + 3 priorités d'action pour les 90 prochains jours.",{"title":8,"searchDepth":524,"depth":524,"links":3581},[3582,3583,3584,3585,3586,3587,3588],{"id":2913,"depth":524,"text":2916},{"id":2956,"depth":524,"text":2959},{"id":3044,"depth":524,"text":3047},{"id":3136,"depth":524,"text":3139},{"id":3220,"depth":524,"text":3223},{"id":3315,"depth":524,"text":3318},{"id":3492,"depth":524,"text":3495},"content:fr:dette-technique:legacy-code-evaluer-risque.md","fr\u002Fdette-technique\u002Flegacy-code-evaluer-risque.md","fr\u002Fdette-technique\u002Flegacy-code-evaluer-risque",1784113934106]