<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Djamel Bougouffa | Blog</title><description>Articles sur le développement web, TypeScript, React, Node.js et les meilleures pratiques de développement.</description><link>https://djamel-bougouffa.com/</link><language>fr-FR</language><atom:link href="https://djamel-bougouffa.com/fr/rss.xml" rel="self" type="application/rss+xml"/><item><title>L&apos;IA écrit le code maintenant, raison de plus pour maîtriser l&apos;architecture</title><link>https://djamel-bougouffa.com/fr/blog/why-developers-should-learn-architecture-ai-era/</link><guid isPermaLink="true">https://djamel-bougouffa.com/fr/blog/why-developers-should-learn-architecture-ai-era/</guid><description>L&apos;IA n&apos;a pas rendu les développeurs obsolètes. Elle a déplacé la valeur. Écrire du code se banalise ; décider de la forme du système, non. Voici pourquoi l&apos;architecture et le craft sont devenus les compétences qui prennent de la valeur, et pourquoi le développeur « classique » a intérêt à monter en compétence dès maintenant.</description><pubDate>Thu, 16 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;img src=&quot;https://djamel-bougouffa.com/assets/blog/why-developers-should-learn-architecture-ai-era.webp&quot; alt=&quot;Un développeur pilotant un agent IA depuis un schéma d&apos;architecture plutôt qu&apos;en tapant le code ligne par ligne&quot; /&gt;
&lt;p&gt;Pendant l’essentiel de ma carrière, être un bon développeur voulait dire être bon pour &lt;em&gt;produire&lt;/em&gt; du code. Vite, juste, idiomatique. Cette compétence compte toujours, mais elle a discrètement cessé d’être celle qui te distingue. Un agent produit désormais du code vite, juste et idiomatique, à la demande. La partie du métier devenue rare, et donc précieuse, c’est celle qui décide &lt;em&gt;ce qu’il faut produire et quelle forme lui donner&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Ce n’est pas un petit glissement. Il déplace l’endroit où vit la valeur d’un développeur : du clavier vers la conception. Et il a une implication inconfortable pour toute une catégorie de développeurs qui ont bâti leur identité sur la vitesse d’implémentation. Voici pourquoi l’architecture et le craft sont les compétences qui viennent de prendre de la valeur, et pourquoi les développeurs « classiques » ont intérêt à s’y former maintenant plutôt que plus tard.&lt;/p&gt;
&lt;h2 id=&quot;lia-est-douée-exactement-pour-la-partie-qui-na-jamais-été-la-difficile&quot;&gt;L’IA est douée exactement pour la partie qui n’a jamais été la difficile&lt;/h2&gt;
&lt;p&gt;Voici ce qu’on ne dit pas assez : écrire une fonction a rarement été le point dur du logiciel. Savoir &lt;em&gt;quelle&lt;/em&gt; fonction, où elle doit vivre, ce qu’elle doit ou ne doit pas toucher, et comment elle s’imbrique dans le reste du système : ça, ça a toujours été le point dur. L’IA est spectaculaire sur le premier et structurellement faible sur le second.&lt;/p&gt;
&lt;p&gt;Un agent génère du code localement plausible. Demande-lui une feature et il produira quelque chose qui a l’air juste, et qui souvent marche. Ce qu’il n’a pas, c’est le goût au niveau système : le sens de ce qu’il faut protéger, du couplage qui te fera mal dans six mois, de l’endroit où placer une frontière, de ce que le domaine signifie vraiment sous les tickets. Il optimise pour « faire passer cette tâche », pas pour « garder ce système cohérent ». (J’ai vu cet écart précis &lt;a href=&quot;https://djamel-bougouffa.com/fr/blog/software-craftsmanship-ai-era&quot;&gt;casser une règle métier vieille de dix ans en production&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;La capacité n’a donc pas disparu. Elle s’est inversée. Ce qui était cher est devenu abondant. Ce qui était rare, le jugement sur la structure, est devenu le goulet d’étranglement. Et c’est dans les goulets que se concentre la valeur.&lt;/p&gt;
&lt;h2 id=&quot;larchitecture-cest-le-volant&quot;&gt;L’architecture, c’est le volant&lt;/h2&gt;
&lt;p&gt;Voici la raison pratique pour laquelle ce n’est pas de la philosophie de carrière abstraite. Si tu ne sais pas décrire la forme que tu veux, tu ne peux pas diriger un agent. Tu peux seulement accepter ce qu’il te tend.&lt;/p&gt;
&lt;p&gt;Bien travailler avec un agent de code est un acte de &lt;em&gt;direction&lt;/em&gt;. Tu lui dis : mets la logique métier dans un cœur pur, garde l’appel Slack derrière un port, ne laisse pas la couche de persistance fuir dans le domaine. Cette instruction ne t’est accessible que si tu comprends les ports et adaptateurs, le couplage, la cohésion, où placer les frontières et pourquoi. &lt;a href=&quot;https://djamel-bougouffa.com/fr/blog/hexagonal-architecture-ai-agents&quot;&gt;L’architecture est ce qui rend un agent sûr et rapide&lt;/a&gt;, mais seulement entre les mains de quelqu’un qui sait nommer la cible.&lt;/p&gt;
&lt;p&gt;Le développeur qui connaît l’architecture traite l’agent comme un implémenteur très rapide de &lt;em&gt;sa&lt;/em&gt; conception. Celui qui ne la connaît pas traite l’agent comme un oracle, accepte sa sortie, et accumule lentement un système que plus personne ne comprend. Même outil. Résultats opposés. Le facteur différenciant, c’est entièrement la culture de conception de l’humain.&lt;/p&gt;
&lt;p&gt;C’est le renversement : l’IA n’a pas remplacé le jugement de l’architecte. Elle a fait de ce jugement l’interface par laquelle tout le reste se construit.&lt;/p&gt;
&lt;h2 id=&quot;la-partie-inconfortable-pour-le-développeur--classique-&quot;&gt;La partie inconfortable pour le développeur « classique »&lt;/h2&gt;
&lt;p&gt;Je veux être direct, parce que l’optimisme vague n’aide personne à planifier une carrière.&lt;/p&gt;
&lt;p&gt;Si ta valeur pour une équipe, c’était &lt;em&gt;implémenter des tickets que quelqu’un d’autre a conçus&lt;/em&gt; (transformer vite une tâche bien spécifiée en code fonctionnel), c’est précisément la couche que l’IA absorbe le plus vite. Non parce que tu n’es pas bon ; parce que c’est la part du travail la plus compressible en « génère du code plausible pour une spec claire ». L’implémenteur qui ne fait qu’implémenter se tient exactement là où l’eau monte.&lt;/p&gt;
&lt;p&gt;Ce n’est pas une prophétie catastrophiste. C’est un panneau de direction. Le mouvement, c’est &lt;em&gt;monter d’une couche&lt;/em&gt; : de « je sais construire ce qu’on me demande » à « je sais décider ce qu’il faut construire et comment le structurer ». D’implémenteur à concepteur. D’écrivain de code à &lt;em&gt;directeur&lt;/em&gt; de code. Et les compétences qui t’y amènent, ce n’est pas plus de vitesse de frappe ni un framework de plus : ce sont les compétences durables et peu glamour que l’industrie sous-estime depuis des années parce qu’elles font une mauvaise démo.&lt;/p&gt;
&lt;p&gt;Ceux qui ont passé la dernière décennie à railler les « astronautes de l’architecture » et le « craft, c’est du luxe, livre et c’est tout » vont découvrir que le luxe, c’était les douves.&lt;/p&gt;
&lt;h2 id=&quot;ce-que--se-former-au-craft--veut-dire-concrètement-pas-des-buzzwords&quot;&gt;Ce que « se former au craft » veut dire concrètement (pas des buzzwords)&lt;/h2&gt;
&lt;p&gt;« Mets-toi au craftsmanship » est facile à dire et facile à transformer en liste de lecture qu’on n’applique jamais. Concrètement, voici ce qui prend de la valeur :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Frontières et couplage.&lt;/strong&gt; Ports et adaptateurs, sens des dépendances, ce qui appartient au domaine par rapport aux bords. C’est le vocabulaire avec lequel tu diriges un agent. Commence là.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Le test comme outil de conception, pas comme corvée.&lt;/strong&gt; Pas la course à la couverture, mais se servir des tests pour figer le comportement et attraper les changements &lt;em&gt;silencieux&lt;/em&gt; d’un agent. Le test qui aurait attrapé la règle métier cassée est une compétence de conception, pas une tâche de QA.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;La modélisation du domaine.&lt;/strong&gt; Comprendre le métier assez profondément pour nommer les choses correctement. L’ubiquitous language n’est pas de la cérémonie ; c’est la carte que toi et l’agent suivez tous les deux.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lire et relire le code de façon critique.&lt;/strong&gt; La compétence la plus importante dans un workflow saturé d’IA, c’est distinguer le &lt;em&gt;bon&lt;/em&gt; code du code &lt;em&gt;plausible&lt;/em&gt;. Si tu ne repères pas un diff confiant mais faux, la vitesse de l’agent est un handicap, pas un levier.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;La pensée système.&lt;/strong&gt; Comment un changement se propage. Ce qu’il faut protéger. Ce que « cohérent » veut dire pour &lt;em&gt;cette&lt;/em&gt; codebase précisément. C’est le goût que l’IA n’a pas, et il s’apprend : par l’exposition, la lecture de grandes codebases, et l’erreur volontaire dans des endroits sans enjeu.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Rien de tout ça n’est la nouvelle mode. Tout ça, c’est ce que les ingénieurs seniors ont toujours fait, discrètement. La différence, c’est que ce n’est plus la part &lt;em&gt;cachée&lt;/em&gt; du métier derrière l’acte visible de taper. C’est &lt;em&gt;le&lt;/em&gt; métier, désormais.&lt;/p&gt;
&lt;h2 id=&quot;la-version-optimiste-parce-quelle-lest&quot;&gt;La version optimiste (parce qu’elle l’est)&lt;/h2&gt;
&lt;p&gt;Je ne pense pas que ce soit un mauvais moment pour être développeur. Je pense que c’est le meilleur moment pour être un développeur &lt;em&gt;qui sait architecturer&lt;/em&gt;, et un moment stressant pour celui qui ne fait qu’implémenter. Et ce sont deux phrases très différentes.&lt;/p&gt;
&lt;p&gt;Pour qui veut monter en compétence, le levier est extraordinaire. Un développeur cultivé en architecture, avec un bon agent, fait le travail d’une petite équipe : il conçoit, l’agent implémente, il relit, il oriente. Le plafond de ce qu’une seule personne peut construire vient de bondir, mais seulement pour ceux dont la compétence est de décider la forme, pas de la remplir.&lt;/p&gt;
&lt;p&gt;Alors si je devais donner un seul conseil de carrière à un développeur « classique » aujourd’hui, il serait court : &lt;strong&gt;arrête de concurrencer l’agent sur ce qu’il fait mieux que toi.&lt;/strong&gt; Il tape plus vite que toi et ne fatigue jamais. Va sur ce qu’il ne sait pas faire : tenir tout le système en tête, savoir quoi protéger, décider la structure, et distinguer le bon du plausible. Apprends l’architecture. Apprends le craft. Pas parce que c’est vertueux, mais parce que c’est la part de ton métier qui vient de devenir tout le métier.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Si tu veux la version concrète de « structure ton code pour qu’un agent y prospère », j’ai écrit &lt;a href=&quot;https://djamel-bougouffa.com/fr/blog/hexagonal-architecture-ai-agents&quot;&gt;pourquoi l’architecture hexagonale est le plus beau cadeau à faire à un agent IA&lt;/a&gt;. Et pour l’histoire vraie derrière tout ça, &lt;a href=&quot;https://djamel-bougouffa.com/fr/blog/software-craftsmanship-ai-era&quot;&gt;voici comment l’IA a cassé notre code de production, et la règle de craftsmanship qui aurait tout évité&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</content:encoded><category>ia</category><category>softwareengineering</category><category>techleadership</category></item><item><title>L&apos;architecture hexagonale est le plus beau cadeau à faire à un agent IA</title><link>https://djamel-bougouffa.com/fr/blog/hexagonal-architecture-ai-agents/</link><guid isPermaLink="true">https://djamel-bougouffa.com/fr/blog/hexagonal-architecture-ai-agents/</guid><description>Les ports et adaptateurs n&apos;ont pas été conçus pour l&apos;IA. Mais isoler sa logique métier derrière des frontières propres se révèle être le moyen le plus efficace de laisser un agent de code travailler vite sans saccager le domaine. Voici pourquoi, avec un vrai avant/après en Java.</description><pubDate>Wed, 08 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;img src=&quot;https://djamel-bougouffa.com/assets/blog/hexagonal-architecture-ai-agents.webp&quot; alt=&quot;Un schéma hexagonal ports-et-adaptateurs avec un agent IA travaillant sur les adaptateurs externes tandis que le cœur métier reste protégé&quot; /&gt;
&lt;p&gt;On a adopté l’architecture hexagonale pour les humains qui reliraient le code plus tard. Il se trouve qu’on l’a aussi bâtie pour un lecteur qu’on n’avait pas vu venir : l’agent IA qui travaille sur la codebase aujourd’hui.&lt;/p&gt;
&lt;p&gt;Je ne l’ai pas planifié. Je l’ai constaté. Le même agent de code, face au même type de tâche, se comportait de façon radicalement différente selon la forme du code dans lequel il atterrissait. Dans une codebase emmêlée, c’était un danger. Dans une codebase proprement structurée, il était réellement rapide et réellement sûr. La différence, ce n’était ni le modèle, ni le prompt, ni l’outillage. C’était l’architecture.&lt;/p&gt;
&lt;p&gt;Voici pourquoi &lt;strong&gt;les ports et adaptateurs sont le plus beau cadeau à faire à un agent IA&lt;/strong&gt;, et les limites honnêtes de cette affirmation.&lt;/p&gt;
&lt;h2 id=&quot;la-même-tâche-deux-codebases&quot;&gt;La même tâche, deux codebases&lt;/h2&gt;
&lt;p&gt;Donne à un agent cette tâche : &lt;em&gt;« Ajoute une notification Slack quand une commande est expédiée. »&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Dans un service emmêlé, la logique d’expédition, la persistance et les appels sortants vivent dans une seule classe de 600 lignes. L’agent la lit, décide que l’endroit naturel pour se brancher est juste après la mise à jour du statut, et édite là. Ça marche. Ça déplace aussi discrètement une ligne, change le moment où le timestamp &lt;code&gt;shippedAt&lt;/code&gt; est fixé, et voilà qu’une règle de facturation vieille de dix ans, indexée sur ce timestamp, se déclenche un jour trop tôt. Aucun test ne l’a attrapé, parce que le test vérifiait « un email est envoyé », pas « le timestamp est intact ». Ça remonte en production. (Si ce mode de défaillance te dit quelque chose, c’est parce que &lt;a href=&quot;https://djamel-bougouffa.com/fr/blog/software-craftsmanship-ai-era&quot;&gt;j’ai vu un agent casser en silence une règle métier non documentée en production&lt;/a&gt; : une autre règle, la même forme d’erreur.)&lt;/p&gt;
&lt;p&gt;Dans une codebase hexagonale, la même tâche est ennuyeuse dans le meilleur sens du terme. Il existe une interface &lt;code&gt;NotificationPort&lt;/code&gt;. L’agent implémente un &lt;code&gt;SlackNotificationAdapter&lt;/code&gt;, le branche, et ne touche jamais au domaine. La règle métier sur &lt;code&gt;shippedAt&lt;/code&gt; vit dans un cœur pur que l’agent n’avait aucune raison d’ouvrir. Le rayon de destruction du changement était borné &lt;em&gt;par l’architecture&lt;/em&gt;, avant même que l’agent ne tape un caractère.&lt;/p&gt;
&lt;p&gt;C’est toute la thèse. La suite du billet détaille pourquoi la seconde histoire arrive à dessein, et non par chance.&lt;/p&gt;
&lt;h2 id=&quot;pourquoi-lhexagonal-marche-pour-un-agent-en-particulier&quot;&gt;Pourquoi l’hexagonal marche &lt;em&gt;pour un agent&lt;/em&gt; en particulier&lt;/h2&gt;
&lt;p&gt;L’architecture hexagonale n’a rien de neuf pour les humains. Ce qui est neuf, c’est que ses propriétés exactes se trouvent être celles dont un agent IA a le plus besoin.&lt;/p&gt;
&lt;h3 id=&quot;1-elle-borne-le-rayon-de-destruction&quot;&gt;1. Elle borne le rayon de destruction&lt;/h3&gt;
&lt;p&gt;Le pire mode de défaillance d’un agent n’est pas d’écrire du code faux : c’est d’écrire du code plausible au mauvais endroit. Il optimise localement pour « faire marcher la tâche » et n’a aucun instinct de ce qu’il met en péril trois couches plus loin.&lt;/p&gt;
&lt;p&gt;Les ports et adaptateurs répondent à ça structurellement. Le domaine n’a aucune dépendance vers l’extérieur ; tout ce qui est volatil (HTTP, base de données, API tierces, files de messages) vit dans des adaptateurs, à la périphérie. Ainsi, la surface où l’agent &lt;em&gt;doit&lt;/em&gt; travailler (les adaptateurs) est physiquement séparée de celle qu’il ne devrait presque jamais toucher (le cœur métier). Tu n’espères pas que l’agent respecte une frontière. La frontière est porteuse.&lt;/p&gt;
&lt;h3 id=&quot;2-le-cœur-est-testable-en-isolation-donc-lagent-a-une-boucle-de-feedback-rapide&quot;&gt;2. Le cœur est testable en isolation, donc l’agent a une boucle de feedback rapide&lt;/h3&gt;
&lt;p&gt;Un domaine pur, sans I/O, se teste sans démarrer une base de données ni un serveur web. Ça compte plus pour un agent que pour toi, parce que toute la boucle de travail de l’agent c’est &lt;em&gt;éditer → lancer → lire le résultat → corriger&lt;/em&gt;. Plus cette boucle est rapide et ciblée, meilleur est l’agent.&lt;/p&gt;
&lt;p&gt;Quand la logique métier est un cœur pur, l’agent peut écrire un test, le lancer en quelques millisecondes, et obtenir un signal sans ambiguïté : « la règle tient toujours » ou « non ». Quand la logique est étalée dans une classe couplée à l’infra, le seul moyen de la tester est un test d’intégration lent, instable et vague sur &lt;em&gt;ce qui&lt;/em&gt; a cassé. Une bonne architecture donne à l’agent un oracle de vérité. Une mauvaise lui donne un haussement d’épaules.&lt;/p&gt;
&lt;h3 id=&quot;3-les-ports-nomment-le-travail-dans-la-langue-du-domaine&quot;&gt;3. Les ports nomment le travail dans la langue du domaine&lt;/h3&gt;
&lt;p&gt;Un port &lt;code&gt;PaymentGateway&lt;/code&gt; avec &lt;code&gt;authorize(Payment)&lt;/code&gt; et &lt;code&gt;capture(Payment)&lt;/code&gt; dit à l’agent ce que fait le système en termes métier. Un fouillis d’appels &lt;code&gt;HttpClient&lt;/code&gt; et de mapping de DTO ne lui dit rien, sinon comment la plomberie se trouve câblée cette semaine.&lt;/p&gt;
&lt;p&gt;Les LLM sont exceptionnellement bons pour suivre une intention bien nommée et facilement perdus dans la complexité accidentelle. Les ports sont, de fait, une interface curée sur laquelle un agent peut raisonner, la même raison pour laquelle &lt;a href=&quot;https://djamel-bougouffa.com/fr/blog/claude-code-jira-mcp-legacy-codebase&quot;&gt;l’ubiquitous language&lt;/a&gt; aide un humain à embarquer. Tu n’organises pas seulement le code ; tu tends à l’agent une carte écrite dans le bon vocabulaire.&lt;/p&gt;
&lt;h2 id=&quot;ce-que-ça-donne-en-java&quot;&gt;Ce que ça donne en Java&lt;/h2&gt;
&lt;p&gt;Voici la version emmêlée qu’un agent abîmera avec plaisir :&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// OrderService.java : tout au même endroit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; void&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; ship&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(Long orderId) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Order order &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; jpaRepository.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;findById&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(orderId).&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;orElseThrow&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    order.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;setStatus&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(SHIPPED);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    order.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;setShippedAt&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(Instant.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;now&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;());        &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// la facturation s&apos;indexe là-dessus&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    jpaRepository.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;save&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(order);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    // l&apos;agent ajoute sa feature juste ici, à côté d&apos;un timestamp qu&apos;il ne comprend pas&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    emailClient.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;send&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(order.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;getCustomerEmail&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(), &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Votre commande est expédiée&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et la version hexagonale, où la même feature a un foyer évident et sûr :&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// Cœur du domaine : pur, sans framework, sans I/O&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; final&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; ShipOrder&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    private&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; final&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; OrderRepository orders;      &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// un port&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    private&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; final&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; NotificationPort notifier;   &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// un port&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    public&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; void&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; handle&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(OrderId &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        Order order &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; orders.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;byId&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(id);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        order.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;markShipped&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();                   &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// la règle du shippedAt vit dans Order&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        orders.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;save&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(order);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        notifier.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;orderShipped&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(order);          &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// l&apos;agent étend derrière ce port&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// Adaptateur : le seul fichier que l&apos;agent a besoin d&apos;ajouter&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; final&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; SlackNotificationAdapter&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; implements&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; NotificationPort&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    public&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; void&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; orderShipped&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(Order &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;order&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) { &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;/* appel Slack */&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Le domaine n’a pas bougé. &lt;code&gt;markShipped()&lt;/code&gt; possède toujours la règle du timestamp, scellée à l’intérieur de l’agrégat. L’agent ajoute un adaptateur, implémente une méthode, et il n’a jamais existé de chemin pour qu’il perturbe l’invariant. Non parce qu’il a été prudent, mais parce que l’architecture ne lui a jamais tendu la corde.&lt;/p&gt;
&lt;h2 id=&quot;les-limites-honnêtes&quot;&gt;Les limites honnêtes&lt;/h2&gt;
&lt;p&gt;Une thèse aussi propre mérite d’être bousculée, alors cadrons-la avant de la surappliquer. Trois réserves auxquelles je tiens :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;L’hexagonal a un coût de cérémonie.&lt;/strong&gt; Pour un petit script ou un service jetable, la surcharge ports-et-adaptateurs est de la pure friction, et un agent qui bosse dans un projet de deux fichiers n’en a pas besoin. Ça paie sur de vrais domaines, avec des règles qui méritent d’être protégées, pas partout.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Des ports poreux laissent fuir la protection.&lt;/strong&gt; Si ton « port » est en réalité une entité JPA ou un objet de réponse HTTP déguisé, le domaine n’est pas isolé, et le rayon de destruction de l’agent non plus. L’architecture aide &lt;em&gt;exactement à la mesure de son honnêteté.&lt;/em&gt; À moitié hexagonal, tu n’as que la moitié du garde-fou.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Elle réduit le rayon de destruction ; elle ne le supprime pas.&lt;/strong&gt; Un agent peut toujours écrire une règle métier fausse à l’intérieur d’un cœur parfaitement pur. Les bonnes frontières rendent les mauvais changements &lt;em&gt;contenus et visibles&lt;/em&gt;, pas impossibles. Tu relis toujours. Tu restes propriétaire de la sortie.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;le-basculement-quil-faut-nommer&quot;&gt;Le basculement qu’il faut nommer&lt;/h2&gt;
&lt;p&gt;Pendant vingt ans, on a justifié les frontières propres par un argument sur le futur : &lt;em&gt;quelqu’un devra maintenir ça.&lt;/em&gt; La récompense était toujours différée, et c’est bien pour ça que la discipline était la première chose sacrifiée sous la pression des délais.&lt;/p&gt;
&lt;p&gt;L’agent IA écrase cette temporalité. Le mainteneur pour qui tu construisais travaille désormais dans ton dépôt cet après-midi, et l’architecture du code détermine directement si cet agent est un accélérateur ou un danger. Une bonne structure a cessé d’être un cadeau à un hypothétique ingénieur futur. C’est du levier que tu encaisses aujourd’hui.&lt;/p&gt;
&lt;p&gt;La conclusion surprenante est donc le conseil le moins surprenant du métier, avec des enjeux nouveaux : &lt;strong&gt;isole ton domaine.&lt;/strong&gt; Pas parce qu’un livre l’a dit, mais parce que la chose qui écrit désormais la moitié de ton code performe exactement aussi bien que tes frontières le permettent. L’architecture hexagonale a toujours été du bon craftsmanship. C’est maintenant aussi l’interface à travers laquelle tes agents font leur meilleur travail.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Ça rejoint deux choses que j’ai écrites : &lt;a href=&quot;https://djamel-bougouffa.com/fr/blog/software-craftsmanship-ai-era&quot;&gt;pourquoi la discipline d’architecture compte plus, pas moins, quand l’IA écrit le code&lt;/a&gt;, et &lt;a href=&quot;https://djamel-bougouffa.com/fr/blog/claude-code-jira-mcp-legacy-codebase&quot;&gt;comment je fais passer une codebase Java legacy du ticket à la pull request avec un agent&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</content:encoded><category>ia</category><category>softwareengineering</category><category>architecture</category></item><item><title>Pourquoi j&apos;ai quitté l&apos;IDE IA pour le terminal, et l&apos;outillage que j&apos;ai dû construire</title><link>https://djamel-bougouffa.com/fr/blog/why-i-left-the-ai-ide-for-the-terminal/</link><guid isPermaLink="true">https://djamel-bougouffa.com/fr/blog/why-i-left-the-ai-ide-for-the-terminal/</guid><description>J&apos;ai troqué mon IDE boosté à l&apos;IA contre un agent en terminal. Ça a tenu, non pas parce que le terminal est plus cool, mais parce qu&apos;il est composable. Voici ce qui a changé, les vrais compromis, et les outils que j&apos;ai dû bâtir.</description><pubDate>Wed, 01 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;img src=&quot;https://djamel-bougouffa.com/assets/blog/why-i-left-the-ai-ide-for-the-terminal.webp&quot; alt=&quot;Une session terminal exécutant un agent IA aux côtés de git, du shell et de CLIs maison&quot; /&gt;
&lt;p&gt;Pendant environ un an, mon assistant de code a vécu dans mon éditeur. L’autocomplétion dans la marge, un panneau de chat à droite, des diffs en ligne que j’acceptais d’une touche. C’était bien. Puis un après-midi, je l’ai fermé, j’ai ouvert un terminal, et j’ai confié les mêmes tâches à un agent en ligne de commande.&lt;/p&gt;
&lt;p&gt;Je pensais revenir en arrière sous une semaine. Je ne l’ai pas fait. Des mois plus tard, l’IDE est toujours là. Je ne lui demande simplement plus de &lt;em&gt;réfléchir&lt;/em&gt;. Voici le compte rendu honnête de pourquoi le terminal l’a emporté pour moi, ce qu’il perd encore, et l’outillage que j’ai dû construire pour que le changement soit réellement rentable.&lt;/p&gt;
&lt;h2 id=&quot;le-jour-où-jai-fermé-le-panneau&quot;&gt;Le jour où j’ai fermé le panneau&lt;/h2&gt;
&lt;p&gt;Ce qui m’a fait basculer n’était pas une fonctionnalité. C’était une impression qui revenait : l’assistant de l’IDE était un passager dans mon éditeur, et l’agent du terminal était un collègue à mon bureau.&lt;/p&gt;
&lt;p&gt;Dans l’IDE, l’assistant &lt;em&gt;suggérait&lt;/em&gt;, surtout. Il proposait un diff, je l’acceptais ou le rejetais, et la boucle s’arrêtait là. Tout ce qui dépassait l’édition d’un fichier (lancer les tests, consulter l’historique git, reproduire un bug, appeler une API interne), c’était mon boulot. L’assistant regardait.&lt;/p&gt;
&lt;p&gt;Dans le terminal, l’agent &lt;em&gt;agit&lt;/em&gt;. Il lit les fichiers, lance le build, analyse l’échec, modifie, relance les tests, et ne revient vers moi que quand il a quelque chose à montrer ou une décision à demander. L’unité de travail n’était plus « un diff » mais « une tâche ».&lt;/p&gt;
&lt;p&gt;C’est cette différence qui a tout fait tenir. Le reste n’est que ma tentative de me l’expliquer.&lt;/p&gt;
&lt;h2 id=&quot;pourquoi-ça-a-tenu-et-non-pas-parce-que-le-terminal-est-plus-cool&quot;&gt;Pourquoi ça a tenu (et non, pas parce que le terminal est plus cool)&lt;/h2&gt;
&lt;p&gt;Trois raisons, par ordre d’importance réelle.&lt;/p&gt;
&lt;h3 id=&quot;1-la-composabilité&quot;&gt;1. La composabilité&lt;/h3&gt;
&lt;p&gt;C’est la vraie. Le terminal est le seul endroit où tous les outils qu’un développeur utilise déjà parlent la même langue : du texte en entrée, du texte en sortie. Dès que l’agent y vit, il peut se composer avec tout, sans que personne n’écrive d’intégration.&lt;/p&gt;
&lt;p&gt;Il lance &lt;code&gt;git&lt;/code&gt; directement. Il pipe la sortie du build dans un grep. Il appelle mes propres CLIs. Il dialogue avec des systèmes externes via &lt;a href=&quot;https://djamel-bougouffa.com/fr/blog/model-context-protocol-mcp-cli-rust-ide&quot;&gt;MCP&lt;/a&gt;. Rien de tout cela n’a demandé un plugin, une API d’extension ou la bénédiction d’un éditeur. L’assistant de l’IDE, lui, ne pouvait faire que ce que sa surface d’extension autorisait : un jardin clos, aussi joli soit le mur.&lt;/p&gt;
&lt;p&gt;Un IDE est un produit. Le terminal est un protocole. On peut bâtir sur un protocole.&lt;/p&gt;
&lt;h3 id=&quot;2-un-contexte-qui-survit-à-la-session&quot;&gt;2. Un contexte qui survit à la session&lt;/h3&gt;
&lt;p&gt;Sur une vraie codebase, surtout legacy, l’essentiel du travail, c’est &lt;em&gt;comprendre&lt;/em&gt; avant de modifier. L’agent du terminal traite tout le dépôt comme son espace de travail : il greppe, suit les chaînes d’appels, lit ce dont il a besoin, et garde ce contexte sur une longue session. Je ne réexplique pas la structure du projet toutes les cinq minutes.&lt;/p&gt;
&lt;p&gt;Dans l’IDE, le contexte était implicitement « les fichiers que j’ai ouverts ». Puissant pour la fonction sous mon curseur, faible pour « trace comment cette valeur traverse huit couches de services ». Le terminal a inversé ça, et tracer des flux, c’est l’essentiel du travail sur du legacy.&lt;/p&gt;
&lt;h3 id=&quot;3-le-contrôle&quot;&gt;3. Le contrôle&lt;/h3&gt;
&lt;p&gt;Dans le terminal, je vois exactement ce que l’agent s’apprête à faire, je peux l’interrompre et le réorienter. La boucle est lisible. Quand l’agent compose de vraies commandes plutôt que de piloter une API d’éditeur cachée, je peux le superviser au lieu de lui faire confiance. Pour tout ce qui touche au code de production, cette lisibilité vaut plus que le confort.&lt;/p&gt;
&lt;h2 id=&quot;le-compromis-honnête&quot;&gt;Le compromis honnête&lt;/h2&gt;
&lt;p&gt;Si je ne te racontais que les gains, ce serait un argumentaire commercial, pas un compte rendu. L’IDE fait encore plusieurs choses mieux, et prétendre le contraire serait malhonnête.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;La navigation visuelle.&lt;/strong&gt; Aller à la définition, trouver les références, la hiérarchie d’appels, un vrai debugger avec points d’arrêt et fenêtre de surveillance : c’est &lt;em&gt;meilleur&lt;/em&gt; dans l’IDE, point. Quand je dois &lt;em&gt;dérouler&lt;/em&gt; du code pas à pas, je reviens dans l’éditeur.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Les éditions locales et serrées.&lt;/strong&gt; Pour un petit changement bien cadré dans un fichier que je regarde déjà, l’autocomplétion en ligne est plus rapide que de décrire la tâche à un agent. Tout changement ne mérite pas une phrase.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;La découvrabilité.&lt;/strong&gt; L’IDE te montre ce qui est possible. Le terminal suppose que tu sais. C’est une vraie barrière, et j’y reviens.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Les artefacts visuels.&lt;/strong&gt; Rendre un composant, inspecter une UI, lire un flame graph : le terminal est le mauvais outil, et je ne fais pas semblant du contraire.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Donc ce n’est pas « le CLI bat l’IDE ». J’utilise toujours les deux. La version exacte : &lt;strong&gt;le terminal est là où l’agent doit réfléchir ; l’IDE est là où je vais encore regarder.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;ce-que-le-terminal-a-débloqué-et-ma-forcé-à-construire&quot;&gt;Ce que le terminal a débloqué (et m’a forcé à construire)&lt;/h2&gt;
&lt;p&gt;Voici la partie que la plupart des billets « j’ai switché vers le terminal » sautent. La composabilité est une promesse, pas une fonctionnalité. Le terminal te &lt;em&gt;permet&lt;/em&gt; de composer des outils ; il ne te les offre pas. Une fois l’agent devenu un véritable acteur à mon bureau, les manques sont apparus vite, et j’ai dû construire la couche qui rend le changement rentable.&lt;/p&gt;
&lt;p&gt;Trois problèmes ont surgi presque immédiatement.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;La sortie était trop bruyante.&lt;/strong&gt; Les agents lisent la sortie des outils comme des tokens, et la sortie brute d’un développeur est surtout du bruit : codes de couleur ANSI, JSON verbeux, frames de pile dupliquées. L’agent payait, en contexte et en dollars, pour lire de la camelote. J’ai donc construit &lt;a href=&quot;https://github.com/rustkit-ai/trimcp&quot;&gt;&lt;strong&gt;trimcp&lt;/strong&gt;&lt;/a&gt;, un proxy MCP en Rust qui s’intercale entre l’agent et ses outils et compresse ce qui revient : il retire l’ANSI, minifie le JSON et déduplique les blocs répétitifs avant même que l’agent ne les voie. Même information, une fraction des tokens.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Les commandes étaient trop coûteuses.&lt;/strong&gt; Beaucoup d’opérations de dev courantes produisent bien plus de sortie que ce dont l’agent a besoin. Celui-là, je n’ai pas eu à le construire : &lt;a href=&quot;https://www.rtk-ai.app/&quot;&gt;&lt;strong&gt;RTK&lt;/strong&gt;&lt;/a&gt; (Rust Token Killer) est un proxy CLI open source qui réécrit de façon transparente les commandes usuelles pour ne renvoyer que le signal. Je l’ai branché via les hooks de Claude Code, donc sans surcoût dans le prompt et sans rien à retenir de ma part.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Trouver le bon code était trop lent.&lt;/strong&gt; Sur un gros dépôt, un agent peut gâcher la moitié d’une session juste à redécouvrir la structure. &lt;a href=&quot;https://github.com/rustkit-ai/semtree&quot;&gt;&lt;strong&gt;semtree&lt;/strong&gt;&lt;/a&gt; fait de la recherche de code sémantique en local (parsing tree-sitter, embeddings locaux, index HNSW, zéro clé d’API) pour que l’agent demande « où est la logique qui fait X ? » et obtienne le code pertinent, pas mille résultats de grep.&lt;/p&gt;
&lt;p&gt;Au-dessus, il y a les pièces ennuyeuses mais essentielles : un &lt;code&gt;CLAUDE.md&lt;/code&gt; par projet qui encode les conventions et les pièges (le fichier au plus fort effet de levier que je maintiens ; &lt;a href=&quot;https://djamel-bougouffa.com/fr/blog/claude-code-jira-mcp-legacy-codebase&quot;&gt;j’en ai parlé ailleurs&lt;/a&gt;), et quelques skills maison pour les workflows que je répète.&lt;/p&gt;
&lt;p&gt;Rien d’exotique là-dedans. C’est de la plomberie. Mais une plomberie que l’IDE ne m’a jamais laissé construire, parce que je ne possédais pas les tuyaux. &lt;strong&gt;C’est tout l’argument en une phrase : le terminal a gagné parce que j’ai pu construire autour.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;pour-qui-cest-et-pour-qui-ça-ne-lest-pas&quot;&gt;Pour qui c’est, et pour qui ça ne l’est pas&lt;/h2&gt;
&lt;p&gt;Je me méfie des conseils universels, alors cadrons.&lt;/p&gt;
&lt;p&gt;Ce setup convient à un &lt;strong&gt;ingénieur sénior et autonome&lt;/strong&gt; qui connaît déjà assez la codebase pour superviser un agent et juger sa production. Si tu sais distinguer le bon travail du travail qui &lt;em&gt;a l’air&lt;/em&gt; bon, le terminal te démultiplie. La composabilité et le contrôle sont du levier entre des mains expérimentées.&lt;/p&gt;
&lt;p&gt;Il convient particulièrement au &lt;strong&gt;legacy et au back-end&lt;/strong&gt;, où le travail est surtout comprendre, tracer et modifier avec prudence, et où l’outillage visuel compte moins.&lt;/p&gt;
&lt;p&gt;Il convient &lt;strong&gt;moins bien&lt;/strong&gt; si tu vis toute la journée dans le debug visuel et l’UI, ou si tu es assez tôt dans ta carrière pour ne pas encore repérer les erreurs assurées d’un agent. Les garde-fous et la découvrabilité de l’IDE y ont une vraie valeur, et « le terminal suppose que tu sais » cesse d’être une bizarrerie pour devenir un mur.&lt;/p&gt;
&lt;h2 id=&quot;à-retenir&quot;&gt;À retenir&lt;/h2&gt;
&lt;p&gt;L’opposition « IDE IA vs agent en terminal » est un peu un piège. Ce n’est pas vraiment le CLI contre l’IDE ; c’est &lt;strong&gt;la composabilité contre le verrouillage.&lt;/strong&gt; Je ne suis pas passé au terminal parce qu’il est minimaliste ou nostalgique. J’y suis passé parce que c’est le seul environnement où l’agent peut atteindre tous les outils que j’utilise, où je vois ce qu’il fait, et, surtout, où je peux construire moi-même les pièces manquantes.&lt;/p&gt;
&lt;p&gt;L’IDE est toujours ouvert dans une autre fenêtre. J’ai juste arrêté de lui demander de réfléchir.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;trimcp et semtree, l’outillage Rust que j’ai construit, font partie de &lt;a href=&quot;https://djamel-bougouffa.com/fr/experiences/rustkit-ai&quot;&gt;rustkit-ai&lt;/a&gt;. Je détaillerai les chiffres de tokens derrière trimcp dans un prochain article.&lt;/em&gt;&lt;/p&gt;</content:encoded><category>ia</category><category>softwareengineering</category></item><item><title>Déployer une app RAG Rust sur AWS avec Terraform : pourquoi j&apos;ai choisi EC2 plutôt que Bedrock</title><link>https://djamel-bougouffa.com/fr/blog/deploying-rust-rag-app-aws-terraform-ec2-bedrock/</link><guid isPermaLink="true">https://djamel-bougouffa.com/fr/blog/deploying-rust-rag-app-aws-terraform-ec2-bedrock/</guid><description>Retour d&apos;expérience concret sur le déploiement d&apos;une application RAG en Rust sur AWS EC2 avec Terraform : gestion des données Qdrant sur EBS, CI/CD GitLab sans credentials statiques, et les vrais arbitrages entre self-hosting et AWS Bedrock.</description><pubDate>Sat, 30 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;img src=&quot;https://djamel-bougouffa.com/assets/deploying-rust-rag-aws-terraform.webp&quot; alt=&quot;Infrastructure AWS EC2 pour une application RAG Rust avec Terraform (schéma généré)&quot; /&gt;
&lt;p&gt;Quand j’ai commencé à construire un système RAG en Rust pour documenter automatiquement des codebases legacy, la première question de production n’était pas sur le modèle ni sur les embeddings. C’était : où est-ce que je fais tourner ça ?&lt;/p&gt;
&lt;p&gt;La réponse évidente en 2026, c’est AWS Bedrock. C’est managé, ça scale, et AWS le pousse fort. J’ai choisi EC2. Cet article explique pourquoi, et détaille l’infrastructure Terraform que j’ai construite pour que ça tourne correctement.&lt;/p&gt;
&lt;h2 id=&quot;pourquoi-pas-bedrock-&quot;&gt;Pourquoi pas Bedrock ?&lt;/h2&gt;
&lt;p&gt;Bedrock est le bon choix pour beaucoup d’équipes. Mais pas pour moi, pour trois raisons.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;J’avais besoin de Qdrant.&lt;/strong&gt; L’application utilise &lt;a href=&quot;https://qdrant.tech&quot;&gt;Qdrant&lt;/a&gt; comme base de données vectorielle, un moteur de recherche de similarité haute performance écrit en Rust. Le store vectoriel natif de Bedrock, c’est Amazon OpenSearch Serverless ou pgvector. Migrer vers l’un ou l’autre aurait signifié réécrire la logique de requêtes principale et perdre des fonctionnalités dont je dépendais : collections nommées, filtrage de payload, métriques de distance fine.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Je fais tourner un binaire Rust, pas du Python.&lt;/strong&gt; La plupart des tutos Bedrock supposent LangChain + Python. Mon backend est un binaire Rust compilé. L’overhead d’adapter l’architecture à Lambda (cold starts, taille du binaire, async runtime) ne valait pas le coup pour un outil interne à trafic prévisible.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;La prédictibilité des coûts comptait.&lt;/strong&gt; Bedrock facture au token consommé. Pour un outil qui indexe des codebases Java entières (parfois des millions de tokens par projet), la facturation devient imprévisible. Un EC2 &lt;code&gt;t3.medium&lt;/code&gt; à ~30€/mois est plus facile à raisonner.&lt;/p&gt;
&lt;h2 id=&quot;la-stack&quot;&gt;La stack&lt;/h2&gt;
&lt;p&gt;Avant d’entrer dans Terraform, voici ce que je déploie :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Backend&lt;/strong&gt; : binaire Rust packagé en image Docker, faisant tourner le moteur RAG, l’API HTTP et le serveur MCP&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Frontend&lt;/strong&gt; : app Angular servie via nginx dans un second conteneur Docker&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Store vectoriel&lt;/strong&gt; : Qdrant, en conteneur Docker sur la même instance&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Base de données&lt;/strong&gt; : SQLite pour les métadonnées de projets (pas de RDS, ça garde les coûts bas)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LLM provider&lt;/strong&gt; : OpenRouter (appelle Claude, GPT-4, etc. via une seule API)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Tout tourne sur une instance EC2 derrière un Application Load Balancer. Pas de microservices. Pas de Kubernetes. Une instance avec Docker Compose.&lt;/p&gt;
&lt;h2 id=&quot;linfrastructure&quot;&gt;L’infrastructure&lt;/h2&gt;
&lt;p&gt;Voici la vue complète de ce que Terraform provisionne :&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Internet&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    │&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    ▼&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ALB (HTTPS:443 / HTTP:80→301)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    │&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    ▼&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;EC2 t3.medium (Amazon Linux 2023)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    ├── conteneur nginx    (port 3000 → frontend)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    ├── conteneur backend  (port 8080 → API Rust)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    └── conteneur qdrant   (port 6333 → vector DB)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;         │&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;         ▼&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    Volume EBS gp3 (/data persistant)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;         ├── qdrant_storage/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;         └── bases SQLite&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tous les secrets (clés API LLM, credentials OAuth) vivent dans AWS Secrets Manager et sont injectés dans l’instance EC2 au démarrage via &lt;code&gt;user_data&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Les images sont stockées dans ECR et tirées à chaque déploiement.&lt;/p&gt;
&lt;h2 id=&quot;les-décisions-terraform-clés&quot;&gt;Les décisions Terraform clés&lt;/h2&gt;
&lt;h3 id=&quot;le-remote-state-dabord&quot;&gt;Le remote state d’abord&lt;/h3&gt;
&lt;p&gt;Avant &lt;code&gt;terraform apply&lt;/code&gt;, il faut un endroit pour stocker le state. Deux commandes AWS CLI :&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;aws&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; s3&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; mb&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; s3://my-app-tfstate&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --region&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; eu-west-1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;aws&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; dynamodb&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; create-table&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  --table-name&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; my-app-tflock&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  --attribute-definitions&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; AttributeName=LockID,AttributeType=S&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  --key-schema&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; AttributeName=LockID,KeyType=HASH&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  --billing-mode&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; PAY_PER_REQUEST&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  --region&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; eu-west-1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Puis l’init avec le backend :&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;terraform&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; init&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  -backend-config=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;bucket=my-app-tfstate&quot;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  -backend-config=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;key=prod/terraform.tfstate&quot;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  -backend-config=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;region=eu-west-1&quot;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  -backend-config=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;dynamodb_table=my-app-tflock&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La table DynamoDB fournit le state locking, ce qui est critique si plusieurs ingénieurs ou pipelines CI exécutent Terraform en parallèle.&lt;/p&gt;
&lt;h3 id=&quot;le-problème-du-stateful--qdrant-sur-ebs&quot;&gt;Le problème du stateful : Qdrant sur EBS&lt;/h3&gt;
&lt;p&gt;C’est la décision architecturale la plus importante. Qdrant stocke les collections vectorielles sur disque. Si on laisse Terraform détruire et recréer l’instance EC2 (ce qui arrive lors de mises à jour d’AMI ou de changements de type d’instance), on perd toutes les données indexées. Réindexer une grande codebase prend des heures.&lt;/p&gt;
&lt;p&gt;La solution : un &lt;strong&gt;volume EBS séparé&lt;/strong&gt; avec &lt;code&gt;prevent_destroy&lt;/code&gt; :&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;hcl&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;resource&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; &quot;aws_ebs_volume&quot;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; &quot;data&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  availability_zone&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;availability_zones[&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  size&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;              =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;data_volume_size_gb&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  type&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;              =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;gp3&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  encrypted&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;         =&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  lifecycle&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    prevent_destroy&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;resource&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; &quot;aws_volume_attachment&quot;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; &quot;data&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  device_name&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;/dev/xvdf&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  volume_id&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; aws_ebs_volume&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;id&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  instance_id&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; aws_instance&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;app&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;id&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  force_detach&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; false&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Le &lt;code&gt;prevent_destroy = true&lt;/code&gt; signifie que Terraform refusera d’exécuter tout plan qui supprimerait ce volume. C’est un garde-fou contre un &lt;code&gt;terraform destroy&lt;/code&gt; accidentel. Si j’ai vraiment besoin de le supprimer, je retire le bloc lifecycle, j’applique, puis je détruis.&lt;/p&gt;
&lt;p&gt;Sur l’instance, &lt;code&gt;/data&lt;/code&gt; est monté depuis ce volume au démarrage via &lt;code&gt;user_data&lt;/code&gt;. Les bases SQLite et les collections Qdrant vivent toutes là. L’instance est remplaçable ; le volume de données ne l’est pas.&lt;/p&gt;
&lt;p&gt;J’ai aussi mis en place des sauvegardes AWS Backup quotidiennes :&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;hcl&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;resource&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; &quot;aws_backup_plan&quot;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; &quot;daily&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  name&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;local&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;prefix&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;-backup-daily&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  rule&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    rule_name&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;         =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;daily-7days&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    target_vault_name&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; aws_backup_vault&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;main&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;name&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    schedule&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;          =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;cron(0 2 * * ? *)&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    lifecycle&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      delete_after&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 7&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sept jours de snapshots quotidiens. Si quelque chose corrompt le storage Qdrant, je restaure depuis la sauvegarde de la nuit précédente.&lt;/p&gt;
&lt;h3 id=&quot;secrets-manager-pour-les-clés-llm&quot;&gt;Secrets Manager pour les clés LLM&lt;/h3&gt;
&lt;p&gt;Ne jamais mettre des clés API dans &lt;code&gt;terraform.tfvars&lt;/code&gt; ou dans des variables d’environnement dans &lt;code&gt;user_data&lt;/code&gt;. J’utilise Secrets Manager :&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;hcl&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;resource&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; &quot;aws_secretsmanager_secret&quot;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; &quot;openrouter_api_key&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  name&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;                    =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;local&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;prefix&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;/openrouter-api-key&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  recovery_window_in_days&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 7&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;resource&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; &quot;aws_secretsmanager_secret_version&quot;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; &quot;openrouter_api_key&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  secret_id&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;     =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; aws_secretsmanager_secret&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;openrouter_api_key&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;id&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  secret_string&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;PLACEHOLDER_SET_ME&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  lifecycle&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    ignore_changes&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [secret_string]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Le &lt;code&gt;ignore_changes = [secret_string]&lt;/code&gt; est essentiel : Terraform crée le secret avec un placeholder, puis je mets la vraie valeur via CLI. Les &lt;code&gt;terraform apply&lt;/code&gt; suivants n’écraseront pas ce que j’ai défini manuellement.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;aws&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; secretsmanager&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; put-secret-value&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  --secret-id&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; arn:aws:secretsmanager:eu-west-1:...:secret:prod/openrouter-api-key&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  --secret-string&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;sk-or-v1-...&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;L’instance EC2 a un rôle IAM avec la permission de lire ces secrets. Le &lt;code&gt;user_data&lt;/code&gt; les récupère au démarrage et les injecte comme variables d’environnement dans le stack Docker Compose.&lt;/p&gt;
&lt;h3 id=&quot;alb-avec-tls-automatique&quot;&gt;ALB avec TLS automatique&lt;/h3&gt;
&lt;p&gt;L’ALB gère la terminaison TLS. Si je fournis un &lt;code&gt;domain_name&lt;/code&gt; et un &lt;code&gt;zone_id&lt;/code&gt; Route 53, Terraform crée et valide automatiquement un certificat ACM :&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;hcl&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;resource&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; &quot;aws_acm_certificate&quot;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; &quot;main&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  count&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;             =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; local&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;has_domain &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;?&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; :&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  domain_name&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;       =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;domain_name&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  validation_method&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;DNS&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  lifecycle&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    create_before_destroy&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Le HTTP sur le port 80 redirige vers HTTPS avec un 301. La politique TLS est &lt;code&gt;ELBSecurityPolicy-TLS13-1-2-2021-06&lt;/code&gt;, qui impose TLS 1.3 et 1.2 uniquement.&lt;/p&gt;
&lt;p&gt;Sans domaine, la stack fonctionne quand même : j’obtiens un endpoint DNS ALB en HTTP uniquement. Pratique pour les environnements de staging.&lt;/p&gt;
&lt;h2 id=&quot;cicd-sans-credentials-long-lived&quot;&gt;CI/CD sans credentials long-lived&lt;/h2&gt;
&lt;p&gt;L’approche classique pour GitLab CI → AWS consiste à créer un utilisateur IAM, générer des access keys et les stocker dans les variables CI. Ça marche, mais vous vous retrouvez avec des credentials statiques qui n’expirent jamais et sont stockés dans les paramètres GitLab pour toujours.&lt;/p&gt;
&lt;p&gt;La meilleure approche : la &lt;strong&gt;fédération OIDC&lt;/strong&gt;. GitLab génère un JWT signé pour chaque exécution de pipeline. AWS vérifie le JWT auprès du provider OIDC de GitLab et émet des credentials temporaires. Aucune clé statique nulle part.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;hcl&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;resource&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; &quot;aws_iam_openid_connect_provider&quot;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; &quot;gitlab&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  url&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;             =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;gitlab_url&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  client_id_list&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [var&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;gitlab_url]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  thumbprint_list&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [var&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;gitlab_oidc_thumbprint]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;resource&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; &quot;aws_iam_role&quot;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; &quot;gitlab_ci&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  name&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;                 =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;local&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;prefix&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;-gitlab-ci&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  assume_role_policy&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;   =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; data&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;aws_iam_policy_document&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;gitlab_ci_assume&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;json&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  max_session_duration&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 3600&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La trust policy restreint quels pipelines peuvent assumer le rôle : uniquement la branche &lt;code&gt;main&lt;/code&gt; et les tags du projet spécifique :&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;hcl&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;condition&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  test&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;     =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;StringLike&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  variable&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;&amp;#x3C;votre-gitlab-host&gt;:sub&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  values&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    &quot;project_path:&amp;#x3C;votre-groupe&gt;/&amp;#x3C;votre-projet&gt;:ref_type:branch:ref:main&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    &quot;project_path:&amp;#x3C;votre-groupe&gt;/&amp;#x3C;votre-projet&gt;:ref_type:tag:ref:*&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Dans &lt;code&gt;.gitlab-ci.yml&lt;/code&gt;, le job demande un token OIDC et l’échange contre des credentials AWS :&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;deploy&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;  id_tokens&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    AWS_OIDC_TOKEN&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      aud&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;https://&amp;#x3C;votre-gitlab-host&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;  script&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    - &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;      export $(aws sts assume-role-with-web-identity&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;      --role-arn $OIDC_ROLE_ARN&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;      --web-identity-token $AWS_OIDC_TOKEN&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;      --role-session-name gitlab-ci&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;      --query &apos;Credentials.[AccessKeyId,SecretAccessKey,SessionToken]&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;      --output text | awk &apos;{print &quot;AWS_ACCESS_KEY_ID=&quot;$1&quot;\nAWS_SECRET_ACCESS_KEY=&quot;$2&quot;\nAWS_SESSION_TOKEN=&quot;$3}&apos;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    - &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;docker build -t $ECR_BACKEND_IMAGE .&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    - &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;docker push $ECR_BACKEND_IMAGE&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    - &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;aws ssm send-command --instance-ids $INSTANCE_ID --document-name AWS-RunShellScript --parameters &apos;commands=[&quot;cd /app &amp;#x26;&amp;#x26; docker compose pull &amp;#x26;&amp;#x26; docker compose up -d&quot;]&apos;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Le rôle CI a uniquement la permission de pusher vers ECR et de déclencher des commandes SSM sur l’instance EC2. Rien d’autre.&lt;/p&gt;
&lt;h2 id=&quot;ce-que-je-referais-différemment&quot;&gt;Ce que je referais différemment&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;ECS Fargate pour les conteneurs applicatifs, EC2 pour Qdrant.&lt;/strong&gt; Les conteneurs applicatifs (backend, frontend) sont stateless : ils sont parfaits pour Fargate. La seule raison pour laquelle j’ai tout gardé sur EC2, c’était la simplicité opérationnelle au départ. Avec Fargate pour l’app et une instance EC2 dédiée pour Qdrant, on obtient des déploiements rolling et une meilleure isolation des pannes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;RDS PostgreSQL plutôt que SQLite pour les métadonnées.&lt;/strong&gt; SQLite fonctionne bien pour une instance unique, mais crée des problèmes dès qu’on veut scaler horizontalement ou faire du blue/green deployment. Le coût de migration est faible ; le gain en flexibilité future est élevé.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Mettre en place les alarmes CloudWatch dès le premier jour.&lt;/strong&gt; J’ai ajouté le monitoring après le premier incident en production. L’alarme sur le compteur d’hôtes unhealthy de l’ALB devrait être la première chose qu’on crée, pas la dernière.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Choisir EC2 plutôt que Bedrock n’était pas idéologique. C’était une réponse pragmatique à trois contraintes : Qdrant comme store vectoriel, un binaire Rust compilé, et la prédictibilité des coûts pour une charge d’indexation variable.&lt;/p&gt;
&lt;p&gt;L’infrastructure Terraform décrite ici (environ 500 lignes réparties sur huit fichiers) provisionne un déploiement production avec TLS, sauvegardes quotidiennes, CI/CD sans credentials, et stockage persistant protégé. Ce n’est pas le setup le plus simple, mais chaque élément justifie sa présence.&lt;/p&gt;
&lt;p&gt;Si vous faites tourner une application AI self-hosted avec des contraintes similaires, le pattern EC2 + EBS + Secrets Manager vaut le coup d’être considéré. La charge opérationnelle est réelle, mais le contrôle et le profil de coût le justifient souvent.&lt;/p&gt;
&lt;p&gt;Le template Terraform complet de cet article, généralisé et prêt à forker, est disponible sur &lt;a href=&quot;https://github.com/Strawbang/terraform-aws-ai-stack&quot;&gt;github.com/Strawbang/terraform-aws-ai-stack&lt;/a&gt;.&lt;/p&gt;</content:encoded><category>devops</category><category>aws</category><category>rust</category></item><item><title>Comment la standardisation d&apos;une stack technique a réduit les cycles de création de 80%</title><link>https://djamel-bougouffa.com/fr/blog/stack-standardization-80-percent-faster/</link><guid isPermaLink="true">https://djamel-bougouffa.com/fr/blog/stack-standardization-80-percent-faster/</guid><description>Chez METRO France, 93 entrepôts, plusieurs équipes produit et une base de code fragmentée ralentissaient tout. Voici comment l&apos;alignement sur une stack commune a transformé la vélocité de livraison, et ce que tout leader tech devrait savoir avant de se lancer.</description><pubDate>Tue, 05 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;img src=&quot;https://djamel-bougouffa.com/assets/optimized/metro-france.webp&quot; alt=&quot;Entrepôt METRO France, engineering à l&apos;échelle sur 93 sites&quot; /&gt;
&lt;p&gt;Quand j’ai rejoint l’équipe produit de METRO France en tant que consultant via Wemanity Group, l’organisation d’ingénierie avait un problème qui n’était pas évident de l’extérieur : chaque produit était construit en silo.&lt;/p&gt;
&lt;p&gt;Les différentes équipes avaient fait des choix technologiques différents. Certains produits utilisaient un framework, d’autres un autre. Pas de librairie de composants partagée, pas de conventions API communes, pas de stratégie de déploiement unifiée. Un ingénieur qui passait d’un produit à l’autre devait repartir de zéro. Chaque nouvelle fonctionnalité impliquait de réinventer des décisions déjà prises ailleurs.&lt;/p&gt;
&lt;p&gt;Le résultat était prévisible : onboarding lent, effort dupliqué, déploiements fragiles, et un sentiment croissant que l’organisation d’ingénierie ne pouvait pas suivre le rythme du business.&lt;/p&gt;
&lt;p&gt;Voici comment nous avons résolu le problème, et ce que j’ai appris sur le vrai coût de la fragmentation technique.&lt;/p&gt;
&lt;h2 id=&quot;le-problème-avec-la-diversité-suffisante&quot;&gt;Le problème avec la diversité “suffisante”&lt;/h2&gt;
&lt;p&gt;Il existe un argument séduisant pour laisser les équipes choisir leurs outils : l’autonomie stimule la créativité, et chaque équipe connaît mieux son problème. Ce n’est pas entièrement faux. Mais à une certaine échelle, cette autonomie devient une taxe.&lt;/p&gt;
&lt;p&gt;Chez METRO France, la taxe se manifestait de trois façons :&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Latence à l’onboarding.&lt;/strong&gt; Un nouvel ingénieur rejoignant une équipe ne pouvait pas contribuer pendant des semaines, car il devait apprendre une stack propre au produit depuis zéro. Il n’existait aucune connaissance fondamentale transférable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pas de réutilisation.&lt;/strong&gt; Un composant construit par une équipe pour résoudre un problème UX ne pouvait pas être utilisé par une autre, même quand le problème était identique. Chaque équipe réinventait les mêmes solutions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fragilité des déploiements.&lt;/strong&gt; Sans patterns d’infrastructure partagés, chaque produit avait ses propres particularités de déploiement. Un correctif dans un pipeline n’aidait pas les autres. Les incidents étaient isolés mais fréquents.&lt;/p&gt;
&lt;p&gt;L’impact business était clair : construire un nouveau produit prenait des mois. Itérer sur un produit existant prenait plus de temps que nécessaire. La dette technique s’accumulait silencieusement dans chaque codebase.&lt;/p&gt;
&lt;h2 id=&quot;choisir-un-standard-qui-tient-dans-le-temps&quot;&gt;Choisir un standard qui tient dans le temps&lt;/h2&gt;
&lt;p&gt;La première décision, et la plus politiquement délicate, était de choisir la stack.&lt;/p&gt;
&lt;p&gt;Nous avons retenu &lt;strong&gt;TypeScript, React, Node.js, TypeORM et GraphQL&lt;/strong&gt; pour le développement applicatif, &lt;strong&gt;GCP et Kubernetes&lt;/strong&gt; pour l’infrastructure, et &lt;strong&gt;GitHub Actions&lt;/strong&gt; pour la CI/CD.&lt;/p&gt;
&lt;p&gt;Ce n’était pas un choix arbitraire. Chaque technologie a été sélectionnée pour une raison précise :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;TypeScript&lt;/strong&gt; plutôt que JavaScript seul : typage fort, meilleur support IDE, et un contrat partagé entre les équipes front-end et back-end.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;React&lt;/strong&gt; pour le front-end : écosystème large, réutilisation de composants, et un vivier de talents suffisamment profond pour recruter.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Node.js + GraphQL&lt;/strong&gt; pour la couche API : interface de requête unifiée, typage fort avec génération de code, et capacité à agréger des sources de données sans sur-récupération.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GCP + Kubernetes&lt;/strong&gt; : déjà partiellement en place, bien documenté, et scalable jusqu’aux 93 entrepôts.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;L’insight clé : &lt;strong&gt;un standard ne tient que s’il est réellement meilleur pour les équipes, pas seulement sur le papier.&lt;/strong&gt; Nous avons choisi des technologies avec lesquelles il était genuinement productif de travailler, pas les plus à la mode ni les plus théoriquement correctes.&lt;/p&gt;
&lt;h2 id=&quot;implémentation--adoption-avant-obligation&quot;&gt;Implémentation : adoption avant obligation&lt;/h2&gt;
&lt;p&gt;La plus grande erreur des organisations qui standardisent est de rendre la conformité obligatoire avant de démontrer la valeur.&lt;/p&gt;
&lt;p&gt;Nous avons fait l’inverse. Avant de demander à quelque équipe que ce soit de changer sa stack, nous avons construit une implémentation de référence : un vrai petit produit avec le nouveau standard, déployé en production. Il fallait que ça marche, pas juste que ça soit beau en démo.&lt;/p&gt;
&lt;p&gt;Une fois cette référence existante, l’adoption s’est propagée plus vite que prévu. Les ingénieurs pouvaient voir les patterns en action. Ils pouvaient poser des questions avec de vrais exemples en main. Le nouveau standard n’était pas un document de politique. C’était du code fonctionnel.&lt;/p&gt;
&lt;p&gt;À partir de là, nous avons introduit des outils partagés : un design system avec des composants React réutilisables, des configs TypeScript communes, des règles ESLint encodant les décisions architecturales, et un template de pipeline CI/CD que les équipes pouvaient copier et adapter.&lt;/p&gt;
&lt;p&gt;Les nouveaux produits démarraient sur la nouvelle stack par défaut. Les produits existants migraient progressivement, pas d’un coup pour éviter les perturbations, mais module par module, au fil des nouvelles fonctionnalités ou des refactorisations.&lt;/p&gt;
&lt;h2 id=&quot;le-produit-de-gestion-des-primes--une-étude-de-cas-dans-létude-de-cas&quot;&gt;Le produit de gestion des primes : une étude de cas dans l’étude de cas&lt;/h2&gt;
&lt;p&gt;L’exemple le plus concret de ce qu’a permis cette standardisation est le produit de gestion des primes employés.&lt;/p&gt;
&lt;p&gt;Les 93 entrepôts de METRO France géraient les primes via des tableurs. Les responsables de rayons saisaient les données manuellement, les RH consolidaient avec un décalage significatif, et les erreurs étaient fréquentes. Le processus était chronophage, non auditable, et une source récurrente d’insatisfaction des employés.&lt;/p&gt;
&lt;p&gt;Construire ce produit dans l’ancien monde aurait nécessité des mois de mise en place, des outils custom, et une équipe qui n’aurait pas pu être réutilisée ailleurs ensuite.&lt;/p&gt;
&lt;p&gt;Dans le nouveau monde, nous l’avons construit sur la stack standardisée dès le départ. Nous avons réutilisé des composants partagés pour l’UI. L’API GraphQL suivait les patterns établis. Le pipeline de déploiement était templaté. L’équipe qui l’a construit avait déjà travaillé sur d’autres produits dans la nouvelle stack et a été opérationnelle immédiatement.&lt;/p&gt;
&lt;p&gt;Le produit a été déployé sur les 93 entrepôts à l’échelle nationale. Le calcul des primes est passé des tableurs à un système digital auditable. Les responsables ont signalé un traitement plus rapide. Les RH ont libéré du temps significatif. Les scores de satisfaction employé dans les entrepôts se sont améliorés de façon mesurable.&lt;/p&gt;
&lt;p&gt;Mais au-delà du produit lui-même, le résultat le plus significatif était ce qu’il démontrait : &lt;strong&gt;la stack standardisée était devenue un multiplicateur&lt;/strong&gt;. Construire ce produit avait pris une fraction du temps qu’il aurait fallu avant.&lt;/p&gt;
&lt;h2 id=&quot;les-80&quot;&gt;Les 80%&lt;/h2&gt;
&lt;p&gt;L’accélération de 80% des cycles de création de produits que nous avons mesurée n’était pas de la magie : c’était la somme de plusieurs effets qui se composent :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Pas de délibération sur le choix de stack.&lt;/strong&gt; Les équipes commençaient à construire immédiatement, sans débat sur les technologies.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Les composants partagés réduisaient le travail UI&lt;/strong&gt; de 40 à 60% sur les nouveaux produits.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Les pipelines CI/CD templatés&lt;/strong&gt; éliminaient des semaines de mise en place d’infrastructure par projet.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Le transfert de connaissance inter-équipes&lt;/strong&gt; signifiait que les ingénieurs pouvaient contribuer à n’importe quel produit dès le premier jour.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Moins d’incidents&lt;/strong&gt; grâce à des patterns d’infrastructure mieux compris et mieux documentés.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Chacun de ces effets seul aurait été significatif. Ensemble, ils ont transformé la capacité de l’organisation à livrer.&lt;/p&gt;
&lt;h2 id=&quot;ce-que-les-leaders-techniques-devraient-savoir&quot;&gt;Ce que les leaders techniques devraient savoir&lt;/h2&gt;
&lt;p&gt;Si vous envisagez un effort de standardisation dans votre organisation, voici ce que je retiens de cette expérience :&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Partez de la douleur.&lt;/strong&gt; L’argument pour la standardisation doit être ancré dans des problèmes réels, pas dans l’élégance théorique. Montrez le coût de l’onboarding. Montrez l’effort dupliqué. Montrez les incidents de déploiement. Rendez visible le coût de l’état actuel.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Construisez avant d’imposer.&lt;/strong&gt; Une implémentation de référence vaut dix documents de stratégie. Livrez quelque chose de réel en production, puis montrez pourquoi la nouvelle façon est meilleure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Standardisez les décisions, pas les outils.&lt;/strong&gt; L’objectif n’est pas l’uniformité pour elle-même : c’est réduire la fatigue décisionnelle et permettre la réutilisation. Une config TypeScript partagée est plus précieuse qu’un framework imposé, parce qu’elle supprime une décision de bas niveau sans contraindre l’architecture de plus haut niveau.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Attendez-vous à une migration progressive.&lt;/strong&gt; Tout réécrire d’un coup casse les choses et épuise les ingénieurs. Migrez incrémentalement, produit par produit, module par module. Les nouveaux projets adoptent le standard par défaut ; les existants migrent au fil de leur évolution.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Mesurez le résultat, pas le processus.&lt;/strong&gt; La métrique qui compte est la vélocité de livraison, pas le “pourcentage de produits sur la nouvelle stack”. Suivez ce qui vous importe vraiment.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Standardiser une stack technique est, dans le fond, un pari que l’infrastructure partagée crée plus de valeur que l’autonomie individuelle. Chez METRO France, ce pari a clairement payé : livraison plus rapide, meilleurs produits, et une équipe d’ingénierie capable de travailler ensemble plutôt qu’en silos parallèles.&lt;/p&gt;
&lt;p&gt;Les 80% sont réels, mais ce qu’ils représentent est plus important : quand les ingénieurs arrêtent de réinventer les mêmes décisions encore et encore, ils peuvent passer ce temps à construire des choses qui comptent vraiment pour les utilisateurs.&lt;/p&gt;
&lt;p&gt;C’est ça, le vrai point.&lt;/p&gt;</content:encoded><category>softwareengineering</category><category>techleadership</category></item><item><title>L&apos;IA a cassé notre code de production. Voilà la règle du Craftsmanship qui aurait tout évité</title><link>https://djamel-bougouffa.com/fr/blog/software-craftsmanship-ai-era/</link><guid isPermaLink="true">https://djamel-bougouffa.com/fr/blog/software-craftsmanship-ai-era/</guid><description>Un agent IA a silencieusement cassé une règle métier vieille de 10 ans sur une codebase Java legacy. Aucun test ne l&apos;a attrapé. Ça a remonté en production. Voici ce que ça m&apos;a appris sur le Software Craftsmanship, et pourquoi ces principes comptent plus que jamais quand l&apos;IA écrit votre code.</description><pubDate>Mon, 04 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;img src=&quot;https://djamel-bougouffa.com/assets/software-craftmanship-ai-era.webp&quot; alt=&quot;Un développeur qui relit du code à l&apos;écran, les principes du craftsmanship à l&apos;ère de l&apos;IA&quot; /&gt;
&lt;p&gt;Quand des outils capables de générer du code à grande échelle deviennent courants, on est tenté de penser que les anciennes règles ne s’appliquent plus. Pourquoi se battre sur le nommage si l’IA comprend n’importe quel nom de variable ? Pourquoi écrire des tests si l’IA va de toute façon réécrire la fonction ? Pourquoi refactorer quand générer une nouvelle implémentation est plus rapide que nettoyer l’ancienne ?&lt;/p&gt;
&lt;p&gt;Ce raisonnement est dangereux. Et dans mon expérience, les codebases qui souffrent le plus du développement assisté par IA sont ceux qui ont abandonné la discipline en premier.&lt;/p&gt;
&lt;h2 id=&quot;ce-quest-vraiment-le-software-craftsmanship&quot;&gt;Ce qu’est vraiment le Software Craftsmanship&lt;/h2&gt;
&lt;p&gt;Le Software Craftsmanship, formalisé dans le &lt;a href=&quot;https://manifesto.softwarecraftsmanship.org/&quot;&gt;manifeste de 2009&lt;/a&gt;, est un ensemble de standards professionnels : code propre, développement piloté par les tests, refactoring continu, design simple, et ownership collectif. Il traite le code comme un artisanat : quelque chose dont on est fier, quelque chose qu’on entretient.&lt;/p&gt;
&lt;p&gt;Ce n’est pas une quête de perfection. C’est une responsabilité professionnelle.&lt;/p&gt;
&lt;h2 id=&quot;les-principes-qui-ont-survécu&quot;&gt;Les principes qui ont survécu&lt;/h2&gt;
&lt;p&gt;Après avoir travaillé avec des outils IA sur des codebases de production pendant plus d’un an, voici ce que j’ai constaté : les principes du craftsmanship ne disparaissent pas. Ils deviennent porteurs.&lt;/p&gt;
&lt;h3 id=&quot;1-les-tests-sont-votre-seul-filet-de-sécurité&quot;&gt;1. Les tests sont votre seul filet de sécurité&lt;/h3&gt;
&lt;p&gt;C’était vrai avant l’IA. C’est critique maintenant.&lt;/p&gt;
&lt;p&gt;L’IA génère du code rapidement mais sans compréhension profonde de l’intention. Elle peut produire du code qui passe la revue manuelle et casse silencieusement une règle métier qui n’a jamais été écrite. Les tests sont le seul moyen systématique de détecter ça.&lt;/p&gt;
&lt;p&gt;Je l’ai vécu directement. Sur une codebase Java legacy sans couverture de tests, un agent IA a refactorisé en toute confiance un calcul qui tournait depuis des années. Le changement semblait correct, passait le linter, et introduisait une régression silencieuse sur une règle métier que personne n’avait documentée. Il n’y avait aucun test pour l’attraper. Ça a remonté en production. C’est cette session qui m’a fait arrêter de traiter le craftsmanship comme optionnel avec l’IA. J’ai commencé à le traiter comme une précondition.&lt;/p&gt;
&lt;p&gt;Le TDD en particulier change la dynamique : écrire le test en premier vous force à définir ce que “correct” signifie avant que l’IA ne propose une implémentation. L’IA a alors une cible à atteindre. Sans cette cible, vous relisez du code en espérant qu’il soit juste. Ce n’est pas de l’ingénierie, c’est de l’optimisme.&lt;/p&gt;
&lt;p&gt;J’ai arrêté de traiter la couverture de tests comme une métrique. Je la traite comme une précondition. Si l’IA fait un changement qui n’est pas couvert par des tests, soit les tests passent en premier, soit le changement n’a pas lieu.&lt;/p&gt;
&lt;p&gt;Certains dans la communauté craftsmanship avancent que le TDD ne s’applique plus de la même façon quand c’est l’IA qui écrit le code, et ils ont raison sur un point : la &lt;em&gt;mécanique&lt;/em&gt; change. Le cycle classique Rouge → Vert → Refactoring était conçu pour un humain qui écrit à la fois le test et l’implémentation, où l’acte de coder est lui-même une activité de design.&lt;/p&gt;
&lt;p&gt;Avec l’IA, cette partie change. Mais le contrat que crée le test, lui, ne change pas.&lt;/p&gt;
&lt;p&gt;Le test écrit avant l’implémentation définit toujours ce que “correct” signifie. Il vous force toujours à réfléchir au comportement attendu avant de générer quoi que ce soit. L’IA écrit le Vert, mais vous possédez le Rouge. Et le Refactoring reste entièrement le vôtre. Ce qui disparaît, c’est la boucle de feedback design-par-implémentation. Ce qui reste, et ce qui compte le plus sur des codebases legacy avec l’IA, c’est le contrat de sécurité.&lt;/p&gt;
&lt;p&gt;Concrètement, avant de demander à une IA de toucher de la logique métier, j’écris d’abord un test de caractérisation :&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// Étape 1 : écrire le test qui définit ce que &quot;correct&quot; signifie&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;Test&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;void&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; shouldApplyReducedVatRateForB2bGermanOrders&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Order order &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Order.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;builder&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;amount&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; BigDecimal&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;1000&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;customerType&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(CustomerType.B2B)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;country&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;DE&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;build&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    BigDecimal vat &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; vatService.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;calculateVat&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(order);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    // Allemagne B2B : 19%, pas le taux standard de 20%&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    assertThat&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(vat).&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;isEqualByComparingTo&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; BigDecimal&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;190&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// Étape 2 : maintenant l&apos;IA a une cible, et un filet de sécurité&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sans ce test, l’IA aurait refactorisé &lt;code&gt;calculateVat()&lt;/code&gt; pour utiliser une constante &lt;code&gt;VAT_RATE = 0.20&lt;/code&gt; : propre, cohérent, et silencieusement faux pour toutes les commandes B2B allemandes.&lt;/p&gt;
&lt;h3 id=&quot;2-la-lisibilité-cest-pour-le-prochain-humain&quot;&gt;2. La lisibilité, c’est pour le prochain humain&lt;/h3&gt;
&lt;p&gt;L’IA peut parser n’importe quel code. Les humains, non.&lt;/p&gt;
&lt;p&gt;L’argument “l’IA le comprend, donc le nommage n’a pas d’importance” confond l’outil et le processus. Quand un bug remonte à 2h du matin, ce n’est pas l’IA qui est réveillée. Quand un nouveau développeur rejoint l’équipe, il lit le code dans une revue de PR, pas dans une fenêtre de chat. Quand votre tech lead remet en question un choix d’architecture, il a besoin de comprendre l’intention, pas seulement la mécanique.&lt;/p&gt;
&lt;p&gt;Le code propre est une documentation qui ne vieillit jamais mal. Dans le développement assisté par IA, où le volume de code généré augmente, la lisibilité devient une propriété plus précieuse, pas moins.&lt;/p&gt;
&lt;p&gt;Une variable nommée &lt;code&gt;result&lt;/code&gt; est de la dette technique. Une variable nommée &lt;code&gt;facturesEligiblesAuRecouvrement&lt;/code&gt; est autodocumentée. L’IA s’en fiche. Votre équipe, non.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// Ce que l&apos;IA génère souvent : correct, illisible&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;BigDecimal r &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; o.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;getA&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;().&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;multiply&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;getR&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(o.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;getC&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(), o.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;getT&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;()));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// Ce que le craftsmanship exige&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;BigDecimal montantTVA &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; commande.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;getSousTotal&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;multiply&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(resolveurTauxTVA.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;resoudre&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(commande.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;getPays&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(), commande.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;getTypeClient&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;()));&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La deuxième version ne fait pas que mieux paraître. Quand le bug TVA remonte à 2h du matin, votre collègue trouve la bonne classe en 10 secondes plutôt qu’en 10 minutes.&lt;/p&gt;
&lt;h3 id=&quot;3-des-changements-petits-réversibles-par-défaut&quot;&gt;3. Des changements petits, réversibles par défaut&lt;/h3&gt;
&lt;p&gt;Les outils IA adorent générer de grands changements. Une seule invite peut toucher 20 fichiers, renommer 3 abstractions et restructurer 2 modules. C’est genuinement utile, et genuinement dangereux.&lt;/p&gt;
&lt;p&gt;Le principe du craftsmanship ici est : &lt;em&gt;gardez les changements petits et réversibles&lt;/em&gt;. Un concept par commit. Les tests avant le refactoring. Un diff qu’un collègue peut relire en 15 minutes sans perdre le contexte.&lt;/p&gt;
&lt;p&gt;C’est aussi là que la règle du Boy Scout prend tout son sens : &lt;em&gt;laissez le code un peu meilleur que vous l’avez trouvé&lt;/em&gt;. Pas complètement redesigné. Pas entièrement modernisé. Juste un peu plus propre. Cette discipline évite le problème “l’IA a fait un gros refactoring en corrigeant un bug” qui transforme une PR de routine en une session de revue de 3 heures.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Ce que l&apos;IA tend à produire : un commit massif impossible à relire&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; log&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --oneline&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# a3f9c12 refactor order processing&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# (touche : OrderService, VatCalculator, InvoiceGenerator,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;#  PaymentProcessor, OrderRepository, OrderMapper : 847 lignes modifiées)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Ce que le craftsmanship donne&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# 1f2a3b4 PROJ-421: extraire VatRateResolver de OrderService&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# 8c9d0e1 PROJ-421: ajouter taux TVA B2B pour DE, AT, CH&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# 2e3f4a5 PROJ-421: couvrir VatRateResolver avec des tests unitaires&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# 7b8c9d0 PROJ-421: corriger taux TVA incorrect sur les commandes B2B allemandes&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Le deuxième historique est relisible, bisectable et réversible. Le premier est un pari.&lt;/p&gt;
&lt;h3 id=&quot;4-le-refactoring-est-une-pratique-continue-pas-un-sprint&quot;&gt;4. Le refactoring est une pratique continue, pas un sprint&lt;/h3&gt;
&lt;p&gt;Le code généré par IA tend vers la duplication. Demandez à une IA d’implémenter une fonctionnalité, et elle créera souvent une nouvelle version de quelque chose qui existe déjà à proximité. Ce n’est pas faux. Elle répond au problème immédiat sans connaître toute la codebase.&lt;/p&gt;
&lt;p&gt;La discipline, c’est l’étape de refactoring qui suit. Avant de merger, posez-vous la question : est-ce que ça duplique quelque chose ? Est-ce que ça appartient ici ? Y a-t-il une meilleure abstraction ?&lt;/p&gt;
&lt;p&gt;Ce n’est pas le travail de l’IA. C’est le vôtre. L’IA est bonne pour exécuter. Elle n’est pas bonne pour savoir quand quelque chose est déjà fait correctement ailleurs dans une codebase de 200k lignes. C’est la connaissance institutionnelle. C’est le craftsmanship.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// L&apos;IA a généré ça dans OrderService : correct en isolation&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;private&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; BigDecimal &lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;appliquerRemise&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(BigDecimal montant, String typeClient) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;B2B&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;equals&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(typeClient)) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; montant.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;multiply&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; BigDecimal&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;0.90&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; montant;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// Mais ça existait déjà dans PricingService, depuis 3 ans&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; BigDecimal &lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;appliquerRemiseClient&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(BigDecimal prixBase, TypeClient type) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; prixBase.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;multiply&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(configRemise.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;getTauxPour&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(type));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Deux implémentations de la même logique, divergeant silencieusement. L’IA ne savait pas. Vous, si, à condition de regarder.&lt;/p&gt;
&lt;h3 id=&quot;5-lownership-collectif-exige-des-standards-explicites&quot;&gt;5. L’ownership collectif exige des standards explicites&lt;/h3&gt;
&lt;p&gt;Le principe le plus sous-estimé du manifeste craftsmanship est l’ownership collectif du code : l’idée que toute l’équipe est responsable de toute la codebase, pas seulement de “ses” modules.&lt;/p&gt;
&lt;p&gt;Les outils IA rendent ça plus difficile par défaut. Tout le monde utilise ses propres prompts, son propre modèle, son propre style. Sans standards partagés explicites, le développement assisté par IA accélère la divergence : conventions de nommage différentes, styles de tests différents, niveaux d’abstraction différents dans le même service.&lt;/p&gt;
&lt;p&gt;La solution est explicite : un &lt;code&gt;CLAUDE.md&lt;/code&gt;, un &lt;code&gt;CONTRIBUTING.md&lt;/code&gt;, des règles de linting sans exceptions, des templates de PR qui imposent la checklist de revue. Ce n’est pas de la bureaucratie. C’est l’échafaudage qui rend l’ownership collectif possible quand une partie de l’équipe est une IA.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;markdown&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF;font-weight:bold&quot;&gt;# CLAUDE.md (extrait)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF;font-weight:bold&quot;&gt;## Règles non négociables&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Ne jamais modifier &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;`LegacyOrderMapper`&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; sans tests de caractérisation d&apos;abord&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Toutes les valeurs monétaires en &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8;font-weight:bold&quot;&gt;**centimes (Long)**&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, jamais BigDecimal en base de données&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Les taux de TVA vivent dans &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;`VatRateConfig`&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; : ne jamais les coder en dur&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Ne PAS ajouter &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;`@Transactional`&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; aux jobs du scheduler : ils maintiennent des connexions pendant des minutes&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF;font-weight:bold&quot;&gt;## Avant tout refactoring&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;1.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Écrire des tests qui décrivent le comportement actuel&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;2.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Confirmer que les tests passent sur le code non modifié&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;3.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Refactoriser&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;4.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Confirmer que les tests passent toujours&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ce fichier est le contrat partagé. Chaque développeur le lit. L’agent IA le lit. Les standards cessent d’être des habitudes individuelles et deviennent de l’infrastructure d’équipe.&lt;/p&gt;
&lt;p&gt;Mais le &lt;code&gt;CLAUDE.md&lt;/code&gt; est une contrainte &lt;em&gt;douce&lt;/em&gt; : l’IA le respecte si vous le configurez. Les règles de linting sont des contraintes &lt;em&gt;dures&lt;/em&gt; : le CI casse si quiconque, humain ou IA, les viole.&lt;/p&gt;
&lt;p&gt;Pour l’architecture hexagonale en particulier, &lt;code&gt;eslint-plugin-boundaries&lt;/code&gt; impose l’isolation des couches au niveau des imports :&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// .eslintrc : les frontières architecturales comme règles de linting&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;boundaries/element-types&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: [&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;error&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  default: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;disallow&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  rules: [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    // Le domaine est pur : aucun import externe autorisé&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    { from: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;domain&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, allow: [] },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    // La couche application ne dépend que du domaine&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    { from: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;application&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, allow: [&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;domain&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    // Seuls les adapters peuvent toucher l&apos;infrastructure&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    { from: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;adapters&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, allow: [&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;application&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;domain&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Si l’IA génère &lt;code&gt;import { PrismaClient } from &apos;@prisma/client&apos;&lt;/code&gt; à l’intérieur de votre domaine, le pipeline échoue immédiatement, avant la revue, avant le merge, avant la production. L’architecture s’applique d’elle-même.&lt;/p&gt;
&lt;p&gt;En Java, vous avez trois options selon jusqu’où vous souhaitez aller :&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ArchUnit&lt;/strong&gt; : appliqué comme assertion de test, utile sur les codebases legacy où restructurer n’est pas une option :&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;Test&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;void&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; domainShouldNotDependOnInfrastructure&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    noClasses&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;().&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;that&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;().&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;resideInAPackage&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;..domain..&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;should&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;().&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;dependOnClassesThat&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;resideInAPackage&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;..infrastructure..&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;check&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(importedClasses);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Multi-module Maven/Gradle&lt;/strong&gt; : contrainte structurelle. Si &lt;code&gt;domain&lt;/code&gt; ne déclare pas &lt;code&gt;infrastructure&lt;/code&gt; comme dépendance, l’import ne peut physiquement pas compiler :&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;xml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;&amp;#x3C;!-- domain/pom.xml : infrastructure n&apos;est tout simplement pas là --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;dependencies&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    &amp;#x3C;!-- pas de dépendance infrastructure = aucun import possible --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;dependencies&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;JPMS (Java 9+)&lt;/strong&gt; : l’option la plus native. &lt;code&gt;module-info.java&lt;/code&gt; déclare exactement ce que chaque module peut voir. Pas de &lt;code&gt;requires infrastructure&lt;/code&gt; = erreur de compilation, point final :&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// domain/src/main/java/module-info.java&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;module&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; com&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.example.domain {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    exports&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; com.example.domain.model;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    exports&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; com.example.domain.port;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    // pas de &apos;requires infrastructure&apos; : l&apos;import est une erreur de compilation&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sur un projet greenfield, multi-module ou JPMS est la bonne réponse. ArchUnit brille sur les codebases legacy où vous ne pouvez pas encore restructurer mais avez besoin du garde-fou immédiatement.&lt;/p&gt;
&lt;p&gt;C’est la différence entre les &lt;em&gt;guidelines&lt;/em&gt; et la &lt;em&gt;gouvernance&lt;/em&gt;. Le &lt;code&gt;CLAUDE.md&lt;/code&gt; dit à l’IA quoi faire. Le linter, ou le compilateur, rend impossible de faire autrement.&lt;/p&gt;
&lt;h2 id=&quot;ce-que-lia-change-vraiment&quot;&gt;Ce que l’IA change vraiment&lt;/h2&gt;
&lt;p&gt;Pour être honnête : l’IA change bien certaines choses.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L’exploration est plus rapide.&lt;/strong&gt; Comprendre une nouvelle codebase, tracer un flux de données, trouver où vit un bug. L’IA compresse ça de plusieurs heures à quelques minutes. C’est genuinement bon. La partie archéologie du travail logiciel est de toute façon la moins porteuse de valeur artisanale.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Le boilerplate a disparu.&lt;/strong&gt; Écrire un endpoint CRUD, un DTO, un mapper : ces choses ne nécessitent plus de craft. Elles nécessitent une spec claire. C’est un gain net : ça déplace le focus là où le jugement compte vraiment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Le craft est dans les contraintes.&lt;/strong&gt; La compétence n’est plus dans la frappe. Elle est dans la définition de ce qui est correct avant de générer. La spécification, le test, le &lt;code&gt;CLAUDE.md&lt;/code&gt;, la revue : ce sont les surfaces du craft. Elles ont toujours été les parties les plus importantes. L’IA ne fait que le rendre évident.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Le risque se multiplie sans discipline.&lt;/strong&gt; Un développeur sans rigueur écrit du mauvais code lentement. Un développeur sans rigueur avec l’IA écrit du mauvais code vite. Le volume d’output augmente, mais la surface des régressions silencieuses, du couplage caché, et des règles métier cassées par un modèle confiant qui n’avait aucun test pour le corriger, augmente avec lui.&lt;/p&gt;
&lt;h2 id=&quot;la-responsabilité-reste-la-vôtre&quot;&gt;La responsabilité reste la vôtre&lt;/h2&gt;
&lt;p&gt;Voici où j’en suis arrivé : l’IA ne réduit pas la responsabilité d’un développeur senior. Elle la déplace en amont.&lt;/p&gt;
&lt;p&gt;Avant : écrire le code, relire le code, tester le code.&lt;br&gt;
Avec l’IA : définir ce que correct signifie, contraindre la génération, valider l’output.&lt;/p&gt;
&lt;p&gt;La deuxième boucle demande autant de discipline. Peut-être plus. Parce que le volume d’output est plus élevé, la surface de revue est plus large, et les raccourcis cognitifs sont plus faciles à prendre.&lt;/p&gt;
&lt;p&gt;Le Software Craftsmanship est la discipline qui vous garde honnête dans cette boucle.&lt;/p&gt;
&lt;p&gt;Les principes n’ont pas été remplacés. Ils sont devenus le travail.&lt;/p&gt;
&lt;p&gt;Et si vous managez une équipe, votre responsabilité est double : pratiquer ces standards vous-même, mais aussi en faire le socle commun depuis lequel tout le monde, humain et IA, opère.&lt;/p&gt;
&lt;h2 id=&quot;points-clés-à-retenir&quot;&gt;Points clés à retenir&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Les tests sont une précondition, pas une métrique.&lt;/strong&gt; Si l’IA fait un changement sans couverture de tests, soit les tests passent en premier, soit le changement n’a pas lieu.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;La lisibilité est pour les humains, pas les modèles.&lt;/strong&gt; L’IA peut parser n’importe quoi. Votre équipe, non. Le code propre est une documentation qui ne vieillit jamais mal.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gardez les changements petits et réversibles.&lt;/strong&gt; L’IA génère de grands diffs par défaut. Votre discipline est de la contraindre : un concept par commit, relisible en 15 minutes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Le refactoring est votre travail, pas celui de l’IA.&lt;/strong&gt; L’IA ne sait pas ce qui existe déjà ailleurs dans une codebase de 200k lignes. Vous, si.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Les standards explicites permettent l’ownership collectif.&lt;/strong&gt; Sans &lt;code&gt;CLAUDE.md&lt;/code&gt;, sans &lt;code&gt;CONTRIBUTING.md&lt;/code&gt;, sans linting appliqué, le développement assisté par IA accélère la divergence dans votre équipe.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Le risque se multiplie sans discipline.&lt;/strong&gt; Une équipe sans rigueur avec l’IA n’écrit pas du mauvais code lentement : elle en écrit vite.&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>softwareengineering</category></item><item><title>Comment j&apos;utilise Claude Code et Jira MCP pour moderniser les codebases legacy</title><link>https://djamel-bougouffa.com/fr/blog/claude-code-jira-mcp-legacy-codebase/</link><guid isPermaLink="true">https://djamel-bougouffa.com/fr/blog/claude-code-jira-mcp-legacy-codebase/</guid><description>Comment je connecte Claude Code à Jira via MCP pour passer du ticket à la pull request sur du legacy Java : ma config exacte, CLAUDE.md et résultats concrets.</description><pubDate>Tue, 28 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;img src=&quot;https://djamel-bougouffa.com/assets/claude-code-jira-mcp-legacy-code.webp&quot; alt=&quot;Session Claude Code dans le terminal naviguant une codebase legacy avec le Jira MCP&quot; /&gt;
&lt;p&gt;Le code legacy, c’est le squelette de chaque entreprise qui a réussi. Il gère la facturation, traite les commandes, et contient des règles métier que personne ne comprend plus vraiment depuis des années. Il ne va nulle part et c’est précisément pour ça que le moderniser est essentiel.&lt;/p&gt;
&lt;p&gt;Depuis plusieurs mois, j’utilise &lt;strong&gt;Claude Code&lt;/strong&gt; avec le &lt;strong&gt;Jira MCP&lt;/strong&gt; et un &lt;code&gt;CLAUDE.md&lt;/code&gt; soigneusement rédigé pour naviguer et implémenter des changements dans des codebases Java legacy. Cet article est un retour concret sur ce workflow comment ça se passe, pourquoi ça fonctionne, et où ça échoue encore.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://djamel-bougouffa.com/assets/blog/claude-code-jira-mcp-workflow.svg&quot; alt=&quot;Schéma du workflow : le Jira MCP et CLAUDE.md alimentent Claude Code, qui navigue la codebase legacy et produit une pull request&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;le-problème-avec-le-code-legacy&quot;&gt;Le problème avec le code legacy&lt;/h2&gt;
&lt;p&gt;Avant de parler du setup, il faut nommer le vrai problème. Le code legacy n’est pas difficile parce qu’il est vieux. Il est difficile à cause de tout ce qui l’entoure :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Aucune documentation&lt;/strong&gt; (ou pire, une doc qui ment)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Règles métier implicites&lt;/strong&gt; enfouies dans des conditions que personne n’ose toucher&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pas de tests&lt;/strong&gt; qui confirmeraient que ton changement ne casse rien&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Les auteurs originaux sont partis&lt;/strong&gt;, leur connaissance est partie avec eux&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;La peur&lt;/strong&gt;, le vrai tueur de vélocité sur les projets legacy&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;L’approche classique c’est de lire le code, poser des questions, et faire des changements prudents. Ça marche, mais c’est lent. Un agent IA ne résout pas tout ça, mais il réduit drastiquement le temps passé sur la phase d’archéologie.&lt;/p&gt;
&lt;h2 id=&quot;le-stack&quot;&gt;Le stack&lt;/h2&gt;
&lt;p&gt;Mon setup comporte trois éléments :&lt;/p&gt;
&lt;h3 id=&quot;1-claude-code&quot;&gt;1. Claude Code&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://claude.ai/code&quot;&gt;Claude Code&lt;/a&gt; est l’outil de coding agentique d’Anthropic. Il tourne dans ton terminal, a accès à ton système de fichiers, peut exécuter des commandes, et itère de lui-même. Contrairement à une interface de chat, il ne se contente pas de suggérer du code il lit des fichiers, fait des modifications, lance des tests, et corrige des erreurs.&lt;/p&gt;
&lt;p&gt;Ce qui le rend puissant pour le travail legacy, c’est sa capacité à naviguer une large codebase sans que je lui dicte chaque étape. Je lui donne une tâche et du contexte, il détermine quels fichiers lire.&lt;/p&gt;
&lt;h3 id=&quot;2-claudemd-le-manuel-du-projet&quot;&gt;2. CLAUDE.md Le manuel du projet&lt;/h3&gt;
&lt;p&gt;Chaque projet reçoit un &lt;code&gt;CLAUDE.md&lt;/code&gt; à la racine. Ce fichier, c’est le document d’onboarding de Claude Code. Il indique à l’agent ce qu’il doit savoir avant de toucher quoi que ce soit.&lt;/p&gt;
&lt;p&gt;Voici le type de contenu que je mets dans un &lt;code&gt;CLAUDE.md&lt;/code&gt; pour un projet Java enterprise typique :&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;markdown&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF;font-weight:bold&quot;&gt;## Vue d&apos;ensemble&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;Application Spring Boot multi-modules Maven, Java 17.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;Deux bases de données : PostgreSQL (données applicatives) + Oracle (ERP legacy, lecture seule).&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;Tous les services tournent en Docker en local.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF;font-weight:bold&quot;&gt;## Commandes du quotidien&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF;font-weight:bold&quot;&gt;### Environnement&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;docker compose up -d          # démarre tous les conteneurs&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;docker compose logs -f api    # suit les logs de l&apos;API&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;docker compose down           # arrête tous les conteneurs&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF;font-weight:bold&quot;&gt;### Build &amp;#x26; run&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;./mvnw clean install -DskipTests   # build complet&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;./mvnw -pl api spring-boot:run     # lance uniquement le module api&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;./mvnw test -pl api                # lance les tests unitaires du module api&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF;font-weight:bold&quot;&gt;### Base de données&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF;font-weight:bold&quot;&gt;# PostgreSQL localhost:5432, base: appdb, user: appuser&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;./scripts/db-reset.sh              # drop + recréation + seed en local&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;./scripts/db-load-dump.sh prod     # charge un dump prod anonymisé (~5min)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF;font-weight:bold&quot;&gt;# Oracle (ERP legacy) lecture seule, localhost:1521&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF;font-weight:bold&quot;&gt;# Ne jamais écrire directement utiliser le wrapper OracleService dédié&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF;font-weight:bold&quot;&gt;## Architecture&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; api/          Contrôleurs REST + logique métier&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; domain/       Entités, repositories (Spring Data JPA)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; integration/  Adaptateurs vers l&apos;ERP Oracle (polling via Apache Camel)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; scheduler/    Jobs batch (Spring Batch)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; common/       DTOs, utils, constantes partagées&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF;font-weight:bold&quot;&gt;## Points sensibles&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; L&apos;ERP Oracle est en lecture seule ne jamais tenter d&apos;écriture, ça corrompt les données en aval&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; OrderService.createOrder() n&apos;est PAS idempotent toujours vérifier les doublons avant&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; LegacyCodeMapper utilise la réflexion, ajouter des champs casse silencieusement sans tests&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Ne PAS ajouter @Transactional aux jobs scheduler ils gardent les connexions ouvertes des minutes&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF;font-weight:bold&quot;&gt;## Git&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;Branches : main (prod), develop (intégration), PROJ-XXXX-description-courte (feature)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;Commits : toujours préfixer avec le ticket &quot;PROJ-123: ajout calcul TVA commandes EU&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;Push : --force-with-lease uniquement, jamais --force&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ce fichier fait la différence entre Claude Code qui fait un changement correct et confiant, et un agent qui casse quelque chose par ignorance. Plus tu es précis, meilleurs sont les résultats.&lt;/p&gt;
&lt;p&gt;La section &lt;strong&gt;points sensibles&lt;/strong&gt; est la plus précieuse. Ce sont les choses qu’un nouveau développeur mettrait des semaines à découvrir. Les écrire une fois protège tout le monde y compris l’agent des régressions douloureuses.&lt;/p&gt;
&lt;h3 id=&quot;3-jira-mcp&quot;&gt;3. Jira MCP&lt;/h3&gt;
&lt;p&gt;Le &lt;a href=&quot;https://github.com/sooperset/mcp-atlassian&quot;&gt;Jira MCP&lt;/a&gt; expose ton projet Jira comme un ensemble d’outils que Claude Code peut appeler. Concrètement, ça signifie que Claude Code peut :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Lire la description du ticket, les critères d’acceptance et les commentaires&lt;/li&gt;
&lt;li&gt;Comprendre le contexte complet avant d’écrire la moindre ligne&lt;/li&gt;
&lt;li&gt;Référencer les tickets liés et les sous-tâches&lt;/li&gt;
&lt;li&gt;Poster un commentaire avec un résumé de ce qui a été fait&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Mais la chose la plus puissante que j’ai construite par-dessus, c’est un &lt;strong&gt;skill custom dans &lt;code&gt;CLAUDE.md&lt;/code&gt;&lt;/strong&gt; :&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;markdown&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF;font-weight:bold&quot;&gt;## Skills disponibles&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;| Commande                    | Description                                                  |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;|-----------------------------|--------------------------------------------------------------|&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;| &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;`/tester-ticket &amp;#x3C;PROJ-XXXX&gt;`&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;| Lance les tests Playwright des critères d&apos;acceptance Jira    |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;| &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;`/mr-push [app\|connector]`&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; | Amend + rebase + force-push with lease                       |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;| &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;`/mr-comment [répondre\|poster]`&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; | Lit et répond aux commentaires de MR GitLab             |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;| &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;`/build [app\|connector]`&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;   | Build l&apos;app ou le connecteur                                 |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;| &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;`/sonar [app\|connector]`&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;   | Analyse SonarQube locale                                     |&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Le skill &lt;code&gt;/tester-ticket&lt;/code&gt; est celui qui a changé ma façon de travailler. Je lui donne une référence de ticket, Claude Code lit les critères d’acceptance depuis Jira, identifie les fichiers Playwright correspondants, les lance, et rapporte les résultats. La boucle complète écrire le code, tester contre le ticket réel sans quitter le terminal.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Mise en garde honnête cependant&lt;/strong&gt; : il brûle beaucoup de tokens par exécution. Lire le ticket, naviguer les fichiers de test, lancer Playwright, interpréter les résultats tout ça s’accumule. Sur un ticket complexe avec plusieurs critères d’acceptance, une seule exécution de &lt;code&gt;/tester-ticket&lt;/code&gt; peut coûter plus que tout le reste de la session réunis. Je l’utilise encore, mais de façon sélective pas comme étape par défaut à chaque changement.&lt;/p&gt;
&lt;h2 id=&quot;le-workflow--du-ticket-à-limplémentation&quot;&gt;Le workflow : du ticket à l’implémentation&lt;/h2&gt;
&lt;p&gt;Voici à quoi ressemble une session type.&lt;/p&gt;
&lt;h3 id=&quot;étape-1-démarrer-avec-une-référence-de-ticket&quot;&gt;Étape 1 Démarrer avec une référence de ticket&lt;/h3&gt;
&lt;p&gt;J’ouvre un terminal à la racine du projet et je lance Claude Code avec un prompt simple :&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Lis le ticket JIRA PROJ-4821 et implémente le changement demandé.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Utilise CLAUDE.md pour le contexte du projet avant de toucher des fichiers.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;C’est tout. Claude Code lit d’abord &lt;code&gt;CLAUDE.md&lt;/code&gt;, puis appelle le Jira MCP pour récupérer le ticket, lit la description et les critères d’acceptance, et commence à explorer la codebase.&lt;/p&gt;
&lt;h3 id=&quot;étape-2-claude-code-explore-la-codebase&quot;&gt;Étape 2 Claude Code explore la codebase&lt;/h3&gt;
&lt;p&gt;À partir du contenu du ticket, l’agent recherche les fichiers pertinents. Pour un ticket comme &lt;em&gt;“Corriger le calcul de TVA incorrect pour les commandes B2B en Allemagne”&lt;/em&gt;, il va :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Chercher &lt;code&gt;TVA&lt;/code&gt;, &lt;code&gt;tax&lt;/code&gt;, &lt;code&gt;germany&lt;/code&gt;, &lt;code&gt;B2B&lt;/code&gt; dans toute la codebase&lt;/li&gt;
&lt;li&gt;Lire les fichiers de service et de modèle concernés&lt;/li&gt;
&lt;li&gt;Suivre la chaîne d’appels pour comprendre ce qui alimente le calcul&lt;/li&gt;
&lt;li&gt;Vérifier s’il existe des tests pour ce flux&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;C’est la phase d’archéologie. La faire manuellement prend 30 à 60 minutes sur une codebase inconnue. Claude Code le fait en 2-3 minutes.&lt;/p&gt;
&lt;h3 id=&quot;étape-3-implémentation-avec-des-guardrails&quot;&gt;Étape 3 Implémentation avec des guardrails&lt;/h3&gt;
&lt;p&gt;Claude Code propose des changements basés sur ce qu’il a trouvé. Parce que &lt;code&gt;CLAUDE.md&lt;/code&gt; établit les contraintes (pas de Lombok, montants en centimes, ne pas toucher les tables &lt;code&gt;legacy_&lt;/code&gt;), les changements respectent les conventions existantes du projet.&lt;/p&gt;
&lt;p&gt;Si l’agent est incertain, il demande. S’il trouve quelque chose de suspect dans le code qui pourrait être lié au ticket, il le signale.&lt;/p&gt;
&lt;h3 id=&quot;étape-4-review-et-itération&quot;&gt;Étape 4 Review et itération&lt;/h3&gt;
&lt;p&gt;Je review le diff, lance les tests, et itère. Claude Code ne remplace pas la review il compresse le temps passé avant la review.&lt;/p&gt;
&lt;h2 id=&quot;ce-qui-fonctionne-vraiment-bien&quot;&gt;Ce qui fonctionne vraiment bien&lt;/h2&gt;
&lt;p&gt;Après plusieurs mois d’utilisation de ce workflow, voici ce qui apporte de la valeur de manière constante :&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Comprendre les flux de données :&lt;/strong&gt; tracer comment une valeur passe de la base de données à la réponse API à travers 8 couches de services est fastidieux. Claude Code le fait bien.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Écrire des tests pour du code non testé :&lt;/strong&gt; c’est sous-estimé. Avant de faire un changement, je demande à Claude Code d’écrire des tests de caractérisation pour le code concerné. Il lit le comportement existant et écrit des tests qui le décrivent. Ces tests détectent les régressions pendant le changement.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Faire remonter le code associé :&lt;/strong&gt; l’agent trouve souvent du code lié au ticket mais non mentionné dedans. Logique dupliquée, code mort, une fonction similaire dans un autre module. Cette connaissance s’accumule avec le temps.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Traduire le langage du ticket en code :&lt;/strong&gt; les parties prenantes métier rédigent des tickets en langage business. Les développeurs doivent traduire mentalement. Claude Code comble ce fossé plus efficacement que je ne l’aurais anticipé.&lt;/p&gt;
&lt;h2 id=&quot;ce-qui-ne-fonctionne-pas-encore&quot;&gt;Ce qui ne fonctionne pas encore&lt;/h2&gt;
&lt;p&gt;L’honnêteté s’impose ici. Ce workflow n’est pas magique.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Les règles métier implicites :&lt;/strong&gt; si la règle n’est pas écrite (dans &lt;code&gt;CLAUDE.md&lt;/code&gt;, les commentaires, ou les tests), Claude Code ne l’inférera pas depuis les patterns du code. Une codebase legacy pleine d’hypothèses silencieuses le reste.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Les modules fortement couplés :&lt;/strong&gt; quand tout dépend de tout, l’agent a du mal à faire un changement ciblé sans trop toucher. La solution, c’est l’architecture, pas le prompt.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Le coût en tokens des boucles de test automatisées :&lt;/strong&gt; automatiser le cycle complet test-contre-ticket est techniquement impressionnant mais coûteux. Plus le contexte que l’agent doit charger est important (ticket + fichiers de test + output Playwright), plus ça brûle de tokens. Certaines automatisations qui semblent parfaites sur le papier ne survivent pas au contact d’une vraie facture mensuelle.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L’opération 100% autonome :&lt;/strong&gt; Claude Code est un junior développeur senior. Il est rapide, minutieux, et suit bien les instructions. Mais il a toujours besoin d’un humain pour définir ce que “correct” signifie et valider le résultat. Le traiter comme un agent totalement autonome mène à de mauvais résultats.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Les préoccupations transversales :&lt;/strong&gt; sécurité, performance, observabilité. Elles requièrent un jugement qui dépasse l’implémentation de ce que le ticket demande. Je les revois toujours séparément.&lt;/p&gt;
&lt;h2 id=&quot;le-claudemd-est-le-vrai-débloquant&quot;&gt;Le CLAUDE.md est le vrai débloquant&lt;/h2&gt;
&lt;p&gt;Si je devais nommer la partie la plus impactante de ce setup, c’est le &lt;code&gt;CLAUDE.md&lt;/code&gt; pas le modèle, pas les outils.&lt;/p&gt;
&lt;p&gt;Un bon &lt;code&gt;CLAUDE.md&lt;/code&gt; capture la connaissance institutionnelle qui vit normalement uniquement dans la tête des personnes qui ont le plus travaillé sur le projet. L’écrire te force à articuler des choses qui sont habituellement implicites. Et une fois qu’il existe, n’importe quel développeur (humain ou IA) s’onboard plus vite.&lt;/p&gt;
&lt;p&gt;Je le mets à jour chaque fois que je découvre quelque chose qui m’a surpris pendant une session. Avec le temps, il devient la documentation vivante que le projet n’a jamais eue.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;La combinaison Claude Code, Jira MCP et un &lt;code&gt;CLAUDE.md&lt;/code&gt; bien maintenu n’élimine pas les parties difficiles de la modernisation legacy. Elle compresse le temps passé sur les parties mécaniques lire du code, tracer des flux, écrire du boilerplate pour que plus de ton temps aille au jugement, à l’architecture, et à la vraie résolution de problèmes.&lt;/p&gt;
&lt;p&gt;Ce décalage vaut plus que n’importe quelle fonctionnalité individuelle des outils.&lt;/p&gt;
&lt;p&gt;Si tu travailles sur une codebase legacy et que tu n’as pas essayé ce workflow, commence par le &lt;code&gt;CLAUDE.md&lt;/code&gt;. Même sans Claude Code, te forcer à écrire ce que le projet nécessite que les nouveaux venus sachent est les 30 minutes les mieux investies de ta semaine.&lt;/p&gt;</content:encoded><category>ia</category><category>softwareengineering</category></item><item><title>Model Context Protocol (MCP) : exposer un CLI Rust dans votre IDE</title><link>https://djamel-bougouffa.com/fr/blog/model-context-protocol-mcp-cli-rust-ide/</link><guid isPermaLink="true">https://djamel-bougouffa.com/fr/blog/model-context-protocol-mcp-cli-rust-ide/</guid><description>Retour d&apos;expérience sur l&apos;utilisation de MCP pour exposer un CLI Rust à un agent IA dans l&apos;IDE : architecture, bonnes pratiques et implémentation TypeScript.</description><pubDate>Wed, 11 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;img src=&quot;https://blog.ippon.fr/content/images/2026/03/mcp-cli-rust-ide-schema-architecture.png&quot; alt=&quot;Architecture MCP : CLI Rust connecté à un IDE via un serveur MCP&quot; /&gt;
&lt;p&gt;&lt;strong&gt;Réponse rapide :&lt;/strong&gt; Le Model Context Protocol (MCP) permet à un agent IA, dans votre IDE, d’appeler directement vos propres outils, sans plugin spécifique. Vous gardez votre CLI Rust comme moteur, vous l’enveloppez dans un petit serveur MCP TypeScript qui déclare ses capacités, et n’importe quel host compatible MCP (Claude Code, Cursor, Windsurf) peut alors exécuter de vraies actions sur votre codebase au lieu de simplement les suggérer.&lt;/p&gt;
&lt;p&gt;Le &lt;strong&gt;Model Context Protocol (MCP)&lt;/strong&gt; permet d’exposer des outils internes à un agent IA directement dans l’IDE, sans plugin spécifique, sans intégration ad hoc. Plutôt que de donner des suggestions, l’agent peut exécuter de vraies actions sur votre codebase.&lt;/p&gt;
&lt;h2 id=&quot;le-problème-que-mcp-résout&quot;&gt;Le problème que MCP résout&lt;/h2&gt;
&lt;p&gt;Les agents IA intégrés aux IDE sont excellents pour comprendre le contexte : analyser du code, expliquer des erreurs, suggérer des refactors. Mais dès qu’il faut exécuter une action réelle (lancer un script interne, déclencher une logique métier, valider des sorties), une boucle inefficace apparaît : l’agent propose → on exécute ailleurs → on recolle la sortie dans le chat. À chaque fois.&lt;/p&gt;
&lt;p&gt;MCP comble ce fossé en fournissant un &lt;strong&gt;standard pour exposer des capacités&lt;/strong&gt; (CLIs, APIs, services internes) via un contrat structuré directement consommable par un IDE ou un assistant IA.&lt;/p&gt;
&lt;h2 id=&quot;mcp-vs-rest--pas-juste-une-api-de-plus&quot;&gt;MCP vs REST : pas juste une API de plus&lt;/h2&gt;
&lt;p&gt;La différence n’est pas dans le format des messages, elle est dans le modèle d’intégration :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;REST&lt;/strong&gt; expose des endpoints. L’orchestration est écrite côté client, le client est connu à l’avance, rien ne standardise l’usage par un agent.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MCP&lt;/strong&gt; expose des capacités déclarées (&lt;code&gt;tools&lt;/code&gt;, &lt;code&gt;resources&lt;/code&gt;, &lt;code&gt;prompts&lt;/code&gt;). Le host les découvre dynamiquement, l’agent décide comment les enchaîner, les réponses sont structurées pour être interprétables et replanifiables.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;REST expose des services. MCP expose des actions pilotables par des agents.&lt;/p&gt;
&lt;h2 id=&quot;architecture--moteur--façade--cockpit&quot;&gt;Architecture : moteur / façade / cockpit&lt;/h2&gt;
&lt;p&gt;Dans notre implémentation chez Ippon :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;CLI Rust&lt;/strong&gt; = le moteur : logique métier, performances, source de vérité. Rien ne change ici.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Serveur MCP TypeScript&lt;/strong&gt; = la façade : le contrat, la validation des paramètres, l’exposition des capacités via le SDK MCP.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IDE/agent&lt;/strong&gt; = le cockpit : l’orchestration et l’expérience utilisateur.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Une cible d’intégration unique remplace N plugins spécifiques à chaque IDE. Exposer un outil est étonnamment concis :&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;server.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;tool&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;analyze_module&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  inputSchema: { type: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;object&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, properties: { module: { type: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; } }, required: [&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;module&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}, &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;async&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ({ &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;module&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; result&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; runCli&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;([&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;analyze&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;module&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; JSON&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;parse&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(result);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;pourquoi-pas-un-plugin-vscode-&quot;&gt;Pourquoi pas un plugin VSCode ?&lt;/h2&gt;
&lt;p&gt;L’approche “plugin intelligent” se heurte rapidement à des problèmes structurels : l’effet N-IDE (chaque host devient une cible), la duplication de logique métier qui diverge du CLI, le parsing fragile de stdout qui transforme des logs en API implicite, et le lock-in IDE. MCP inverse la posture : on part des capacités, pas de l’interface.&lt;/p&gt;
&lt;h2 id=&quot;3-principes-de-design-pour-des-outils-agent-ready&quot;&gt;3 principes de design pour des outils agent-ready&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Outputs orientés décision :&lt;/strong&gt; ne pas dumper de l’état, renvoyer ce dont l’agent a besoin pour agir.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Erreurs actionnables :&lt;/strong&gt; &lt;code&gt;code + message + recoveryHint&lt;/code&gt; structurés pour que l’agent puisse se corriger sans intervention humaine.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tools atomiques et composables :&lt;/strong&gt; &lt;code&gt;analyze_module()&lt;/code&gt; → &lt;code&gt;generate_patch()&lt;/code&gt; → &lt;code&gt;apply_patch()&lt;/code&gt; → &lt;code&gt;run_tests()&lt;/code&gt;. Un outil, une intention. L’agent orchestre.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;rendre-un-cli-agent-ready-3-changements&quot;&gt;Rendre un CLI agent-ready (3 changements)&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;JSON-first sur stdout :&lt;/strong&gt; sortie machine stable et prévisible ; les logs humains vont sur stderr.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Logs séparés :&lt;/strong&gt; ne pas mélanger résultat et logs de diagnostic.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Erreurs catégorisées :&lt;/strong&gt; code d’erreur + message + hint de recovery.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;questions-fréquentes&quot;&gt;Questions fréquentes&lt;/h2&gt;
&lt;h3 id=&quot;quest-ce-que-le-model-context-protocol-mcp-&quot;&gt;Qu’est-ce que le Model Context Protocol (MCP) ?&lt;/h3&gt;
&lt;p&gt;MCP est un standard ouvert qui permet à un agent IA de découvrir et d’appeler des capacités externes (CLIs, APIs, services internes) via un contrat structuré. Le host découvre dynamiquement les outils déclarés, et l’agent décide comment les enchaîner : le même outil fonctionne ainsi dans n’importe quel IDE ou assistant compatible MCP.&lt;/p&gt;
&lt;h3 id=&quot;en-quoi-mcp-diffère-t-il-dune-api-rest-&quot;&gt;En quoi MCP diffère-t-il d’une API REST ?&lt;/h3&gt;
&lt;p&gt;REST expose des endpoints et laisse l’orchestration à un client connu à l’avance. MCP expose des capacités déclarées (&lt;code&gt;tools&lt;/code&gt;, &lt;code&gt;resources&lt;/code&gt;, &lt;code&gt;prompts&lt;/code&gt;) qu’un host découvre à l’exécution, et renvoie des réponses structurées qu’un agent peut interpréter et replanifier. REST expose des services ; MCP expose des actions pilotables par un agent.&lt;/p&gt;
&lt;h3 id=&quot;faut-il-réécrire-son-cli-rust-pour-le-rendre-agent-ready-&quot;&gt;Faut-il réécrire son CLI Rust pour le rendre agent-ready ?&lt;/h3&gt;
&lt;p&gt;Pas de réécriture, trois ajustements. Émettre du JSON lisible par machine sur stdout et envoyer les logs humains sur stderr, séparer résultat et diagnostic, et renvoyer des erreurs catégorisées (code, message, hint de recovery) pour que l’agent se corrige sans intervention humaine.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Cet article a d’abord été publié sur le &lt;a href=&quot;https://blog.ippon.fr/2026/03/11/model-context-protocol-mcp-cli-rust-ide/&quot;&gt;blog Ippon Technologies&lt;/a&gt;.&lt;/p&gt;</content:encoded><atom:link href="https://blog.ippon.fr/2026/03/11/model-context-protocol-mcp-cli-rust-ide/" rel="canonical"/><category>ia</category><category>softwareengineering</category></item><item><title>Spec-Driven Development : de la spécification au code avec l&apos;IA</title><link>https://djamel-bougouffa.com/fr/blog/spec-driven-development/</link><guid isPermaLink="true">https://djamel-bougouffa.com/fr/blog/spec-driven-development/</guid><description>Découvrez le Spec-Driven Development (SDD) et comment l&apos;IA, avec Kiro, transforme une spécification en code structuré et vérifié par les tests.</description><pubDate>Wed, 18 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;img src=&quot;https://blog.ippon.fr/content/images/2026/02/spec-driven-developement.png&quot; alt=&quot;Spec-Driven Development avec l&apos;IA&quot; /&gt;
&lt;p&gt;Cet article a été co-écrit et publié sur le &lt;a href=&quot;https://blog.ippon.fr/2026/02/18/spec-driven-development/&quot;&gt;blog Ippon Technologies&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Le &lt;strong&gt;Spec-Driven Development (SDD)&lt;/strong&gt; propose un changement simple mais radical : la spécification devient la source de vérité du projet. Avant d’écrire du code, on décrit précisément ce que le système doit garantir. Ces garanties guident directement l’architecture, l’implémentation et les tests.&lt;/p&gt;
&lt;h2 id=&quot;lidée-centrale&quot;&gt;L’idée centrale&lt;/h2&gt;
&lt;p&gt;Le SDD est une approche où la spécification formalisée (exigences, critères d’acceptation, invariants) devient l’artefact central du projet. Elle est versionnée avec le code et sert de référence pour le design, l’implémentation et les tests.&lt;/p&gt;
&lt;p&gt;Ce qui définit réellement une approche Spec-Driven, ce n’est pas la présence d’un document « requirements ». Ce sont trois principes :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;La spec agit comme un contrat :&lt;/strong&gt; si une implémentation s’en écarte, c’est un défaut, pas une variante acceptable.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Chaque décision est traçable :&lt;/strong&gt; un composant répond à une exigence, un test vérifie un critère d’acceptation, une validation protège un invariant.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;La spec est vivante :&lt;/strong&gt; elle évolue au même rythme que le code, versionnée dans le même repository et intégrée au même cycle de revue.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;le-cycle-sdd&quot;&gt;Le cycle SDD&lt;/h2&gt;
&lt;p&gt;Le cycle est itératif : &lt;strong&gt;Spécifier → Planifier → Développer &amp;#x26; Tester → Itérer&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;La discipline clé est de séparer le &lt;em&gt;quoi&lt;/em&gt; du &lt;em&gt;comment&lt;/em&gt; : on s’accorde sur les comportements attendus avant de débattre de la meilleure façon de les implémenter. Cela évite que l’architecture ne dicte accidentellement le produit.&lt;/p&gt;
&lt;h2 id=&quot;sdd-vs-bdd-vs-pdd&quot;&gt;SDD vs BDD vs PDD&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;BDD&lt;/strong&gt; formalise le comportement via des tests (Given/When/Then). Le SDD organise tout le projet autour de la spec. Les deux peuvent coexister.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PDD&lt;/strong&gt; (Prompt-Driven Development) optimise l’échange avec l’IA via des prompts : efficace pour prototyper, mais le prompt est éphémère. Le SDD utilise les prompts comme outil, la spec reste la référence.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;ce-que-lia-change-vraiment&quot;&gt;Ce que l’IA change vraiment&lt;/h2&gt;
&lt;p&gt;Des outils comme &lt;strong&gt;Kiro&lt;/strong&gt; permettent aujourd’hui d’organiser le travail autour de &lt;strong&gt;Requirements → Design → Tasks&lt;/strong&gt;, rendant la spec un artefact actif plutôt qu’un document statique.&lt;/p&gt;
&lt;p&gt;L’IA peut désormais :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;transformer une intention produit en exigences structurées&lt;/li&gt;
&lt;li&gt;dériver un design cohérent&lt;/li&gt;
&lt;li&gt;générer des tâches d’implémentation&lt;/li&gt;
&lt;li&gt;produire des tests alignés sur les critères d’acceptation&lt;/li&gt;
&lt;li&gt;vérifier que le code respecte toujours la promesse initiale&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;La spécification cesse d’être un poids. Elle devient un accélérateur.&lt;/p&gt;
&lt;p&gt;Retrouvez l’article complet sur le blog Ippon : &lt;a href=&quot;https://blog.ippon.fr/2026/02/18/spec-driven-development/&quot;&gt;Spec-Driven Development : de la spécification au code avec l’IA&lt;/a&gt;&lt;/p&gt;</content:encoded><atom:link href="https://blog.ippon.fr/2026/02/18/spec-driven-development/" rel="canonical"/><category>ia</category><category>softwareengineering</category></item><item><title>Créer un portfolio ultra-rapide avec Astro et le déployer sur Netlify</title><link>https://djamel-bougouffa.com/fr/blog/building-a-blazing-fast-portfolio-with-astro-and-deploying-with-netlify/</link><guid isPermaLink="true">https://djamel-bougouffa.com/fr/blog/building-a-blazing-fast-portfolio-with-astro-and-deploying-with-netlify/</guid><description>Comment créer un portfolio performant avec Astro et le thème Portfolio, puis le déployer facilement sur Netlify.</description><pubDate>Mon, 24 Jul 2023 00:00:00 GMT</pubDate><content:encoded>&lt;img src=&quot;https://djamel-bougouffa.com/assets/stats-website.webp&quot; alt=&quot;Métriques de performance du portfolio Astro, score Lighthouse 100/100&quot; /&gt;
&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Dans cet article, nous allons voir comment créer un portfolio ultra-rapide avec Astro et son thème Portfolio, puis le déployer facilement sur Netlify. Avec cette combinaison puissante, vous pouvez mettre votre portfolio en ligne rapidement et impressionner clients et recruteurs.&lt;/p&gt;
&lt;h2 id=&quot;quest-ce-quastro-&quot;&gt;Qu’est-ce qu’Astro ?&lt;/h2&gt;
&lt;p&gt;Astro est un générateur de sites statiques moderne qui combine les meilleures caractéristiques des générateurs traditionnels avec la puissance des frameworks contemporains. Son approche unique compile votre site en une application JavaScript entièrement optimisée, offrant des performances exceptionnelles et une expérience utilisateur fluide. De plus, Astro supporte les frameworks populaires comme React, Vue et Svelte, permettant aux développeurs de créer des applications web complexes en toute simplicité.&lt;/p&gt;
&lt;h2 id=&quot;le-thème-portfolio&quot;&gt;Le thème Portfolio&lt;/h2&gt;
&lt;p&gt;Ce thème inclut les fonctionnalités essentielles comme les sections projets. Il constitue une excellente base pour mettre en valeur votre travail et vos réalisations efficacement.&lt;/p&gt;
&lt;h2 id=&quot;démarrer-avec-astro-et-le-thème-portfolio&quot;&gt;Démarrer avec Astro et le thème Portfolio&lt;/h2&gt;
&lt;p&gt;Suivez ces étapes pour créer votre portfolio rapide avec Astro et le thème Portfolio :&lt;/p&gt;
&lt;h3 id=&quot;étape-1--configurer-votre-environnement-de-développement&quot;&gt;Étape 1 : Configurer votre environnement de développement&lt;/h3&gt;
&lt;p&gt;Assurez-vous d’avoir Node.js et npm installés sur votre système. Vous pouvez vérifier leur présence en exécutant les commandes suivantes dans votre terminal :&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;node&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -v&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;npm&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -v&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;étape-2--créer-un-nouveau-projet-astro-avec-le-template-portfolio&quot;&gt;Étape 2 : Créer un nouveau projet Astro avec le template portfolio&lt;/h3&gt;
&lt;p&gt;Ouvrez votre terminal et créez un nouveau projet Astro avec la commande suivante :&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;npm&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; create&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; astro@latest&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --template&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; portfolio&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;étape-3--personnaliser-votre-portfolio&quot;&gt;Étape 3 : Personnaliser votre portfolio&lt;/h3&gt;
&lt;p&gt;Remplacez le contenu par défaut du répertoire &lt;code&gt;src&lt;/code&gt; par vos propres projets, images et informations. Vous pouvez facilement modifier les composants et mises en page du thème Portfolio pour qu’ils correspondent à votre style et vos préférences.&lt;/p&gt;
&lt;h3 id=&quot;étape-4--tester-votre-portfolio-en-local&quot;&gt;Étape 4 : Tester votre portfolio en local&lt;/h3&gt;
&lt;p&gt;Pour visualiser votre portfolio en local, lancez le serveur de développement :&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;npm&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; dev&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Votre portfolio sera accessible à l’adresse &lt;code&gt;http://localhost:4321&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;déployer-avec-netlify&quot;&gt;Déployer avec Netlify&lt;/h2&gt;
&lt;p&gt;Netlify simplifie considérablement le processus de déploiement, offrant une expérience fluide pour les sites statiques. Suivez ces étapes pour déployer votre portfolio Astro sur Netlify :&lt;/p&gt;
&lt;h3 id=&quot;étape-1--créer-un-compte-netlify&quot;&gt;Étape 1 : Créer un compte Netlify&lt;/h3&gt;
&lt;p&gt;Si vous n’en avez pas encore, inscrivez-vous gratuitement sur Netlify à &lt;a href=&quot;https://www.netlify.com/&quot;&gt;netlify.com&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;étape-2--installer-la-cli-netlify&quot;&gt;Étape 2 : Installer la CLI Netlify&lt;/h3&gt;
&lt;p&gt;Installez la CLI Netlify globalement sur votre machine pour interagir avec Netlify depuis la ligne de commande :&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;npm&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; netlify-cli&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -g&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;étape-3--builder-votre-portfolio&quot;&gt;Étape 3 : Builder votre portfolio&lt;/h3&gt;
&lt;p&gt;Avant de déployer, buildez votre projet Astro pour générer les fichiers optimisés :&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;npm&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; build&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;étape-4--initialiser-netlify&quot;&gt;Étape 4 : Initialiser Netlify&lt;/h3&gt;
&lt;p&gt;Naviguez vers le répertoire de votre projet dans le terminal et lancez :&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;netlify&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; init&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Suivez les instructions pour lier votre compte Netlify et sélectionner le projet à déployer.&lt;/p&gt;
&lt;h3 id=&quot;étape-5--déployer-votre-portfolio&quot;&gt;Étape 5 : Déployer votre portfolio&lt;/h3&gt;
&lt;p&gt;Enfin, déployez votre portfolio avec la commande suivante :&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;netlify&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; deploy&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --prod&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Votre portfolio sera automatiquement déployé sur Netlify, et vous recevrez une URL unique où votre portfolio ultra-rapide sera accessible par tous, partout dans le monde.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Créer un portfolio rapide et impressionnant n’a jamais été aussi simple grâce à Astro et son thème Portfolio. L’approche orientée performance d’Astro et son intégration fluide avec les frameworks modernes vous permettent de construire un portfolio de qualité sans effort. De plus, déployer votre portfolio sur Netlify garantit qu’il soit accessible au monde entier en quelques clics. Alors, pourquoi attendre ? Créez votre portfolio avec Astro et mettez en valeur vos compétences auprès de la communauté tech mondiale dès aujourd’hui !&lt;/p&gt;</content:encoded><category>frontend</category><category>softwareengineering</category></item><item><title>Full-Remote Bliss : l&apos;avantage du jet lag pour les devs fullstack</title><link>https://djamel-bougouffa.com/fr/blog/full-remote-bliss-fullstack-devs-jet-lag-advantage/</link><guid isPermaLink="true">https://djamel-bougouffa.com/fr/blog/full-remote-bliss-fullstack-devs-jet-lag-advantage/</guid><description>Comment travailler à distance sur plusieurs fuseaux horaires peut transformer le jet lag d&apos;une contrainte en levier de productivité et de croissance personnelle.</description><pubDate>Wed, 21 Jun 2023 00:00:00 GMT</pubDate><content:encoded>&lt;img src=&quot;https://djamel-bougouffa.com/assets/full-remote.webp&quot; alt=&quot;Travail à distance sur plusieurs fuseaux horaires&quot; /&gt;
&lt;h2 id=&quot;mon-expérience&quot;&gt;Mon expérience&lt;/h2&gt;
&lt;p&gt;À l’ère numérique, le concept de travail a profondément évolué, transcendant les espaces de bureau traditionnels. Le travail à distance a gagné en popularité, offrant aux professionnels la liberté de travailler depuis n’importe où. En tant que développeur fullstack pleinement ancré dans ce mode de vie, j’ai découvert un avantage inattendu : utiliser le jet lag comme levier de productivité et de croissance personnelle. Dans cet article, je partage mon expérience et comment transformer ce qui semble être un inconvénient en véritable atout.&lt;/p&gt;
&lt;h2 id=&quot;la-liberté-du-full-remote&quot;&gt;La liberté du full-remote&lt;/h2&gt;
&lt;p&gt;L’un des aspects les plus séduisants du travail fullstack à distance, c’est la liberté qu’il procure. En s’affranchissant des contraintes d’un bureau physique, j’ai la flexibilité de travailler depuis n’importe quel endroit, à n’importe quelle heure. Cette autonomie me permet de concevoir mon environnement de travail selon mes préférences et d’atteindre un équilibre sain entre vie professionnelle et vie personnelle. Que ce soit depuis chez moi, un café animé ou une plage paisible, choisir où et comment je travaille a considérablement amélioré mon bien-être et ma satisfaction professionnelle.&lt;/p&gt;
&lt;h2 id=&quot;tirer-parti-du-jet-lag&quot;&gt;Tirer parti du jet lag&lt;/h2&gt;
&lt;p&gt;Le jet lag, souvent perçu comme un désagrément inévitable lors de voyages longue distance, peut en réalité se transformer en avantage pour les travailleurs à distance. Lorsqu’on travaille sur plusieurs fuseaux horaires, il devient un outil puissant pour booster productivité et développement personnel.&lt;/p&gt;
&lt;h3 id=&quot;exploiter-les-fuseaux-horaires-pour-maximiser-lefficacité&quot;&gt;Exploiter les fuseaux horaires pour maximiser l’efficacité&lt;/h3&gt;
&lt;p&gt;La beauté du travail à distance réside dans la possibilité de collaborer avec des collègues ou des clients aux quatre coins du monde. En planifiant stratégiquement mes horaires de travail en fonction des fuseaux de mon équipe, j’optimise la communication et la collaboration. Par exemple, basé en Europe et travaillant avec une équipe en Asie, je mets à profit le décalage horaire pour avancer sur mes tâches tôt le matin, pendant qu’ils dorment encore. Cela me permet de leur fournir mises à jour et rapports d’avancement avant même leur réveil, favorisant efficacité et fluidité de travail.&lt;/p&gt;
&lt;h3 id=&quot;concentration-et-focus-décuplés&quot;&gt;Concentration et focus décuplés&lt;/h3&gt;
&lt;p&gt;Le jet lag perturbe nos habitudes de sommeil, mais il crée aussi des opportunités uniques pour une concentration accrue. En exploitant les heures calmes du petit matin ou de la nuit, quand la plupart des gens dorment encore, je peux m’immerger dans un travail profond sans interruption. Ce temps préservé s’est avéré précieux pour attaquer des défis de code complexes, déboguer et résoudre des problèmes créatifs.&lt;/p&gt;
&lt;h3 id=&quot;croissance-personnelle-et-ouverture-culturelle&quot;&gt;Croissance personnelle et ouverture culturelle&lt;/h3&gt;
&lt;p&gt;Travailler à distance sur différents fuseaux horaires nous expose à des cultures et des perspectives variées. En tant que développeur fullstack, c’est une expérience enrichissante qui élargit ma compréhension des marchés mondiaux, des comportements utilisateurs et des avancées technologiques. Collaborer avec des collègues de fuseaux différents favorise la coopération interculturelle, développe les réseaux professionnels et ouvre des portes à des opportunités d’apprentissage uniques, difficilement accessibles dans un cadre de bureau traditionnel.&lt;/p&gt;
&lt;h2 id=&quot;maintenir-léquilibre-et-gérer-les-défis&quot;&gt;Maintenir l’équilibre et gérer les défis&lt;/h2&gt;
&lt;p&gt;Si les avantages du full-remote et de l’optimisation du jet lag sont substantiels, il est essentiel de préserver un équilibre sain et d’anticiper les défis potentiels.&lt;/p&gt;
&lt;h3 id=&quot;établir-une-routine&quot;&gt;Établir une routine&lt;/h3&gt;
&lt;p&gt;Pour optimiser la productivité et atténuer les effets négatifs du jet lag, une routine cohérente est indispensable. Construire un emploi du temps qui tient compte à la fois des engagements personnels et professionnels apporte une structure bénéfique pour l’efficacité au travail et le bien-être global.&lt;/p&gt;
&lt;h3 id=&quot;prioriser-le-soin-de-soi&quot;&gt;Prioriser le soin de soi&lt;/h3&gt;
&lt;p&gt;Le travail à distance peut brouiller les frontières entre vie professionnelle et vie personnelle, rendant l’auto-soin encore plus crucial. Faire des pauses, pratiquer une activité physique et maintenir des liens sociaux sont essentiels pour la santé mentale et physique. Face au jet lag, veiller à un repos suffisant et à une bonne hygiène du sommeil devient encore plus important pour éviter le burn-out.&lt;/p&gt;
&lt;h3 id=&quot;communication-et-collaboration-efficaces&quot;&gt;Communication et collaboration efficaces&lt;/h3&gt;
&lt;p&gt;Le travail à distance repose largement sur une communication et une collaboration efficaces. Adopter des outils numériques (plateformes de gestion de projet, messagerie instantanée, visioconférence) est indispensable pour maintenir des interactions fluides avec des collègues répartis sur différents fuseaux. Des canaux de communication clairs et transparents permettent d’établir les attentes, d’assurer l’alignement et de créer une cohésion d’équipe.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;En tant que développeur fullstack pleinement ancré dans le mode de vie full-remote, les bénéfices ont été transformateurs. La flexibilité de travailler depuis n’importe où, combinée à l’exploitation stratégique du jet lag, m’a permis d’atteindre des niveaux de productivité plus élevés, de m’ouvrir à des cultures diverses et de trouver un meilleur équilibre vie pro/perso. En maintenant une routine, en priorisant le soin de soi et en favorisant une communication efficace, le parcours full-remote peut devenir une expérience remarquable, propice à la croissance personnelle et au succès professionnel. Dans un monde qui embrasse de plus en plus le travail à distance, tirer parti de ces avantages peut s’avérer décisif pour les développeurs et professionnels de tous horizons.&lt;/p&gt;</content:encoded><category>visionmtiertendances</category></item></channel></rss>