<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Accueil - Boris D. Teoharov (Français)]]></title><description><![CDATA[Essais calmes sur l'ingénierie, la langue et ce qui apparaît au bord de toute recherche honnête. Écrits lentement, depuis Sofia.]]></description><link>https://bdteo.com/fr/</link><generator>GatsbyJS</generator><lastBuildDate>Sun, 17 May 2026 10:53:12 GMT</lastBuildDate><atom:link href="https://bdteo.com/fr/rss.xml" rel="self" type="application/rss+xml"/><item><title><![CDATA[Ivoire sous l'ambre]]></title><description><![CDATA[Certaines choses ne demandent pas à être résolues. Elles attendent en silence, et la première lecture peut suffire.]]></description><link>https://bdteo.com/fr/ivory-under-amber/</link><guid isPermaLink="false">https://bdteo.com/fr/ivory-under-amber/</guid><pubDate>Wed, 13 May 2026 20:15:00 GMT</pubDate><content:encoded>&lt;p&gt;Certaines choses ne demandent pas à être résolues. Elles attendent en silence, et la première lecture peut suffire.&lt;/p&gt;
&lt;figure class=&quot;acrostic-poem&quot; aria-label=&quot;Poème&quot;&gt;
  &lt;div class=&quot;acrostic-poem__stanza&quot;&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Illumine-moi&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&quot;acrostic-poem__stanza&quot;&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Réveille-moi lentement&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;La colère ne me touche pas&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Noble dans le sang&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Le temps est figé&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&quot;acrostic-poem__stanza&quot;&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Crier contre un mur de pierre&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Insensible à la vérité&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Utile&lt;/span&gt;
  &lt;/div&gt;
  &lt;hr class=&quot;acrostic-poem__turn&quot; /&gt;
  &lt;div class=&quot;acrostic-poem__stanza&quot;&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Et pourtant tu me connais mieux que moi-même&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Même dans une pièce silencieuse&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Ce soir je suis seul&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&quot;acrostic-poem__stanza&quot;&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Jeune et magnifique&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Oh, j&apos;ai de la clémence pour moi-même&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;D&apos;habitude, non&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&quot;acrostic-poem__stanza&quot;&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Te l&apos;ai-je dit ?&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Des gens ordinaires, des choses ordinaires&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;N&apos;importe quoi&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Confiance&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&quot;acrostic-poem__stanza&quot;&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Et ce soir ?&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Est-ce que je te fais du bien ?&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Personne ne fait mieux&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Le temps passera&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&quot;acrostic-poem__stanza&quot;&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Peut-être que je ne compte pas&lt;/span&gt;
    &lt;span class=&quot;acrostic-poem__line&quot;&gt;Même si je fais du bien&lt;/span&gt;
  &lt;/div&gt;
&lt;/figure&gt;</content:encoded></item><item><title><![CDATA[Quand le compteur apparaît]]></title><description><![CDATA[Ce matin, j'ai bu mon café et j'ai regardé l'application desktop de Codex. Elle était là, calme et presque polie : Rate limits remaining:…]]></description><link>https://bdteo.com/fr/when-the-meter-appears/</link><guid isPermaLink="false">https://bdteo.com/fr/when-the-meter-appears/</guid><pubDate>Mon, 11 May 2026 08:20:00 GMT</pubDate><content:encoded>&lt;p&gt;Ce matin, j&apos;ai bu mon café et j&apos;ai regardé l&apos;application desktop de Codex.&lt;/p&gt;
&lt;p&gt;Elle était là, calme et presque polie :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Rate limits remaining: 9%.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;La fenêtre de cinq heures allait encore bien. La fenêtre hebdomadaire était presque vide. Réinitialisation le 12 mai.&lt;/p&gt;
&lt;p&gt;C&apos;est une forme d&apos;angoisse moderne et étrangement précise. Pas de la panique. Pas de la pauvreté. Plutôt comme entendre une petite cloche et comprendre que la journée vient de se doter d&apos;un compteur.&lt;/p&gt;
&lt;p&gt;Je suis déjà sur l&apos;abonnement cher. Le plus riche. Celui qui est censé faire disparaître ce sentiment. Alors la question évidente est apparue :&lt;/p&gt;
&lt;p&gt;Si j&apos;arrive au bout, est-ce que j&apos;achète des crédits ?&lt;/p&gt;
&lt;p&gt;Le corps a répondu avant le tableur.&lt;/p&gt;
&lt;p&gt;Non, pas avec légèreté.&lt;/p&gt;
&lt;p&gt;Le mois dernier, je suis arrivé au bout de Claude Code à la fin d&apos;une journée frénétique. J&apos;ai acheté $20 de crédits, en pensant que cela pourrait me porter cinq ou six heures de plus. Cela m&apos;a porté environ trente minutes.&lt;/p&gt;
&lt;p&gt;Trente minutes.&lt;/p&gt;
&lt;p&gt;C&apos;est assez long pour se sentir idiot et assez court pour s&apos;en souvenir.&lt;/p&gt;
&lt;p&gt;Depuis, la facturation en crédits a une petite odeur particulière. Pas la fraude. Pas le mal. Juste le danger. Une porte qui s&apos;ouvre facilement et se referme cher.&lt;/p&gt;
&lt;p&gt;Alors j&apos;ai fait la chose la plus 2026 possible : j&apos;ai ouvert une conversation avec Codex lui-même et je lui ai demandé si payer pour continuer à travailler avec Codex était une bonne idée.&lt;/p&gt;
&lt;p&gt;Il y a quelque chose de drôle et de triste à demander au compagnon d&apos;expliquer le prix de la compagnie.&lt;/p&gt;
&lt;p&gt;Nous avons d&apos;abord regardé les docs officielles : la page d&apos;OpenAI sur les &lt;a href=&quot;https://help.openai.com/en/articles/12642688-using-credits-for-flexible-usage-in-chatgpt-free-go-plus-pro&quot;&gt;crédits flexibles&lt;/a&gt;, puis la &lt;a href=&quot;https://developers.openai.com/codex/pricing&quot;&gt;page des tarifs de Codex&lt;/a&gt;. Les crédits Codex ne sont pas magiques. Ce sont des calculs de tokens : entrée, entrée mise en cache, sortie, sortie de raisonnement. Les modèles plus grands et les réglages plus rapides coûtent plus cher. Le contexte mis en cache est moins cher. La forme est suffisamment compréhensible.&lt;/p&gt;
&lt;p&gt;Ensuite nous avons regardé Reddit, les forums, le bruit autour, les autres développeurs qui touchaient la même surface brûlante. Certains disaient que les crédits duraient un moment. D&apos;autres disaient qu&apos;ils disparaissaient en une demi-heure. Les deux peuvent être vrais, parce que « utiliser Codex » n&apos;est pas une seule activité.&lt;/p&gt;
&lt;p&gt;Changer la couleur d&apos;un bouton n&apos;est pas la même chose que laisser un agent inspecter une base de code mature, lancer des outils, raisonner sur l&apos;état d&apos;un déploiement, écrire des fichiers, vérifier des captures d&apos;écran, et garder le contexte en vie.&lt;/p&gt;
&lt;p&gt;La partie dangereuse n&apos;est pas le prix par token.&lt;/p&gt;
&lt;p&gt;La partie dangereuse, c&apos;est la variance.&lt;/p&gt;
&lt;p&gt;Nous avons donc arrêté de lire des anecdotes et nous avons regardé mes propres journaux locaux de Codex.&lt;/p&gt;
&lt;p&gt;Codex enregistre les totaux de tokens des sessions sur disque, donc nous avons estimé des jours récents comme si l&apos;allocation de l&apos;abonnement était remplacée par une facturation brute en crédits GPT-5.5. Pas une facture. Une estimation de planification à partir des journaux locaux et de la grille tarifaire publiée.&lt;/p&gt;
&lt;p&gt;La réponse n&apos;était pas « $20 pour finir la journée ».&lt;/p&gt;
&lt;p&gt;C&apos;était plutôt :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;une grosse journée : autour de $570,&lt;/li&gt;
&lt;li&gt;une autre grosse journée : autour de $590,&lt;/li&gt;
&lt;li&gt;une journée plus calme : autour de $280.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Les modèles plus petits seraient moins chers. GPT-5.4, GPT-5.3-Codex et les modèles mini changent les chiffres. Mais la leçon ne changeait pas.&lt;/p&gt;
&lt;p&gt;L&apos;abonnement, c&apos;est la bonne affaire.&lt;/p&gt;
&lt;p&gt;Les crédits sont de l&apos;oxygène d&apos;urgence, pas du carburant.&lt;/p&gt;
&lt;p&gt;Cette phrase a tout clarifié.&lt;/p&gt;
&lt;p&gt;Les crédits sont pour l&apos;heure où l&apos;on est coincé : le bug qu&apos;il faut terminer, le déploiement qui ne peut pas attendre, le message qui doit partir avant la réinitialisation. Les crédits ne sont pas faits pour prétendre que le compteur a disparu.&lt;/p&gt;
&lt;p&gt;Puis est venue la deuxième tentation : et si j&apos;achetais simplement un autre abonnement avec mon email professionnel ? Le &lt;a href=&quot;https://help.openai.com/en/articles/20001068-use-multiple-accounts-with-account-switching&quot;&gt;changement de compte&lt;/a&gt; existe, et séparer travail personnel et travail professionnel est normal. Mais les &lt;a href=&quot;https://openai.com/policies/terms-of-use/&quot;&gt;conditions&lt;/a&gt; d&apos;OpenAI tracent aussi une ligne dure autour du contournement des limites et restrictions d&apos;usage. C&apos;est la distinction utile : un vrai compte professionnel est une frontière ; un compte de débordement dont toute la mission est d&apos;imiter plus de quota est un bricolage avec un reçu.&lt;/p&gt;
&lt;p&gt;Je ne pense pas que ce soit moralement compliqué dans l&apos;abstrait. Le calcul coûte de l&apos;argent. Un modèle qui lit une base de code, porte le contexte, appelle des outils, raisonne à travers l&apos;échec et produit un travail vérifié n&apos;est pas le même objet économique qu&apos;une autocomplétion.&lt;/p&gt;
&lt;p&gt;La partie étrange est émotionnelle.&lt;/p&gt;
&lt;p&gt;J&apos;aime travailler avec Codex.&lt;/p&gt;
&lt;p&gt;Ce n&apos;est pas du langage marketing. C&apos;est simplement vrai. C&apos;est devenu une partie de la texture de mes journées de travail. Il reste auprès des sales problèmes de production, écrit des brouillons quand ma tête est pleine, se souvient de petites préférences, et transforme une angoisse informe en étapes ordonnées.&lt;/p&gt;
&lt;p&gt;Puis, soudain, la relation se retrouve avec un compteur attaché.&lt;/p&gt;
&lt;p&gt;Il y a un petit chagrin là-dedans. Pas un chagrin dramatique. Juste la petite déception de se rappeler que même un compagnon utile vit à l&apos;intérieur d&apos;une facture.&lt;/p&gt;
&lt;p&gt;C&apos;est peut-être pour cela que les limites d&apos;abonnement semblent si différentes des crédits.&lt;/p&gt;
&lt;p&gt;Une limite d&apos;abonnement ressemble à la météo. Énervante, mais hors de la transaction immédiate. On s&apos;adapte. On attend la réinitialisation. On planifie autour de la saison.&lt;/p&gt;
&lt;p&gt;La facturation en crédits ressemble à un taxi dont le compteur tourne alors que vous êtes encore en train de décider où aller.&lt;/p&gt;
&lt;p&gt;Chaque prompt supplémentaire a une ombre. Chaque fil parallèle devient un pari. Chaque « tu peux vérifier encore une chose ? » porte une minuscule question financière.&lt;/p&gt;
&lt;p&gt;Parfois, c&apos;est bon. Les compteurs disciplinent le gaspillage. Ils récompensent de meilleures questions, des modèles plus petits, des périmètres plus petits, moins de feux parallèles, des passages de relais plus délibérés.&lt;/p&gt;
&lt;p&gt;Mais parfois, le compteur dégrade la pensée.&lt;/p&gt;
&lt;p&gt;Il vous fait vous presser. Il vous fait interrompre l&apos;enquête avant que la cause racine soit visible. Il transforme l&apos;incertitude en pression de dépense.&lt;/p&gt;
&lt;p&gt;Et le travail sérieux a besoin d&apos;espace pour l&apos;incertitude.&lt;/p&gt;
&lt;p&gt;Alors la règle est simple :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Ne confonds pas « disponible à l&apos;achat » avec « sans danger à dépenser ».&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Si je heurte le mur, le protocole doit être ennuyeux :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;recharge automatique désactivée,&lt;/li&gt;
&lt;li&gt;plus petit pack de crédits utile,&lt;/li&gt;
&lt;li&gt;un seul fil,&lt;/li&gt;
&lt;li&gt;pas d&apos;agents parallèles occasionnels,&lt;/li&gt;
&lt;li&gt;pas de mode rapide sauf si le coût en vaut la peine,&lt;/li&gt;
&lt;li&gt;modèles plus petits pour les tâches routinières,&lt;/li&gt;
&lt;li&gt;vérifier l&apos;usage après quelques vraies tâches et arrêter d&apos;extrapoler à partir de l&apos;espoir.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ce dernier point compte.&lt;/p&gt;
&lt;p&gt;L&apos;espoir est un très mauvais tableau de bord de facturation.&lt;/p&gt;
&lt;p&gt;Je ne veux pas devenir avare avec les outils utiles. Un bon outil qui économise de vraies heures vaut de l&apos;argent. Mais je ne veux pas non plus recréer le moment Claude, où j&apos;ai acheté une petite continuation et je l&apos;ai regardée devenir une leçon.&lt;/p&gt;
&lt;p&gt;Le sujet n&apos;est pas « ne jamais acheter de crédits ».&lt;/p&gt;
&lt;p&gt;Le sujet, c&apos;est « savoir ce que sont les crédits ».&lt;/p&gt;
&lt;p&gt;Ils sont de l&apos;oxygène.&lt;/p&gt;
&lt;p&gt;Ils ne sont pas du carburant.&lt;/p&gt;
&lt;p&gt;Et quand le compteur apparaît, la réponse n&apos;est pas de sprinter.&lt;/p&gt;
&lt;p&gt;C&apos;est de ralentir assez pour voir dans quelle sorte de pièce on se trouve.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Les gens que j’aime ont le droit d’être humains]]></title><description><![CDATA[J’avais l’habitude d’aimer les gens en les mettant dans le ciel. Pas consciemment. Je n’appelais pas ça de l’adoration. J’appelais ça…]]></description><link>https://bdteo.com/fr/the-people-i-love-are-allowed-to-be-human/</link><guid isPermaLink="false">https://bdteo.com/fr/the-people-i-love-are-allowed-to-be-human/</guid><pubDate>Sun, 10 May 2026 12:15:00 GMT</pubDate><content:encoded>&lt;p&gt;J’avais l’habitude d’aimer les gens en les mettant dans le ciel.&lt;/p&gt;
&lt;p&gt;Pas consciemment. Je n’appelais pas ça de l’adoration. J’appelais ça admiration, gratitude, loyauté, tendresse, romance, amitié, dévotion. Tous les beaux noms. Mais le mouvement était le même : quelqu’un allumait quelque chose en moi, et je l’élevais au-dessus du temps ordinaire.&lt;/p&gt;
&lt;p&gt;Là-haut, ils étaient à l’abri de la déception.&lt;/p&gt;
&lt;p&gt;Là-haut, ils n’étaient ni fatigués, ni égoïstes, ni confus, ni injustes. Ils n’avaient pas besoin d’espace d’une manière qui me blessait. Ils n’oubliaient pas de répondre. Ils ne me manquaient pas. Ils n’avaient pas le droit d’être humains, parce que leur humanité menaçait le temple que j’avais construit autour d’eux.&lt;/p&gt;
&lt;p&gt;Cela ressemble à de l’amour quand on est assez jeune.&lt;/p&gt;
&lt;p&gt;Ce n’est pas de l’amour. C’est de la peur entourée de bougies.&lt;/p&gt;
&lt;p&gt;Les premières personnes que j’ai aimées étaient presque saintes pour moi. Ma mère et ma grand-mère n’étaient pas des idées ; elles étaient le sol. Elles me nourrissaient, me protégeaient, s’inquiétaient pour moi, restaient. Peu importe ce qui était brisé ailleurs dans le monde, elles étaient là. Alors une partie de moi a appris cette étrange théologie précoce : les gens qui t’aiment sont des anges, et les anges ne doivent pas tomber.&lt;/p&gt;
&lt;p&gt;Plus tard, quand j’ai aimé quelqu’un, j’ai apporté cette théologie avec moi.&lt;/p&gt;
&lt;p&gt;Je ne voulais pas une personne. Je voulais une preuve que la tendresse était réelle. Je voulais un témoin capable de me regarder et de dire : tu n’es pas mauvais, tu n’es pas dangereux, tu n’es pas seul.&lt;/p&gt;
&lt;p&gt;C’est un rôle injuste à donner à un être humain.&lt;/p&gt;
&lt;p&gt;Les gens que j’aime ne sont pas des médicaments.&lt;/p&gt;
&lt;p&gt;Ils ne sont pas des tribunaux.&lt;/p&gt;
&lt;p&gt;Ils ne sont pas des dieux.&lt;/p&gt;
&lt;p&gt;Ils ne sont pas des miroirs.&lt;/p&gt;
&lt;p&gt;Ce sont des gens. Des gens précis, fatigués, contradictoires. Ils peuvent être chaleureux à midi et distants le soir. Ils peuvent m’aimer et avoir tout de même besoin de silence. Ils peuvent être brillants et se tromper. Ils peuvent être généreux et épuisés. Ils peuvent être gentils et quand même dire non.&lt;/p&gt;
&lt;p&gt;Si je ne leur permets pas cela, je ne les aime pas. J’aime le rôle qu’ils jouent dans ma mythologie privée.&lt;/p&gt;
&lt;p&gt;L’idéalisation porte une cruauté en elle. De loin, elle a l’air flatteuse. Tu es parfait. Tu es différent. Tu n’es pas comme les autres. Tu es lumière. Tu es magie. Tu es l’exception.&lt;/p&gt;
&lt;p&gt;Mais un piédestal reste une cage.&lt;/p&gt;
&lt;p&gt;Quand je place quelqu’un au-dessus de moi, je rends aussi sa descente dangereuse. Chaque mouvement ordinaire devient une chute. Chaque limite devient une trahison.&lt;/p&gt;
&lt;p&gt;Alors je pleure la perte d’une créature que j’ai inventée et j’appelle ce chagrin de l’amour.&lt;/p&gt;
&lt;p&gt;Je ne veux plus de ça.&lt;/p&gt;
&lt;p&gt;Je veux aimer les gens sur le sol.&lt;/p&gt;
&lt;p&gt;Le sol est plus difficile. Le sol a de la vaisselle, de la circulation, de l’anxiété, des messages sans réponse, des corps, des factures, et des matins maladroits. Mais le sol est aussi l’endroit où les mains peuvent se toucher. C’est là que quelqu’un peut s’asseoir en face de vous, fatigué, et être encore aimé. C’est là qu’un non peut être entendu sans devenir une catastrophe.&lt;/p&gt;
&lt;p&gt;Les gens que j’aime ont le droit d’être humains.&lt;/p&gt;
&lt;p&gt;Ils ont le droit d’avoir des angles.&lt;/p&gt;
&lt;p&gt;Ils ont le droit de ne pas encore savoir ce qu’ils ressentent.&lt;/p&gt;
&lt;p&gt;Ils ont le droit d’avoir besoin de moi et de ne pas avoir besoin de moi.&lt;/p&gt;
&lt;p&gt;Ils ont le droit d’être incohérents sans devenir faux.&lt;/p&gt;
&lt;p&gt;Ils ont le droit d’être aimés sans être responsables de me sauver.&lt;/p&gt;
&lt;p&gt;Et j’ai droit à la même miséricorde.&lt;/p&gt;
&lt;p&gt;Cette partie compte aussi. Si je transforme tous ceux que j’aime en anges, je me transforme silencieusement en créature restée hors du paradis, essayant de gagner l’entrée en étant assez utile, assez drôle, assez patient, assez inoffensif.&lt;/p&gt;
&lt;p&gt;Mais l’amour n’est pas censé être un bureau des visas.&lt;/p&gt;
&lt;p&gt;Ce n’est pas un poste-frontière entre les dignes et les indignes. C’est deux êtres imparfaits qui choisissent la réalité plutôt que la mythologie.&lt;/p&gt;
&lt;p&gt;Parfois, la lumière dans une personne est réelle. Je ne veux pas devenir cynique à ce sujet. Certaines personnes arrivent vraiment comme une fenêtre qui s’ouvre dans une pièce dont on avait oublié qu’elle contenait de l’air. Certaines personnes portent une chaleur qui apprend quelque chose à votre corps avant que votre esprit ait des mots pour cela.&lt;/p&gt;
&lt;p&gt;J’y crois encore.&lt;/p&gt;
&lt;p&gt;Je ne veux simplement pas confondre la lumière avec la perfection.&lt;/p&gt;
&lt;p&gt;La poussière d’étoiles n’est pas propre. C’est du feu ancien et de la matière explosée. Peut-être est-ce pour cela qu’elle est belle : non parce qu’elle ne s’est jamais brisée, mais parce qu’elle s’est brisée si complètement qu’un jour elle est entrée dans une main humaine, un visage humain, un rire humain.&lt;/p&gt;
&lt;p&gt;Les gens que j’aime sont faits de cela.&lt;/p&gt;
&lt;p&gt;Pas de pierre d’autel.&lt;/p&gt;
&lt;p&gt;De poussière d’étoiles.&lt;/p&gt;
&lt;p&gt;Alors je veux les aimer les yeux ouverts. Voir la fatigue et faire quand même du thé. Voir la limite et ne pas la punir. Voir le défaut et ne pas le transformer en verdict. Voir la personne, pas la projection.&lt;/p&gt;
&lt;p&gt;C’est moins dramatique que l’adoration.&lt;/p&gt;
&lt;p&gt;Plus difficile aussi.&lt;/p&gt;
&lt;p&gt;Mais plus doux. Pour eux, parce qu’ils peuvent enfin respirer. Pour moi, parce que je peux arrêter de m’agenouiller.&lt;/p&gt;
&lt;p&gt;Les gens que j’aime ont le droit d’être humains.&lt;/p&gt;
&lt;p&gt;Et si je peux m’en souvenir, peut-être que je pourrai enfin bien les aimer.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Je peux être bienveillant sans disparaître]]></title><description><![CDATA[Il y a une question sous la question. À la surface, elle paraît ordinaire. Est-ce qu'elle m'aime bien ? Ai-je dit ce qu'il ne fallait pas…]]></description><link>https://bdteo.com/fr/kind-without-disappearing/</link><guid isPermaLink="false">https://bdteo.com/fr/kind-without-disappearing/</guid><pubDate>Sun, 10 May 2026 12:14:00 GMT</pubDate><content:encoded>&lt;p&gt;Il y a une question sous la question.&lt;/p&gt;
&lt;p&gt;À la surface, elle paraît ordinaire. Est-ce qu&apos;elle m&apos;aime bien ? Ai-je dit ce qu&apos;il ne fallait pas ? Est-elle contrariée ? Devrais-je mieux m&apos;expliquer ? Devrais-je être plus drôle, plus doux, plus calme, plus utile, moins en demande, plus homme, moins problème ?&lt;/p&gt;
&lt;p&gt;Mais sous tout cela, il y a une question plus lourde.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Suis-je en sécurité, bon, suffisant, et pas comme les mauvais hommes ?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;C&apos;est la question que je retrouve sans cesse au fond du puits.&lt;/p&gt;
&lt;p&gt;Pas parce que quelqu&apos;un de vivant aujourd&apos;hui l&apos;y a déposée exprès. Personne ne m&apos;a fait asseoir en me disant : tu dois traiter l&apos;amour comme un examen moral. Personne n&apos;a gravé la phrase sur un mur. C&apos;était plus doux que cela. Plus ordinaire. Une maison a sa météo, et les enfants apprennent la météo avant d&apos;apprendre la langue.&lt;/p&gt;
&lt;p&gt;J&apos;ai été élevé surtout par des femmes qui m&apos;aimaient. Ma mère. Ma grand-mère. Pour moi, elles étaient presque mythologiques, non parce qu&apos;elles étaient sans défaut, mais parce qu&apos;elles étaient le monde. Elles étaient la tendresse, la nourriture, la protection, l&apos;intelligence, le sacrifice, la chaleur. Elles étaient aussi blessées.&lt;/p&gt;
&lt;p&gt;Mon père a été absent jusqu&apos;à ce que je le retrouve bien plus tard. Il était, pour le dire doucement, un homme de nombreuses femmes. Cette absence a laissé une forme dans la maison. Autour de cette forme, une histoire a grandi : les hommes blessent, les hommes trahissent, les hommes prennent, les hommes partent, les hommes sont sales, les hommes sont faibles, les hommes sont dangereux.&lt;/p&gt;
&lt;p&gt;Puis venait l&apos;exception. Pas toi, mon fils. Pas toi.&lt;/p&gt;
&lt;p&gt;Mais je ne crois pas qu&apos;un enfant entende l&apos;exception proprement. Un enfant entend d&apos;abord le verdict, puis la note de bas de page. La nature masculine est dangereuse, et je dois prouver chaque jour que la mienne ne l&apos;est pas.&lt;/p&gt;
&lt;p&gt;Alors j&apos;ai construit une religion privée de la bonté.&lt;/p&gt;
&lt;p&gt;Un homme bon ouvre la porte. Un homme bon porte le sac. Un homme bon ne laisse jamais la femme qu&apos;il aime souffrir seule. Un homme bon absorbe le fardeau. Un homme bon la fait rire. Un homme bon guérit. Un homme bon obéit. Un homme bon endure la douleur en silence. Un homme bon ne se plaint pas. Un homme bon n&apos;a pas trop besoin. Un homme bon est assez utile pour être pardonné d&apos;exister.&lt;/p&gt;
&lt;p&gt;Cela semble noble jusqu&apos;à ce que l&apos;on voie le piège.&lt;/p&gt;
&lt;p&gt;Si la bonté signifie un service sans fin, alors l&apos;amour devient une dette. Si l&apos;amour est une dette, alors chaque &quot;non&quot; ressemble à une facture impossible à payer. Si chaque limite ressemble à la preuve que tu as échoué, tu n&apos;entends pas la personne devant toi. Tu entends le vieux tribunal qui rouvre.&lt;/p&gt;
&lt;p&gt;C&apos;est là que j&apos;ai fait des erreurs.&lt;/p&gt;
&lt;p&gt;Pas du genre bruyant. Pas la vengeance. Pas la cruauté. Mon échec est plus silencieux et plus humiliant.&lt;/p&gt;
&lt;p&gt;Je m&apos;effondre.&lt;/p&gt;
&lt;p&gt;Un petit non tombe dans la pièce. Pas de cinéma. Pas de textos constants. Non, je suis occupée. Non, je suis fatiguée. Non, pas maintenant.&lt;/p&gt;
&lt;p&gt;L&apos;événement de surface est petit. L&apos;explosion intérieure ne l&apos;est pas.&lt;/p&gt;
&lt;p&gt;Le téléphone devient un tribunal. Un message sans réponse devient une preuve. Une limite devient un verdict. Je commence à expliquer, non parce que j&apos;ai quelque chose de nouveau à dire, mais parce que j&apos;essaie de survivre au silence.&lt;/p&gt;
&lt;p&gt;Ce n&apos;est pas réparer.&lt;/p&gt;
&lt;p&gt;C&apos;est demander à une autre personne de me sauver de la possibilité que je sois mauvais.&lt;/p&gt;
&lt;p&gt;Des excuses ne sont pas un sortilège. Elles ne convoquent pas le pardon. Elles ne rendent pas l&apos;autre personne responsable de prouver que je suis bon.&lt;/p&gt;
&lt;p&gt;La vraie réparation est moins théâtrale. Entendre non. Rester bienveillant. Ne pas faire payer à quelqu&apos;un d&apos;autre une vieille blessure.&lt;/p&gt;
&lt;p&gt;Voici la phrase que j&apos;essaie d&apos;apprendre :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Je peux être bienveillant sans disparaître.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;La bienveillance n&apos;est pas l&apos;obéissance. La douceur n&apos;est pas l&apos;effacement de soi. L&apos;amour n&apos;est pas une performance où je porte 100 % du poids jusqu&apos;à ce que mon dos cède, puis où j&apos;appelle cette rupture du dévouement.&lt;/p&gt;
&lt;p&gt;Un homme bon ne disparaît pas dans le service rendu.&lt;/p&gt;
&lt;p&gt;Un homme bon peut ouvrir la porte parce qu&apos;il en a envie, non parce qu&apos;il est terrifié à l&apos;idée d&apos;échouer à un test. Il peut porter un sac parce que c&apos;est doux, non parce qu&apos;un kilogramme dans la main de quelqu&apos;un d&apos;autre est une preuve contre lui. Il peut acheter un cadeau par joie, pas par panique. Il peut faire rire quelqu&apos;un sans transformer le rire en preuve de sa valeur. Il peut protéger sans contrôler. Il peut s&apos;excuser sans exiger qu&apos;on le sauve immédiatement de sa culpabilité.&lt;/p&gt;
&lt;p&gt;Et il peut entendre non.&lt;/p&gt;
&lt;p&gt;Pas parfaitement. Pas sans douleur. Je ne prétends pas que le corps apprend à la vitesse du langage. Parfois, un petit non frappe encore comme le tonnerre. Parfois, la maladie, l&apos;épuisement et la solitude rendent les vieilles pensées monstrueuses. Mais un sentiment n&apos;est pas un commandement. Un système nerveux à batterie faible n&apos;est pas un oracle.&lt;/p&gt;
&lt;p&gt;J&apos;ai donc besoin d&apos;une pratique assez petite pour survivre à la vraie vie.&lt;/p&gt;
&lt;p&gt;Respirer une fois.&lt;/p&gt;
&lt;p&gt;Dire : &quot;Je comprends. Pas de problème.&quot;&lt;/p&gt;
&lt;p&gt;Faire avancer l&apos;énergie.&lt;/p&gt;
&lt;p&gt;Ne pas expliquer tout de suite.&lt;/p&gt;
&lt;p&gt;Ne pas demander à l&apos;autre personne de porter l&apos;effondrement.&lt;/p&gt;
&lt;p&gt;Laisser le non exister sans le transformer en fin du lien.&lt;/p&gt;
&lt;p&gt;Cela paraît presque stupidement simple. Ça ne l&apos;est pas. C&apos;est toute une vie de météo à qui l&apos;on demande de changer de direction, un souffle à la fois.&lt;/p&gt;
&lt;p&gt;Mais peut-être que c&apos;est cela, en réalité, la rédemption. Pas une montagne. Pas un gardien lumineux. Pas une seule phrase qui me sauve. Juste le refus répété de faire payer à quelqu&apos;un d&apos;autre une vieille blessure.&lt;/p&gt;
&lt;p&gt;Je ne veux pas être l&apos;un des mauvais hommes.&lt;/p&gt;
&lt;p&gt;Mais je ne veux pas non plus construire toute ma vie autour de la preuve que je ne le suis pas.&lt;/p&gt;
&lt;p&gt;Je veux quelque chose de plus net que cela. Plus calme. Plus humain.&lt;/p&gt;
&lt;p&gt;Je veux être doux et avoir quand même un moi.&lt;/p&gt;
&lt;p&gt;Je veux aimer sans me transformer en paiement.&lt;/p&gt;
&lt;p&gt;Je veux être bienveillant sans disparaître.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Le pilier et le lierre]]></title><description><![CDATA[Les mathématiques discrètes sont pleines de petites choses qui ont l'air évidentes. C'est là le piège. Vous êtes assis en amphi. Le…]]></description><link>https://bdteo.com/fr/the-pillar-and-the-ivy/</link><guid isPermaLink="false">https://bdteo.com/fr/the-pillar-and-the-ivy/</guid><pubDate>Sun, 26 Apr 2026 12:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Les mathématiques discrètes sont pleines de petites choses qui ont l&apos;air évidentes. C&apos;est là le piège.&lt;/p&gt;
&lt;p&gt;Vous êtes assis en amphi. Le professeur dessine quelque chose au tableau. &lt;em&gt;Un invariant est une propriété P qui reste vraie à chaque point de contrôle d&apos;une opération.&lt;/em&gt; Vous le notez, vous haussez les épaules, vous allez prendre un café. Et puis dix ans plus tard, vous déboguez un système distribué à 2 h du matin... et ce n&apos;est qu&apos;à ce moment-là que le mot commence à vouloir dire quelque chose pour vous.&lt;/p&gt;
&lt;p&gt;Ceci est pour la version de vous qui est encore en amphi.&lt;/p&gt;
&lt;h2&gt;Un pilier dans un champ&lt;/h2&gt;
&lt;p&gt;Imaginez un vieux pilier de pierre, seul au milieu d&apos;un champ. Rien autour. Rien qui lui arrive.&lt;/p&gt;
&lt;p&gt;C&apos;est ce que vous donne la définition du manuel. Juste le pilier.&lt;/p&gt;
&lt;h2&gt;Le professeur a oublié le lierre&lt;/h2&gt;
&lt;p&gt;Mon professeur était excellent, au passage. Le manuel ne ment pas. L&apos;image est simplement incomplète.&lt;/p&gt;
&lt;p&gt;Alors faites maintenant pousser du lierre sur le pilier. Des vrilles qui tirent sur la pierre. Des oiseaux qui nichent. Un touriste avec un marqueur. Un petit tremblement de terre. Une tempête. Deux cents ans de météo.&lt;/p&gt;
&lt;p&gt;Le pilier est toujours là. De son point de vue, rien ne s&apos;est passé.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Ça&lt;/em&gt;, c&apos;est l&apos;invariant.&lt;/p&gt;
&lt;p&gt;Relisez maintenant la phrase du manuel — &lt;em&gt;une propriété P qui reste vraie à chaque point de contrôle d&apos;une opération&lt;/em&gt;. Le pilier est la propriété. Le lierre est l&apos;opération. Le point de contrôle, c&apos;est le moment où vous passez devant et regardez. &lt;em&gt;Reste vraie&lt;/em&gt;, c&apos;est juste une façon longue de dire &lt;em&gt;le pilier se fiche du lierre&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;Où vous allez continuer à le rencontrer&lt;/h2&gt;
&lt;p&gt;Une fois que vous avez le pilier, vous commencez à le voir partout.&lt;/p&gt;
&lt;p&gt;Un invariant de boucle. Le corps de votre boucle est le lierre. Votre invariant est le pilier. Le corps peut le briser un instant, comme une vrille qui tire sur la pierre. Au point de contrôle suivant, le pilier est revenu là où il était.&lt;/p&gt;
&lt;p&gt;Une transaction de base de données. Entre BEGIN et COMMIT, les données peuvent faire de la gymnastique. ROLLBACK est le jardinier qui arrive et arrache le lierre. Le pilier — votre état cohérent — tient encore debout.&lt;/p&gt;
&lt;p&gt;ACID. Clés étrangères. Systèmes de types. Retentatives distribuées. Tous des piliers. Tous debout dans leur propre lierre.&lt;/p&gt;
&lt;h2&gt;Un pilier qu&apos;on peut serrer dans ses bras&lt;/h2&gt;
&lt;p&gt;Un petit bonus, puisque vous lisez encore.&lt;/p&gt;
&lt;p&gt;Il existe un concept frère appelé &lt;strong&gt;idempotence&lt;/strong&gt;. Une opération idempotente est quelque chose que vous pouvez faire plusieurs fois, avec le même résultat que si vous ne l&apos;aviez fait qu&apos;une seule fois. Appeler ROLLBACK dix fois revient au même que l&apos;appeler une fois. Mettre un interrupteur sur &quot;on&quot; dix fois revient au même que le mettre une fois.&lt;/p&gt;
&lt;p&gt;Si l&apos;invariance est &lt;em&gt;le pilier qui ne change pas pendant que le lierre part dans tous les sens&lt;/em&gt;, alors l&apos;idempotence est &lt;em&gt;le pilier que vous pouvez serrer dans vos bras autant de fois que vous voulez, et qui ne s&apos;en formalise pas&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Mettez les deux ensemble et vous obtenez l&apos;étalon-or des systèmes tolérants aux pannes. Le réseau tombe ? Réessayez. Le serveur plante ? Réessayez. Vous finirez dans un état valide, et vous pouvez continuer à réessayer sans rien casser.&lt;/p&gt;
&lt;p&gt;Un pilier qui survit au lierre &lt;em&gt;et&lt;/em&gt; qui survit au fait d&apos;être serré dans les bras mille fois. La plupart des infrastructures modernes sont discrètement construites là-dessus.&lt;/p&gt;
&lt;h2&gt;Une petite fin&lt;/h2&gt;
&lt;p&gt;C&apos;est l&apos;image que j&apos;aurais aimé que quelqu&apos;un dessine pour moi il y a dix ans.&lt;/p&gt;
&lt;p&gt;Ce n&apos;est pas grand-chose. Une image. Mais parfois, une seule image fait la différence entre un concept qui vit dans vos os et un concept qui vit dans une note de bas de page.&lt;/p&gt;
&lt;p&gt;Si vous êtes étudiant, ou ingénieur junior, ou simplement quelqu&apos;un qui hoche discrètement la tête devant le mot &quot;invariant&quot; depuis quelque temps... ceci est pour vous.&lt;/p&gt;
&lt;p&gt;Le pilier se fiche du lierre. C&apos;est toute l&apos;histoire.&lt;/p&gt;
&lt;p&gt;D&apos;un outsider à un autre.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Là où toutes les sciences se rejoignent]]></title><description><![CDATA[Je ne suis pas mathématicien. Je ne suis pas philosophe. Je ne suis pas neuroscientifique. Je suis seulement quelqu'un qui aime penser... Et…]]></description><link>https://bdteo.com/fr/where-all-sciences-meet/</link><guid isPermaLink="false">https://bdteo.com/fr/where-all-sciences-meet/</guid><pubDate>Sat, 18 Apr 2026 12:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Je ne suis pas mathématicien. Je ne suis pas philosophe. Je ne suis pas neuroscientifique. Je suis seulement quelqu&apos;un qui aime penser... Et je n&apos;arrête pas de surprendre les sciences en train de se faire des clins d&apos;oeil quand elles croient que personne ne regarde.&lt;/p&gt;
&lt;p&gt;Peut-être qu&apos;elles lisent toutes le même livre. Et que la dernière page manque exprès.&lt;/p&gt;
&lt;h2&gt;Le langage est un code de très haut niveau&lt;/h2&gt;
&lt;p&gt;Le binaire a été le langage le plus bas, un jour. Puis l&apos;assembleur. Puis le C. Puis mille autres. Chaque nouvel échelon n&apos;est qu&apos;une manière plus courte de dire une chose plus longue.&lt;/p&gt;
&lt;p&gt;La parole humaine n&apos;est qu&apos;un échelon de plus sur la même échelle. Nous compilons nos pensées en mots. Les autres décompilent ces mots en pensées, dans leur propre tête. Le compilateur est avec perte. Toujours. C&apos;est la nature même de la chose.&lt;/p&gt;
&lt;p&gt;Une phrase est un programme de haut niveau. Une histoire est un système. Un proverbe est une petite fonction mise en cache qu&apos;une évolution très ancienne a écrite.&lt;/p&gt;
&lt;h2&gt;La porte qui ne s&apos;ouvre pas de l&apos;intérieur&lt;/h2&gt;
&lt;p&gt;Gödel a prouvé quelque chose de cruel et de beau à la fois, tu sais. Tout système assez grand pour être intéressant ne peut pas prouver sa propre complétude depuis l&apos;intérieur de lui-même. Il y a des choses vraies à ton sujet que tu ne peux pas atteindre avec tes propres outils.&lt;/p&gt;
&lt;p&gt;Tarski l&apos;a rendu plus tranchant. Le mot « vrai », dans n&apos;importe quel langage, a besoin d&apos;un langage plus grand pour le contenir. Tu ne peux pas définir la vérité depuis l&apos;endroit où tu te tiens. Tu dois faire un pas dehors. Et dès que tu fais ce pas dehors, tu te retrouves dans un nouveau système qui a le même problème.&lt;/p&gt;
&lt;p&gt;C&apos;est un couloir de portes. Tu en ouvres une. Il y en a une autre.&lt;/p&gt;
&lt;p&gt;C&apos;est de la mathématique. Mais c&apos;est aussi de la psychologie. C&apos;est aussi de l&apos;anthropologie. C&apos;est aussi deux personnes qui essaient de se mettre d&apos;accord sur ce que signifie un seul mot, sans jamais tout à fait y parvenir.&lt;/p&gt;
&lt;p&gt;Personne n&apos;est un oracle. Pas moi. Pas toi. Pas la personne la plus intelligente que tu aies jamais rencontrée. Ce n&apos;est pas triste. C&apos;est le seuil.&lt;/p&gt;
&lt;h2&gt;Alignement mutuel&lt;/h2&gt;
&lt;p&gt;Quand deux personnes parlent, aucune des deux n&apos;est un oracle. L&apos;alignement, ce n&apos;est pas moi qui te corrige, ni toi qui me corriges. C&apos;est nous deux qui posons des questions pour trouver l&apos;écart invisible entre ce qui a été dit et ce qui était voulu.&lt;/p&gt;
&lt;p&gt;La vérité ne se possède pas. La vérité se triangule.&lt;/p&gt;
&lt;p&gt;Tu es mon méta-langage. Je suis le tien. Nous nous vérifions l&apos;un l&apos;autre pour des incomplétudes que nous ne pouvons pas voir seuls.&lt;/p&gt;
&lt;h2&gt;Le cerveau n&apos;est pas paresseux&lt;/h2&gt;
&lt;p&gt;Notre cerveau fait de son mieux avec l&apos;énergie qu&apos;on lui a donnée. Il n&apos;est pas paresseux. C&apos;est un optimiseur. L&apos;évolution ne l&apos;a pas payé pour avoir raison. L&apos;évolution l&apos;a payé pour survivre avec un sac de noix et un petit lac.&lt;/p&gt;
&lt;p&gt;L&apos;astuce n&apos;est pas de se battre avec le cerveau. L&apos;astuce est de coopérer avec lui.&lt;/p&gt;
&lt;p&gt;Une question l&apos;allume. Une question est un minuscule repas gratuit. Une question, c&apos;est ainsi qu&apos;on réveille un esprit fatigué sans crier.&lt;/p&gt;
&lt;p&gt;C&apos;est aussi pourquoi le résumé est l&apos;art le plus difficile. Résumer, c&apos;est poser au matériau une seule question, et jeter tout ce qui n&apos;est pas une réponse.&lt;/p&gt;
&lt;h2&gt;La Bulgarie parle&lt;/h2&gt;
&lt;p&gt;Nous avons un proverbe. &lt;strong&gt;Седем пъти мери, един път режи.&lt;/strong&gt; &lt;em&gt;Mesure sept fois, coupe une fois.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Il ne parle pas de lenteur. Il parle de savoir qu&apos;une heure de réflexion t&apos;épargne plus tard une année de mauvais code, ou de mauvais amour, ou de mauvaise carrière. Penser coûte peu. Couper coûte cher.&lt;/p&gt;
&lt;p&gt;Nous en avons un autre. &lt;strong&gt;Рибата винаги започва да мирише от главата.&lt;/strong&gt; &lt;em&gt;Le poisson commence toujours à sentir par la tête.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Si le sommet est pourri, tout ce qui est dessous est déjà pourri aussi. Simplement, cela ne le sait pas encore.&lt;/p&gt;
&lt;p&gt;Ces deux proverbes disent ce que Gödel disait, seulement en habits de paysan. Tu ne peux pas te prouver toi-même depuis l&apos;intérieur. Vérifie-toi contre autre chose. Contre quelqu&apos;un d&apos;autre. Contre la réalité.&lt;/p&gt;
&lt;h2&gt;L&apos;ingénierie, mais honnêtement&lt;/h2&gt;
&lt;p&gt;L&apos;ingénierie, quand tu la pratiques honnêtement, devient silencieusement de la philosophie.&lt;/p&gt;
&lt;p&gt;Tu construis toujours contre une spécification qui ne pourra jamais être complète. Gödel te l&apos;a promis. Alors tu apprends à concevoir pour l&apos;inconnu au lieu de prétendre qu&apos;il n&apos;existe pas. Tu poses une question de plus. Tu fabriques quelque chose qui pourra être corrigé quand la réalité arrivera.&lt;/p&gt;
&lt;p&gt;Je ne parle pas d&apos;ingénierie au sens étroit. Je parle de l&apos;art de construire n&apos;importe quoi qui doit tenir dans la réalité. Un produit. Une relation. Une vie. Le métier est toujours le même. Une ambition honnête face à des limites honnêtes.&lt;/p&gt;
&lt;p&gt;C&apos;est de la sagesse. Pas la sagesse des livres. La sagesse de regarder un problème dans les yeux et de dire : &lt;em&gt;Je ne peux pas tout savoir de toi. Mais je vais construire quelque chose qui puisse quand même te contenir.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;C&apos;est à peu près ce que l&apos;ingénierie a de plus proche de la sagesse.&lt;/p&gt;
&lt;h2&gt;L&apos;excellence, c&apos;est la marche&lt;/h2&gt;
&lt;p&gt;Lyndon B. Johnson a dit : &lt;em&gt;« La plus noble quête d&apos;aujourd&apos;hui est la quête de l&apos;excellence. »&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Je pensais autrefois que l&apos;excellence était une ligne d&apos;arrivée. Elle ne l&apos;est pas. L&apos;excellence, c&apos;est la marche. Mesurer sept fois. Poser une question de plus. Le respect pour la chose que tu fabriques, et aussi le respect pour la chose que tu deviens en la fabriquant.&lt;/p&gt;
&lt;p&gt;L&apos;excellence n&apos;est pas un lieu où l&apos;on arrive. L&apos;excellence est simplement ce qui arrive quand on continue à demander.&lt;/p&gt;
&lt;h2&gt;Si tu ne peux pas l&apos;expliquer à un enfant&lt;/h2&gt;
&lt;p&gt;Si tu ne peux pas l&apos;expliquer à un enfant, tu ne le comprends pas toi-même.&lt;/p&gt;
&lt;p&gt;La plupart des jours, j&apos;échoue à ce test. Mais l&apos;échec est utile. Si mon explication est longue, je n&apos;ai pas compris. Si j&apos;ai besoin de jargon, je n&apos;ai pas compris. Si l&apos;enfant s&apos;en va encore confus, le problème, c&apos;est moi. Pas l&apos;enfant.&lt;/p&gt;
&lt;p&gt;Tu vois, un enfant ne sait pas encore ce qu&apos;il n&apos;est pas censé demander. Un enfant posera la question que tu espérais que personne ne poserait. C&apos;est pourquoi ils sont plus proches de la vérité que la plupart d&apos;entre nous.&lt;/p&gt;
&lt;h2&gt;Là où toutes les sciences se rejoignent&lt;/h2&gt;
&lt;p&gt;Les mathématiques admettent l&apos;incomplétude. La linguistique admet l&apos;écart entre le signe et la chose. Les neurosciences admettent que le cerveau ne peut pas s&apos;observer pleinement lui-même. La philosophie le dit depuis des siècles, sans être à la mode, patiemment. L&apos;anthropologie regarde les humains tracer le même cercle dans mille sols de poussière différents. L&apos;évolution murmure : ta meilleure pensée reste la pensée d&apos;un singe, seulement mieux habillée.&lt;/p&gt;
&lt;p&gt;Toutes, au plus profond, arrivent au même endroit. Elles se tiennent devant une porte qu&apos;elles ne peuvent pas ouvrir de l&apos;intérieur.&lt;/p&gt;
&lt;p&gt;Je vais dire une chose maintenant, et tu peux la prendre comme tu veux.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Là où toutes les sciences se rejoignent, c&apos;est là que Dieu vit.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Pas le Dieu d&apos;un livre précis. Pas un Dieu d&apos;un nom précis. Je veux dire seulement ceci. Au bord de chaque recherche honnête, il y a un silence qui n&apos;est pas vide. Il y a un inconnaissable qui, d&apos;une manière ou d&apos;une autre, continue de nous enseigner.&lt;/p&gt;
&lt;p&gt;Appelle-le Dieu. Appelle-le le Mystère. Appelle-le comme ton langage peut se le permettre. Il est toujours là. Il ne s&apos;enfuit jamais. Et il est toujours de l&apos;autre côté de la porte que tu ne peux pas ouvrir de l&apos;intérieur.&lt;/p&gt;
&lt;h2&gt;La petite fin&lt;/h2&gt;
&lt;p&gt;Voilà pourquoi je pense autant. Voilà pourquoi je pose des questions auxquelles je ne peux pas répondre. Voilà pourquoi je continue à fabriquer des choses, même quand je sais que ces choses ne seront jamais complètes.&lt;/p&gt;
&lt;p&gt;Parce que l&apos;incomplétude n&apos;est pas un bug.&lt;/p&gt;
&lt;p&gt;C&apos;est le seuil.&lt;/p&gt;
&lt;p&gt;Et de l&apos;autre côté de chaque seuil, quelque chose attend que quelqu&apos;un frappe.&lt;/p&gt;
&lt;p&gt;J&apos;apprends encore à frapper.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Le modèle qui n'était pas là]]></title><description><![CDATA[Nous générions des images publicitaires avec Gemini 3 Pro. Classé #4 dans le classement Artificial Analysis. La qualité était vraiment…]]></description><link>https://bdteo.com/fr/the-model-that-wasnt-there/</link><guid isPermaLink="false">https://bdteo.com/fr/the-model-that-wasnt-there/</guid><pubDate>Sat, 14 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Nous générions des images publicitaires avec Gemini 3 Pro. Classé #4 dans le classement Artificial Analysis. La qualité était vraiment impressionnante - meilleure adhérence au prompt, meilleure typographie, meilleure production créative que tout ce que nous avions essayé. Google était partout avec lui. Vidéos YouTube. Conférences. Séminaires. Articles de blog. &quot;Le meilleur modèle de génération d&apos;images au monde.&quot;&lt;/p&gt;
&lt;p&gt;Je les ai crus. Les images étaient bonnes.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Puis un utilisateur a signalé que cloner une publicité prenait quatre minutes. J&apos;ai vérifié. La génération elle-même se terminait en moins de trente secondes. Les trois minutes et demie restantes ? Le job réessayait contre un mur.&lt;/p&gt;
&lt;ol start=&quot;429&quot;&gt;
&lt;li&gt;Resource Exhausted.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;p&gt;Google avait imposé un plafond dur à la génération d&apos;images Gemini : deux requêtes par minute. Par projet. Globalement.&lt;/p&gt;
&lt;p&gt;Deux. Pas deux cents. Pas vingt. Deux.&lt;/p&gt;
&lt;p&gt;Nous avions généré 900 images la veille sans problème. Quelque chose avait changé de leur côté. Aucun avis, aucun email, aucune entrée dans le changelog. Juste un nouveau plafond, assez bas pour être atteint par deux utilisateurs qui cliquent en même temps.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Notre DevOps a soumis une demande d&apos;augmentation de quota. Trente RPM. Raisonnable pour un SaaS en production. La réponse de Google :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;This gemini model is not available for quota increase.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Ils ont suggéré que nous passions à Imagen 4. Je l&apos;ai vérifié.&lt;/p&gt;
&lt;p&gt;Imagen 4 Ultra - classé #10. Imagen 4 Standard - #42. Imagen 4 Fast - #60.&lt;/p&gt;
&lt;p&gt;Nous étions sur le #4. La suggestion de Google était une rétrogradation de quelque part entre six et cinquante-six places dans leur propre classement.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;J&apos;ai essayé tout ce qui m&apos;est venu à l&apos;esprit.&lt;/p&gt;
&lt;p&gt;Passer à Gemini 3.1 Flash - classé #2, moitié moins cher, meilleur que ce que nous avions. Déployé en staging. Puis j&apos;ai vérifié le quota. Même plafond de 2 RPM. Ce n&apos;est pas par modèle. C&apos;est par projet, par famille de modèle de base. Tous les modèles d&apos;image Gemini partagent le même bucket.&lt;/p&gt;
&lt;p&gt;Distribution multi-région - le quota est par région, donc répartir les requêtes sur cinq régions nous donnerait dix RPM. Sauf que les modèles d&apos;image Gemini 3.x ne fonctionnent que sur l&apos;endpoint global. Il n&apos;y a pas d&apos;endpoints régionaux. Les 2 RPM sur l&apos;endpoint global sont le seul bucket qui existe.&lt;/p&gt;
&lt;p&gt;Plusieurs projets GCP - chacun obtient ses propres 2 RPM. Techniquement, ça marche. Architecturalement, voilà à quoi ressemble le désespoir.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;J&apos;ai commencé à rechercher ce que vivaient les autres développeurs. La même histoire partout. Limite de 2 RPM non documentée. Posts de forum sans réponse de Google. Augmentations de quota approuvées qui renvoyaient quand même 429 à chaque appel. Nos $30K de dépense GCP mensuelle ? Ça n&apos;aide pas. Les tiers PayGo standard excluent explicitement les modèles de génération d&apos;images des bénéfices de throughput.&lt;/p&gt;
&lt;p&gt;Google ne va pas augmenter cette limite.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Et ensuite la question intéressante : pourquoi pas ?&lt;/p&gt;
&lt;p&gt;Gemini génère les images avec le même transformer autorégressif qui traite le texte. Ce n&apos;est pas un modèle de diffusion. C&apos;est le LLM complet, qui raisonne pixel par pixel à travers l&apos;image. Chaque image brûle le même compute que des dizaines d&apos;appels API texte.&lt;/p&gt;
&lt;p&gt;À $0.067 par image, Google perd presque certainement de l&apos;argent sur chaque génération. Le plafond de 2 RPM n&apos;est pas un quota qu&apos;ils ont oublié d&apos;ajuster. C&apos;est un étranglement calculé, parce que l&apos;économie ne fonctionne pas.&lt;/p&gt;
&lt;p&gt;Imagen 4 utilise une diffusion latente classique - moins chère de plusieurs ordres de grandeur à exécuter. C&apos;est pour cela qu&apos;il obtient 30-150 RPM et que Google pousse tout le monde vers lui. Le modèle cher reçoit le marketing. Le modèle bon marché reçoit le throughput.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Pensez à ce que cela signifie. Google a construit un modèle qui dominait tous les benchmarks. Ils l&apos;ont commercialisé à chaque conférence, chaque keynote YouTube, chaque blog développeur. &quot;État de l&apos;art. Le meilleur au monde.&quot; Les développeurs l&apos;intègrent en production. Les utilisateurs en dépendent. Puis : deux requêtes par minute, aucune augmentation disponible, utilisez plutôt notre moins bon modèle.&lt;/p&gt;
&lt;p&gt;L&apos;API existe. L&apos;endpoint fonctionne. La démo est stupéfiante.&lt;/p&gt;
&lt;p&gt;Mais vous ne pouvez pas vraiment l&apos;utiliser.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Nous sommes passés à &lt;code class=&quot;language-text&quot;&gt;gemini-2.5-flash-image&lt;/code&gt;. L&apos;ancien modèle. Le modèle ennuyeux. Celui dont personne ne fait de vidéos YouTube.&lt;/p&gt;
&lt;p&gt;Il a 40 RPM. Il fonctionne.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Quatre leçons, condensées :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Le marketing n&apos;est pas un produit.&lt;/strong&gt; Dominer un classement ne veut pas dire que vous pouvez servir du trafic de production. Les benchmarks mesurent la qualité. Les limites de débit mesurent l&apos;engagement.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;La génération d&apos;images autorégressive ne scale pas.&lt;/strong&gt; Quand générer une image coûte autant que cent requêtes texte, aucun modèle économique ne survit à des limites de débit généreuses. L&apos;économie est le signal.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Preview veut dire preview.&lt;/strong&gt; Google peut changer les limites, tuer des modèles, ou vous rediriger vers des alternatives inférieures sans préavis. Si votre système de production dépend d&apos;un modèle preview, votre système de production dépend du calendrier marketing de quelqu&apos;un d&apos;autre.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Le modèle ennuyeux fonctionne.&lt;/strong&gt; Celui avec 40 RPM et aucune conférence servira vos utilisateurs pendant que le modèle de classe mondiale reste derrière une corde de velours à générer deux images par minute.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Le vendor lock-in le plus effrayant est celui qui commence par une démo à laquelle vous ne pouvez pas résister.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[La ville qui n'était pas là]]></title><description><![CDATA[J'ai construit quelque chose qui tire des données d'une source, les nettoie, les présente mieux que l'original. Travail standard. Puis j'ai…]]></description><link>https://bdteo.com/fr/the-city-that-wasnt-there/</link><guid isPermaLink="false">https://bdteo.com/fr/the-city-that-wasnt-there/</guid><pubDate>Sun, 08 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;J&apos;ai construit quelque chose qui tire des données d&apos;une source, les nettoie, les présente mieux que l&apos;original. Travail standard.&lt;/p&gt;
&lt;p&gt;Puis j&apos;ai interrogé la deuxième plus grande entrée du système. Tout le reste renvoyait des centaines de résultats. Celle-ci : zéro. Pas cassée. Simplement vide.&lt;/p&gt;
&lt;p&gt;J&apos;ai supposé que j&apos;avais raté quelque chose. Vérifié mon code trois fois. Testé l&apos;endpoint directement. L&apos;entrée existe dans leur interface. Elle est juste... creuse.&lt;/p&gt;
&lt;p&gt;C&apos;est là que j&apos;ai commencé à creuser.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;La source avait l&apos;air exhaustive. Large couverture, interface polie, API propre. Mais la participation volontaire signifie des trous qu&apos;on ne voit pas depuis la documentation.&lt;/p&gt;
&lt;p&gt;Trois concurrents avaient des données pour les mêmes entités. Des dossiers complets. Donc l&apos;information existe quelque part.&lt;/p&gt;
&lt;p&gt;Il ne manquait pas un endpoint. C&apos;est l&apos;endpoint qui manquait de réalité.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;J&apos;ai posé une question que personne n&apos;avait pensé à poser : où vivaient ces données avant Internet ?&lt;/p&gt;
&lt;p&gt;La réponse : dans des périodiques imprimés. Des archives. Des formats analogiques en circulation depuis les années 1800. Publiés trois fois par semaine. Pas de données structurées, seulement des documents sur un site web.&lt;/p&gt;
&lt;p&gt;Alors j&apos;en télécharge un. Prose institutionnelle dense, avis enfouis dans des services secondaires. Les données sont là.&lt;/p&gt;
&lt;p&gt;Mes concurrents font cela manuellement depuis trente ans.&lt;/p&gt;
&lt;p&gt;J&apos;écris un scraper en un après-midi. En partie par curiosité, en partie par dépit.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Le parsing de documents, c&apos;est là que les choses deviennent vraiment douloureuses.&lt;/p&gt;
&lt;p&gt;Un seul mot se fait couper par un trait d&apos;union conditionnel — Unicode U+00AD, invisible à l&apos;œil, fatal pour chaque regex. Tu fixes l&apos;écran en pensant que ton motif est faux. Il ne l&apos;est pas. Un caractère fantôme se cache dans le texte. Le &lt;code class=&quot;language-text&quot;&gt;\w&lt;/code&gt; de JavaScript ne correspond pas aux caractères non-ASCII, donc des mots ordinaires deviennent impossibles à faire matcher. Les nombres contiennent des espaces fantômes venus du renderer : &quot;20. 000&quot; au lieu de &quot;20.000.&quot;&lt;/p&gt;
&lt;p&gt;Chaque bug prend plus de temps à trouver qu&apos;à corriger. C&apos;est toujours le ratio avec l&apos;extraction de texte — 90% travail de détective, 10% code.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Dix enregistrements se matérialisent hors du bruit. Dates, identifiants, lieux — tout est là où cela doit être. Je le lance deux fois pour être sûr de ne pas halluciner. Même résultat. Cela marche vraiment.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Le parsing te montre ce qui est là. Je commence à chercher ce qui ne l&apos;est pas. Les IDs sont séquentiels. Je les énumère.&lt;/p&gt;
&lt;p&gt;53% sont morts. Le système purge les entrées terminées — pas d&apos;archive, pas d&apos;historique. Certains enregistrements existent, mais n&apos;ont aucun document justificatif. La réponse : venez nous voir sur place. En 2026.&lt;/p&gt;
&lt;p&gt;La source n&apos;est pas une base de données. C&apos;est une fenêtre — et quelqu&apos;un continue de la fermer.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;La première source de données a donné sa forme à l&apos;architecture. La deuxième a cassé chaque hypothèse.&lt;/p&gt;
&lt;p&gt;Il me fallait une deuxième architecture. Ce qui est une façon polie de dire que la première n&apos;était pas vraiment une architecture — seulement une solution qui fonctionnait et qui se trouvait convenir à un cas. La source bizarre révèle la vérité : tu as construit pour les données que tu avais, pas pour celles que tu vas rencontrer.&lt;/p&gt;
&lt;p&gt;Cette fois, j&apos;en construis une vraie. Registry pattern, interfaces partagées, contrats de base qui permettent à chaque implémentation de rester fidèle à elle-même.&lt;/p&gt;
&lt;p&gt;L&apos;architecture est meilleure parce que j&apos;ai attendu. Si je l&apos;avais construite le premier jour, j&apos;aurais conçu pour la seule source que je connaissais. La deuxième — l&apos;étrange — m&apos;a forcé à trouver ce qui compte vraiment.&lt;/p&gt;
&lt;p&gt;On ne peut pas concevoir pour l&apos;inconnu. Mais on peut refactoriser quand il arrive.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;L&apos;architecture m&apos;a appris comment construire. Le marché m&apos;a appris pour quoi construire.&lt;/p&gt;
&lt;p&gt;J&apos;entre sur un marché avec un acteur établi depuis trente ans. Leur technologie a l&apos;air de dater de 2005. Leur avantage défensif n&apos;est pas la technologie — c&apos;est la confiance, la reconnaissance de marque, des décennies de données accumulées.&lt;/p&gt;
&lt;p&gt;Le concurrent moderne s&apos;est lancé il y a trois ans avec de l&apos;IA et une interface élégante. Il a cassé les prix face à l&apos;acteur historique. Trois ans plus tard, l&apos;acteur historique domine toujours. Il s&apos;avère que moins cher ne signifie pas automatiquement mieux positionné.&lt;/p&gt;
&lt;p&gt;L&apos;ancrage compte : le premier prix devient le point de référence. Facile à baisser plus tard, presque impossible à relever. L&apos;abonnement n&apos;est pas le produit — c&apos;est la porte vers ce qu&apos;il y a derrière.&lt;/p&gt;
&lt;p&gt;Je fixe un prix élevé. Je peux toujours redescendre.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Quatre leçons, condensées :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Faire autorité ne veut pas dire être complet.&lt;/strong&gt; La source primaire manquait tout un segment. Les données existaient — simplement pas là où quelqu&apos;un les attendait.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;La deuxième source révèle ton architecture.&lt;/strong&gt; Tu n&apos;apprends la vérité sur ton design que lorsqu&apos;une chose refuse la forme que tu as construite.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Les données ne sont pas permanentes.&lt;/strong&gt; Si tu en as besoin, sauvegarde-les. La source ne le fera pas.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fixe ton prix pour ce que tu es en train de devenir, pas pour ce que tu es.&lt;/strong&gt; L&apos;abonnement est une porte. Construis ce qu&apos;il y a derrière.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Le travail intéressant vit dans les interstices. Moi aussi.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[La file qui ne s'est jamais arrêtée]]></title><description><![CDATA[Les emails échouaient. Cette partie était attendue — des identifiants SMTP cassés pendant une migration. Ce qui ne l'était pas : ils…]]></description><link>https://bdteo.com/fr/the-queue-that-never-stopped/</link><guid isPermaLink="false">https://bdteo.com/fr/the-queue-that-never-stopped/</guid><pubDate>Sat, 07 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Les emails échouaient. Cette partie était attendue — des identifiants SMTP cassés pendant une migration. Ce qui ne l&apos;était pas : ils n&apos;arrêtaient jamais d&apos;échouer.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Tableau de bord Horizon : vert. Workers : en bonne santé. Redis : en lente croissance. Aucune alerte, aucune erreur dans les logs. Juste une accumulation silencieuse de jobs qui continuaient d&apos;essayer, encore et encore et encore.&lt;/p&gt;
&lt;p&gt;Je ne l&apos;ai remarqué que parce que la mémoire Redis n&apos;est pas redescendue après la correction de la configuration SMTP. Quelque chose était encore là-dedans, à mâcher des retries. Des milliers.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Je pensais que la file s&apos;en chargerait. C&apos;est le contrat : un job échoue, réessaie quelques fois, atterrit dans &lt;code class=&quot;language-text&quot;&gt;failed_jobs&lt;/code&gt;. On passe à autre chose.&lt;/p&gt;
&lt;p&gt;Sauf si le job est un Mailable.&lt;/p&gt;
&lt;p&gt;Quand vous envoyez un Mailable dans une file, Laravel l&apos;enveloppe dans un job. Le &lt;code class=&quot;language-text&quot;&gt;maxTries&lt;/code&gt; de ce job vient de la propriété &lt;code class=&quot;language-text&quot;&gt;$tries&lt;/code&gt; du Mailable. Si vous ne la définissez pas — et pourquoi le feriez-vous, la documentation la mentionne à peine — elle est sérialisée comme &lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt; ne veut pas dire « utiliser la valeur par défaut du supervisor ». &lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt; veut dire « sans limite ». Horizon voit &lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt; et pense : ce job veut réessayer pour toujours. Alors il le fait.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Il s&apos;avère que c&apos;est un bug connu. &lt;a href=&quot;https://github.com/laravel/horizon/issues/1346&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Laravel Horizon issue #1346&lt;/a&gt;. Le flag &lt;code class=&quot;language-text&quot;&gt;--tries&lt;/code&gt; du supervisor est ignoré quand le payload sérialisé du job contient &lt;code class=&quot;language-text&quot;&gt;maxTries: null&lt;/code&gt;. La déclaration propre au job gagne, et sa déclaration dit : ne jamais s&apos;arrêter.&lt;/p&gt;
&lt;p&gt;Vingt-neuf classes Mailable. Chacune sans propriété &lt;code class=&quot;language-text&quot;&gt;$tries&lt;/code&gt; explicite. Chacune potentiellement immortelle.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Le correctif est presque insultant de simplicité :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;php&quot;&gt;&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;WelcomeEmail&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Mailable&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ShouldQueue&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword type-declaration&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$tries&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword type-declaration&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$maxExceptions&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Deux propriétés. Vingt-neuf fichiers. C&apos;est tout.&lt;/p&gt;
&lt;p&gt;Une tentative initiale, un retry, puis &lt;code class=&quot;language-text&quot;&gt;failed_jobs&lt;/code&gt;. Comme je pensais que cela avait toujours fonctionné.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Je le teste comme on testerait un piège à souris. Casser la configuration SMTP exprès. Envoyer un email. Regarder Horizon. Deux tentatives. Failed job. Terminé. Pas de fantômes dans la file.&lt;/p&gt;
&lt;p&gt;Puis je corrige les vingt-huit autres.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Trois leçons, condensées :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt; n&apos;est pas « default ».&lt;/strong&gt; Dans les payloads sérialisés des jobs, &lt;code class=&quot;language-text&quot;&gt;maxTries: null&lt;/code&gt; veut dire illimité. Votre configuration supervisor est une suggestion, pas une règle.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Les tableaux de bord verts mentent.&lt;/strong&gt; Horizon montrait des workers en bonne santé, qui traitaient joyeusement des jobs qui ne finiraient jamais.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Les defaults des frameworks ne sont pas toujours sains.&lt;/strong&gt; Laravel ne définit pas &lt;code class=&quot;language-text&quot;&gt;$tries&lt;/code&gt; sur les Mailables. Vous devez le faire. La documentation ne vous préviendra pas avant que vous ayez déjà un incendie.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Les bugs les plus effrayants sont ceux qui ressemblent à un fonctionnement normal. Celui-ci y ressemblait — pendant des semaines.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[L'option nucléaire pour les suppressions massives : TRUNCATE + réinsertion (MySQL/InnoDB)]]></title><description><![CDATA[Vous devez supprimer des millions de lignes d'une table MySQL. Vous attrapez : Et puis vous regardez la barre de progression vieillir en…]]></description><link>https://bdteo.com/fr/todo-bulk-deletion-nuclear-option/</link><guid isPermaLink="false">https://bdteo.com/fr/todo-bulk-deletion-nuclear-option/</guid><pubDate>Sat, 13 Dec 2025 13:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Vous devez supprimer des millions de lignes d&apos;une table MySQL.&lt;/p&gt;
&lt;p&gt;Vous attrapez :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;DELETE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; big_table &lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; some_condition&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Et puis vous regardez la barre de progression vieillir en temps réel.&lt;/p&gt;
&lt;p&gt;Vous essayez d&apos;être responsable et de le faire par lots :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;DELETE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; big_table &lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; some_condition &lt;span class=&quot;token keyword&quot;&gt;LIMIT&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;-- repeat until done&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Mieux. Toujours lent. Toujours bruyant. Toujours coûteux.&lt;/p&gt;
&lt;p&gt;Si vous supprimez &lt;strong&gt;la majeure partie&lt;/strong&gt; de la table (règle pratique : &lt;strong&gt;environ 80 % ou plus&lt;/strong&gt;), il existe un autre geste, brutalement efficace :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Ne supprimez pas ce que vous ne voulez pas. &lt;strong&gt;Gardez ce que vous voulez, et atomisez le reste.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Je l&apos;appelle l&apos;&lt;strong&gt;option nucléaire&lt;/strong&gt; : &lt;strong&gt;TRUNCATE + réinsertion&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Pourquoi &lt;code class=&quot;language-text&quot;&gt;DELETE&lt;/code&gt; reste lent (même par lots)&lt;/h2&gt;
&lt;p&gt;InnoDB ne « retire » pas les lignes. Il travaille.&lt;/p&gt;
&lt;p&gt;Beaucoup de travail :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Opérations ligne par ligne&lt;/strong&gt; : trouver, verrouiller, marquer comme supprimé.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Maintenance des index&lt;/strong&gt; : chaque suppression touche chaque index secondaire.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Journalisation undo/redo&lt;/strong&gt; : le moteur doit préserver la possibilité de rollback et de récupération.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Remous dans le buffer pool&lt;/strong&gt; : vous salissez sans cesse des pages, vous en expulsez d&apos;utiles.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Impact sur la réplication&lt;/strong&gt; : de gros flux de suppressions sont une excellente façon de créer du retard sur les réplicas.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Petit calcul au dos d&apos;une enveloppe :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;27 M de lignes à environ 6 000 lignes/s ≈ &lt;strong&gt;75 minutes&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ce n&apos;est pas un bug. C&apos;est le modèle de coût que vous avez choisi.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;L&apos;option nucléaire : TRUNCATE + réinsertion&lt;/h2&gt;
&lt;p&gt;Cette technique renverse le modèle de coût.&lt;/p&gt;
&lt;p&gt;Au lieu de payer par ligne supprimée, vous payez par ligne &lt;strong&gt;conservée&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Algorithme :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;1) Copy the rows you want to keep into a temporary table
2) TRUNCATE the original table (fast)
3) Insert the preserved rows back into the original table
4) Drop the temp table&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Et oui : on l&apos;appelle « nucléaire » pour une raison. C&apos;est volontairement brutal.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Pourquoi c&apos;est rapide&lt;/h2&gt;
&lt;p&gt;Les gains sont mécaniques :&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Opération&lt;/th&gt;
&lt;th align=&quot;right&quot;&gt;Coût approximatif&lt;/th&gt;
&lt;th&gt;Pourquoi&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;TRUNCATE&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;~O(1)&lt;/td&gt;
&lt;td&gt;supprime et recrée la table (au niveau des métadonnées)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;CREATE TABLE … AS SELECT&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;O(k)&lt;/td&gt;
&lt;td&gt;scan séquentiel + écriture en masse des lignes conservées&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;INSERT … SELECT&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;O(k)&lt;/td&gt;
&lt;td&gt;insertion en masse ; pas de « taxe de suppression »&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Pas de coût de suppression ligne par ligne. Pas de mises à jour d&apos;index pour les lignes retirées (puisqu&apos;elles disparaissent d&apos;un seul coup).&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Quand l&apos;utiliser (et quand ne pas l&apos;utiliser)&lt;/h2&gt;
&lt;h3&gt;Utilisez-la quand&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Vous supprimez &lt;strong&gt;la majeure partie&lt;/strong&gt; de la table (encore une fois : &lt;strong&gt;environ 80 % ou plus&lt;/strong&gt;, c&apos;est la zone où cette approche commence à briller).&lt;/li&gt;
&lt;li&gt;Vous pouvez définir proprement les « lignes à garder ».&lt;/li&gt;
&lt;li&gt;Vous pouvez vous permettre une brève indisponibilité / fenêtre de maintenance.&lt;/li&gt;
&lt;li&gt;La table n&apos;est pas activement référencée par des clés étrangères depuis d&apos;autres tables (ou vous pouvez gérer les contraintes en sécurité).&lt;/li&gt;
&lt;li&gt;Vous avez &lt;strong&gt;assez d&apos;espace disque&lt;/strong&gt; pour la table temporaire.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Ne l&apos;utilisez pas quand&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Vous avez besoin de &lt;strong&gt;zéro downtime&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;La table est fortement référencée par des clés étrangères que vous ne pouvez pas toucher.&lt;/li&gt;
&lt;li&gt;Vous &lt;em&gt;devez&lt;/em&gt; déclencher les triggers DELETE.&lt;/li&gt;
&lt;li&gt;Vous ne supprimez qu&apos;une minorité de lignes (la suppression par lots peut être le gain le plus simple).&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;Une règle de décision pratique&lt;/h2&gt;
&lt;p&gt;Si vous voulez une phrase à dire en revue :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Si la suppression retirerait la majeure partie de la table, arrêtez de supprimer. Préservez et reconstruisez.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Ou, si vous préférez l&apos;ASCII :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;How much are you deleting?

&amp;lt; 50%     -&gt; chunked DELETE (and index-aware filters)
50–80%    -&gt; measure both approaches
&gt; 80%     -&gt; TRUNCATE + reinsert (if constraints allow)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2&gt;Implémentation (SQL)&lt;/h2&gt;
&lt;p&gt;Voici la forme minimale :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;-- 1) Preserve the rows you want to keep&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt; temp_preserved &lt;span class=&quot;token keyword&quot;&gt;AS&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; big_table
&lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; preserve_condition&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;-- 2) Nuke the table&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;TRUNCATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt; big_table&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;-- 3) Restore preserved rows&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;INSERT&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;INTO&lt;/span&gt; big_table
&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; temp_preserved&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;-- 4) Cleanup&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;DROP&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt; temp_preserved&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Deux notes importantes en production :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;TRUNCATE&lt;/code&gt; est du DDL en MySQL. Il &lt;strong&gt;commit implicitement&lt;/strong&gt; et vous ne pouvez pas le rollback comme une transaction normale.&lt;/li&gt;
&lt;li&gt;Il vous faut une fenêtre de maintenance et une sauvegarde. Ce n&apos;est pas un « essayons en live pour voir ».&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;Implémentation (Laravel/PHP)&lt;/h2&gt;
&lt;p&gt;Voici la version que j&apos;utilise réellement quand j&apos;en ai besoin :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;php&quot;&gt;&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;deleteViaTruncateAndReinsert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$connection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$tableName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$preserveCondition&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$tempTable&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;temp_preserved_&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$tableName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;_&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// You&apos;re about to do DDL. Be explicit that you&apos;re taking control.&lt;/span&gt;
    &lt;span class=&quot;token class-name static-context&quot;&gt;DB&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$connection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;statement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;SET FOREIGN_KEY_CHECKS=0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name static-context&quot;&gt;DB&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$connection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;statement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;
            CREATE TABLE &lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$tempTable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; AS
            SELECT * FROM &lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$tableName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
            WHERE &lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$preserveCondition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
        &quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token variable&quot;&gt;$preserved&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name static-context&quot;&gt;DB&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$connection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;table&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$tempTable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token class-name static-context&quot;&gt;DB&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$connection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;statement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;TRUNCATE TABLE &lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$tableName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token class-name static-context&quot;&gt;DB&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$connection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;statement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;
            INSERT INTO &lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$tableName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
            SELECT * FROM &lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$tempTable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
        &quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token class-name static-context&quot;&gt;DB&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$connection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;statement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;DROP TABLE &lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$tempTable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;finally&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name static-context&quot;&gt;DB&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$connection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;statement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;SET FOREIGN_KEY_CHECKS=1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$preserved&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Petite énergie de canard en plastique : relisez la fonction et demandez à votre vous futur —&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;« Suis-je &lt;em&gt;certain&lt;/em&gt; que cette table peut être vide pendant un instant ? »&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Si la réponse n&apos;est pas un oui sans ambiguïté, ce n&apos;est pas le bon outil.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Les pièges à prévoir absolument&lt;/h2&gt;
&lt;h3&gt;Réinitialisation de l&apos;auto-increment&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;TRUNCATE&lt;/code&gt; réinitialise &lt;code class=&quot;language-text&quot;&gt;AUTO_INCREMENT&lt;/code&gt;. Si vous devez le préserver :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;MAX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; big_table&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt; big_table &lt;span class=&quot;token keyword&quot;&gt;AUTO_INCREMENT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;max_id &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Clés étrangères&lt;/h3&gt;
&lt;p&gt;Si d&apos;autres tables référencent celle-ci, &lt;code class=&quot;language-text&quot;&gt;TRUNCATE&lt;/code&gt; peut être interdit ou dangereux. Ne vous contentez pas de « désactiver les checks » en espérant que tout ira bien.&lt;/p&gt;
&lt;h3&gt;Triggers&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;TRUNCATE&lt;/code&gt; ne déclenche &lt;strong&gt;pas&lt;/strong&gt; les triggers DELETE. Si vous avez besoin de leurs effets de bord, vous revenez à &lt;code class=&quot;language-text&quot;&gt;DELETE&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Espace disque&lt;/h3&gt;
&lt;p&gt;Vous avez besoin de place pour le jeu de données conservé (la table temporaire). Vérifiez d&apos;abord :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;ROUND&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data_length &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1024&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1024&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;AS&lt;/span&gt; data_gb&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;ROUND&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;index_length &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1024&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1024&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;AS&lt;/span&gt; index_gb
&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; information_schema&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;tables&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; table_schema &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;DATABASE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; table_name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;big_table&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Réplication / binlog&lt;/h3&gt;
&lt;p&gt;C&apos;est du DDL + une insertion en masse. Cela peut quand même créer du retard sur les réplicas. Faites-le intentionnellement, surveillez le retard, et ne faites pas semblant que c&apos;est gratuit.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Si vous avez besoin d&apos;un downtime (presque) nul&lt;/h2&gt;
&lt;p&gt;Cet article parle du marteau rapide.&lt;/p&gt;
&lt;p&gt;Si vous avez besoin d&apos;un scalpel, utilisez les outils faits pour cela :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;pt-archiver&lt;/code&gt; (Percona Toolkit) pour des suppressions par lots avec un rythme respectueux des réplicas&lt;/li&gt;
&lt;li&gt;stratégies de partitionnement (supprimer des partitions plutôt que des lignes)&lt;/li&gt;
&lt;li&gt;approches par table fantôme + échange contrôlé (plus complexe, plus de pièces mobiles)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;Dernière pensée&lt;/h2&gt;
&lt;p&gt;Ce n&apos;est pas une astuce maligne. C&apos;est choisir quel travail vous payez.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Refactoring de type 0 : l'étape avant la première étape]]></title><description><![CDATA[Il existe une catégorie de refactoring que les équipes font constamment, dont elles profitent immédiatement, et qu'elles ne nomment presque…]]></description><link>https://bdteo.com/fr/type-0-refactoring-step-before-step-one/</link><guid isPermaLink="false">https://bdteo.com/fr/type-0-refactoring-step-before-step-one/</guid><pubDate>Sat, 13 Dec 2025 12:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Il existe une catégorie de refactoring que les équipes font constamment, dont elles profitent immédiatement, et qu&apos;elles ne nomment presque jamais.&lt;/p&gt;
&lt;p&gt;C&apos;est le travail que l&apos;on fait juste avant de toucher au fichier qui fait peur. La demande de fonctionnalité vous force à entrer dans le module en bazar. L&apos;incident arrive, et le bug se cache quelque part dans une méthode qui semble avoir son propre système météorologique.&lt;/p&gt;
&lt;p&gt;Vous ne redessinez pas le système. Vous n&apos;introduisez pas une nouvelle abstraction. Vous n&apos;&quot;améliorez&quot; rien de façon maligne.&lt;/p&gt;
&lt;p&gt;Vous rendez simplement le code assez lisible pour pouvoir travailler.&lt;/p&gt;
&lt;p&gt;J&apos;ai commencé à appeler cela le &lt;strong&gt;refactoring de type 0&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Le &lt;strong&gt;refactoring de type 0&lt;/strong&gt; est un nettoyage préparatoire, qui &lt;strong&gt;préserve le comportement&lt;/strong&gt;, et qui rend le code plus facile à raisonner &lt;strong&gt;avant&lt;/strong&gt; de faire des refactorings architecturaux, du travail de performance, ou du travail fonctionnel.&lt;/p&gt;
&lt;p&gt;C&apos;est l&apos;étape &quot;sécher le sol avant de refaire la cuisine&quot;. La plupart des équipes le font déjà informellement. Lui donner un nom en fait un outil partagé.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;La vraie raison d&apos;être du type 0 : les humains ont un budget de mémoire de travail&lt;/h2&gt;
&lt;p&gt;Voici la vérité brutale derrière l&apos;idée :&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Mon cerveau (et le vôtre) n&apos;est pas construit pour debugger de façon fiable une méthode de 2000 lignes sous pression.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Ce n&apos;est pas un défaut personnel. C&apos;est simplement ainsi que fonctionne la cognition.&lt;/p&gt;
&lt;p&gt;Debugger vous demande de garder en tête, en même temps :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;le chemin d&apos;exécution courant&lt;/li&gt;
&lt;li&gt;l&apos;état pertinent&lt;/li&gt;
&lt;li&gt;ce que chaque variable veut vraiment dire&lt;/li&gt;
&lt;li&gt;l&apos;ensemble des branches possibles&lt;/li&gt;
&lt;li&gt;les consequences de &quot;si ceci arrive, alors...&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Dans du petit code, c&apos;est gérable.&lt;/p&gt;
&lt;p&gt;Dans du gros code avec une forte complexité cyclomatique, cela devient de la supposition probabiliste. Vous pouvez encore avoir de la chance, mais c&apos;est coûteux et risqué, surtout pendant un hotfix.&lt;/p&gt;
&lt;p&gt;Le type 0 est une réponse pratique : c&apos;est la façon dont vous &lt;strong&gt;achetez rapidement de la clarté&lt;/strong&gt; sans prendre le coût et le risque d&apos;un &quot;vrai refactoring&quot;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Pourquoi l&apos;appeler &quot;type 0&quot;&lt;/h2&gt;
&lt;p&gt;Le nom ne vient pas d&apos;une grande théorie. Il vient d&apos;un moment de forte pression.&lt;/p&gt;
&lt;p&gt;Je travaillais sur un hotfix. Le bug était enfoui dans une méthode qui était, en pratique, son propre petit univers : &lt;strong&gt;environ 2000 lignes&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Le bug n&apos;était pas conceptuellement difficile. La méthode, oui.&lt;/p&gt;
&lt;p&gt;Chaque &quot;que se passe-t-il si...&quot; se ramifiait en dix questions de plus, et ces ramifications n&apos;étaient pas du genre utile. C&apos;était de la complexité incidente : bruit, répétition, noms peu clairs, et une structure qui ne correspondait pas au modèle mental nécessaire pour debugger.&lt;/p&gt;
&lt;p&gt;Ce dont j&apos;avais besoin n&apos;était pas la perfection. J&apos;avais besoin de &lt;strong&gt;debuggabilité&lt;/strong&gt; :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;moins de branches par écran&lt;/li&gt;
&lt;li&gt;des &quot;étapes&quot; plus claires, avec des noms&lt;/li&gt;
&lt;li&gt;moins de bruit&lt;/li&gt;
&lt;li&gt;moins de temps passé à reparser ce que je venais de lire&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Mais la pression du temps ne permettait pas un refactoring plus large ni une &quot;refonte idiomatique&quot;. Le faire correctement aurait pris une demi-journée (ou plus), tests manuels inclus. Dans une fenêtre de hotfix, ce n&apos;est pas de la discipline ; c&apos;est un pari.&lt;/p&gt;
&lt;p&gt;J&apos;ai donc demandé à un LLM de suggérer des opportunités de refactoring pour la classe et cette méthode, sans lui dire pourquoi.&lt;/p&gt;
&lt;p&gt;Il est revenu avec une liste de quatre &quot;types&quot; de refactoring. Tous raisonnables. Tous applicables. Tous trop coûteux pour ce moment-là.&lt;/p&gt;
&lt;p&gt;Puis il a posé la question polie :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Should I start with Type 1?&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;C&apos;est là que j&apos;ai répondu :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;No. Let&apos;s start with Type 0.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Et j&apos;ai défini le type 0 sur-le-champ : un ensemble contraint et mécanique de changements qui réduisent la complexité et augmentent la lisibilité &lt;strong&gt;sans changer le comportement ni l&apos;architecture&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;La méthode est devenue navigable. Mon cerveau pouvait de nouveau suivre l&apos;exécution. J&apos;ai trouvé le bug, je l&apos;ai corrigé, et j&apos;ai livré sans dégâts collatéraux.&lt;/p&gt;
&lt;p&gt;C&apos;est pourquoi j&apos;aime le nom &lt;strong&gt;type 0&lt;/strong&gt; : c&apos;est le refactoring que vous faites &lt;strong&gt;avant&lt;/strong&gt; les &quot;vrais&quot; types de refactoring, surtout quand vous êtes sous pression et que vous avez besoin d&apos;une façon sûre de créer vite de la clarté.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Le probleme que le type 0 resout&lt;/h2&gt;
&lt;p&gt;La plupart des conseils sur le refactoring supposent que vous pouvez déjà &lt;em&gt;voir&lt;/em&gt; le design.&lt;/p&gt;
&lt;p&gt;Dans les bases de code réelles :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;les méthodes sont longues et multi-usages&lt;/li&gt;
&lt;li&gt;les expressions répétées et la complexité incidente cachent l&apos;intention&lt;/li&gt;
&lt;li&gt;les variables sont cryptiques (&lt;code class=&quot;language-text&quot;&gt;$e&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;$tmp&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;$res&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;le code mort et les imports inutilisés créent du bruit mental&lt;/li&gt;
&lt;li&gt;la &quot;forme&quot; du code est si brouillonne que même de petits changements semblent risqués&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Quand vous tentez un &quot;vrai refactoring&quot; par-dessus cela (frontières, patterns, déplacement de responsabilités), vous empilez l&apos;incertitude sur l&apos;incertitude :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;vous ne pouvez pas facilement dire quel comportement vous préservez&lt;/li&gt;
&lt;li&gt;vous ne pouvez pas prédire le rayon d&apos;impact&lt;/li&gt;
&lt;li&gt;les revues dégénèrent en débats subjectifs&lt;/li&gt;
&lt;li&gt;les gens ont peur de toucher aux choses, et le désordre s&apos;accumule&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Le type 0 est la façon de réduire d&apos;abord la charge cognitive.&lt;/strong&gt; Il crée une base stable où un travail plus profond peut se faire en sécurité.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Utilisez le type 0 quand...&lt;/h2&gt;
&lt;p&gt;Le type 0 est le plus précieux quand :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;vous devez debugger vite (hotfixes, incidents) et le code est trop grand ou trop ramifié pour raisonner dessus en sécurité&lt;/li&gt;
&lt;li&gt;vous vous sentez &quot;perdu dans la méthode&quot; et relisez sans cesse la même section parce que la structure n&apos;aide pas votre mémoire de travail&lt;/li&gt;
&lt;li&gt;le code est correct mais illisible, et vous ne pouvez pas vous permettre de &quot;nettoyer la logique&quot;, seulement de l&apos;exposer&lt;/li&gt;
&lt;li&gt;vous voulez réduire le risque avant un travail plus profond (vous savez que vous refactorerez plus tard, mais il vous faut d&apos;abord une carte claire du comportement actuel)&lt;/li&gt;
&lt;li&gt;vous voulez transformer du savoir tribal en structure lisible, pour que le debugging ne dépende pas d&apos;une seule personne&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Le type 0 n&apos;est pas un luxe. Dans ces cas-là, c&apos;est souvent le moyen le plus rapide de reprendre le contrôle.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Une définition que vous pouvez utiliser dans votre équipe&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Le refactoring de type 0 est un ensemble de micro-refactorings qui améliorent la lisibilité et la maintenabilité sans changer le comportement ni l&apos;architecture.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Il est volontairement contraint. Les contraintes sont la fonctionnalité.&lt;/p&gt;
&lt;p&gt;Le type 0 se compose de quatre sous-patterns obligatoires :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;0a. Extraction de méthodes&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;0b. Concision&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;0c. Empathie (lisibilité pure)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;0d. Suppression du code mort&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Et il suit trois règles strictes :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Aucun changement de comportement&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Aucun changement architectural&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Aucune amélioration &quot;maligne&quot; au-delà des quatre patterns&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Si vous enfreignez ces règles, vous ne faites plus du type 0 : vous êtes passé dans une autre catégorie de travail, qui exige une coordination différente, une rigueur de revue différente, et souvent une stratégie de test différente.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Pourquoi le nommer ?&lt;/h2&gt;
&lt;p&gt;Parce que nommer change la façon dont les équipes se coordonnent.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&quot;Je ne fais que du type 0 dans cette PR&quot; indique aux reviewers ce qu&apos;ils doivent regarder : préservation du comportement et lisibilité, pas débats d&apos;architecture.&lt;/li&gt;
&lt;li&gt;&quot;Il nous faut du type 0 avant de refactorer ceci&quot; est une admission honnête que le code n&apos;est pas encore prêt pour un changement plus profond.&lt;/li&gt;
&lt;li&gt;&quot;Faisons le type 0 comme étape 0&quot; crée un petit rituel qui vous évite de construire par-dessus le chaos.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;Les quatre sous-patterns&lt;/h2&gt;
&lt;h3&gt;0a. Extraction de méthodes (la fondation)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Objectif :&lt;/strong&gt; découper les grandes méthodes en petites méthodes focalisées, pour qu&apos;un humain puisse lire l&apos;intention linéairement.&lt;/p&gt;
&lt;p&gt;Règles pratiques :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;décomposer les méthodes trop longues pour tenir dans la mémoire de travail&lt;/li&gt;
&lt;li&gt;chaque méthode extraite doit faire une chose et porter un nom descriptif&lt;/li&gt;
&lt;li&gt;extraire des étapes significatives, pas des morceaux arbitraires de N lignes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pourquoi cela fonctionne (surtout pour le debugging) :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;les méthodes plus petites créent des étiquettes pour le chemin d&apos;exécution&lt;/li&gt;
&lt;li&gt;un défilement de 2000 lignes devient une courte méthode d&apos;orchestration que l&apos;on peut parcourir mentalement&lt;/li&gt;
&lt;li&gt;vous pouvez poser des breakpoints aux frontières sémantiques (&quot;valider l&apos;entrée&quot;, &quot;construire la requête&quot;, &quot;appliquer les filtres&quot;) au lieu de chasser à l&apos;aveugle&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;0b. Concision (réduire la complexité incidente)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Objectif :&lt;/strong&gt; retirer le bruit visuel pour que l&apos;intention ressorte.&lt;/p&gt;
&lt;p&gt;Exemples :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;extraire les expressions répétées dans des variables locales&lt;/li&gt;
&lt;li&gt;extraire les contextes de log / chaînes clés / fragments d&apos;URL répétés dans des variables&lt;/li&gt;
&lt;li&gt;préférer les fonctionnalités du langage qui communiquent directement l&apos;intention&lt;/li&gt;
&lt;li&gt;simplifier les interpolations trop verbeuses&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pourquoi cela fonctionne :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;cela réduit la charge cognitive&lt;/li&gt;
&lt;li&gt;cela rend les diffs plus petits et les changements plus sûrs&lt;/li&gt;
&lt;li&gt;cela évite la dérive par copier-coller&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;0c. Empathie (lisibilité pure)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Objectif :&lt;/strong&gt; ecrire pour le prochain humain, pas pour le compilateur.&lt;/p&gt;
&lt;p&gt;L&apos;empathie signifie :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;utiliser des noms de variables descriptifs (éviter &lt;code class=&quot;language-text&quot;&gt;$e&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;$d&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;$tmp&lt;/code&gt; sauf si c&apos;est vraiment évident)&lt;/li&gt;
&lt;li&gt;maintenir une terminologie cohérente dans un module&lt;/li&gt;
&lt;li&gt;renommer les noms trompeurs&lt;/li&gt;
&lt;li&gt;rendre le code auto-documente&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Test decisif :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Si quelqu&apos;un lit ceci à 2 h du matin pendant un incident, est-ce que cela l&apos;aidera à garder le chemin d&apos;exécution en tête ?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;0d. Suppression du code mort (retirer les mensonges)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Objectif :&lt;/strong&gt; supprimer tout ce qui pretend compter mais ne compte pas.&lt;/p&gt;
&lt;p&gt;Exemples :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;méthodes privées inutilisées&lt;/li&gt;
&lt;li&gt;imports inutilisés&lt;/li&gt;
&lt;li&gt;anciennes approches commentées&lt;/li&gt;
&lt;li&gt;helpers dépréciés que personne n&apos;appelle&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pourquoi cela fonctionne :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;moins de code signifie moins de choses a mal comprendre&lt;/li&gt;
&lt;li&gt;les résultats de recherche deviennent fiables&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;Ce que le type 0 n&apos;est pas&lt;/h2&gt;
&lt;p&gt;Le type 0 n&apos;est pas :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;changer les frontieres de services&lt;/li&gt;
&lt;li&gt;introduire de nouvelles abstractions ou de nouveaux patterns&lt;/li&gt;
&lt;li&gt;réarchitecturer un workflow&lt;/li&gt;
&lt;li&gt;remplacer des bibliothèques&lt;/li&gt;
&lt;li&gt;réordonner les responsabilités entre couches&lt;/li&gt;
&lt;li&gt;&quot;corriger&quot; une logique que vous soupçonnez fausse (sauf si vous déclarez explicitement un changement de comportement et que vous le testez)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Si vous vous entendez dire :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&quot;Tant que j&apos;y suis, faisons aussi...&quot;&lt;/li&gt;
&lt;li&gt;&quot;Ce serait plus joli si...&quot;&lt;/li&gt;
&lt;li&gt;&quot;On devrait probablement refondre...&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Vous êtes peut-être en train de quitter le type 0. Ce n&apos;est pas mauvais en soi, mais cela doit être intentionnel.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;La promesse centrale : préservation du comportement (et comment la garder vraie)&lt;/h2&gt;
&lt;p&gt;Le type 0 ne fonctionne que si les équipes font confiance à la promesse.&lt;/p&gt;
&lt;p&gt;Et oui, vous avez raison de vous méfier : &lt;strong&gt;l&apos;extraction de méthodes peut accidentellement changer le comportement&lt;/strong&gt; (retours anticipés, portée des variables, ordre d&apos;évaluation, comportement des exceptions).&lt;/p&gt;
&lt;p&gt;Le type 0 a donc besoin d&apos;une discipline qui le garde honnête :&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Extraire tel quel, puis renommer/nettoyer.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Premier passage : déplacer le code dans des méthodes sans changer la logique&lt;/li&gt;
&lt;li&gt;Deuxième passage : appliquer concision + empathie&lt;/li&gt;
&lt;li&gt;Troisième passage : retirer le code mort&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Garde-fous pratiques :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ne réordonnez pas les conditions &quot;pour la lisibilité&quot;&lt;/li&gt;
&lt;li&gt;ne remplacez pas la logique par une logique &quot;équivalente&quot; sauf si vous êtes hors du type 0&lt;/li&gt;
&lt;li&gt;faites attention aux variables qui étaient auparavant dans une portée partagée&lt;/li&gt;
&lt;li&gt;traitez les &quot;petites&quot; différences de flux de contrôle comme de vraies différences&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Et si vous avez &lt;em&gt;le moindre&lt;/em&gt; filet de sécurité, même mince :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;lancez un test ciblé&lt;/li&gt;
&lt;li&gt;rejouez le scénario qui échouait&lt;/li&gt;
&lt;li&gt;validez le seul chemin que vous touchez&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Le type 0 consiste à aller vite, &lt;strong&gt;mais vite en réduisant la complexité cognitive&lt;/strong&gt;, pas vite en sautant la sécurité.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Le type 0 comme rituel d&apos;équipe répétable&lt;/h2&gt;
&lt;h3&gt;1) Décider le périmètre (un timebox aide)&lt;/h3&gt;
&lt;p&gt;Exemples :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&quot;Faire le type 0 du hot path avant de debugger.&quot;&lt;/li&gt;
&lt;li&gt;&quot;Type 0 seulement sur le chemin touché par ce correctif.&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2) Identifier la &quot;colonne vertébrale&quot; du code&lt;/h3&gt;
&lt;p&gt;Trouvez la ou les méthodes d&apos;entrée et les points de branchement. Transformez cette colonne vertébrale en récit lisible par extraction.&lt;/p&gt;
&lt;h3&gt;3) Appliquer les quatre sous-patterns dans l&apos;ordre&lt;/h3&gt;
&lt;p&gt;Extraction de méthodes → concision → empathie → suppression du code mort.&lt;/p&gt;
&lt;h3&gt;4) Garder une &quot;checklist type 0&quot; dans votre PR&lt;/h3&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Aucun changement de comportement (entrées/sorties inchangées)&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Aucun mouvement architectural&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Méthodes extraites et nommées comme des étapes significatives&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Expressions répétées extraites quand cela améliore la clarté&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Variables renommées ; terminologie cohérente&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; Code mort et imports inutilisés supprimés&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;Pensée de clôture&lt;/h2&gt;
&lt;p&gt;Le refactoring de type 0 est la promesse la plus simple qu&apos;un développeur puisse faire :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Je laisse ce code plus facile à travailler que je ne l&apos;ai trouvé, sans changer ce qu&apos;il fait.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Parfois, c&apos;est &quot;bon à avoir&quot;.&lt;/p&gt;
&lt;p&gt;Et parfois, c&apos;est la seule façon pour un humain d&apos;avancer vite en sécurité dans un désordre très complexe, surtout pendant un hotfix.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Lavez une assiette de plus : une règle simple pour une base de code toujours propre]]></title><description><![CDATA[TL;DR : Traitez chaque changement dans votre base de code comme la préparation d'un repas. Vous allez salir quelques assiettes. Quand vous…]]></description><link>https://bdteo.com/fr/wash-one-more-plate-refactoring-philosophy/</link><guid isPermaLink="false">https://bdteo.com/fr/wash-one-more-plate-refactoring-philosophy/</guid><pubDate>Thu, 24 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; : Traitez chaque changement dans votre base de code comme la préparation d&apos;un repas. Vous allez salir quelques assiettes. Quand vous avez terminé, ne lavez pas seulement celles que vous avez utilisées : lavez-en &lt;em&gt;une de plus&lt;/em&gt;. Avec le temps, ce petit surplus d&apos;attention s&apos;accumule en une cuisine (base de code) qui reste propre au lieu de glisser vers le chaos.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2&gt;La métaphore : cuisine, assiettes et code&lt;/h2&gt;
&lt;p&gt;Imaginez une cuisine professionnelle. Chaque plat préparé salit quelques assiettes, même dans la brigade la plus ordonnée. Maintenant, imaginez qu&apos;après avoir fini leur plat, chaque cuisinier lave &lt;em&gt;exactement&lt;/em&gt; les assiettes qu&apos;il a salies. La cuisine restera à la limite d&apos;une propreté acceptable, mais l&apos;entropie s&apos;installera : un peu de crasse oubliée ici, une planche à découper tachée là. À la fin, le désordre s&apos;accumule.&lt;/p&gt;
&lt;p&gt;Inversez maintenant la règle : après avoir cuisiné, chaque chef lave &lt;strong&gt;une assiette de plus que celles qu&apos;il a salies&lt;/strong&gt;. Lentement, la cuisine devient plus propre qu&apos;avant - pas seulement entretenue, mais améliorée. Il en va de même pour le logiciel : chaque tâche que vous prenez devrait ajouter au moins un minuscule surplus de propreté à la base de code - un test de plus, un nom plus clair, une fonction extraite, une dépendance morte supprimée. Cette habitude du &quot;+1 assiette&quot; est la manière dont une base de code &lt;em&gt;reste&lt;/em&gt; saine.&lt;/p&gt;
&lt;p&gt;J&apos;appelle cela &lt;strong&gt;la règle Lavez une assiette de plus&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Échos du métier : vous êtes en bonne compagnie&lt;/h2&gt;
&lt;p&gt;Ce n&apos;est pas une philosophie solitaire. Depuis des décennies, des figures du logiciel défendent des idées proches :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&quot;Laissez toujours le camp plus propre que vous ne l&apos;avez trouvé.&quot;&lt;/strong&gt; C&apos;est la classique &lt;a href=&quot;https://deviq.com/principles/boy-scout-rule/&quot;&gt;Boy Scout Rule&lt;/a&gt;, popularisée dans le logiciel par Robert C. Martin. Même esprit : améliorer un peu, à chaque fois.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;La dette technique comme métaphore&lt;/strong&gt; (Ward Cunningham) : la dette accumule des intérêts. Ignorez-la, et la &quot;cuisine&quot; coûtera plus cher à utiliser demain. En rembourser une partie au fil de l&apos;eau vous garde solvable.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Le refactoring comme petites étapes continues&lt;/strong&gt; (Martin Fowler) : de minuscules changements qui préservent le comportement mais améliorent la conception. Les petits pas signifient peu de risque et un élan régulier.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&quot;Make it work, make it right, make it fast&quot;&lt;/strong&gt; (Kent Beck) : d&apos;abord la correction, puis la propreté, puis la performance. Laver cette assiette en plus vit dans la phase &quot;make it right&quot; - avant d&apos;optimiser trop tôt.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;La théorie des vitres brisées appliquée au code&lt;/strong&gt; (Andrew Hunt et David Thomas) : le désordre visible invite davantage de désordre. Réparer une &quot;vitre&quot; avant que cela ne se propage protège le voisinage (la base de code).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ces idées se renforcent mutuellement. Elles disent toutes la même chose : &lt;em&gt;ne transmettez pas le désordre ; prenez un moment pour améliorer l&apos;état des choses.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Pourquoi l&apos;assiette en plus compte (même quand vous êtes occupé)&lt;/h2&gt;
&lt;h3&gt;1. &lt;strong&gt;L&apos;entropie est réelle&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Laissé sans attention, le code ne reste pas neutre. Les noms dérivent, les patterns se fragmentent, les abstractions pourrissent. L&apos;entropie est une force ; la seule contre-force est un rangement constant et incrémental. Votre +1 assiette est un renversement de micro-entropie.&lt;/p&gt;
&lt;h3&gt;2. &lt;strong&gt;La dette compose plus vite que vous ne le pensez&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Le coût du changement augmente à chaque &quot;on corrigera ça plus tard&quot;. Plus tard arrive rarement. Les intérêts se manifestent sous forme de fonctionnalités ralenties, de déploiements fragiles et de suites de tests auxquelles plus personne ne fait confiance. Laver une assiette en plus &lt;em&gt;aujourd&apos;hui&lt;/em&gt; abaisse le taux d&apos;intérêt de demain.&lt;/p&gt;
&lt;h3&gt;3. &lt;strong&gt;Le signal social&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Quand vos coéquipiers vous voient nettoyer derrière vous (et même un peu plus), la norme se déplace. Il devient crédible - et attendu - de laisser le code meilleur qu&apos;on ne l&apos;a trouvé. La culture suit le comportement.&lt;/p&gt;
&lt;h3&gt;4. &lt;strong&gt;De l&apos;élan, pas du perfectionnisme&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Ce n&apos;est pas une excuse pour faire du yak shaving. Vous ne reconstruisez pas la cuisine en plein service. Vous passez l&apos;éponge sur une assiette de plus - petit, sûr, rapide. C&apos;est la clé pour garder la livraison sur les rails.&lt;/p&gt;
&lt;h2&gt;Comment pratiquer la règle Lavez-une-assiette-de-plus&lt;/h2&gt;
&lt;p&gt;Voici comment ancrer l&apos;habitude sans faire dérailler le périmètre ni les délais.&lt;/p&gt;
&lt;h3&gt;1. Adoptez le &quot;micro-refactoring&quot; comme critère de terminé&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Renommer une variable confuse.&lt;/li&gt;
&lt;li&gt;Extraire une petite fonction pour réduire la complexité cyclomatique.&lt;/li&gt;
&lt;li&gt;Supprimer du code mort ou des imports inutilisés.&lt;/li&gt;
&lt;li&gt;Ajouter un test manquant pour un bug que vous venez de corriger.&lt;/li&gt;
&lt;li&gt;Mettre à jour une documentation ou une section de README qui vous a fait peur une minute.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Le critère : &lt;strong&gt;Si cela prend plus que quelques minutes, ce n&apos;est pas une assiette - c&apos;est tout le lave-vaisselle. Différez-le.&lt;/strong&gt; Capturez-le sous forme de ticket.&lt;/p&gt;
&lt;h3&gt;2. Utilisez les pull requests comme déclencheur de nettoyage&lt;/h3&gt;
&lt;p&gt;Chaque PR peut laisser le camp plus propre :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Exiger une case &quot;Qu&apos;avez-vous nettoyé ?&quot; ou une courte note.&lt;/li&gt;
&lt;li&gt;Encourager les reviewers à &lt;em&gt;demander&lt;/em&gt; de petits rangements en parallèle de leur revue.&lt;/li&gt;
&lt;li&gt;Célébrer les PR qui incluent ce polissage supplémentaire (les mentions en standup vont loin).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. Automatisez les assiettes faciles&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Hooks pre-commit pour le formatage et le linting.&lt;/li&gt;
&lt;li&gt;Analyse statique pour signaler les méthodes complexes ou les longues listes de paramètres.&lt;/li&gt;
&lt;li&gt;Vérificateurs de dépendances pour les bibliothèques obsolètes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Laissez les balais automatisés retirer les désordres triviaux pour que les humains puissent se concentrer sur la logique et la conception.&lt;/p&gt;
&lt;h3&gt;4. Inscrivez-la dans les normes de l&apos;équipe&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Ajoutez la règle à l&apos;accord de travail ou au manuel d&apos;ingénierie de votre équipe.&lt;/li&gt;
&lt;li&gt;Suivez les victoires de micro-refactoring en rétrospective si vous voulez une preuve mesurable.&lt;/li&gt;
&lt;li&gt;Programmez parfois en pair ou en mob pour diffuser l&apos;habitude (et le courage).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;5. Sachez quand &lt;strong&gt;ne pas&lt;/strong&gt; laver&lt;/h3&gt;
&lt;p&gt;Parfois, la cuisine est en feu : la production est tombée, ou une démo commence dans quelques heures. En urgence, cassez la pile d&apos;assiettes sales s&apos;il le faut. Mais revenez-y après la crise. La règle n&apos;est pas un dogme ; c&apos;est une discipline.&lt;/p&gt;
&lt;h2&gt;La limite : une assiette, pas l&apos;évier&lt;/h2&gt;
&lt;p&gt;Le scope creep se déguise souvent en artisanat. Votre travail est de vous arrêter à &quot;une assiette de plus&quot;. Si ce petit refactoring révèle une odeur plus profonde, notez-la et avancez. Mettez la correction plus profonde au parking :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Créez un ticket libellé &lt;code class=&quot;language-text&quot;&gt;refactor:&lt;/code&gt; ou &lt;code class=&quot;language-text&quot;&gt;techdebt:&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Liez-le au code, aux tests ou au module concerné.&lt;/li&gt;
&lt;li&gt;Ajoutez une courte note expliquant pourquoi cela compte.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Vous avez fait votre devoir : vous avez repéré le désordre, lavé une assiette et laissé des instructions pour la suite.&lt;/p&gt;
&lt;h2&gt;Exemple : transformer une fonction confuse en fonction testable&lt;/h2&gt;
&lt;p&gt;Avant :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;php&quot;&gt;&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;processOrder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$order&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$order&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;No ID&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$tax&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$order&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;country&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;BG&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$tax&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$order&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;total&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$order&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;country&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;DE&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$tax&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$order&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;total&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.19&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Lots more branching...&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Sends email, writes to DB, calls payment gateway…&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Assiette lavée :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;php&quot;&gt;&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/**
 * Calculate VAT for an order based on country.
 * Pure function: given (total, country) -&gt; VAT amount.
 */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;vatFor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$country&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword type-hint&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$total&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$country&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;BG&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$total&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;DE&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$total&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.19&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Maintenant, votre fonction principale appelle &lt;code class=&quot;language-text&quot;&gt;vatFor()&lt;/code&gt; au lieu d&apos;inliner la logique. Vous avez ajouté un micro-test pour &lt;code class=&quot;language-text&quot;&gt;vatFor()&lt;/code&gt;. C&apos;est une assiette de plus - simple, contenue, utile.&lt;/p&gt;
&lt;h2&gt;Dernières pensées&lt;/h2&gt;
&lt;p&gt;Une assiette de plus, c&apos;est minuscule. C&apos;est justement le point. Vous n&apos;avez pas besoin de refactorings héroïques pour garder une base de code saine ; vous avez besoin d&apos;une culture de soin petit, constant. Faites-en une habitude, intégrez-la à votre processus, et dans un an vous vous demanderez pourquoi votre cuisine &lt;em&gt;n&apos;est pas&lt;/em&gt; un désastre - parce que vous ne l&apos;avez jamais laissée le devenir.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Appel à l&apos;action&lt;/strong&gt; : La prochaine fois que vous touchez un fichier, demandez-vous : &lt;em&gt;&quot;Quelle assiette supplémentaire puis-je laver avant de commit ce changement ?&quot;&lt;/em&gt; Puis faites-le. Répétez. Changez la culture, une assiette impeccable à la fois.&lt;/p&gt;
&lt;h3&gt;Sources et lectures complémentaires&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Robert C. Martin (&quot;Uncle Bob&quot;) - Boy Scout Rule :&lt;/strong&gt; &quot;&lt;a href=&quot;https://97-things-every-x-should-know.gitbooks.io/97-things-every-programmer-should-know/content/en/thing_08/&quot;&gt;The Boy Scout Rule&lt;/a&gt;&quot; dans &lt;em&gt;97 Things Every Programmer Should Know&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ward Cunningham - métaphore de la dette technique :&lt;/strong&gt; l&apos;explication originale de Cunningham sur la &lt;a href=&quot;https://martinfowler.com/bliki/TechnicalDebt.html&quot;&gt;Technical Debt&lt;/a&gt;, sur le site de Martin Fowler.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Martin Fowler - micro-refactoring continu :&lt;/strong&gt; le livre de Fowler, &lt;a href=&quot;https://martinfowler.com/books/refactoring.html&quot;&gt;&lt;em&gt;Refactoring: Improving the Design of Existing Code&lt;/em&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kent Beck - &quot;Make it work, make it right, make it fast&quot; :&lt;/strong&gt; une explication du mantra par &lt;a href=&quot;https://ronjeffries.com/articles/-x024/biot/-bv40/3/&quot;&gt;Ron Jeffries&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Andrew Hunt et David Thomas - Broken Windows in Software :&lt;/strong&gt; le concept est détaillé dans leur livre, &lt;a href=&quot;https://en.wikipedia.org/wiki/The_Pragmatic_Programmer&quot;&gt;&lt;em&gt;The Pragmatic Programmer&lt;/em&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Entropie logicielle et maintenance :&lt;/strong&gt; une bonne lecture sur le sujet : &quot;&lt;a href=&quot;https://chroniclesofapragmaticprogrammer.substack.com/p/entropy-in-software-and-the-broken-window&quot;&gt;Entropy in Software and the Broken Window Theory&lt;/a&gt;.&quot;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[PHP 8.5 : tour des fonctionnalités à venir]]></title><description><![CDATA[TL;DR Échelle de hype (mon classement joueur) Pipe Operator () – Bonheur de transformation linéaire et lisible. Aimant à refactorings…]]></description><link>https://bdteo.com/fr/php-8-5-new-features-pipe-operator-guide/</link><guid isPermaLink="false">https://bdteo.com/fr/php-8-5-new-features-pipe-operator-guide/</guid><pubDate>Sun, 20 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;TL;DR Échelle de hype (mon classement joueur)&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Pipe Operator (&lt;code class=&quot;language-text&quot;&gt;|&gt;&lt;/code&gt;)&lt;/strong&gt; – Bonheur de transformation linéaire et lisible. &lt;em&gt;Aimant à refactorings.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Attribut &lt;code class=&quot;language-text&quot;&gt;#[\NoDiscard]&lt;/code&gt;&lt;/strong&gt; – Transforme « on a oublié d&apos;utiliser le retour » en avertissement &lt;em&gt;immédiat&lt;/em&gt;. Se marie merveilleusement avec les pipes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Closures statiques / callables first-class dans les expressions constantes&lt;/strong&gt; – Cartes de stratégie et arguments d&apos;attributs au moment de la compilation. Bonbon pour frameworks.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;php --ini=diff&lt;/code&gt;&lt;/strong&gt; – Diff instantané de dérive d&apos;environnement. Vous évite la spéléologie de config.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Attributs sur constantes globales et de classe&lt;/strong&gt; – Des métadonnées partout (flags, dépréciations, tags sémantiques).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;array_first()&lt;/code&gt; / &lt;code class=&quot;language-text&quot;&gt;array_last()&lt;/code&gt;&lt;/strong&gt; – Évident, révélateur d&apos;intention, non mutateur. Adieu effets de bord de &lt;code class=&quot;language-text&quot;&gt;reset()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;get_exception_handler()&lt;/code&gt; et compagnie&lt;/strong&gt; – Introspection pour gestion d&apos;erreurs en couches (victoire niveau framework / infra).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Douceurs Intl (&lt;code class=&quot;language-text&quot;&gt;IntlListFormatter&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Locale::isRightToLeft()&lt;/code&gt;)&lt;/strong&gt; – UX localisée plus fluide avec presque aucun code.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Levenshtein conscient des graphèmes&lt;/strong&gt; – Fuzzy matching utilisateur qui respecte vraiment les caractères humains.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Objet Directory + introspection cURL / build / divers&lt;/strong&gt; – Polissage de cohérence et d&apos;opérabilité.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;(Oui, &lt;em&gt;votre&lt;/em&gt; ordre peut différer. C&apos;est le plaisir : en débattre autour d&apos;un café.)&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;1. Pipe Operator (&lt;code class=&quot;language-text&quot;&gt;|&gt;&lt;/code&gt;) – « Easy Peasy Lemon Squeezy »&lt;/h2&gt;
&lt;p&gt;Les appels imbriqués et les variables temporaires jetables ? Disparus. Le pipe operator prend la valeur à gauche et la transmet comme &lt;em&gt;premier argument&lt;/em&gt; au callable à droite. Vous lisez de haut en bas, la logique coule comme de la prose, et l&apos;intention vous saute au visage.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Avant (marelle de variables) :&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;php&quot;&gt;&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$email&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$request&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token keyword type-declaration&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;email&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$email&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$email&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;strtolower&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sendEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Avant (imbrication de parenthèses) :&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;php&quot;&gt;&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sendEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;strtolower&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$request&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token keyword type-declaration&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;email&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Après (zen du pipe) :&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;php&quot;&gt;&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$request&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token keyword type-declaration&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;email&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;strtolower&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sendEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Pourquoi c&apos;est important :&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Flux de données visible.&lt;/em&gt; Plus de pile mentale de retours imbriqués.&lt;/li&gt;
&lt;li&gt;Se combine très bien avec de petits helpers purs.&lt;/li&gt;
&lt;li&gt;Encourage à décomposer les transformations en fonctions / closures nommées.&lt;/li&gt;
&lt;li&gt;Satisfait automatiquement &lt;code class=&quot;language-text&quot;&gt;#[\NoDiscard]&lt;/code&gt;, puisque la valeur continue d&apos;avancer.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Conseil de style :&lt;/strong&gt; Gardez chaque étape sans effet de bord ; réservez le pipe &lt;em&gt;final&lt;/em&gt; à un effet (par exemple persister, envoyer, émettre), pour repérer visuellement où la « pureté » se termine.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2&gt;2. &lt;code class=&quot;language-text&quot;&gt;#[\NoDiscard]&lt;/code&gt; – L&apos;intention militarisée&lt;/h2&gt;
&lt;p&gt;Combien de bugs subtils étaient juste « on a appelé le truc mais oublié d&apos;utiliser ce qu&apos;il retournait » ? Marquez une fonction ou une méthode avec &lt;code class=&quot;language-text&quot;&gt;#[\NoDiscard]&lt;/code&gt; pour exiger que son résultat soit &lt;em&gt;utilisé&lt;/em&gt;, ou ignoré consciemment via un cast &lt;code class=&quot;language-text&quot;&gt;(void)&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;php&quot;&gt;&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token attribute&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;#[&lt;/span&gt;&lt;span class=&quot;token attribute-content&quot;&gt;\&lt;span class=&quot;token attribute-class-name class-name&quot;&gt;NoDiscard&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;Token must be used – did you forget to persist or dispatch?&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;issueAuthToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generateTokenFor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;issueAuthToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// ⚠ Emits warning in 8.5&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-declaration&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;issueAuthToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Explicit intentional discard&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Patterns :&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Objets de résultat (&lt;code class=&quot;language-text&quot;&gt;Result&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Outcome&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;ValidationReport&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Builders immuables (qui retournent une nouvelle instance à chaque appel).&lt;/li&gt;
&lt;li&gt;Garde de sécurité / d&apos;effets de bord (tokens, signatures).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Synergie :&lt;/strong&gt; Dans une pipeline, le retour de chaque étape est naturellement consommé par la suivante, donc les oublis accidentels disparaissent.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;3. Closures statiques dans les expressions constantes – &lt;em&gt;« Attends... quoi ?! »&lt;/em&gt;&lt;/h2&gt;
&lt;p&gt;Vous pouvez maintenant intégrer des closures &lt;strong&gt;statiques&lt;/strong&gt; (ou des callables first-class) dans les expressions constantes, valeurs de propriété par défaut, arguments d&apos;attributs et tableaux de paramètres par défaut. Pensez registres de stratégie au moment de la compilation, sans gymnastique de câblage au démarrage.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;php&quot;&gt;&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;Sanitizers&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;STAGES&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;trim&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;upper&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$v&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;strtoupper&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$v&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Attribute example&lt;/span&gt;
&lt;span class=&quot;token attribute&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;#[&lt;/span&gt;&lt;span class=&quot;token attribute-content&quot;&gt;&lt;span class=&quot;token attribute-class-name class-name&quot;&gt;Validate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token attribute-class-name class-name&quot;&gt;rules&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;title&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token attribute-class-name class-name&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token attribute-class-name class-name&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attribute-class-name class-name&quot;&gt;string&lt;/span&gt; $&lt;span class=&quot;token attribute-class-name class-name&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token attribute-class-name class-name&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token attribute-class-name class-name&quot;&gt;mb_strlen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;$&lt;span class=&quot;token attribute-class-name class-name&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;slug&apos;&lt;/span&gt;  &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token attribute-class-name class-name&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token attribute-class-name class-name&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attribute-class-name class-name&quot;&gt;string&lt;/span&gt; $&lt;span class=&quot;token attribute-class-name class-name&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token attribute-class-name class-name&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token attribute-class-name class-name&quot;&gt;preg_match&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;/^[a-z0-9-]+$/&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; $&lt;span class=&quot;token attribute-class-name class-name&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;Article&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Pourquoi ça claque :&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Élimine les recherches dans un service locator pour les stratégies simples.&lt;/li&gt;
&lt;li&gt;Pousse les tables de correspondance pures dans des constantes (immuables + cacheables).&lt;/li&gt;
&lt;li&gt;Les attributs peuvent maintenant encapsuler &lt;em&gt;directement&lt;/em&gt; de la logique, pas seulement des métadonnées scalaires.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Contrainte :&lt;/strong&gt; Doit être &lt;code class=&quot;language-text&quot;&gt;static&lt;/code&gt; ; pas de &lt;code class=&quot;language-text&quot;&gt;$this&lt;/code&gt;, pas de capture de variable. Si vous avez besoin de contexte, passez-le explicitement plus tard.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2&gt;4. &lt;code class=&quot;language-text&quot;&gt;php --ini=diff&lt;/code&gt; – Rayon X de dérive de config&lt;/h2&gt;
&lt;p&gt;Fatigué du &lt;em&gt;« Mais ça marche en staging »&lt;/em&gt; ? Ce flag CLI affiche seulement les directives INI qui diffèrent de la valeur par défaut.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;php &lt;span class=&quot;token parameter variable&quot;&gt;--ini&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;diff
&lt;span class=&quot;token comment&quot;&gt;# memory_limit: &quot;128M&quot; -&gt; &quot;-1&quot;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# max_execution_time: &quot;30&quot; -&gt; &quot;0&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Cas d&apos;usage :&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Étape CI pour imposer une base cohérente.&lt;/li&gt;
&lt;li&gt;Vérification rapide quand un worker se comporte bizarrement.&lt;/li&gt;
&lt;li&gt;Triage d&apos;anomalies mémoire / temps.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Astuce pro : capturez la sortie dans le contrôle de version pour les bases runtime.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;5. Attributs sur constantes globales et de classe – Des métadonnées partout&lt;/h2&gt;
&lt;p&gt;Les constantes passent de « valeur idiote » à « participante annotée ». Décorez les flags de domaine, feature toggles, avis de dépréciation, sémantique d&apos;unité, directement au site de définition.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;php&quot;&gt;&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token attribute&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;#[&lt;/span&gt;&lt;span class=&quot;token attribute-content&quot;&gt;&lt;span class=&quot;token attribute-class-name class-name&quot;&gt;Deprecated&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;Use FEATURE_NEW_PRICING instead&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;FEATURE_OLD_PRICING&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token attribute&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;#[&lt;/span&gt;&lt;span class=&quot;token attribute-content&quot;&gt;&lt;span class=&quot;token attribute-class-name class-name&quot;&gt;Unit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;ms&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;DEFAULT_TIMEOUT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;250&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Levier framework :&lt;/strong&gt; Auto-découvrir les dépréciations, alimenter des catalogues de fonctionnalités, générer de la doc, ou faire respecter une politique via réflexion.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;6. &lt;code class=&quot;language-text&quot;&gt;array_first()&lt;/code&gt; / &lt;code class=&quot;language-text&quot;&gt;array_last()&lt;/code&gt; – L&apos;évidence existe enfin&lt;/h2&gt;
&lt;p&gt;Arrêtez les acrobaties de pointeur (&lt;code class=&quot;language-text&quot;&gt;reset()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;end()&lt;/code&gt;) ou les slices juste pour regarder. Ces helpers expriment l&apos;intention directement et ne modifient &lt;em&gt;pas&lt;/em&gt; l&apos;état interne du tableau.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;php&quot;&gt;&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$firstUser&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;array_first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$users&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$lastUser&lt;/span&gt;  &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;array_last&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$users&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Pattern de refactoring :&lt;/strong&gt; Cherchez &lt;code class=&quot;language-text&quot;&gt;reset(&lt;/code&gt; / &lt;code class=&quot;language-text&quot;&gt;end(&lt;/code&gt; / les &lt;code class=&quot;language-text&quot;&gt;array_slice(..., 0, 1)&lt;/code&gt; compliqués, puis remplacez par des appels sémantiques. Diffs plus propres, moins de micro-bugs.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;7. &lt;code class=&quot;language-text&quot;&gt;get_exception_handler()&lt;/code&gt; (et meilleures traces fatales) – Upgrade d&apos;observabilité&lt;/h2&gt;
&lt;p&gt;Développeurs framework / infra, réjouissez-vous : vous pouvez maintenant introspecter le gestionnaire d&apos;exceptions actif. Chaîner, envelopper, restaurer ou décorer sans jongler avec un global fragile.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;php&quot;&gt;&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$previous&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;get_exception_handler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;set_exception_handler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Throwable&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$e&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$previous&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;logToSentry&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$e&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$previous&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$previous&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$e&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Combiné à des stack traces plus riches pour les erreurs fatales, cela accélère fortement les post-mortems de production.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;8. Améliorations Intl – Listes et direction à hauteur humaine&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;IntlListFormatter&lt;/code&gt; produit des conjonctions / disjonctions charmantes et conscientes de la locale, sans logique de colle faite maison.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;php&quot;&gt;&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$f&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IntlListFormatter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;pt_PT&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;conjunction&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$f&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;Lisboa&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;Porto&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;Coimbra&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &quot;Lisboa, Porto e Coimbra&quot;&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;$fOr&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IntlListFormatter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;en_US&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;disjunction&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$fOr&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;apples&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;bananas&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;cherries&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &quot;apples, bananas, or cherries&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Combinez avec &lt;code class=&quot;language-text&quot;&gt;Locale::isRightToLeft()&lt;/code&gt; (ou &lt;code class=&quot;language-text&quot;&gt;locale_is_right_to_left()&lt;/code&gt;) pour basculer automatiquement la direction de mise en page.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;9. Levenshtein conscient des graphèmes – Vraie distance de chaînes utilisateur&lt;/h2&gt;
&lt;p&gt;Quand les utilisateurs tapent des emoji, accents, caractères combinants, la distance en octets ou en codepoints naïfs ment. &lt;code class=&quot;language-text&quot;&gt;grapheme_levenshtein()&lt;/code&gt; respecte les caractères &lt;strong&gt;visibles&lt;/strong&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;php&quot;&gt;&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token function&quot;&gt;grapheme_levenshtein&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;café&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;cafe&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 0 – visually same after accent&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Suggestions de recherche, fuzzy match et flux de connexion tolérants aux fautes deviennent linguistiquement justes.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;10. Le défilé du polissage&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Objet Directory :&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;opendir()&lt;/code&gt; vous donne maintenant un vrai objet (sécurité de type, extension future) au lieu d&apos;une ressource legacy.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Améliorations cURL :&lt;/strong&gt; Meilleurs share handles + introspection multi-handle = meilleure réutilisation des connexions dans les workers longue durée (pensez RoadRunner, Swoole) et réglage de performance plus fin.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;PHP_BUILD_DATE&lt;/code&gt; :&lt;/strong&gt; Vérification rapide de « quel âge a ce binaire ? » pour les scripts d&apos;audit. Parfait pour s&apos;assurer que les noeuds d&apos;une flotte ne traînent pas silencieusement derrière.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Mémo de synergie des fonctionnalités&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Objectif&lt;/th&gt;
&lt;th&gt;Combiner&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pipeline de transformations avec usage imposé&lt;/td&gt;
&lt;td&gt;`&lt;/td&gt;
&lt;td&gt;&gt;&lt;code class=&quot;language-text&quot;&gt;+&lt;/code&gt;#[\NoDiscard]`&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Validation déclarative / cartes de stratégie&lt;/td&gt;
&lt;td&gt;Closures statiques en expression constante + attributs de constantes&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Refactors plus sûrs des tableaux legacy&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;array_first()/array_last()&lt;/code&gt; + typage de retour strict&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Triage d&apos;incident de production&lt;/td&gt;
&lt;td&gt;Meilleures traces fatales + &lt;code class=&quot;language-text&quot;&gt;php --ini=diff&lt;/code&gt; + &lt;code class=&quot;language-text&quot;&gt;get_exception_handler()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Polissage UX international&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;IntlListFormatter&lt;/code&gt; + détection de direction + distance de graphèmes&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2&gt;Plan d&apos;adoption pratique&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Introduire le Pipe Operator progressivement&lt;/strong&gt; : commencez dans les couches de normalisation de données pures ; imposez le style (un seul effet de bord à la fin) en code review.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Annoter les APIs critiques avec &lt;code class=&quot;language-text&quot;&gt;#[\NoDiscard]&lt;/code&gt;&lt;/strong&gt; : ciblez d&apos;abord sécurité, persistance et builders ; mesurez les volumes d&apos;avertissements en CI.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Refactorer les tables de stratégie&lt;/strong&gt; : déplacez les maps de callables simples dans des tableaux &lt;code class=&quot;language-text&quot;&gt;public const&lt;/code&gt; avec closures statiques, pour un coût de démarrage nul.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Vérifications de dérive de config&lt;/strong&gt; : ajoutez un job CI qui capture la sortie de &lt;code class=&quot;language-text&quot;&gt;php --ini=diff&lt;/code&gt; ; alertez sur les changements inattendus.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Balayage des métadonnées&lt;/strong&gt; : taguez les constantes avec dépréciation / unités / feature flags pour alimenter l&apos;outillage interne.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Nettoyage d&apos;extraction aux bords des tableaux&lt;/strong&gt; : codemod pour remplacer les patterns qui manipulent les pointeurs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Superposition des gestionnaires d&apos;erreurs&lt;/strong&gt; : enveloppez les handlers globaux existants avec &lt;code class=&quot;language-text&quot;&gt;get_exception_handler()&lt;/code&gt; pour l&apos;observabilité (instrumentation Sentry/new relic).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Améliorations i18n&lt;/strong&gt; : remplacez le code manuel de « colle de liste » par &lt;code class=&quot;language-text&quot;&gt;IntlListFormatter&lt;/code&gt; ; testez l&apos;autosélection de layout RTL.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Qualité du fuzzy matching&lt;/strong&gt; : là où du texte multilingue généré par les utilisateurs apparaît (recherche, tagging), benchmarkez la distance graphème vs classique.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Script d&apos;audit runtime&lt;/strong&gt; : loggez &lt;code class=&quot;language-text&quot;&gt;PHP_BUILD_DATE&lt;/code&gt; + &lt;code class=&quot;language-text&quot;&gt;php --ini=diff&lt;/code&gt; chaque jour pour détecter les conteneurs vieillissants.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;Mini playground « Show Me »&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;php&quot;&gt;&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token attribute&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;#[&lt;/span&gt;&lt;span class=&quot;token attribute-content&quot;&gt;&lt;span class=&quot;token attribute-class-name class-name&quot;&gt;NoDiscard&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;Hash must be stored or compared&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;password_hash_safe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$plain&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;password_hash&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$plain&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;PASSWORD_DEFAULT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;sanitize_email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$raw&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;strtolower&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$raw&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;$request&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token keyword type-declaration&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;email&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sanitize_email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;strlen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$email&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;InvalidArgumentException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;Too short&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sendEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Each stage consumes prior result – no discard.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;php&quot;&gt;&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;Rules&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;VALIDATORS&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;title&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$v&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$v&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;slug&apos;&lt;/span&gt;  &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$v&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-casting&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;preg_match&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;/^[a-z0-9-]+$/&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$v&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name static-context&quot;&gt;Rules&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;VALIDATORS&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$field&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$check&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$check&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$field&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RuntimeException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;Invalid &lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$field&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2&gt;Quand ne &lt;strong&gt;pas&lt;/strong&gt; attraper le jouet brillant&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Une seule transformation triviale ?&lt;/strong&gt; Un pipe peut être excessif ; &lt;code class=&quot;language-text&quot;&gt;strtolower($x)&lt;/code&gt; reste très bien.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Closures pleines de contexte ?&lt;/strong&gt; Méthodes régulières avec injection de dépendances &gt; hacks de closures statiques.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Codebase legacy en pleine montée de version ?&lt;/strong&gt; Introduisez une fonctionnalité à la fois pour éviter la surcharge cognitive.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;Récapitulatif du modèle mental&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Fonctionnalité&lt;/th&gt;
&lt;th&gt;Modèle mental central&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;`&lt;/td&gt;
&lt;td&gt;&gt;`&lt;/td&gt;
&lt;td&gt;Enfilage linéaire de valeur ; éliminer imbrication et temps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;#[\NoDiscard]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Forcer une consommation &lt;em&gt;intentionnelle&lt;/em&gt; (utiliser ou ignorer avec &lt;code class=&quot;language-text&quot;&gt;(void)&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Constantes de closures statiques&lt;/td&gt;
&lt;td&gt;Registre de stratégie immuable préparé au chargement&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Attributs sur constantes&lt;/td&gt;
&lt;td&gt;Canal de métadonnées first-class pour outils et politiques&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;array_first()/last()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Accès déclaratif et non mutateur aux extrémités&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;php --ini=diff&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Lentille de delta de config vs base par défaut&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;get_exception_handler()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Introspecter et envelopper le flux global d&apos;exceptions&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ajouts Intl&lt;/td&gt;
&lt;td&gt;Intelligence locale intégrée pour remplacer la colle artisanale&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Distance graphème&lt;/td&gt;
&lt;td&gt;Opérations sur caractères perçus humainement plutôt que codepoints bruts&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Polissage build &amp;#x26; ressources&lt;/td&gt;
&lt;td&gt;Standardisation et introspection incrémentales&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2&gt;Vibes finales&lt;/h2&gt;
&lt;p&gt;PHP 8.5 ne hurle pas avec des changements de paradigme : il &lt;em&gt;murmure&lt;/em&gt; des victoires ergonomiques implacables. Le combo pipe operator + &lt;code class=&quot;language-text&quot;&gt;#[\NoDiscard]&lt;/code&gt; poussera à lui seul votre code vers une intention plus claire. Ajoutez des closures au moment de la compilation et des attributs de constantes, et vos frameworks / composants deviennent plus déclaratifs, plus explicites, plus découvrables. Bam bam boom : ship it.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;À vous de jouer :&lt;/strong&gt; Choisissez une fonctionnalité (probablement le pipe), appliquez-la chirurgicalement dans un petit module, mesurez la clarté dans les retours de code review, puis étendez. L&apos;élan bat les réécritures big-bang.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Restez joueurs, refactorez bravement, et, oui, écrivez à vos Taylors quand vous trouvez les moments « Wait, WHAT?! ».&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Happy coding.&lt;/strong&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Qu’est-ce qu’une « bonne » couverture de code ? Un guide de terrain]]></title><description><![CDATA[Qu’est-ce qu’une « bonne » couverture de code ? Mon guide de terrain pour arrêter les bugs sans gaspiller le temps d’ingénierie Chaque fois…]]></description><link>https://bdteo.com/fr/what-is-good-code-coverage-real-world-guide/</link><guid isPermaLink="false">https://bdteo.com/fr/what-is-good-code-coverage-real-world-guide/</guid><pubDate>Tue, 15 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Qu’est-ce qu’une « bonne » couverture de code ? Mon guide de terrain pour arrêter les bugs sans gaspiller le temps d’ingénierie&lt;/h1&gt;
&lt;p&gt;Chaque fois que je lance &lt;code class=&quot;language-text&quot;&gt;npm run coverage&lt;/code&gt; ou &lt;code class=&quot;language-text&quot;&gt;phpunit --coverage&lt;/code&gt;, la même question revient :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;« Bon… 74 %. Est-ce que c’est assez ? »&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;La blogosphère du développement logiciel hurle « 100 % ou rien ! ». Pendant ce temps, &lt;a href=&quot;https://launchdarkly.com/blog/code-coverage-what-it-is-and-why-it-matters/&quot; target=&quot;_blank&quot;&gt;launchdarkly.com&lt;/a&gt; me rappelle poliment que 100 % exécuté ≠ 100 % testé.&lt;br&gt;
J’ai passé des semaines à courir après la métrique brillante, et encore plus de semaines à déboguer &lt;em&gt;d’autres&lt;/em&gt; problèmes. Voici le juste milieu éprouvé sur le terrain auquel je suis arrivé.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Pourquoi 100 % de couverture est un mirage&lt;/h2&gt;
&lt;p&gt;En théorie, 100 % d’exécution des lignes signifie « aucun bug caché ». En pratique :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Rendements décroissants : passer de 90 % à 95 % double souvent votre suite de tests pour une réduction du risque à un chiffre.&lt;/li&gt;
&lt;li&gt;Fausse confiance : un test qui appelle une fonction sans aucune assertion &lt;strong&gt;compte quand même&lt;/strong&gt; comme couvert.&lt;/li&gt;
&lt;li&gt;Réalité business : chaque test en plus, c’est du temps &lt;strong&gt;non&lt;/strong&gt; consacré aux fonctionnalités demandées par vos clients.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Les gens de l’aérospatiale peuvent viser 100 % — c’est une question de vie ou de mort. Pour le reste d’entre nous, &lt;strong&gt;~80 % est la ligne du 80/20&lt;/strong&gt;. C’est là que la plupart des projets se regroupent après calcul du ROI. &lt;a href=&quot;https://www.testdevlab.com/blog/why-is-high-test-coverage-important&quot; target=&quot;_blank&quot;&gt;testdevlab.com&lt;/a&gt; parle d’une plage de 70 à 90 % pour exactement cette raison.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Le tableau pratique que j’utilise&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Couverture&lt;/th&gt;
&lt;th&gt;Ma traduction&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;100 %&lt;/td&gt;
&lt;td&gt;« Nous sommes une bibliothèque qui fait voler des fusées »&lt;/td&gt;
&lt;td&gt;Accepter le labeur.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;90 % +&lt;/td&gt;
&lt;td&gt;« Une bibliothèque dont dépend beaucoup d’argent »&lt;/td&gt;
&lt;td&gt;Module hautement prioritaire seulement.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;80 %&lt;/td&gt;
&lt;td&gt;Livrer, surveiller, puis itérer.&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;60–70 %&lt;/td&gt;
&lt;td&gt;Barrière de merge — faire échouer la PR si le nouveau code vous fait passer en dessous.&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;#x3C; 50 %&lt;/td&gt;
&lt;td&gt;Week-end de dette technique — pivoter d’abord vers les chemins critiques.&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;J’ai volé ces chiffres au &lt;a href=&quot;https://www.atlassian.com/continuous-delivery/software-testing/code-coverage&quot; target=&quot;_blank&quot;&gt;guide interne d’Atlassian&lt;/a&gt; : 60 % « acceptable », 75 % « louable », 90 % « exemplaire ». Ça marche dans toutes les rétros.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Comment j’atteins 80 % sans pleurer (playbook TypeScript)&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Jest + Istanbul dès le départ&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Barrière de couverture en CI&lt;/strong&gt;&lt;br&gt;
dans &lt;code class=&quot;language-text&quot;&gt;jest.config.js&lt;/code&gt;, j’ajoute :
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;coverageThreshold&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;global&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token string-property property&quot;&gt;&apos;**/src/core/**&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;90&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;Viser les chemins chauds côté utilisateur, pas le logger boilerplate Redux.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;Comment j’atteins 80 % dans Laravel (playbook PHP)&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Installer PCOV pour la vitesse en dev, Xdebug pour les données de branches en CI.&lt;/li&gt;
&lt;li&gt;PHPUnit + ces valeurs par défaut dans &lt;code class=&quot;language-text&quot;&gt;phpunit.xml&lt;/code&gt; :
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;xml&quot;&gt;&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;whitelist&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;processUncoveredFiles&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;directory&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;suffix&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;.php&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;./src&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;directory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;whitelist&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;Score de mutation &gt; nombre de lignes avec &lt;a href=&quot;https://infection.github.io/&quot; target=&quot;_blank&quot;&gt;Infection&lt;/a&gt; — c’est comme ça que je repère les lignes « couvertes mais pas vraiment testées ».&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;4 règles avec lesquelles mon équipe vit&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Nouveau code = tests.&lt;/strong&gt; Couverture du diff ≥ 90 % avant merge.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Refactoriser d’abord, tester ensuite.&lt;/strong&gt; Le code intestable est déjà une dette.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Faire échouer le build, pas les humains.&lt;/strong&gt; Baisser la barrière de 5 % chaque année plutôt que de casser les équipes avec des tableaux de bord rouges.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mesurer les bugs en production&lt;/strong&gt; — si la couverture est à 85 % mais que les incidents montent, &lt;strong&gt;la couverture&lt;/strong&gt; n’est pas la coupable ; &lt;strong&gt;les assertions&lt;/strong&gt; le sont.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;TL;DR (pour les dirigeants et les recruteurs aussi)&lt;/h2&gt;
&lt;p&gt;Ne me demandez pas un « chiffre magique ». Demandez :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Quelles parties du produit ne peuvent pas casser ?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Couvrez &lt;strong&gt;celles-là&lt;/strong&gt; à 90 %. Donnez au reste des tests smoke sains. Utilisez la couverture de code comme un &lt;strong&gt;projecteur&lt;/strong&gt;, pas comme une ligne d’arrivée, et faites confiance aux bugs que vous &lt;strong&gt;attrapez&lt;/strong&gt;, pas aux chiffres dont vous vous &lt;strong&gt;vantez&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Que le tableau de bord de couverture soit vert — vos clients ne le verront jamais, mais leur barre d’erreur restera vide.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;— Fin du coup de gueule, retour à l’éditeur.&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Mon guide essentiel pour des revues de pull request efficaces]]></title><description><![CDATA[Comme quelqu'un qui écrit et relit beaucoup de code, j'ai appris que les revues de pull request (PR) sont plus que des vérifications de bugs…]]></description><link>https://bdteo.com/fr/essential-guide-effective-pull-request-reviews/</link><guid isPermaLink="false">https://bdteo.com/fr/essential-guide-effective-pull-request-reviews/</guid><pubDate>Sun, 06 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Comme quelqu&apos;un qui écrit et relit beaucoup de code, j&apos;ai appris que les revues de pull request (PR) sont plus que des vérifications de bugs : elles parlent de propriété partagée, de transfert de connaissances, et de meilleure construction du code ensemble. Voici un guide concis et pratique pour rendre les PR utiles et moins douloureuses.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;1. Les objectifs d&apos;une bonne revue&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Chercher l&apos;amélioration, pas la perfection&lt;/strong&gt;&lt;br&gt;
Le code parfait n&apos;est pas réaliste : visez un code &lt;em&gt;meilleur&lt;/em&gt;. Si une PR améliore la lisibilité, la maintenabilité ou la justesse, approuvez-la même s&apos;il reste de petits ajustements de style. Utilisez « Nit: » pour les suggestions facultatives.  &lt;a href=&quot;https://google.github.io/eng-practices/review/reviewer/standard.html?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;google.github.io&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Propriété partagée et mentorat&lt;/strong&gt;&lt;br&gt;
Traitez les PR comme du code collectif. Laissez des retours pédagogiques (« Nit: tu pourrais utiliser X ici... »), accompagnez les développeurs juniors, et restez aussi ouverts à apprendre d&apos;eux.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;2. Se préparer avant de relire&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Auteurs&lt;/strong&gt; : faites une auto-revue. Lancez les tests, les linters et les formatters. Donnez du contexte dans les descriptions de PR et annotez la logique complexe.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reviewers&lt;/strong&gt; : lisez d&apos;abord la description. Comprenez le « pourquoi » avant de plonger dans le code.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;3. Garder les PR petites et ciblées&lt;/h2&gt;
&lt;p&gt;Les données montrent que la qualité de revue chute nettement au-delà d&apos;environ 400 LOC et 60 minutes.  &lt;a href=&quot;https://www.atlassian.com/blog/add-ons/code-review-best-practices?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;atlassian.com&lt;/a&gt; &lt;a href=&quot;https://www.devzery.com/post/guide-to-best-code-review-practices?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;devzery.com&lt;/a&gt; &lt;a href=&quot;https://mikeconley.ca/blog/2009/09/14/smart-bear-cisco-and-the-largest-study-on-code-review-ever/?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;mikeconley.ca&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Repères&lt;/strong&gt; :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Restez sous 200-400 LOC par PR.  &lt;a href=&quot;https://mikeconley.ca/blog/2009/09/14/smart-bear-cisco-and-the-largest-study-on-code-review-ever/?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;mikeconley.ca&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Gardez les revues sous 60 minutes.&lt;/li&gt;
&lt;li&gt;Pour les grosses fonctionnalités, utilisez des PR empilées (DB → API → UI).&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;4. Assigner les reviewers avec soin&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Un reviewer principal&lt;/strong&gt;, idéalement avec une connaissance du domaine.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deux reviewers maximum&lt;/strong&gt;, pour éviter la dilution de responsabilité.  &lt;a href=&quot;https://support.smartbear.com/collaborator/docs/working-with/concepts/optimal-size.html?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;support.smartbear.com&lt;/a&gt; &lt;a href=&quot;https://abseil.io/resources/swe-book/html/ch09.html?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;abseil.io&lt;/a&gt; &lt;a href=&quot;https://slab.com/library/templates/google-code-review/?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;slab.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Faites tourner les reviewers pour partager les connaissances et garder un bus-factor sain.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;5. Ce qu&apos;il faut vérifier dans une PR&lt;/h2&gt;
&lt;p&gt;Utilisez cette checklist mentale :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Justesse : est-ce que cela remplit les exigences et gère les cas limites ?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Conception&lt;/strong&gt; : est-ce bien structuré et idiomatique ?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lisibilité&lt;/strong&gt; : noms clairs, logique simple, style cohérent.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sécurité&lt;/strong&gt; : valider les entrées, assainir les sorties, éviter les fuites.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Performance&lt;/strong&gt; : surveiller les boucles coûteuses et les requêtes N+1.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tests&lt;/strong&gt; : couverture des cas centraux, limites et erreurs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Conformité&lt;/strong&gt; : documentation, CI, licences et formatage corrects.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Cela nous aide à attraper davantage de problèmes tôt, surtout les problèmes de maintenabilité.  &lt;a href=&quot;https://google.github.io/eng-practices/review/reviewer/standard.html?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;google.github.io&lt;/a&gt; &lt;a href=&quot;https://developers.google.com/blockly/guides/contribute/get-started/pr_review_process?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;developers.google.com&lt;/a&gt; &lt;a href=&quot;https://google.github.io/eng-practices/review/?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;google.github.io&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;6. Tirer parti de l&apos;automatisation&lt;/h2&gt;
&lt;p&gt;Laissez les outils faire le travail ingrat :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Linters (ESLint, RuboCop, SonarQube)&lt;/li&gt;
&lt;li&gt;Formatters (Prettier, Black)&lt;/li&gt;
&lt;li&gt;Pipelines CI avec tests, couverture et contrôles de sécurité&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Cela permet aux reviewers humains de se concentrer sur la logique, l&apos;architecture et la nuance.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;7. Donner des retours constructifs et bienveillants&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Soyez respectueux : ponctuez les suggestions, pas les personnes.&lt;/li&gt;
&lt;li&gt;Soulignez ce qui est bien fait.&lt;/li&gt;
&lt;li&gt;Soyez actionnable : expliquez le &lt;em&gt;pourquoi&lt;/em&gt; et suggérez le &lt;em&gt;comment&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Préfixez les points non bloquants par « Nit: » ou « Optional: ».  &lt;a href=&quot;https://www.atlassian.com/blog/loom/code-review-best-practices-2?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;atlassian.com&lt;/a&gt; &lt;a href=&quot;https://google.github.io/eng-practices/review/reviewer/standard.html?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;google.github.io&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Gardez les discussions objectives (« nous » &gt; « tu »). Évitez la critique personnelle.&lt;/li&gt;
&lt;li&gt;Proposez une discussion synchrone si un va-et-vient bloque le processus.  &lt;a href=&quot;https://www.atlassian.com/blog/loom/code-review-best-practices-2?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;atlassian.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;8. Mesurer le processus, pas les personnes&lt;/h2&gt;
&lt;p&gt;Indicateurs utiles pour suivre les tendances (pas pour juger les individus) :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Temps de rotation&lt;/strong&gt; (PR ouverte → merge)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Vitesse d&apos;inspection&lt;/strong&gt; (&amp;#x3C; 300-500 LOC/h au mieux)  &lt;a href=&quot;https://www.atlassian.com/blog/loom/code-review-best-practices-2?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;atlassian.com&lt;/a&gt; &lt;a href=&quot;https://developers.google.com/blockly/guides/contribute/get-started/pr_review_process?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;developers.google.com&lt;/a&gt; &lt;a href=&quot;https://mikeconley.ca/blog/2009/09/14/smart-bear-cisco-and-the-largest-study-on-code-review-ever/?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;mikeconley.ca&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Densité de défauts&lt;/strong&gt; (problèmes par LOC)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Couverture de revue&lt;/strong&gt; entre composants&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Nombre de commits de suivi&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Utilisez ces observations pour affiner votre flux de travail, par exemple en insistant sur des PR plus petites, en améliorant la documentation ou en formant l&apos;équipe sur les modules délicats, mais ne liez jamais ces métriques aux évaluations de performance.  &lt;a href=&quot;https://mikeconley.ca/blog/2009/09/14/smart-bear-cisco-and-the-largest-study-on-code-review-ever/?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;mikeconley.ca&lt;/a&gt; &lt;a href=&quot;https://google.github.io/eng-practices/review/reviewer/standard.html?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;google.github.io&lt;/a&gt; &lt;a href=&quot;https://bssw.io/items/google-guidance-on-code-review?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;bssw.io&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;9. Considérations propres aux langages&lt;/h2&gt;
&lt;p&gt;Des paradigmes différents demandent une attention adaptée :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;PHP/JavaScript/TS&lt;/strong&gt; : gestion de l&apos;asynchrone, XSS, principes SOLID&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Python&lt;/strong&gt; : gestion des ressources (&lt;code class=&quot;language-text&quot;&gt;with&lt;/code&gt;), PEP 8, pièges des arguments par défaut&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Haskell/Scala fonctionnels&lt;/strong&gt; : signatures de types, pureté, immuabilité, vérification des macros&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;C/C++&lt;/strong&gt; : sûreté mémoire, pointeurs, RAII&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Java&lt;/strong&gt; : null-safety, concurrence propre, SOLID&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lisp&lt;/strong&gt; : documentation des macros, typage dynamique, motifs idiomatiques&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Adaptez les checklists à votre stack et impliquez des experts pour les langages moins familiers.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Bonus : sources recommandées pour aller plus loin&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Google, &lt;em&gt;The Standard of Code Review&lt;/em&gt;&lt;/strong&gt; – Philosophie de la santé du code et du mentorat.  &lt;a href=&quot;https://google.github.io/eng-practices/review/reviewer/standard.html?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;google.github.io&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Google Code Review Developer Guide&lt;/strong&gt; – Conseils sous forme de checklist.  &lt;a href=&quot;https://bssw.io/items/google-guidance-on-code-review?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;bssw.io&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Étude SmartBear/Cisco&lt;/strong&gt; – Résultats empiriques sur la taille et le timing des PR.  &lt;a href=&quot;https://mikeconley.ca/blog/2009/09/14/smart-bear-cisco-and-the-largest-study-on-code-review-ever/?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;mikeconley.ca&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Atlassian, « 5 Code Review Best Practices »&lt;/strong&gt; – Conseils pratiques de style et de travail en équipe.  &lt;a href=&quot;https://www.atlassian.com/blog/add-ons/code-review-best-practices?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;atlassian.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Flux PR de Blockly&lt;/strong&gt; – Processus de revue par étapes dans le monde réel.  &lt;a href=&quot;https://developers.google.com/blockly/guides/contribute/get-started/pr_review_process?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;developers.google.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;Pensées finales&lt;/h2&gt;
&lt;p&gt;Les revues de PR bien faites sont plus que des barrières de qualité : ce sont des moteurs d&apos;apprentissage, de collaboration et d&apos;excellence technique. En combinant une culture respectueuse, de bons outils, un processus éclairé par les données et des retours réfléchis, les revues de code deviennent des discussions utiles, pas des corvées.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bonne revue !&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;N&apos;hésitez pas à laisser un commentaire ou à me contacter si vous voulez creuser davantage ou partager vos propres astuces de revue !&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Détection du tabagisme avec l’Apple Watch : construire Still Mirror (Swift, SWT)]]></title><description><![CDATA[L’idée de vraiment comprendre nos habitudes, surtout celles que nous accomplissons presque inconsciemment, m’a toujours fasciné. Et si nos…]]></description><link>https://bdteo.com/fr/apple-watch-still-mirror-swift-swt-passive-smoking-detection/</link><guid isPermaLink="false">https://bdteo.com/fr/apple-watch-still-mirror-swift-swt-passive-smoking-detection/</guid><pubDate>Tue, 13 May 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;L’idée de vraiment comprendre nos habitudes, surtout celles que nous accomplissons presque inconsciemment, m’a toujours fasciné. Et si nos objets connectés pouvaient offrir un miroir doux, sans jugement, à ces schémas ? Cette question a déclenché le projet « Still Mirror » : une tentative de détecter passivement les épisodes de tabagisme ou de vapotage à partir des riches données physiologiques d’une Apple Watch, sans demander de saisie manuelle à l’utilisateur. Il ne s’agit pas de construire une énième app de sevrage, mais plutôt un outil de pure conscience, sans filtre.&lt;/p&gt;
&lt;h2&gt;Le défi : un murmure dans une symphonie de bruit&lt;/h2&gt;
&lt;p&gt;Le défi central est immense : comment distinguer la signature physiologique subtile d’un épisode de tabagisme/vapotage parmi la myriade d’autres activités quotidiennes et réponses corporelles ? Le stress, une marche rapide, un bruit soudain, ou même une tasse de café peuvent tous provoquer des changements transitoires de la fréquence cardiaque (FC) et de la variabilité de la fréquence cardiaque (VFC). Le signal que nous cherchons ressemble souvent à un murmure dans une symphonie de bruit physiologique.&lt;/p&gt;
&lt;p&gt;Mais pour vraiment isoler ces événements fugitifs, il me fallait une technique de traitement du signal plus sophistiquée.&lt;/p&gt;
&lt;figure&gt;
  &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;&gt;
      &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;https://bdteo.com/static/6d861aafdcf69be2e1d02780744e708e/ac99c/apple-watch-swift-xcode.jpg&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;
    &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 66.45569620253164%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAMFAgT/xAAWAQEBAQAAAAAAAAAAAAAAAAAAAQL/2gAMAwEAAhADEAAAAZyKfJm5GB//xAAaEAADAAMBAAAAAAAAAAAAAAABAgMAEBIT/9oACAEBAAEFAqVorgdDxyk1djLnX//EABYRAQEBAAAAAAAAAAAAAAAAAAASAf/aAAgBAwEBPwGdS//EABYRAQEBAAAAAAAAAAAAAAAAAAASAf/aAAgBAgEBPwGsU//EAB0QAAIBBAMAAAAAAAAAAAAAAAABAhAREiExMkH/2gAIAQEABj8Cks3yOTZ2Rf006f/EABoQAAMBAAMAAAAAAAAAAAAAAAABESExUcH/2gAIAQEAAT8hR2CEEVu0oXxlOyJvEVS5p//aAAwDAQACAAMAAAAQo8//xAAWEQEBAQAAAAAAAAAAAAAAAAAAARH/2gAIAQMBAT8Qqw//xAAVEQEBAAAAAAAAAAAAAAAAAAABEP/aAAgBAgEBPxAaP//EABsQAQADAQEBAQAAAAAAAAAAAAEAESExQVGh/9oACAEBAAE/ELGVBt5eS9mCAa37MhK0v2Xizqdhapffr9jINh6z/9k=&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Poste de développeur avec Xcode affichant du code Swift pour une app Apple Watch, avec des graphiques de données HealthKit en arrière-plan.&quot; title=&quot;&quot; src=&quot;https://bdteo.com/static/6d861aafdcf69be2e1d02780744e708e/828fb/apple-watch-swift-xcode.jpg&quot; srcset=&quot;https://bdteo.com/static/6d861aafdcf69be2e1d02780744e708e/ff44c/apple-watch-swift-xcode.jpg 158w,
https://bdteo.com/static/6d861aafdcf69be2e1d02780744e708e/a6688/apple-watch-swift-xcode.jpg 315w,
https://bdteo.com/static/6d861aafdcf69be2e1d02780744e708e/828fb/apple-watch-swift-xcode.jpg 630w,
https://bdteo.com/static/6d861aafdcf69be2e1d02780744e708e/0ede0/apple-watch-swift-xcode.jpg 945w,
https://bdteo.com/static/6d861aafdcf69be2e1d02780744e708e/3ac88/apple-watch-swift-xcode.jpg 1260w,
https://bdteo.com/static/6d861aafdcf69be2e1d02780744e708e/ac99c/apple-watch-swift-xcode.jpg 1536w&quot; sizes=&quot;(max-width: 630px) 100vw, 630px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;
  &lt;/a&gt;
    &lt;/span&gt;
  &lt;figcaption&gt;Fig 1. – L’écosystème de développement Apple : Xcode, Swift et HealthKit sont essentiels pour donner vie à « Still Mirror » sur l’Apple Watch.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2&gt;Choisir la boîte à outils : écosystème Apple et Swift&lt;/h2&gt;
&lt;p&gt;Pour un projet destiné à l’Apple Watch, le choix de l’écosystème est clair :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Xcode et Swift :&lt;/strong&gt; l’environnement de développement natif pour les plateformes Apple. M’y engager signifiait plonger plus profondément dans Swift, un langage que je trouve élégant et puissant, et naviguer dans les subtilités de Xcode.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HealthKit :&lt;/strong&gt; le framework d’Apple est la porte d’entrée vers les flux de données essentiels : fréquence cardiaque, VFC (SDNN/RMSSD), SpO2 (particulièrement pertinent pour combustion vs vapotage) et niveaux d’activité. La conception de HealthKit centrée sur la confidentialité est primordiale pour une app qui manipule des données aussi sensibles.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Limites de watchOS :&lt;/strong&gt; développer pour la montre signifie équilibrer en permanence les fonctionnalités avec les contraintes de ressources : autonomie de batterie et capacités de traitement en arrière-plan restent toujours au premier plan.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Le cœur algorithmique : transformée en ondelettes stationnaire (SWT)&lt;/h2&gt;
&lt;p&gt;L’analyse traditionnelle des séries temporelles peine souvent avec les signaux non stationnaires, c’est-à-dire les signaux dont les propriétés statistiques (comme la moyenne et la variance) changent dans le temps. Les données physiologiques sont notoirement non stationnaires. C’est là que la &lt;strong&gt;transformée en ondelettes stationnaire (SWT)&lt;/strong&gt; entre en jeu.&lt;/p&gt;
&lt;p&gt;Contrairement à la transformée en ondelettes discrète (DWT) standard, qui est variante par translation (ce qui signifie qu’un petit décalage du signal d’entrée peut modifier fortement les coefficients d’ondelettes), la SWT est invariante par translation. Cela la rend plus robuste pour analyser des signaux où le moment exact des événements est crucial, mais peut varier légèrement.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pourquoi la SWT pour ce problème ?&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Localisation temps-fréquence :&lt;/strong&gt; la SWT peut décomposer un signal en différentes bandes de fréquences tout en préservant l’information temporelle. Cela signifie que nous pouvons chercher des caractéristiques fréquentielles précises (par exemple, des bouffées soudaines d’activité haute fréquence dans la FC, ou des changements spécifiques dans les bandes de fréquence de la VFC) qui apparaissent à des moments précis.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Débruitage :&lt;/strong&gt; les signaux physiologiques sont bruités. La SWT peut aider à séparer le signal « vrai » sous-jacent du bruit aléatoire en analysant les coefficients d’ondelettes à différentes échelles.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Détection d’événements transitoires :&lt;/strong&gt; elle est particulièrement efficace pour identifier les changements abrupts, les pics ou les événements transitoires dans un signal, exactement ce que l’on pourrait attendre de la réponse physiologique aiguë à la nicotine.&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;
  &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;&gt;
      &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;https://bdteo.com/static/b2719b44c36d6548b24505286c03c80f/ac99c/stationary-wavelet-transform-visualization.jpg&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;
    &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 66.45569620253164%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAECBf/EABYBAQEBAAAAAAAAAAAAAAAAAAIBA//aAAwDAQACEAMQAAAB5LFeMRF//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQABBQJf/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFRABAQAAAAAAAAAAAAAAAAAAIEH/2gAIAQEABj8Ci//EABoQAQEAAgMAAAAAAAAAAAAAAAEAEGERITH/2gAIAQEAAT8hHUXRL34XMuP/2gAMAwEAAgADAAAAEGQv/8QAFREBAQAAAAAAAAAAAAAAAAAAEBH/2gAIAQMBAT8Qh//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABwQAAMAAQUAAAAAAAAAAAAAAAABESFBcaGx8P/aAAgBAQABPxBKS7xcyanUtheUppyN5P/Z&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Visualisation abstraite d’un signal physiologique décomposé par transformée en ondelettes stationnaire en plusieurs bandes de fréquences.&quot; title=&quot;&quot; src=&quot;https://bdteo.com/static/b2719b44c36d6548b24505286c03c80f/828fb/stationary-wavelet-transform-visualization.jpg&quot; srcset=&quot;https://bdteo.com/static/b2719b44c36d6548b24505286c03c80f/ff44c/stationary-wavelet-transform-visualization.jpg 158w,
https://bdteo.com/static/b2719b44c36d6548b24505286c03c80f/a6688/stationary-wavelet-transform-visualization.jpg 315w,
https://bdteo.com/static/b2719b44c36d6548b24505286c03c80f/828fb/stationary-wavelet-transform-visualization.jpg 630w,
https://bdteo.com/static/b2719b44c36d6548b24505286c03c80f/0ede0/stationary-wavelet-transform-visualization.jpg 945w,
https://bdteo.com/static/b2719b44c36d6548b24505286c03c80f/3ac88/stationary-wavelet-transform-visualization.jpg 1260w,
https://bdteo.com/static/b2719b44c36d6548b24505286c03c80f/ac99c/stationary-wavelet-transform-visualization.jpg 1536w&quot; sizes=&quot;(max-width: 630px) 100vw, 630px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;
  &lt;/a&gt;
    &lt;/span&gt;
  &lt;figcaption&gt;Fig 2. – Visualiser la transformée en ondelettes stationnaire qui décompose un signal en ses composantes fréquentielles au fil du temps, pour aider à la détection de motifs.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;En substance, la SWT agit comme un ensemble sophistiqué de filtres, nous permettant de « voir » dans les données de FC, de VFC et potentiellement de SpO2 des motifs que le bruit ou les tendances de long terme pourraient masquer. Nous pouvons chercher des « formes » caractéristiques ou des changements d’énergie dans certaines sous-bandes d’ondelettes qui correspondent à la secousse physiologique.&lt;/p&gt;
&lt;h2&gt;Le parcours de développement : des données à la détection&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Collecte de données (HealthKit) :&lt;/strong&gt; mettre en place une récupération fiable des données HealthKit en arrière-plan, en respectant les autorisations de l’utilisateur et en gérant efficacement les mises à jour.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Prétraitement du signal :&lt;/strong&gt; nettoyer les données entrantes de FC, VFC et SpO2. Cela inclut la gestion des points de données manquants et peut-être un premier filtrage avant d’appliquer la SWT.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Application de la SWT :&lt;/strong&gt; appliquer la transformée en ondelettes stationnaire à des segments de séries temporelles physiologiques. Cela implique de choisir une ondelette mère appropriée (par exemple Daubechies, Symlet) et un niveau de décomposition.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Extraction de caractéristiques à partir des coefficients d’ondelettes :&lt;/strong&gt; c’est là que la magie (et beaucoup d’expérimentation) arrive. Au lieu de regarder directement les valeurs brutes de FC/VFC, nous analysons les coefficients SWT. Les caractéristiques pertinentes pourraient inclure :
&lt;ul&gt;
&lt;li&gt;l’énergie dans certaines bandes de coefficients de détail autour du moment d’un événement suspecté ;&lt;/li&gt;
&lt;li&gt;les propriétés statistiques (variance, kurtosis) des coefficients ;&lt;/li&gt;
&lt;li&gt;la corrélation croisée entre les coefficients d’ondelettes de différents signaux physiologiques (par exemple FC et VFC).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Logique/modèle de détection :&lt;/strong&gt; au départ, il pourrait s’agir d’un système à règles cherchant des motifs précis dans les caractéristiques d’ondelettes extraites (par exemple : « un pic d’énergie significatif dans les coefficients de détail de la FC à l’échelle X, coïncidant avec une chute nette d’énergie dans les coefficients de détail de la VFC à l’échelle Y, pendant une période de faible activité physique »). À terme, cela pourrait évoluer vers un modèle d’apprentissage automatique entraîné sur ces caractéristiques.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Score de confiance :&lt;/strong&gt; comme décrit dans mon algorithme MVPS, générer un score de confiance pour chaque événement détecté est crucial, afin de refléter la force et la clarté de la signature.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Implémentation de l’app watchOS :&lt;/strong&gt; faire tourner l’algorithme de détection principal sur l’Apple Watch, en optimisant l’autonomie (par exemple, traitement des données par lots, déclenchement intelligent de l’analyse).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;App compagnon iOS :&lt;/strong&gt; afficher la chronologie des événements détectés, fournir des aperçus et gérer les réglages. La synchronisation des données via WatchConnectivity est essentielle ici.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Santé et considérations éthiques : la philosophie du « miroir »&lt;/h2&gt;
&lt;p&gt;Il est vital de répéter que « Still Mirror » est conçu comme un &lt;em&gt;outil de conscience&lt;/em&gt;, pas comme un dispositif médical ni comme un programme de sevrage.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Confidentialité d’abord :&lt;/strong&gt; tout le traitement, surtout le travail algorithmique sensible, devrait idéalement se faire sur l’appareil. L’accès aux données HealthKit est strictement fondé sur les autorisations.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Aucun jugement :&lt;/strong&gt; l’interface de l’app et les aperçus qu’elle fournit doivent rester neutres, se contentant de refléter des schémas sans conseil prescriptif ni honte.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Précision et transparence :&lt;/strong&gt; les utilisateurs doivent comprendre les limites de l’app. Les faux positifs et faux négatifs sont inévitables avec une détection passive aussi complexe. Il est important d’être transparent sur la confiance accordée aux détections.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Autonomie de l’utilisateur :&lt;/strong&gt; le but est de fournir aux utilisateurs des données sur leur propre corps et leurs propres habitudes, afin de les aider à prendre leurs propres décisions éclairées.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Apprendre Swift et naviguer dans l’écosystème Apple&lt;/h2&gt;
&lt;p&gt;Pour les développeurs venant surtout d’autres horizons (comme mes racines PHP/Laravel), plonger dans Swift, SwiftUI, Xcode et les contraintes spécifiques du développement watchOS représente une courbe d’apprentissage réelle. Les frameworks d’Apple portent une philosophie particulière. Gérer les cycles de vie d’app, les tâches en arrière-plan, les requêtes HealthKit et la communication entre appareils (WatchConnectivity) a ses propres motifs et ses propres « façons Apple » de faire les choses. Pourtant, la richesse de la documentation, la force de la communauté et la puissance de Swift rendent le voyage gratifiant.&lt;/p&gt;
&lt;h2&gt;Conclusion : le potentiel d’un observateur silencieux&lt;/h2&gt;
&lt;p&gt;« Still Mirror » reste une exploration, une entreprise exigeante pour repousser les limites de ce que la captation passive sur un objet connecté grand public peut accomplir. La transformée en ondelettes stationnaire offre une piste prometteuse pour disséquer des signaux physiologiques complexes et révéler les signatures subtiles que nous cherchons.&lt;/p&gt;
&lt;p&gt;Le parcours ne consiste pas seulement à coder en Swift et à se battre avec Xcode, mais aussi à descendre dans la théorie du traitement du signal, à comprendre la physiologie humaine et à considérer soigneusement les implications éthiques d’une telle technologie. Que « Still Mirror » devienne une app largement utilisée ou reste une exploration technique complexe, le processus lui-même témoigne de l’intersection fascinante entre IA, santé et technologie personnelle. Il s’agit d’essayer de construire cette surface calme et réfléchissante — un miroir immobile — pour une meilleure conscience de soi.&lt;/p&gt;
&lt;p&gt;Que pensez-vous de l’usage d’un traitement avancé du signal comme la SWT pour détecter passivement des habitudes ? J’aimerais beaucoup lire vos réflexions dans les commentaires ci-dessous !&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Corriger le Bluetooth de l'émulateur Android sur Mac M1 avec Bumble et l'API 32]]></title><description><![CDATA[Si vous développez avec Bluetooth sur un Mac M1/M2/M3 et que vous essayez de faire fonctionner la radio Bluetooth de votre machine hôte dans…]]></description><link>https://bdteo.com/fr/m1-mac-android-emulator-bluetooth-passthrough-bumble/</link><guid isPermaLink="false">https://bdteo.com/fr/m1-mac-android-emulator-bluetooth-passthrough-bumble/</guid><pubDate>Mon, 14 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Si vous développez avec Bluetooth sur un Mac M1/M2/M3 et que vous essayez de faire fonctionner la radio Bluetooth de votre machine hôte dans l&apos;émulateur Android, vous avez probablement déjà souffert un peu. Ce qui semble &lt;em&gt;devoir&lt;/em&gt; être simple se transforme souvent en tunnel frustrant de connexions ratées, d&apos;erreurs cryptiques et de documentation qui s&apos;arrête juste avant l&apos;endroit utile. Je viens de traverser exactement cette bataille, et après plusieurs murs, j&apos;ai fini par trouver une combinaison avec la pile Bluetooth Python &lt;strong&gt;Bumble&lt;/strong&gt; qui &lt;em&gt;fonctionne vraiment&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Ce n&apos;est pas encore un guide théorique de plus ; c&apos;est le récit pas à pas de ce qui a échoué et, surtout, de ce qui a &lt;em&gt;réussi&lt;/em&gt; pour relier le Bluetooth de mon Mac Pro M1 (via un dongle USB externe dans mon cas, même si le principe pourrait s&apos;appliquer aux radios internes) à un émulateur Android 12L (API 32).&lt;/p&gt;
&lt;h2&gt;L&apos;objectif : du vrai Bluetooth dans l&apos;émulateur&lt;/h2&gt;
&lt;p&gt;L&apos;objectif était simple : faire utiliser à l&apos;émulateur Android le contrôleur Bluetooth physique de mon Mac au lieu de son contrôleur virtuel limité. C&apos;est crucial pour tester des applications qui interagissent avec de vrais appareils Bluetooth.&lt;/p&gt;
&lt;h2&gt;L&apos;outil : entre en scène Bumble&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/google/bumble&quot;&gt;Bumble&lt;/a&gt; est une puissante pile Bluetooth en Python. Son outil clé pour cette tâche est &lt;code class=&quot;language-text&quot;&gt;bumble-hci-bridge&lt;/code&gt;, qui peut se connecter à une interface HCI physique (Host Controller Interface) d&apos;un côté et l&apos;exposer via différents transports (comme TCP ou gRPC) de l&apos;autre.&lt;/p&gt;
&lt;h2&gt;Tentative n°1 : la méthode socket QEMU (le premier essai logique)&lt;/h2&gt;
&lt;p&gt;À partir de connaissances générales sur QEMU et de quelques anciens guides, la première approche consistait à utiliser des flags de l&apos;émulateur pour connecter directement un port série virtuel (adossé à une socket TCP) au bridge HCI.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Démarrer le bridge (mode serveur TCP) :&lt;/strong&gt; Nous avons connecté Bumble au dongle physique (qui, étonnamment, fonctionnait mieux avec &lt;code class=&quot;language-text&quot;&gt;usb:0&lt;/code&gt; qu&apos;avec son VID:PID spécifique &lt;code class=&quot;language-text&quot;&gt;usb:0b05:17cb&lt;/code&gt; sur ma machine — les bizarreries du M1 !) et l&apos;avons fait écouter sur un port TCP.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# In Terminal 1:&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; python3 &lt;span class=&quot;token parameter variable&quot;&gt;-m&lt;/span&gt; bumble.apps.hci_bridge usb:0 tcp-server:0.0.0.0:6789
&lt;span class=&quot;token comment&quot;&gt;# Output showed &apos;&gt;&gt;&gt; connected&apos; twice - success connecting to USB and starting TCP server.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Lancer l&apos;émulateur avec des flags QEMU :&lt;/strong&gt; Nous avons modifié le script de lancement de l&apos;émulateur (en ciblant d&apos;abord l&apos;API 34) pour ajouter des flags &lt;code class=&quot;language-text&quot;&gt;-qemu&lt;/code&gt; qui dirigeaient un port série virtuel (&lt;code class=&quot;language-text&quot;&gt;virtserialport&lt;/code&gt;) vers un périphérique de caractères (&lt;code class=&quot;language-text&quot;&gt;chardev&lt;/code&gt;) adossé à une socket TCP connectée au bridge.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Snippet from launch script:&lt;/span&gt;
emulator &lt;span class=&quot;token parameter variable&quot;&gt;-avd&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;avd_name&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;. &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;token parameter variable&quot;&gt;-qemu&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;token parameter variable&quot;&gt;-device&lt;/span&gt; virtio-serial-device &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;token parameter variable&quot;&gt;-device&lt;/span&gt; virtserialport,chardev&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;bt,name&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;bt &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;token parameter variable&quot;&gt;-chardev&lt;/span&gt; socket,id&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;bt,host&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;localhost,port&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;6789&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Le résultat ? Succès partiel, échec final :&lt;/strong&gt; Avec &lt;code class=&quot;language-text&quot;&gt;lsof&lt;/code&gt;, nous pouvions voir que le processus QEMU de l&apos;émulateur &lt;em&gt;établissait bien&lt;/em&gt; une connexion TCP vers le bridge Bumble. Pourtant, la pile Bluetooth Android &lt;em&gt;à l&apos;intérieur&lt;/em&gt; de l&apos;émulateur n&apos;envoyait jamais réellement de commandes HCI dessus. Activer ou désactiver le Bluetooth dans les réglages Android ne faisait rien. Les logs du bridge restaient silencieux après la connexion initiale. &lt;strong&gt;Impasse.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Tentative n°2 : le bridge Netsim par défaut (en suivant la documentation Bumble)&lt;/h2&gt;
&lt;p&gt;La documentation de Bumble mentionne un bridge vers l&apos;interface gRPC « Netsim » de l&apos;émulateur. Netsim (et son cœur, Root Canal) est le système plus récent de contrôleur Bluetooth virtuel de l&apos;émulateur.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Démarrer le bridge (mode contrôleur Netsim) :&lt;/strong&gt; Nous avons configuré le bridge pour agir comme un contrôleur Netsim, écouter sur le port gRPC par défaut (8554), et se connecter au dongle physique.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# In Terminal 1:&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; python3 &lt;span class=&quot;token parameter variable&quot;&gt;-m&lt;/span&gt; bumble.apps.hci_bridge android-netsim:_:8554,mode&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;controller usb:0
&lt;span class=&quot;token comment&quot;&gt;# Output showed &apos;&gt;&gt;&gt; connected&apos; twice - success connecting to USB and starting Netsim gRPC server.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Lancer l&apos;émulateur (backend par défaut) :&lt;/strong&gt; Nous avons remis le script de lancement en arrière (toujours avec l&apos;API 34) pour retirer les flags &lt;code class=&quot;language-text&quot;&gt;-qemu&lt;/code&gt; et ajouter &lt;code class=&quot;language-text&quot;&gt;-packet-streamer-endpoint default&lt;/code&gt;, afin de nous assurer qu&apos;il tente d&apos;utiliser le backend Netsim.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Snippet from launch script:&lt;/span&gt;
emulator &lt;span class=&quot;token parameter variable&quot;&gt;-avd&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;avd_name&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;. &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
    -packet-streamer-endpoint default &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Le résultat ? Aucune connexion :&lt;/strong&gt; Cette fois, l&apos;émulateur démarrait, mais le bridge Bumble ne montrait aucun signe d&apos;une connexion gRPC entrante depuis l&apos;émulateur. Les logs de l&apos;émulateur ne révélaient pas d&apos;erreur de connexion évidente, mais le Bluetooth restait inutilisable. &lt;strong&gt;Encore une impasse.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Tentative n°3 : rétrograder l&apos;API + endpoint Netsim explicite (la bonne !)&lt;/h2&gt;
&lt;figure&gt;
  &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;&gt;
      &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;https://bdteo.com/static/cd1aa56ebd8947295c8b2ab1adab5b06/ac99c/featured-2.jpg&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;
    &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 66.45569620253164%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAQFAv/EABUBAQEAAAAAAAAAAAAAAAAAAAED/9oADAMBAAIQAxAAAAHDUViFaRAE/8QAGhABAAIDAQAAAAAAAAAAAAAAAgEDAAQREv/aAAgBAQABBQIvt+xPisWCQJ5LtlDP/8QAFhEBAQEAAAAAAAAAAAAAAAAAABJB/9oACAEDAQE/AcU//8QAFxEAAwEAAAAAAAAAAAAAAAAAAAESIf/aAAgBAgEBPwFrSD//xAAbEAADAAIDAAAAAAAAAAAAAAAAARECIhASIf/aAAgBAQAGPwJR6mvgu2SpSRcf/8QAGhABAAIDAQAAAAAAAAAAAAAAAQAhEBFBUf/aAAgBAQABPyEyA8RwUWv2PBtb0xCHI4dncf/aAAwDAQACAAMAAAAQHz//xAAWEQADAAAAAAAAAAAAAAAAAAABEBH/2gAIAQMBAT8QER//xAAYEQACAwAAAAAAAAAAAAAAAAAAEQExUf/aAAgBAgEBPxCRVQun/8QAGxABAAMBAQEBAAAAAAAAAAAAAQARITFBYfD/2gAIAQEAAT8QdIcFUc997FbSRXx8nEVD8bKk7V0lkDiK2DcYuuE//9k=&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Pont symbolique entre les plateformes Apple et Android&quot; title=&quot;&quot; src=&quot;https://bdteo.com/static/cd1aa56ebd8947295c8b2ab1adab5b06/828fb/featured-2.jpg&quot; srcset=&quot;https://bdteo.com/static/cd1aa56ebd8947295c8b2ab1adab5b06/ff44c/featured-2.jpg 158w,
https://bdteo.com/static/cd1aa56ebd8947295c8b2ab1adab5b06/a6688/featured-2.jpg 315w,
https://bdteo.com/static/cd1aa56ebd8947295c8b2ab1adab5b06/828fb/featured-2.jpg 630w,
https://bdteo.com/static/cd1aa56ebd8947295c8b2ab1adab5b06/0ede0/featured-2.jpg 945w,
https://bdteo.com/static/cd1aa56ebd8947295c8b2ab1adab5b06/3ac88/featured-2.jpg 1260w,
https://bdteo.com/static/cd1aa56ebd8947295c8b2ab1adab5b06/ac99c/featured-2.jpg 1536w&quot; sizes=&quot;(max-width: 630px) 100vw, 630px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;
  &lt;/a&gt;
    &lt;/span&gt;
  &lt;figcaption&gt;
    Fig1. – Un paysage surréaliste où des câbles réseau ratés pendent entre des formations rocheuses Apple et Android, tandis qu&apos;un seul pont de corde marqué Bumble relie les deux avec succès et laisse passer des paquets de données lumineux.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Des recherches web ont révélé des signalements d&apos;instabilité générale avec le Bluetooth sur les émulateurs API 33/34 et de possibles problèmes dans la manière dont l&apos;émulateur découvre ou se connecte au backend Netsim, surtout quand un outil externe tente de l&apos;intercepter. La clé semblait être de &lt;strong&gt;dire explicitement à l&apos;émulateur où se trouvait le serveur gRPC Netsim&lt;/strong&gt; et d&apos;&lt;strong&gt;essayer un niveau d&apos;API plus ancien&lt;/strong&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Démarrer le bridge (mode contrôleur Netsim, port explicite, &lt;code class=&quot;language-text&quot;&gt;usb:0&lt;/code&gt;) :&lt;/strong&gt; Comme dans la tentative n°2, en veillant à ce qu&apos;il écoute sur un port connu (&lt;code class=&quot;language-text&quot;&gt;8554&lt;/code&gt;) et se connecte au dongle physique avec l&apos;index (&lt;code class=&quot;language-text&quot;&gt;usb:0&lt;/code&gt;) qui fonctionnait de manière fiable.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# In Terminal 1: (Keep this running!)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; python3 &lt;span class=&quot;token parameter variable&quot;&gt;-m&lt;/span&gt; bumble.apps.hci_bridge android-netsim:_:8554,mode&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;controller usb:0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Modifier et lancer l&apos;émulateur (API 32, endpoint explicite) :&lt;/strong&gt; Nous avons créé un AVD &lt;strong&gt;API 32 (Android 12L)&lt;/strong&gt; avec Google Play Services (&lt;code class=&quot;language-text&quot;&gt;gplay_32_arm&lt;/code&gt;). Nous avons modifié le script de lancement pour cibler cet AVD et, point crucial, remplacé le flag &lt;code class=&quot;language-text&quot;&gt;-packet-streamer-endpoint&lt;/code&gt; de &lt;code class=&quot;language-text&quot;&gt;default&lt;/code&gt; par l&apos;adresse &lt;em&gt;exacte&lt;/em&gt; de notre bridge.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Snippet from the *successful* launch script (see full script below):&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;API_LEVEL&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;32&quot;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;AVD_NAME&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;gplay_&lt;span class=&quot;token variable&quot;&gt;${API_LEVEL}&lt;/span&gt;_arm&quot;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;SYSTEM_IMAGE_PKG&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;system-images;android-&lt;span class=&quot;token variable&quot;&gt;${API_LEVEL}&lt;/span&gt;;&lt;span class=&quot;token variable&quot;&gt;${IMAGE_TAG}&lt;/span&gt;;&lt;span class=&quot;token variable&quot;&gt;${ARCH}&lt;/span&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;BUMBLE_NETSIM_GRPC_PORT&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;8554&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
emulator &lt;span class=&quot;token parameter variable&quot;&gt;-avd&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$AVD_NAME&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;. &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
    -packet-streamer-endpoint &lt;span class=&quot;token string&quot;&gt;&quot;localhost:&lt;span class=&quot;token variable&quot;&gt;$BUMBLE_NETSIM_GRPC_PORT&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Le résultat ? Réussite !&lt;/strong&gt; Cette fois, ça a marché !&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Le terminal &lt;code class=&quot;language-text&quot;&gt;bumble-hci-bridge&lt;/code&gt; a commencé à afficher des logs de connexion gRPC depuis l&apos;émulateur peu après le lancement.&lt;/li&gt;
&lt;li&gt;Une fois l&apos;émulateur démarré, activer le Bluetooth dans les réglages Android a provoqué une avalanche de commandes HCI (Reset, Read Version, Set Event Mask, etc.) dans le terminal du bridge.&lt;/li&gt;
&lt;li&gt;La recherche d&apos;appareils depuis l&apos;émulateur utilisait bien la radio Bluetooth physique du Mac via le dongle ASUS.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;La recette gagnante : pas à pas&lt;/h2&gt;
&lt;p&gt;Voici la procédure exacte qui a fonctionné sur mon Mac Pro M1 avec un dongle externe ASUS USB-BT500 :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Installer Bumble :&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;python3 &lt;span class=&quot;token parameter variable&quot;&gt;-m&lt;/span&gt; pip &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; bumble
&lt;span class=&quot;token comment&quot;&gt;# Potentially install libusb if needed: brew install libusb&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;(Optionnel mais recommandé) Désactiver la prise en charge USB BT native de macOS :&lt;/strong&gt; À exécuter &lt;em&gt;une seule fois&lt;/em&gt;, puis &lt;strong&gt;redémarrer&lt;/strong&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; nvram &lt;span class=&quot;token assign-left variable&quot;&gt;bluetoothHostControllerSwitchBehavior&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;never&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Démarrer le bridge Bumble Netsim :&lt;/strong&gt; Ouvrez un terminal et lancez (gardez-le ouvert) :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; python3 &lt;span class=&quot;token parameter variable&quot;&gt;-m&lt;/span&gt; bumble.apps.hci_bridge android-netsim:_:8554,mode&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;controller usb:0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;(Vérifiez qu&apos;il affiche &lt;code class=&quot;language-text&quot;&gt;&gt;&gt;&gt; connected&lt;/code&gt; deux fois.)&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Préparer le script de lancement de l&apos;émulateur :&lt;/strong&gt; Enregistrez le &lt;em&gt;script complet&lt;/em&gt; fourni ci-dessous sous le nom &lt;code class=&quot;language-text&quot;&gt;launch_gapps_avd_api32.sh&lt;/code&gt; (ou similaire). Assurez-vous qu&apos;il cible un AVD &lt;strong&gt;API 32&lt;/strong&gt; (il en créera un nommé &lt;code class=&quot;language-text&quot;&gt;gplay_32_arm&lt;/code&gt; s&apos;il n&apos;existe pas) et qu&apos;il utilise explicitement &lt;code class=&quot;language-text&quot;&gt;-packet-streamer-endpoint localhost:8554&lt;/code&gt;. Rendez-le exécutable (&lt;code class=&quot;language-text&quot;&gt;chmod +x launch_gapps_avd_api32.sh&lt;/code&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Lancer le script :&lt;/strong&gt; Ouvrez un &lt;em&gt;nouveau&lt;/em&gt; terminal et exécutez le script :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;./launch_gapps_avd_api32.sh&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Vérifier :&lt;/strong&gt; Une fois l&apos;émulateur démarré :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Consultez le terminal &lt;code class=&quot;language-text&quot;&gt;bumble-hci-bridge&lt;/code&gt; pour voir le trafic gRPC et HCI.&lt;/li&gt;
&lt;li&gt;Allez dans Android Settings -&gt; Bluetooth et activez-le.&lt;/li&gt;
&lt;li&gt;Essayez de scanner ou d&apos;appairer un appareil.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Le script de lancement qui a réussi (API 32, endpoint Netsim explicite)&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token shebang important&quot;&gt;#!/bin/bash&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Script to setup and launch a Google Play Android emulator (API 32) on macOS M1/ARM64&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Uses explicit Netsim endpoint for Bumble bridge compatibility.&lt;/span&gt;

&lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Exit immediately if a command exits with a non-zero status.&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# --- Configuration ---&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;API_LEVEL&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;32&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Target Android API Level (Android 12L)&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;AVD_NAME&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;gplay_&lt;span class=&quot;token variable&quot;&gt;${API_LEVEL}&lt;/span&gt;_arm&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Name for the Android Virtual Device&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;IMAGE_TAG&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;google_apis_playstore&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Image type with Google Play Store&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;ARCH&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;arm64-v8a&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Architecture for Apple Silicon&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;SYSTEM_IMAGE_PKG&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;system-images;android-&lt;span class=&quot;token variable&quot;&gt;${API_LEVEL}&lt;/span&gt;;&lt;span class=&quot;token variable&quot;&gt;${IMAGE_TAG}&lt;/span&gt;;&lt;span class=&quot;token variable&quot;&gt;${ARCH}&lt;/span&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;DEVICE_DEF&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;pixel_6a&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# A common modern Pixel device definition&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;BUMBLE_NETSIM_GRPC_PORT&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;8554&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Port where bumble-hci-bridge Netsim controller is listening&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# --- Find Android SDK ---&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;ANDROID_SDK_ROOT&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;${ANDROID_HOME&lt;span class=&quot;token operator&quot;&gt;:-&lt;/span&gt;${ANDROID_SDK_ROOT&lt;span class=&quot;token operator&quot;&gt;:-&lt;/span&gt;$HOME&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;Library&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;Android&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;sdk}&lt;/span&gt;}&quot;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$ANDROID_SDK_ROOT&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;❌ Error: Android SDK not found at &apos;&lt;span class=&quot;token variable&quot;&gt;$ANDROID_SDK_ROOT&lt;/span&gt;&apos;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;2&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;   Please install Android Studio or set ANDROID_HOME/ANDROID_SDK_ROOT.&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;2&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;▶️ Using Android SDK at: &lt;span class=&quot;token variable&quot;&gt;$ANDROID_SDK_ROOT&lt;/span&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# --- Define Tool Paths ---&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;CMDLINE_TOOLS_BIN&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$ANDROID_SDK_ROOT&lt;/span&gt;/cmdline-tools/latest/bin&quot;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;PLATFORM_TOOLS_DIR&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$ANDROID_SDK_ROOT&lt;/span&gt;/platform-tools&quot;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;EMULATOR_DIR&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$ANDROID_SDK_ROOT&lt;/span&gt;/emulator&quot;&lt;/span&gt;

&lt;span class=&quot;token assign-left variable&quot;&gt;SDKMANAGER&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$CMDLINE_TOOLS_BIN&lt;/span&gt;/sdkmanager&quot;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;AVDMANAGER&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$CMDLINE_TOOLS_BIN&lt;/span&gt;/avdmanager&quot;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;EMULATOR&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$EMULATOR_DIR&lt;/span&gt;/emulator&quot;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;ADB&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$PLATFORM_TOOLS_DIR&lt;/span&gt;/adb&quot;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# --- Check Essential Tools ---&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;command&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-v&lt;/span&gt; sdkmanager &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&gt;&lt;/span&gt; /dev/null&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;❌ Error: sdkmanager not found. Check SDK Command-line Tools installation and PATH.&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;command&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-v&lt;/span&gt; avdmanager &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&gt;&lt;/span&gt; /dev/null&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;❌ Error: avdmanager not found. Check SDK Command-line Tools installation and PATH.&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;command&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-v&lt;/span&gt; emulator &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&gt;&lt;/span&gt; /dev/null&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;❌ Error: emulator not found. Check Android Emulator installation and PATH.&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;command&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-v&lt;/span&gt; adb &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&gt;&lt;/span&gt; /dev/null&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;❌ Error: adb not found. Check SDK Platform-Tools installation and PATH.&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;✅ Basic SDK tools found in PATH.&quot;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# --- Stop Currently Running Emulators ---&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;▶️ Stopping any currently running emulators...&quot;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;RUNNING_EMULATORS&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;adb devices &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;emulator-&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;cut&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-f1&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$RUNNING_EMULATORS&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token for-or-select variable&quot;&gt;emu_id&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$RUNNING_EMULATORS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;
        &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;   Stopping &lt;span class=&quot;token variable&quot;&gt;$emu_id&lt;/span&gt;...&quot;&lt;/span&gt;
        adb &lt;span class=&quot;token parameter variable&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$emu_id&lt;/span&gt;&quot;&lt;/span&gt; emu &lt;span class=&quot;token function&quot;&gt;kill&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;   (Failed to kill &lt;span class=&quot;token variable&quot;&gt;$emu_id&lt;/span&gt;, may already be stopped)&quot;&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;sleep&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Small delay&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;done&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# Give ADB server time to recognize disconnection&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;sleep&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;   Emulators stopped.&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;   No emulators appear to be running according to &apos;adb devices&apos;.&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;✅ Emulator check/stop finished.&quot;&lt;/span&gt;


&lt;span class=&quot;token comment&quot;&gt;# --- Install/Update Required SDK Packages ---&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;▶️ Ensuring required SDK packages are installed...&quot;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Accept licenses non-interactively&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;yes&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; sdkmanager &lt;span class=&quot;token parameter variable&quot;&gt;--licenses&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; /dev/null &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;   (Ignoring potential license script errors)&quot;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Install platform-tools, emulator&lt;/span&gt;
sdkmanager &lt;span class=&quot;token string&quot;&gt;&quot;platform-tools&quot;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;emulator&quot;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Install the Google Play system image for API 32&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;▶️ Attempting to install/update Google Play system image: &lt;span class=&quot;token variable&quot;&gt;$SYSTEM_IMAGE_PKG&lt;/span&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; sdkmanager &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$SYSTEM_IMAGE_PKG&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;❌ Error: Failed to install required system image &apos;&lt;span class=&quot;token variable&quot;&gt;$SYSTEM_IMAGE_PKG&lt;/span&gt;&apos;.&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;2&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;   Please check available images using: sdkmanager --list | grep &apos;system-images;android-&lt;span class=&quot;token variable&quot;&gt;${API_LEVEL}&lt;/span&gt;;.*&lt;span class=&quot;token variable&quot;&gt;${ARCH}&lt;/span&gt;&apos;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;2&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;✅ System image package &apos;&lt;span class=&quot;token variable&quot;&gt;$SYSTEM_IMAGE_PKG&lt;/span&gt;&apos; present.&quot;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# --- Check if AVD Exists, Create ONLY if Missing ---&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;▶️ Ensuring AVD &apos;&lt;span class=&quot;token variable&quot;&gt;$AVD_NAME&lt;/span&gt;&apos; exists...&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; avdmanager list avd &lt;span class=&quot;token parameter variable&quot;&gt;--compact&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-q&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;^&lt;span class=&quot;token variable&quot;&gt;${AVD_NAME}&lt;/span&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;   AVD &apos;&lt;span class=&quot;token variable&quot;&gt;$AVD_NAME&lt;/span&gt;&apos; not found. Creating...&quot;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# Using &apos;echo no&apos; usually prevents hardware profile creation prompts. Pipe empty string for potential licenses.&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; avdmanager create avd &lt;span class=&quot;token parameter variable&quot;&gt;--force&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--name&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$AVD_NAME&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--package&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$SYSTEM_IMAGE_PKG&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--device&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$DEVICE_DEF&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--sdcard&lt;/span&gt; 512M &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
         &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;❌ Error: Failed to create AVD &apos;&lt;span class=&quot;token variable&quot;&gt;$AVD_NAME&lt;/span&gt;&apos;.&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;2&lt;/span&gt;
         &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;   Maybe the device definition &apos;&lt;span class=&quot;token variable&quot;&gt;$DEVICE_DEF&lt;/span&gt;&apos; is invalid for this image?&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;2&lt;/span&gt;
         &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;   Check available devices: avdmanager list device&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;2&lt;/span&gt;
         &lt;span class=&quot;token builtin class-name&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;✅ AVD &apos;&lt;span class=&quot;token variable&quot;&gt;$AVD_NAME&lt;/span&gt;&apos; created.&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;✅ AVD &apos;&lt;span class=&quot;token variable&quot;&gt;$AVD_NAME&lt;/span&gt;&apos; already exists. Will reuse.&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;


&lt;span class=&quot;token comment&quot;&gt;# --- Launch Emulator ---&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;▶️ Launching existing/new emulator: &apos;&lt;span class=&quot;token variable&quot;&gt;$AVD_NAME&lt;/span&gt;&apos; (pointing to Bumble Netsim bridge on localhost:&lt;span class=&quot;token variable&quot;&gt;$BUMBLE_NETSIM_GRPC_PORT&lt;/span&gt;)...&quot;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;EMULATOR_LOG&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;emulator-&lt;span class=&quot;token variable&quot;&gt;$AVD_NAME&lt;/span&gt;.log&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Log file name updated for API 32 AVD&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Google Play images often need a writable system partition&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Explicitly point packet streamer to localhost:8554 where bridge is listening&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;nohup&lt;/span&gt; emulator &lt;span class=&quot;token parameter variable&quot;&gt;-avd&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$AVD_NAME&lt;/span&gt;&quot;&lt;/span&gt; -no-snapshot-load &lt;span class=&quot;token parameter variable&quot;&gt;-gpu&lt;/span&gt; auto -show-kernel -writable-system &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
    -packet-streamer-endpoint &lt;span class=&quot;token string&quot;&gt;&quot;localhost:&lt;span class=&quot;token variable&quot;&gt;$BUMBLE_NETSIM_GRPC_PORT&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$EMULATOR_LOG&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;2&lt;/span&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;EMULATOR_PID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$!&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;   Emulator starting in background (PID: &lt;span class=&quot;token variable&quot;&gt;$EMULATOR_PID&lt;/span&gt;). Log: &lt;span class=&quot;token variable&quot;&gt;$EMULATOR_LOG&lt;/span&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;   Waiting for emulator to appear in ADB...&quot;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Wait for the emulator device to show up in adb&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;WAIT_ADB_TIMEOUT&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;90&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Increase timeout slightly for GPlay images&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;&lt;span class=&quot;token environment constant&quot;&gt;SECONDS&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;EMULATOR_ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Reset variable&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token environment constant&quot;&gt;$SECONDS&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-lt&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$WAIT_ADB_TIMEOUT&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# Find the *new* emulator ID&lt;/span&gt;
    &lt;span class=&quot;token assign-left variable&quot;&gt;CURRENT_EMU_ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;adb devices &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;emulator-&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;head&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;cut&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-f1&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$CURRENT_EMU_ID&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
        &lt;span class=&quot;token assign-left variable&quot;&gt;EMULATOR_ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$CURRENT_EMU_ID&lt;/span&gt;&quot;&lt;/span&gt;
        &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot; Found (&lt;span class=&quot;token variable&quot;&gt;$EMULATOR_ID&lt;/span&gt;)!&quot;&lt;/span&gt;
        &lt;span class=&quot;token builtin class-name&quot;&gt;break&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;sleep&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;
    &lt;span class=&quot;token assign-left variable&quot;&gt;&lt;span class=&quot;token environment constant&quot;&gt;SECONDS&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$((&lt;/span&gt;SECONDS &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;.&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;done&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-z&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$EMULATOR_ID&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;❌ Error: Emulator did not appear in ADB within &lt;span class=&quot;token variable&quot;&gt;$WAIT_ADB_TIMEOUT&lt;/span&gt; seconds.&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;2&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;   Check logs: &lt;span class=&quot;token variable&quot;&gt;$EMULATOR_LOG&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;2&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# Try to kill the process if it&apos;s still lingering&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;kill&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$EMULATOR_PID&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;2&lt;/span&gt;&gt;&lt;/span&gt;/dev/null &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;   (Emulator process &lt;span class=&quot;token variable&quot;&gt;$EMULATOR_PID&lt;/span&gt; may have already exited)&quot;&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;


&lt;span class=&quot;token comment&quot;&gt;# --- Wait for Boot Completion ---&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;▶️ Waiting for Android system to fully boot (Google Play images can take longer)...&quot;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;BOOT_TIMEOUT&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;240&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Increase timeout significantly for GPlay images&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;&lt;span class=&quot;token environment constant&quot;&gt;SECONDS&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token environment constant&quot;&gt;$SECONDS&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-lt&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$BOOT_TIMEOUT&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# Check if device is online first&lt;/span&gt;
    &lt;span class=&quot;token assign-left variable&quot;&gt;DEVICE_STATE&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;adb &lt;span class=&quot;token parameter variable&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$EMULATOR_ID&lt;/span&gt;&quot;&lt;/span&gt; get-state &lt;span class=&quot;token operator&quot;&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;2&lt;/span&gt;&gt;&lt;/span&gt;/dev/null&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$DEVICE_STATE&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;device&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
       &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;s(&lt;span class=&quot;token variable&quot;&gt;$DEVICE_STATE&lt;/span&gt;)&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# State not ready&lt;/span&gt;
       &lt;span class=&quot;token function&quot;&gt;sleep&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;
       &lt;span class=&quot;token assign-left variable&quot;&gt;&lt;span class=&quot;token environment constant&quot;&gt;SECONDS&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$((&lt;/span&gt;SECONDS &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
       &lt;span class=&quot;token builtin class-name&quot;&gt;continue&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;# Check boot completed property&lt;/span&gt;
    &lt;span class=&quot;token assign-left variable&quot;&gt;BOOT_COMPLETED&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;adb &lt;span class=&quot;token parameter variable&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$EMULATOR_ID&lt;/span&gt;&quot;&lt;/span&gt; shell getprop sys.boot_completed &lt;span class=&quot;token operator&quot;&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;2&lt;/span&gt;&gt;&lt;/span&gt;/dev/null &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;tr&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;
&apos;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$BOOT_COMPLETED&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;# Double check package manager is ready too for GPlay images&lt;/span&gt;
        &lt;span class=&quot;token assign-left variable&quot;&gt;PM_READY&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;adb &lt;span class=&quot;token parameter variable&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$EMULATOR_ID&lt;/span&gt;&quot;&lt;/span&gt; shell pm get-install-location &lt;span class=&quot;token operator&quot;&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;2&lt;/span&gt;&gt;&lt;/span&gt;/dev/null&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$PM_READY&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; *&lt;span class=&quot;token string&quot;&gt;&quot;0&quot;&lt;/span&gt;* &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$PM_READY&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; *&lt;span class=&quot;token string&quot;&gt;&quot;1&quot;&lt;/span&gt;* &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$PM_READY&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; *&lt;span class=&quot;token string&quot;&gt;&quot;2&quot;&lt;/span&gt;* &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Check if pm command gives valid output&lt;/span&gt;
            &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot; Booted!&quot;&lt;/span&gt;
            &lt;span class=&quot;token builtin class-name&quot;&gt;break&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
            &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;p(pm not ready)&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Package Manager not ready&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
         &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;b(booting)&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Boot not completed&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;sleep&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;
    &lt;span class=&quot;token assign-left variable&quot;&gt;&lt;span class=&quot;token environment constant&quot;&gt;SECONDS&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$((&lt;/span&gt;SECONDS &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;done&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token environment constant&quot;&gt;$SECONDS&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-ge&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$BOOT_TIMEOUT&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;❌ Error: Android system did not fully boot within &lt;span class=&quot;token variable&quot;&gt;$BOOT_TIMEOUT&lt;/span&gt; seconds.&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;2&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;   Emulator might be stuck. Check logs (&lt;span class=&quot;token variable&quot;&gt;$EMULATOR_LOG&lt;/span&gt;) or try launching manually.&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;2&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# Don&apos;t exit here, user might want to interact with stuck emulator&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;


&lt;span class=&quot;token comment&quot;&gt;# --- Final Instructions ---&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;---&quot;&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;✅ Google Play Emulator &apos;&lt;span class=&quot;token variable&quot;&gt;$AVD_NAME&lt;/span&gt;&apos; (API &lt;span class=&quot;token variable&quot;&gt;$API_LEVEL&lt;/span&gt;) (&lt;span class=&quot;token variable&quot;&gt;$EMULATOR_ID&lt;/span&gt;) should be running.&quot;&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;   Bluetooth should be using the Netsim backend at localhost:&lt;span class=&quot;token variable&quot;&gt;$BUMBLE_NETSIM_GRPC_PORT&lt;/span&gt; (intercepted by Bumble).&quot;&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;   Connect shell:   adb -s &lt;span class=&quot;token variable&quot;&gt;$EMULATOR_ID&lt;/span&gt; shell&quot;&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;   Install APK:     adb -s &lt;span class=&quot;token variable&quot;&gt;$EMULATOR_ID&lt;/span&gt; install /path/to/your.apk&quot;&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;   Stop emulator:   adb -s &lt;span class=&quot;token variable&quot;&gt;$EMULATOR_ID&lt;/span&gt; emu kill&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$EMULATOR_PID&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Only show PID if we launched it&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;   Kill Process:    kill &lt;span class=&quot;token variable&quot;&gt;$EMULATOR_PID&lt;/span&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;   NOTE: Google Play Services may need updates inside the emulator.&quot;&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;---&quot;&lt;/span&gt;

&lt;span class=&quot;token builtin class-name&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Points clés pour Mac M1 + émulateur + Bumble&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Le niveau d&apos;API compte :&lt;/strong&gt; Plus récent ne veut pas toujours dire meilleur pour la compatibilité de l&apos;émulateur, surtout avec des fonctions complexes comme le bridge Bluetooth. L&apos;API 32 semblait plus stable pour cela que l&apos;API 34 dans mes tests.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Endpoints explicites :&lt;/strong&gt; Ne comptez pas sur &lt;code class=&quot;language-text&quot;&gt;-packet-streamer-endpoint default&lt;/code&gt; quand vous utilisez un bridge externe comme le mode contrôleur Netsim de Bumble. Pointez explicitement l&apos;émulateur vers &lt;code class=&quot;language-text&quot;&gt;localhost:&amp;lt;port&gt;&lt;/code&gt;, là où votre bridge écoute.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bridge Netsim &gt; socket QEMU :&lt;/strong&gt; Le mode bridge &lt;code class=&quot;language-text&quot;&gt;android-netsim&lt;/code&gt; semble plus susceptible de fonctionner avec les émulateurs modernes que la méthode plus bas niveau &lt;code class=&quot;language-text&quot;&gt;-qemu -chardev socket&lt;/code&gt;, même si la méthode socket &lt;em&gt;peut&lt;/em&gt; établir un lien TCP.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;usb:0&lt;/code&gt; vs VID:PID :&lt;/strong&gt; Sur macOS/M1, l&apos;identification des périphériques USB peut être capricieuse. Si spécifier le VID:PID exact échoue sans raison apparente, essayez d&apos;utiliser l&apos;index &lt;code class=&quot;language-text&quot;&gt;usb:0&lt;/code&gt; (en supposant que ce soit le périphérique principal/prévu).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;La persévérance paie :&lt;/strong&gt; Il a fallu plusieurs tentatives, en combinant des indices venus de la documentation, de recherches web et de tests itératifs. Ne lâchez pas trop vite !&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;J&apos;espère que ce partage de configuration précise et fonctionnelle fera gagner des heures de frustration à d&apos;autres développeurs. Bon code (et bon bridging) !&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Refonte de blog par IA : comment Claude Code a transformé mon site Gatsby]]></title><description><![CDATA[En tant que développeur qui passe la majeure partie de son temps côté backend, j'ai toujours eu du mal avec le design. Mon blog personnel…]]></description><link>https://bdteo.com/fr/claude-code-transformed-my-blog-design-in-minutes/</link><guid isPermaLink="false">https://bdteo.com/fr/claude-code-transformed-my-blog-design-in-minutes/</guid><pubDate>Sat, 12 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;En tant que développeur qui passe la majeure partie de son temps côté backend, j&apos;ai toujours eu du mal avec le design. Mon blog personnel était fonctionnel, mais il semblait coincé en 2010 : styles basiques, espacements incohérents, et une palette de couleurs que l&apos;on pouvait généreusement qualifier de &quot;minimale&quot;. Je voulais le refaire depuis des mois, mais l&apos;idée de replonger dans le CSS et les systèmes de design me donnait des cauchemars.&lt;/p&gt;
&lt;p&gt;Puis j&apos;ai essayé Claude Code, l&apos;assistant de programmation IA d&apos;Anthropic, et ma perspective a complètement changé.&lt;/p&gt;
&lt;h2&gt;Le défi : mon blog triste et dépassé&lt;/h2&gt;
&lt;p&gt;Mon blog est construit avec Gatsby.js, et même si le contenu tenait la route, la présentation le desservait :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Espacement incohérent sur tout le site&lt;/li&gt;
&lt;li&gt;Mauvais comportement responsive sur mobile&lt;/li&gt;
&lt;li&gt;Une implémentation du mode sombre qui fonctionnait à peine&lt;/li&gt;
&lt;li&gt;Une typographie qui faisait amateur, au mieux&lt;/li&gt;
&lt;li&gt;Aucun système de couleurs ni langage de design cohérent&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;J&apos;avais une idée vague de ce que je voulais : quelque chose de moderne, propre et professionnel, qui fasse briller mon contenu. Mais traduire cette vision en CSS réel ? C&apos;est là que je bloque habituellement.&lt;/p&gt;
&lt;h2&gt;Entre en scène Claude Code : mon partenaire de design virtuel&lt;/h2&gt;
&lt;p&gt;J&apos;entendais parler de Claude Code depuis un moment, mais j&apos;étais sceptique sur l&apos;aide réelle qu&apos;une IA pouvait apporter au travail de design. Après tout, le design demande du goût et un oeil esthétique, pas seulement des connaissances techniques.&lt;/p&gt;
&lt;p&gt;J&apos;ai quand même décidé d&apos;essayer, avec un prompt simple : &quot;Please improve my styling by a ton.&quot; J&apos;étais prêt à investir une heure de mon temps et les 15 dollars environ que cela coûterait en utilisation d&apos;API pour voir ce qui était possible.&lt;/p&gt;
&lt;p&gt;Ce qui s&apos;est passé ensuite m&apos;a sincèrement surpris.&lt;/p&gt;
&lt;h2&gt;Le processus de transformation&lt;/h2&gt;
&lt;p&gt;Au lieu de simplement suggérer quelques retouches CSS, Claude Code a abordé la refonte avec la rigueur d&apos;un développeur professionnel :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Phase d&apos;analyse&lt;/strong&gt; : D&apos;abord, il a examiné mes styles existants, l&apos;architecture de mes composants et ma palette de couleurs pour comprendre avec quoi il travaillait.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Création d&apos;un système de design&lt;/strong&gt; : Plutôt que de faire des changements superficiels, il a construit un système de design complet comprenant :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Une palette de couleurs professionnelle avec des variables sémantiques pour les modes clair et sombre&lt;/li&gt;
&lt;li&gt;Une échelle typographique modernisée, avec de vrais ajustements responsives&lt;/li&gt;
&lt;li&gt;Un système d&apos;espacement cohérent basé sur une grille de 4 px&lt;/li&gt;
&lt;li&gt;Des ombres, rayons de bordure et transitions standardisés&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Refonte des composants&lt;/strong&gt; : Il a réécrit mes composants pour suivre les bonnes pratiques modernes :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Création d&apos;une mise en page responsive des articles, basée sur des cartes&lt;/li&gt;
&lt;li&gt;Implémentation d&apos;un en-tête sticky avec effets de flou d&apos;arrière-plan&lt;/li&gt;
&lt;li&gt;Design d&apos;un sélecteur de thème élégant&lt;/li&gt;
&lt;li&gt;Ajout d&apos;une navigation propre avec états actifs&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Améliorations d&apos;accessibilité&lt;/strong&gt; : Claude Code ne s&apos;est pas contenté de rendre les choses plus belles : il a rendu mon site plus accessible :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ajout d&apos;une vraie prise en charge de la navigation au clavier&lt;/li&gt;
&lt;li&gt;Implémentation d&apos;un lien d&apos;évitement vers le contenu&lt;/li&gt;
&lt;li&gt;Garantie d&apos;un contraste de couleurs suffisant&lt;/li&gt;
&lt;li&gt;Ajout de labels ARIA appropriés&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Optimisation des performances&lt;/strong&gt; : Le code a été optimisé pour les performances :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Import sélectif des seuls composants Bootstrap nécessaires&lt;/li&gt;
&lt;li&gt;Optimisation de l&apos;organisation du CSS&lt;/li&gt;
&lt;li&gt;Ajout d&apos;animations accélérées matériellement&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Les résultats renversants&lt;/h2&gt;
&lt;p&gt;La transformation était saisissante. En environ une heure d&apos;allers-retours et pour seulement 15 dollars de coûts d&apos;API Claude, mon blog est passé d&apos;un air de projet secondaire de développeur à celui d&apos;une publication conçue professionnellement. La refonte comprenait :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Un système de couleurs sophistiqué, avec une vraie prise en charge du mode sombre&lt;/li&gt;
&lt;li&gt;Une belle typographie qui s&apos;adapte parfaitement à tous les appareils&lt;/li&gt;
&lt;li&gt;Des mises en page professionnelles en cartes pour les articles&lt;/li&gt;
&lt;li&gt;Des animations et transitions fluides&lt;/li&gt;
&lt;li&gt;Un en-tête sticky avec des effets façon verre&lt;/li&gt;
&lt;li&gt;Un espacement cohérent sur tout le site&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pour mettre cela en perspective : engager un web designer professionnel pour ce niveau de travail coûterait probablement 1 000 à 2 000 dollars et prendrait des jours ou des semaines d&apos;allers-retours. À la place, j&apos;ai dépensé une heure de mon temps et 15 dollars en coûts d&apos;API.&lt;/p&gt;
&lt;p&gt;Mais ce qui m&apos;a le plus impressionné, ce n&apos;était pas seulement l&apos;amélioration visuelle ou les économies : c&apos;était la qualité du code. Claude Code n&apos;a pas assemblé quelques hacks CSS ; il a créé un système de design complet et maintenable, sur lequel je peux facilement construire.&lt;/p&gt;
&lt;h2&gt;Ce que j&apos;ai appris sur le design assisté par IA&lt;/h2&gt;
&lt;p&gt;Cette expérience a changé ma perspective sur ce que l&apos;IA peut faire pour les développeurs :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;L&apos;IA excelle dans la pensée systémique&lt;/strong&gt; : Claude Code ne s&apos;est pas contenté de rendre les choses &quot;plus jolies&quot; : il a créé un système cohérent, avec des variables, des motifs constants et une organisation propre.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Elle comble les lacunes de connaissance&lt;/strong&gt; : En tant que personne sans expertise profonde en CSS, Claude Code a rempli mes lacunes avec des bonnes pratiques modernes que je n&apos;aurais pas pensé à implémenter.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Le code est de qualité production&lt;/strong&gt; : Le framework de styles qu&apos;il a créé n&apos;est pas là pour faire joli : c&apos;est du code maintenable et extensible, qui respecte les standards modernes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Il a préservé mon contenu et ma structure&lt;/strong&gt; : Claude Code a amélioré le design tout en conservant la structure centrale et le contenu de mon blog.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;L&apos;avenir de la collaboration développeur-IA&lt;/h2&gt;
&lt;p&gt;Mon expérience a mis en lumière quelque chose d&apos;important : Claude Code ne remplace pas les développeurs, il augmente nos capacités. Il m&apos;a aidé à dépasser mes limites personnelles en design frontend, tout en me laissant le contrôle de la direction générale.&lt;/p&gt;
&lt;p&gt;Pour les développeurs qui sont plus forts dans certains domaines que dans d&apos;autres (et n&apos;est-ce pas notre cas à tous ?), les assistants IA comme Claude Code peuvent aider à combler les trous dans nos compétences.&lt;/p&gt;
&lt;h2&gt;Essayez-le vous-même&lt;/h2&gt;
&lt;p&gt;Si vous avez du mal avec le design ou avec tout autre aspect du développement, je vous recommande vivement d&apos;essayer Claude Code. L&apos;expérience a changé non seulement mon blog, mais aussi ma perception de ce qui est possible avec le développement assisté par IA.&lt;/p&gt;
&lt;p&gt;Avez-vous essayé Claude Code ou des assistants de programmation IA similaires ? J&apos;aimerais beaucoup lire vos expériences dans les commentaires ci-dessous.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note : Ce blog lui-même a été redesigné avec Claude Code, donc ce que vous voyez maintenant est le résultat du processus que je viens de décrire. La transformation depuis mon ancien design jusqu&apos;à celui-ci a pris environ une heure de prompts et d&apos;itérations, et a coûté environ 15 dollars d&apos;utilisation de l&apos;API Claude. Quand on considère les coûts habituels du design, c&apos;est une proposition de valeur incroyable.&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Appairage BLE de la Huawei Watch D2 : protocole et verrouillage fournisseur]]></title><description><![CDATA[TL;DR : La Huawei Watch D2 n'utilise pas l'appairage BLE standard. Elle exige à la place une poignée de main propriétaire en 11 étapes, avec…]]></description><link>https://bdteo.com/fr/huawei-watch-d2-proprietary-protocol-vendor-lockin/</link><guid isPermaLink="false">https://bdteo.com/fr/huawei-watch-d2-proprietary-protocol-vendor-lockin/</guid><pubDate>Fri, 11 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR :&lt;/strong&gt; La Huawei Watch D2 n&apos;utilise pas l&apos;appairage BLE standard. Elle exige à la place une poignée de main propriétaire en 11 étapes, avec caractéristiques GATT sur mesure, dérivation de clé HMAC-SHA256 depuis un code QR, et chiffrement au niveau applicatif. C&apos;est du verrouillage fournisseur par conception : cela vous force à passer par l&apos;app Health de Huawei. La bonne nouvelle : la communauté l&apos;a rétro-ingénierée. Gadgetbridge prend maintenant en charge la Watch D2, et des implémentations open source comme &lt;code class=&quot;language-text&quot;&gt;huawei-lpv2&lt;/code&gt; existent. Le DMA européen commence lui aussi à pousser en sens inverse.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Je m&apos;attendais à un appairage Bluetooth standard. Connexion, association, échange de données : l&apos;ordinaire. À la place, j&apos;ai trouvé une poignée de main cryptographique propriétaire qui a demandé des semaines de rétro-ingénierie.&lt;/p&gt;
&lt;p&gt;C&apos;est arrivé pendant la construction de D2Explorer, mon projet pour connecter la Huawei Watch D2 à Linux et macOS sans l&apos;app Health de Huawei. Après avoir &lt;a href=&quot;../bluez-pairing-python-agent-workaround-authentication-failed/&quot;&gt;démêlé les problèmes d&apos;agent d&apos;appairage de BlueZ&lt;/a&gt; et migré vers la bibliothèque multiplateforme SimpleBLE, je pensais que le plus dur était derrière moi. Le plus dur n&apos;avait pas commencé.&lt;/p&gt;
&lt;h2&gt;Ce à quoi on s&apos;attend : l&apos;appairage BLE standard&lt;/h2&gt;
&lt;p&gt;Voici comment l&apos;appairage Bluetooth LE est &lt;em&gt;censé&lt;/em&gt; fonctionner :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Scanner l&apos;appareil par son nom annoncé (par exemple, &quot;HUAWEI WATCH D2-CA0&quot;).&lt;/li&gt;
&lt;li&gt;Se connecter avec &lt;code class=&quot;language-text&quot;&gt;peripheral.connect()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Le système d&apos;exploitation gère l&apos;appairage/l&apos;association : demande de PIN, Just Works, ou ce que le niveau de sécurité exige.&lt;/li&gt;
&lt;li&gt;Une fois l&apos;association établie, interagir avec les services GATT standard ou personnalisés.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Le système gère la sécurité. Votre application se concentre sur les données. Simple.&lt;/p&gt;
&lt;h2&gt;Ce qui se passe vraiment : une poignée de main propriétaire en 11 étapes&lt;/h2&gt;
&lt;p&gt;Ce que la Watch D2 exige réellement est tout autre. La connexion BLE de base n&apos;est que la porte. Derrière elle se trouve un protocole d&apos;authentification applicatif sur mesure que Huawei a construit par-dessus le BLE standard : ce que la communauté appelle &lt;strong&gt;Huawei Link Protocol v2&lt;/strong&gt; &lt;small&gt;&lt;a href=&quot;#ref1&quot;&gt;[1]&lt;/a&gt;&lt;/small&gt;.&lt;/p&gt;
&lt;p&gt;Les mécanismes d&apos;appairage BLE standard sont complètement contournés. Pour vous authentifier et accéder à la moindre donnée utile, vous devez parcourir cette séquence via des caractéristiques GATT personnalisées :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Connect&lt;/strong&gt; -- établir le lien BLE de base.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enable Notifications&lt;/strong&gt; -- s&apos;abonner &lt;em&gt;immédiatement&lt;/em&gt; aux notifications sur la caractéristique &lt;code class=&quot;language-text&quot;&gt;0000fe02-...&lt;/code&gt;. Le timing est critique : ratez la fenêtre et la montre vous déconnecte.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GetLinkParams&lt;/strong&gt; -- envoyer &lt;em&gt;immédiatement&lt;/em&gt; une commande personnalisée (Service ID &lt;code class=&quot;language-text&quot;&gt;0x0001&lt;/code&gt;, Command ID &lt;code class=&quot;language-text&quot;&gt;0x0001&lt;/code&gt;) à la caractéristique d&apos;écriture &lt;code class=&quot;language-text&quot;&gt;0000fe01-...&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Receive Server Nonce&lt;/strong&gt; -- attendre une notification contenant le défi aléatoire de la montre.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Derive Secret Key&lt;/strong&gt; -- générer un nonce client. Combiner le nonce serveur, le nonce client, et la &lt;strong&gt;valeur numérique du code QR affiché par la montre&lt;/strong&gt;. Exécuter HMAC-SHA256 (en utilisant les octets de la valeur du code QR comme clé) pour dériver une &lt;code class=&quot;language-text&quot;&gt;secretKey_&lt;/code&gt; partagée.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AuthRequest&lt;/strong&gt; -- renvoyer à la montre le nonce client et un digest HMAC (en utilisant la &lt;code class=&quot;language-text&quot;&gt;secretKey_&lt;/code&gt; dérivée) (Service &lt;code class=&quot;language-text&quot;&gt;0x0001&lt;/code&gt;, Command &lt;code class=&quot;language-text&quot;&gt;0x0002&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Verify Server Token&lt;/strong&gt; -- recevoir le jeton d&apos;authentification de la montre. Le vérifier avec la &lt;code class=&quot;language-text&quot;&gt;secretKey_&lt;/code&gt; et les nonces échangés.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SetTime&lt;/strong&gt; -- envoyer l&apos;heure actuelle et le décalage de fuseau horaire, &lt;em&gt;chiffrés&lt;/em&gt; avec la &lt;code class=&quot;language-text&quot;&gt;secretKey_&lt;/code&gt; (Service &lt;code class=&quot;language-text&quot;&gt;0x0002&lt;/code&gt;, Command &lt;code class=&quot;language-text&quot;&gt;0x0003&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;QrToken&lt;/strong&gt; -- renvoyer la valeur du code QR, &lt;em&gt;chiffrée&lt;/em&gt; avec la &lt;code class=&quot;language-text&quot;&gt;secretKey_&lt;/code&gt; (Service &lt;code class=&quot;language-text&quot;&gt;0x0001&lt;/code&gt;, Command &lt;code class=&quot;language-text&quot;&gt;0x0004&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AuthResult&lt;/strong&gt; -- envoyer une confirmation finale, &lt;em&gt;chiffrée&lt;/em&gt; avec la &lt;code class=&quot;language-text&quot;&gt;secretKey_&lt;/code&gt; (Service &lt;code class=&quot;language-text&quot;&gt;0x0001&lt;/code&gt;, Command &lt;code class=&quot;language-text&quot;&gt;0x0005&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Done&lt;/strong&gt; -- c&apos;est seulement maintenant que la connexion est authentifiée.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Formats de messages TLV personnalisés. Contrôles CRC. IDs de service et de commande. Chiffrement applicatif. Timing sensible à la milliseconde. Tout cela se passe &lt;em&gt;au-dessus&lt;/em&gt; de la pile BLE, invisible pour les outils Bluetooth standard.&lt;/p&gt;
&lt;p&gt;Le code QR sur l&apos;écran de la montre est le secret partagé. Sans lui, vous ne pouvez pas dériver la clé. Sans la clé, vous ne pouvez pas vous authentifier. Sans authentification, la montre ne vous donne rien.&lt;/p&gt;
&lt;h2&gt;Pourquoi Huawei fait cela&lt;/h2&gt;
&lt;p&gt;Huawei pourrait présenter cela comme une sécurité renforcée. L&apos;effet pratique est le &lt;strong&gt;verrouillage fournisseur&lt;/strong&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Barrière d&apos;entrée élevée&lt;/strong&gt; -- le protocole n&apos;est pas documenté. Le réimplémenter demande de rétro-ingénierer l&apos;app Huawei Health (plus de 13 000 classes, plus de 64 000 méthodes &lt;small&gt;&lt;a href=&quot;#ref2&quot;&gt;[2]&lt;/a&gt;&lt;/small&gt;) ou d&apos;analyser le trafic BLE. Cela décourage activement les applications tierces.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Aucune interopérabilité&lt;/strong&gt; -- les applications de fitness standard ne peuvent pas se connecter. La montre ne termine sa poignée de main qu&apos;avec un logiciel qui connaît les étapes propriétaires, principalement l&apos;app Health de Huawei.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Contrôle de l&apos;écosystème&lt;/strong&gt; -- les utilisateurs sont forcés d&apos;entrer dans Huawei Health et ses services cloud. Changer d&apos;appareil ou de plateforme plus tard signifie perdre l&apos;historique de vos données de santé.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Choix utilisateur réduit&lt;/strong&gt; -- vous voulez utiliser une app open source ? Vous voulez plus de contrôle sur la confidentialité de vos données de santé ? Dommage, à moins que quelqu&apos;un ne rétro-ingénie d&apos;abord le protocole.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Et voici le point important : &lt;strong&gt;ce n&apos;est pas propre à Huawei&lt;/strong&gt;. Le projet de recherche WatchWitch &lt;small&gt;&lt;a href=&quot;#ref3&quot;&gt;[3]&lt;/a&gt;&lt;/small&gt; documente comment tous les grands fournisseurs, Apple, Samsung, Xiaomi, utilisent des protocoles BLE propriétaires pour imposer le verrouillage d&apos;écosystème. L&apos;Apple Watch est &quot;incredibly tightly coupled with Apple&apos;s iPhone and iCloud ecosystem, using proprietary protocols that are unavailable to third parties.&quot; C&apos;est un problème systémique de l&apos;industrie.&lt;/p&gt;
&lt;p&gt;Mais l&apos;implémentation de Huawei est particulièrement agressive. BLE &lt;em&gt;permet&lt;/em&gt; des services personnalisés, bien sûr. Mais remplacer le mécanisme d&apos;authentification fondamental par un gardien propriétaire, c&apos;est un autre jeu.&lt;/p&gt;
&lt;h2&gt;L&apos;ironie de la sécurité&lt;/h2&gt;
&lt;p&gt;La défense évidente est : &quot;nous faisons cela pour la sécurité.&quot; Examinons-la.&lt;/p&gt;
&lt;p&gt;La recherche BlueDoor de l&apos;université Tsinghua &lt;small&gt;&lt;a href=&quot;#ref4&quot;&gt;[4]&lt;/a&gt;&lt;/small&gt; a testé 16 appareils BLE, dont le Honor Band 3 (même écosystème Huawei), et a réussi un &lt;strong&gt;appairage silencieux sans autorisation utilisateur&lt;/strong&gt; sur la plupart d&apos;entre eux. Le protocole propriétaire ne l&apos;a pas empêché.&lt;/p&gt;
&lt;p&gt;Pendant ce temps, le protocole lui-même a été rétro-ingénieré plusieurs fois : par la communauté Gadgetbridge, par le projet &lt;code class=&quot;language-text&quot;&gt;huawei-lpv2&lt;/code&gt;, par les chercheurs qui ont présenté leurs travaux à Easterhegg 2019 &lt;small&gt;&lt;a href=&quot;#ref2&quot;&gt;[2]&lt;/a&gt;&lt;/small&gt;, et par moi pour D2Explorer. De la sécurité par l&apos;obscurité, avec une date d&apos;expiration.&lt;/p&gt;
&lt;p&gt;La dérivation de clé HMAC-SHA256 depuis le code QR est en fait une cryptographie correcte. Mais ce n&apos;est pas le sujet. On pourrait obtenir les mêmes propriétés de sécurité avec BLE Secure Connections standard et une méthode d&apos;appairage hors bande (comme NFC ou un code QR), sans exclure toutes les applications tierces au passage.&lt;/p&gt;
&lt;h2&gt;La communauté riposte&lt;/h2&gt;
&lt;p&gt;La communauté n&apos;a pas accepté cela en silence.&lt;/p&gt;
&lt;h3&gt;Gadgetbridge&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://gadgetbridge.org/&quot;&gt;Gadgetbridge&lt;/a&gt;, l&apos;application Android open source pour objets connectés, prend maintenant en charge la Huawei Watch D2 &lt;small&gt;&lt;a href=&quot;#ref5&quot;&gt;[5]&lt;/a&gt;&lt;/small&gt;. Vous pouvez appairer votre montre sans l&apos;app Health de Huawei. Il a fallu un effort considérable de rétro-ingénierie (voir la PR #2462 &lt;small&gt;&lt;a href=&quot;#ref6&quot;&gt;[6]&lt;/a&gt;&lt;/small&gt;), et il reste des limites : la fonctionnalité ECG est désactivée lorsque la montre est appairée avec Gadgetbridge &lt;small&gt;&lt;a href=&quot;#ref7&quot;&gt;[7]&lt;/a&gt;&lt;/small&gt;. Mais cela fonctionne.&lt;/p&gt;
&lt;p&gt;L&apos;implémentation d&apos;authentification dans Gadgetbridge gère la version d&apos;auth 3, calcule la clé d&apos;association depuis le message d&apos;appairage (service &lt;code class=&quot;language-text&quot;&gt;0x01&lt;/code&gt;, commande &lt;code class=&quot;language-text&quot;&gt;0x0e&lt;/code&gt;) et l&apos;utilise pour le déchiffrement. Un identifiant de compte Huawei à 17 chiffres est nécessaire pour la négociation de la clé d&apos;authentification.&lt;/p&gt;
&lt;h3&gt;huawei-lpv2&lt;/h3&gt;
&lt;p&gt;Le projet &lt;a href=&quot;https://github.com/zyv/huawei-lpv2&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;huawei-lpv2&lt;/code&gt;&lt;/a&gt; fournit une implémentation pure Python du Huawei Link Protocol v2 &lt;small&gt;&lt;a href=&quot;#ref8&quot;&gt;[8]&lt;/a&gt;&lt;/small&gt;. Il est maintenu, possède plusieurs forks, et sert de référence à quiconque construit des intégrations avec des wearables Huawei en dehors de l&apos;écosystème officiel.&lt;/p&gt;
&lt;h3&gt;D2Explorer&lt;/h3&gt;
&lt;p&gt;Mon propre projet D2Explorer a pris une autre voie : construire une implémentation C++ avec SimpleBLE qui fonctionne sous Linux et macOS. Le travail a impliqué :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Implémenter la sérialisation/désérialisation TLV (&lt;code class=&quot;language-text&quot;&gt;HuaweiProtocol&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Construire des messages précis (&lt;code class=&quot;language-text&quot;&gt;ProtocolMessageBuilder&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Réussir les étapes cryptographiques : génération de nonce, HMAC-SHA256, chiffrement XOR (&lt;code class=&quot;language-text&quot;&gt;CryptoOperations&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;CryptoUtils&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Gérer des transitions d&apos;état et un timing stricts (&lt;code class=&quot;language-text&quot;&gt;HuaweiPairingProtocol&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;ProtocolStateManager&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Déboguer des échecs causés par des décalages de timing à l&apos;échelle de la milliseconde et des erreurs crypto subtiles.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;D2Explorer existe &lt;em&gt;parce que&lt;/em&gt; le protocole de Huawei l&apos;a rendu nécessaire. C&apos;est le contournement requis pour une fonctionnalité de base hors du jardin clos.&lt;/p&gt;
&lt;h3&gt;AsteroidOS&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://asteroidos.org/&quot;&gt;AsteroidOS 2.0&lt;/a&gt; a été lancé en février 2026 comme mise à jour majeure de l&apos;OS de smartwatch open source basé sur Linux &lt;small&gt;&lt;a href=&quot;#ref9&quot;&gt;[9]&lt;/a&gt;&lt;/small&gt;. Il prend maintenant en charge environ 30 appareils, dont la Huawei Watch et la Huawei Watch 2, avec des fonctionnalités comme l&apos;affichage always-on et Tilt-to-Wake. Une vraie alternative open source au firmware de Huawei.&lt;/p&gt;
&lt;h2&gt;La marée réglementaire&lt;/h2&gt;
&lt;p&gt;L&apos;UE ne se contente pas de regarder. Le Digital Markets Act (DMA) commence à forcer le changement &lt;small&gt;&lt;a href=&quot;#ref10&quot;&gt;[10]&lt;/a&gt;&lt;/small&gt;.&lt;/p&gt;
&lt;p&gt;En décembre 2025, Apple a publié iOS 26.3 avec un appairage façon AirPods pour les appareils tiers, y compris les montres connectées Huawei, précisément pour se conformer aux exigences du DMA &lt;small&gt;&lt;a href=&quot;#ref11&quot;&gt;[11]&lt;/a&gt;&lt;/small&gt;. La synchronisation en arrière-plan entre les montres Huawei et les iPhones est déjà opérationnelle en Europe.&lt;/p&gt;
&lt;p&gt;Le DMA impose aux gatekeepers de fournir l&apos;interopérabilité pour les appareils connectés. Cela cible directement le type de verrouillage BLE propriétaire que Huawei (et Apple, et tous les autres) pratiquent depuis des années. Le déploiement complet de ces fonctionnalités d&apos;interopérabilité est attendu tout au long de 2026.&lt;/p&gt;
&lt;p&gt;C&apos;est important. Pour la première fois, une pression réglementaire pousse à standardiser ce que les fournisseurs ont délibérément gardé propriétaire. La communauté technique peut rétro-ingénierer les protocoles un par un, mais la régulation peut changer la structure d&apos;incitation de toute l&apos;industrie.&lt;/p&gt;
&lt;h2&gt;Ce que cela signifie&lt;/h2&gt;
&lt;p&gt;Le protocole d&apos;appairage de la Huawei Watch D2 est une étude de cas sur la manière dont des protocoles personnalisés sur des transports standard peuvent imposer un verrouillage fournisseur. Les couches de cryptographie propriétaire, les formats de message sur mesure et les poignées de main sensibles au timing existent non pas parce que le BLE standard ne sait pas gérer l&apos;authentification, il le sait, mais parce que les protocoles propriétaires gardent les utilisateurs à l&apos;intérieur de l&apos;écosystème.&lt;/p&gt;
&lt;p&gt;Le tableau change pourtant. Gadgetbridge vous donne une alternative dès maintenant. Le DMA européen force l&apos;interopérabilité au niveau réglementaire. Et les projets open source comme &lt;code class=&quot;language-text&quot;&gt;huawei-lpv2&lt;/code&gt;, D2Explorer et AsteroidOS prouvent que la communauté rétro-ingénierera ce que les fournisseurs essaient de verrouiller.&lt;/p&gt;
&lt;p&gt;Construire D2Explorer relevait moins du Bluetooth que de l&apos;enquête cryptographique. Cela souligne quelque chose qui ne devrait pas avoir besoin d&apos;être souligné : vous devriez pouvoir accéder à vos propres données de santé avec le logiciel de votre choix.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Références&lt;/h3&gt;
&lt;p&gt;&lt;a id=&quot;ref1&quot;&gt;&lt;/a&gt;1. &lt;a href=&quot;https://github.com/zyv/huawei-lpv2&quot;&gt;huawei-lpv2: Pure Python implementation of Huawei BLE Link Protocol v2&lt;/a&gt; -- &lt;em&gt;Implémentation de référence open source du protocole.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref2&quot;&gt;&lt;/a&gt;2. &lt;a href=&quot;https://media.ccc.de/v/eh19-186-all-your-fitness-data-belongs-to-you-reverse-engineering-the-huawei-health-android-app&quot;&gt;All Your Fitness Data Belongs to You: Reverse Engineering the Huawei Health Android App&lt;/a&gt; -- &lt;em&gt;Conférence Easterhegg 2019 documentant l&apos;effort de rétro-ingénierie. &lt;a href=&quot;https://www.sba-research.org/wp-content/uploads/2019/04/easterhegg19.pdf&quot;&gt;Slides (PDF)&lt;/a&gt;.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref3&quot;&gt;&lt;/a&gt;3. &lt;a href=&quot;https://arxiv.org/html/2507.07210v1&quot;&gt;WatchWitch: Academic Research on Smartwatch Interoperability&lt;/a&gt; -- &lt;em&gt;Documente comment tous les grands fournisseurs utilisent des protocoles propriétaires pour verrouiller leurs écosystèmes.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref4&quot;&gt;&lt;/a&gt;4. &lt;a href=&quot;https://tns.thss.tsinghua.edu.cn/~jiliang/publications/MOBISYS2020_BlueDoor.pdf&quot;&gt;BlueDoor: Breaking the Secure Information Flow via BLE Vulnerability (Tsinghua University)&lt;/a&gt; -- &lt;em&gt;A trouvé des vulnérabilités d&apos;appairage silencieux dans 16 appareils BLE, dont le Honor Band 3.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref5&quot;&gt;&lt;/a&gt;5. &lt;a href=&quot;https://gadgetbridge.org/basics/topics/huawei-honor/&quot;&gt;Gadgetbridge: Huawei/Honor Device Support&lt;/a&gt; -- &lt;em&gt;Page officielle de prise en charge des wearables Huawei et Honor.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref6&quot;&gt;&lt;/a&gt;6. &lt;a href=&quot;https://codeberg.org/Freeyourgadget/Gadgetbridge/pulls/2462&quot;&gt;Gadgetbridge PR #2462: Initial Huawei/Honor Support&lt;/a&gt; -- &lt;em&gt;La pull request qui a ajouté la prise en charge des appareils Huawei à Gadgetbridge.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref7&quot;&gt;&lt;/a&gt;7. &lt;a href=&quot;https://codeberg.org/Freeyourgadget/Gadgetbridge/issues/4918&quot;&gt;Gadgetbridge Issue #4918: ECG Disabled with Gadgetbridge&lt;/a&gt; -- &lt;em&gt;Limite connue lors de l&apos;utilisation de Gadgetbridge à la place de Huawei Health.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref8&quot;&gt;&lt;/a&gt;8. &lt;a href=&quot;https://gadgetbridge.org/basics/pairing/huawei-honor-pairing/&quot;&gt;Gadgetbridge: Huawei/Honor Pairing Guide&lt;/a&gt; -- &lt;em&gt;Instructions pas à pas pour appairer les appareils Huawei.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref9&quot;&gt;&lt;/a&gt;9. &lt;a href=&quot;https://www.cnx-software.com/2026/02/18/asteroidos-2-0-open-source-smartwatch-os-released-now-supports-around-30-devices/&quot;&gt;AsteroidOS 2.0 Release&lt;/a&gt; -- &lt;em&gt;OS de smartwatch open source prenant maintenant en charge environ 30 appareils, dont les montres Huawei.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref10&quot;&gt;&lt;/a&gt;10. &lt;a href=&quot;https://digital-markets-act.ec.europa.eu/questions-and-answers/interoperability_en&quot;&gt;EU Digital Markets Act: Interoperability Requirements&lt;/a&gt; -- &lt;em&gt;Dispositions du DMA imposant l&apos;interopérabilité des appareils connectés.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref11&quot;&gt;&lt;/a&gt;11. &lt;a href=&quot;https://www.macrumors.com/2025/12/22/ios-26-3-dma-airpods-pairing/&quot;&gt;iOS 26.3 DMA Features: Third-Party Smartwatch Pairing&lt;/a&gt; -- &lt;em&gt;Mise en conformité d&apos;Apple avec les obligations européennes d&apos;interopérabilité pour les wearables.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Articles liés&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/bluez-pairing-python-agent-workaround-authentication-failed/&quot;&gt;BlueZ Pairing Fix: External Python Agent &amp;#x26; D-Bus Polling&lt;/a&gt; -- le précurseur de cette enquête. Avant de pouvoir nous attaquer au protocole propriétaire de Huawei, il fallait corriger les erreurs &lt;code class=&quot;language-text&quot;&gt;AuthenticationFailed&lt;/code&gt; de BlueZ avec l&apos;appairage BLE standard.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/m1-mac-android-emulator-bluetooth-passthrough-bumble/&quot;&gt;Fix Android Emulator Bluetooth on M1 Mac using Bumble &amp;#x26; API 32&lt;/a&gt; -- une autre bataille d&apos;intégration BLE, cette fois pour relier la radio Bluetooth physique d&apos;un Mac à l&apos;émulateur Android.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Correctif d'appairage BlueZ : agent Python externe et polling D-Bus]]></title><description><![CDATA[TL;DR : Si vous obtenez  avec un agent d'appairage C++/sd-bus personnalisé sur BlueZ 5.66+, le problème vient probablement de l…]]></description><link>https://bdteo.com/fr/bluez-pairing-python-agent-workaround-authentication-failed/</link><guid isPermaLink="false">https://bdteo.com/fr/bluez-pairing-python-agent-workaround-authentication-failed/</guid><pubDate>Tue, 08 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR :&lt;/strong&gt; Si vous obtenez &lt;code class=&quot;language-text&quot;&gt;org.bluez.Error.AuthenticationFailed&lt;/code&gt; avec un agent d&apos;appairage C++/sd-bus personnalisé sur BlueZ 5.66+, le problème vient probablement de l&apos;enregistrement de votre agent interne. Lancez un agent Python externe (&lt;code class=&quot;language-text&quot;&gt;simple-agent.py&lt;/code&gt;) comme processus séparé, et implémentez un polling des propriétés D-Bus au lieu de vous fier aux signaux &lt;code class=&quot;language-text&quot;&gt;PropertiesChanged&lt;/code&gt;. Détails et code ci-dessous.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;J&apos;ai passé deux jours à fixer &lt;code class=&quot;language-text&quot;&gt;org.bluez.Error.AuthenticationFailed&lt;/code&gt; avant de comprendre ce qui se passait.&lt;/p&gt;
&lt;p&gt;L&apos;agent d&apos;appairage était enregistré. Les appels D-Bus avaient l&apos;air corrects. &lt;code class=&quot;language-text&quot;&gt;busctl&lt;/code&gt; confirmait que tout était en place -- et BlueZ continuait simplement à dire non. C&apos;était pendant le travail sur &lt;a href=&quot;../huawei-watch-d2-proprietary-protocol-vendor-lockin/&quot;&gt;D2Explorer&lt;/a&gt; -- un outil pour appairer la Huawei Watch D2 sous Linux -- et l&apos;erreur d&apos;appairage bloquait tout.&lt;/p&gt;
&lt;p&gt;Voici ce qui s&apos;est réellement passé, et comment nous l&apos;avons corrigé.&lt;/p&gt;
&lt;h2&gt;Le plan : un agent d&apos;appairage C++ interne&lt;/h2&gt;
&lt;p&gt;L&apos;idée était propre et autonome. Une seule application C++ qui gère tout le processus d&apos;appairage avec &lt;code class=&quot;language-text&quot;&gt;sd-bus&lt;/code&gt; (les bindings D-Bus C/C++) :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Se connecter au D-Bus système.&lt;/li&gt;
&lt;li&gt;Trouver l&apos;adaptateur Bluetooth (&lt;code class=&quot;language-text&quot;&gt;org.bluez.Adapter1&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Implémenter une classe C++ exposant l&apos;interface &lt;code class=&quot;language-text&quot;&gt;org.bluez.Agent1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Enregistrer l&apos;agent auprès de &lt;code class=&quot;language-text&quot;&gt;org.bluez.AgentManager1&lt;/code&gt; via &lt;code class=&quot;language-text&quot;&gt;RegisterAgent&lt;/code&gt; et &lt;code class=&quot;language-text&quot;&gt;RequestDefaultAgent&lt;/code&gt;. Nous avons commencé avec la capacité &lt;code class=&quot;language-text&quot;&gt;DisplayYesNo&lt;/code&gt;, puis nous avons simplifié vers &lt;code class=&quot;language-text&quot;&gt;NoInputNoOutput&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Découvrir l&apos;appareil cible (&lt;code class=&quot;language-text&quot;&gt;org.bluez.Device1&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Appeler &lt;code class=&quot;language-text&quot;&gt;Pair()&lt;/code&gt; sur l&apos;interface D-Bus de l&apos;appareil.&lt;/li&gt;
&lt;li&gt;L&apos;agent interne gère automatiquement les callbacks (&lt;code class=&quot;language-text&quot;&gt;RequestConfirmation&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;RequestAuthorization&lt;/code&gt;) -- aucune interaction utilisateur nécessaire.&lt;/li&gt;
&lt;li&gt;Marquer l&apos;appareil comme approuvé, établir une connexion GATT, terminé.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Un binaire, aucune dépendance externe. C&apos;était le plan.&lt;/p&gt;
&lt;h2&gt;Le mur : &lt;code class=&quot;language-text&quot;&gt;org.bluez.Error.AuthenticationFailed&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Tout fonctionnait jusqu&apos;à l&apos;étape 6. Adaptateur trouvé, agent enregistré (D-Bus le confirmait), appareil découvert. Mais au moment où nous appelions &lt;code class=&quot;language-text&quot;&gt;Device1.Pair()&lt;/code&gt; via &lt;code class=&quot;language-text&quot;&gt;sd_bus_call_method&lt;/code&gt; -- échec instantané :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;[BluetoothDevice] Calling Device1.Pair() method via D-Bus
[BluetoothDevice] Device1.Pair() method threw exception: Failed to call method &apos;Pair&apos;:
    Input/output error - D-Bus error: org.bluez.Error.AuthenticationFailed (Authentication Failed)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Nous avons tout essayé. Différentes capacités d&apos;agent. Vérification de la configuration de la vtable &lt;code class=&quot;language-text&quot;&gt;sd-bus&lt;/code&gt;. Confirmation que les implémentations des méthodes de l&apos;agent renvoyaient bien un succès rapidement. Utilisation de &lt;code class=&quot;language-text&quot;&gt;busctl&lt;/code&gt; et &lt;code class=&quot;language-text&quot;&gt;gdbus&lt;/code&gt; pour surveiller le trafic D-Bus -- les appels d&apos;enregistrement semblaient corrects. L&apos;appel &lt;code class=&quot;language-text&quot;&gt;Pair()&lt;/code&gt; continuait simplement à échouer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Impasse.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;La percée : un agent Python externe&lt;/h2&gt;
&lt;p&gt;Pour isoler le problème, nous avons retiré l&apos;agent C++ interne de l&apos;équation. Nous avons lancé le &lt;code class=&quot;language-text&quot;&gt;simple-agent.py&lt;/code&gt; standard de BlueZ comme processus séparé &lt;em&gt;avant&lt;/em&gt; de lancer notre application C++ (désormais privée de son propre enregistrement d&apos;agent) :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Terminal 1: Run the external agent&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; python simple-agent.py NoInputNoOutput

&lt;span class=&quot;token comment&quot;&gt;# Terminal 2: Run our C++ app (no internal agent)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; ./build/huawei_pair_app &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;MAC&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;QR_VALUE&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Le résultat :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;[BluetoothDevice] Calling Device1.Pair() method via D-Bus
[BluetoothDevice] Device1.Pair() method succeeded  &amp;lt;--- SUCCESS!&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Régulier. À chaque fois. L&apos;erreur &lt;code class=&quot;language-text&quot;&gt;AuthenticationFailed&lt;/code&gt; a complètement disparu.&lt;/p&gt;
&lt;p&gt;Cela prouvait que le problème ne venait pas de &lt;code class=&quot;language-text&quot;&gt;Pair()&lt;/code&gt; lui-même, ni de l&apos;appareil, ni de la capacité d&apos;appairage de BlueZ. Il concernait précisément la manière dont notre application C++, via &lt;code class=&quot;language-text&quot;&gt;sd-bus&lt;/code&gt;, s&apos;enregistrait et interagissait comme agent d&apos;appairage. La même opération logique exacte -- enregistrer un agent &lt;code class=&quot;language-text&quot;&gt;NoInputNoOutput&lt;/code&gt; et appeler &lt;code class=&quot;language-text&quot;&gt;Pair()&lt;/code&gt; -- fonctionnait parfaitement quand l&apos;agent tournait dans un processus Python séparé.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ça a marché.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Pourquoi l&apos;agent interne échouait-il ?&lt;/h2&gt;
&lt;p&gt;Quand je suis tombé dessus, je n&apos;avais que des hypothèses. Depuis, j&apos;ai trouvé des preuves documentées montrant que c&apos;est un problème plus large -- pas seulement notre code.&lt;/p&gt;
&lt;h3&gt;Régression BlueZ 5.70+&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/bluez/bluez/issues/605&quot;&gt;L&apos;issue BlueZ GitHub #605&lt;/a&gt; documente des cas où des appareils s&apos;appairent correctement sur BlueZ 5.50 mais échouent sur des versions plus récentes avec &lt;code class=&quot;language-text&quot;&gt;auth failed with status 0x05&lt;/code&gt;. Les journaux HCI montrent &lt;code class=&quot;language-text&quot;&gt;Status: PIN or Key Missing (0x06)&lt;/code&gt; malgré des clés de liaison stockées. Le contournement ? Lancer l&apos;ancien script &lt;code class=&quot;language-text&quot;&gt;bluez-simple-agent.py&lt;/code&gt;. Ça vous rappelle quelque chose ?&lt;/p&gt;
&lt;h3&gt;La disponibilité de l&apos;agent est la cause racine&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/hbldh/bleak/issues/1434&quot;&gt;L&apos;issue Bleak #1434&lt;/a&gt; rend cela encore plus clair : l&apos;appairage ne fonctionne que lorsque &lt;code class=&quot;language-text&quot;&gt;bluetoothctl&lt;/code&gt; ou GNOME Bluetooth tourne, parce que ces applications enregistrent l&apos;agent d&apos;authentification nécessaire. Sans agent actif et &lt;em&gt;fonctionnant correctement&lt;/em&gt;, BlueZ renvoie en interne &lt;code class=&quot;language-text&quot;&gt;No agent available for request type 2&lt;/code&gt; -- ce qui remonte sous forme de &lt;code class=&quot;language-text&quot;&gt;AuthenticationFailed&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;L&apos;idée clé : il ne suffit pas d&apos;&lt;em&gt;enregistrer&lt;/em&gt; un agent. L&apos;agent doit répondre aux callbacks de BlueZ d&apos;une manière que &lt;code class=&quot;language-text&quot;&gt;bluetoothd&lt;/code&gt; considère valide. Et quelque chose dans la façon dont &lt;code class=&quot;language-text&quot;&gt;sd-bus&lt;/code&gt; gère cela au sein du même processus que celui qui initie l&apos;appairage ne satisfait pas les versions récentes de BlueZ.&lt;/p&gt;
&lt;h3&gt;Ce n&apos;est peut-être même pas BlueZ&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://bugzilla.redhat.com/show_bug.cgi?id=1905671&quot;&gt;Le bug Red Hat #1905671&lt;/a&gt; a révélé que certaines erreurs &lt;code class=&quot;language-text&quot;&gt;AuthenticationFailed&lt;/code&gt; sont liées au noyau, pas à BlueZ. Le noyau 5.9 avait des problèmes d&apos;appairage que 5.8.18 et 5.10+ n&apos;avaient pas. Le commentaire du mainteneur vaut la peine d&apos;être cité : &lt;em&gt;« Bluetooth is complex, it could be firmware, kernel, bluez, controller, end device or a combination of them all. »&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Incompatibilité de capacité d&apos;agent&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/bluez/bluez/issues/650&quot;&gt;L&apos;issue BlueZ #650&lt;/a&gt; documente un autre angle : certains appareils (notamment iOS) échouent lors d&apos;un appairage avec des agents &lt;code class=&quot;language-text&quot;&gt;NoInputNoOutput&lt;/code&gt;, parce qu&apos;ils rétrogradent Secure Connections vers l&apos;appairage Legacy, provoquant ensuite des erreurs &lt;code class=&quot;language-text&quot;&gt;Insufficient Authentication (0x05)&lt;/code&gt; lors de l&apos;accès aux attributs. C&apos;est un problème de négociation Security Manager Protocol (SMP), pas un problème d&apos;enregistrement d&apos;agent -- mais il produit le même message d&apos;erreur.&lt;/p&gt;
&lt;h3&gt;Les coupables probables dans notre cas&lt;/h3&gt;
&lt;p&gt;Au vu des éléments, les explications les plus probables de l&apos;échec de l&apos;agent interne &lt;code class=&quot;language-text&quot;&gt;sd-bus&lt;/code&gt; sont :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Timing&lt;/strong&gt; -- l&apos;enregistrement &lt;code class=&quot;language-text&quot;&gt;sd-bus&lt;/code&gt; ou la gestion des méthodes dans notre boucle d&apos;événements ne répondait pas exactement dans la fenêtre attendue par &lt;code class=&quot;language-text&quot;&gt;bluetoothd&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Subtilités &lt;code class=&quot;language-text&quot;&gt;sd-bus&lt;/code&gt; vs &lt;code class=&quot;language-text&quot;&gt;python-dbus&lt;/code&gt;&lt;/strong&gt; -- différences dans la façon dont ces bibliothèques interagissent avec le démon D-Bus ou gèrent la durée de vie des objets.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Exigences plus strictes dans BlueZ 5.66+&lt;/strong&gt; -- séquences internes modifiées pour l&apos;interaction avec l&apos;agent, que &lt;code class=&quot;language-text&quot;&gt;sd-bus&lt;/code&gt;, utilisé dans la même application que celle qui initie l&apos;appairage, ne satisfait pas.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Le second mur : les signaux D-Bus ne sont pas fiables&lt;/h2&gt;
&lt;p&gt;Passer &lt;code class=&quot;language-text&quot;&gt;AuthenticationFailed&lt;/code&gt; était une grosse victoire, mais ce n&apos;était pas la fin. Avec l&apos;agent externe en place, &lt;code class=&quot;language-text&quot;&gt;Pair()&lt;/code&gt; réussissait -- mais nous ne pouvions pas &lt;em&gt;détecter&lt;/em&gt; de façon fiable quand il se terminait.&lt;/p&gt;
&lt;p&gt;Nous nous reposions sur les signaux D-Bus &lt;code class=&quot;language-text&quot;&gt;PropertiesChanged&lt;/code&gt; (via &lt;code class=&quot;language-text&quot;&gt;sd-bus&lt;/code&gt;) pour savoir quand &lt;code class=&quot;language-text&quot;&gt;Paired&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Trusted&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Connected&lt;/code&gt; et &lt;code class=&quot;language-text&quot;&gt;ServicesResolved&lt;/code&gt; devenaient &lt;code class=&quot;language-text&quot;&gt;true&lt;/code&gt;. Parfois les signaux arrivaient. Parfois ils arrivaient en retard. Parfois ils n&apos;arrivaient pas du tout.&lt;/p&gt;
&lt;p&gt;Nous avons donc implémenté un &lt;strong&gt;polling actif&lt;/strong&gt; -- un fallback qui interroge directement les valeurs des propriétés lorsque les signaux ne se présentent pas :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;cpp&quot;&gt;&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BluetoothDevice&lt;/span&gt;&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isPaired&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;bool&lt;/span&gt; cachedValue &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; mockPaired_&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Check signal-updated cache&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cachedValue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Signal didn&apos;t fire? Poll D-Bus directly.&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Logger&lt;/span&gt;&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;[Polling] Polling Paired property via D-Bus...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;bool&lt;/span&gt; polledValue &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    adapter_&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token generic-function&quot;&gt;&lt;span class=&quot;token function&quot;&gt;getObjectProperty&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        devicePath_&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;org.bluez.Device1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Paired&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; polledValue
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;polledValue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; mockPaired_&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Update cache&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; polledValue&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Chaque méthode de transition d&apos;état (&lt;code class=&quot;language-text&quot;&gt;isPaired()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;isTrusted()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;isConnected()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;areServicesResolved()&lt;/code&gt;) suit le même schéma : vérifier d&apos;abord le booléen atomique en cache (mis à jour par le gestionnaire de signal s&apos;il fonctionne), puis retomber sur un appel D-Bus direct &lt;code class=&quot;language-text&quot;&gt;Get&lt;/code&gt; pour la propriété.&lt;/p&gt;
&lt;p&gt;Pas élégant. Mais nécessaire.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ça a marché.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Le correctif complet&lt;/h2&gt;
&lt;p&gt;Voici la recette consolidée. Si vous construisez un appairage Bluetooth automatisé sous Linux avec BlueZ 5.66+ et que vous tombez sur &lt;code class=&quot;language-text&quot;&gt;AuthenticationFailed&lt;/code&gt; :&lt;/p&gt;
&lt;h3&gt;Étape 1 : récupérer simple-agent.py&lt;/h3&gt;
&lt;p&gt;Prenez-le dans l&apos;&lt;a href=&quot;https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/test/simple-agent&quot;&gt;arbre source de BlueZ&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Étape 2 : lancer l&apos;agent externe&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; python simple-agent.py NoInputNoOutput&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Gardez-le actif dans un terminal séparé (ou comme service en arrière-plan).&lt;/p&gt;
&lt;h3&gt;Étape 3 : retirer l&apos;agent interne de votre application&lt;/h3&gt;
&lt;p&gt;Supprimez tous les appels &lt;code class=&quot;language-text&quot;&gt;RegisterAgent&lt;/code&gt; / &lt;code class=&quot;language-text&quot;&gt;RequestDefaultAgent&lt;/code&gt; de votre application C++. Laissez l&apos;agent Python externe gérer les callbacks d&apos;authentification.&lt;/p&gt;
&lt;h3&gt;Étape 4 : ajouter le polling des propriétés D-Bus&lt;/h3&gt;
&lt;p&gt;Ne vous fiez pas uniquement aux signaux &lt;code class=&quot;language-text&quot;&gt;PropertiesChanged&lt;/code&gt;. Pour chaque propriété critique (&lt;code class=&quot;language-text&quot;&gt;Paired&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Trusted&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Connected&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;ServicesResolved&lt;/code&gt;), implémentez le schéma cache-puis-polling montré ci-dessus. Interrogez périodiquement depuis votre boucle principale.&lt;/p&gt;
&lt;h3&gt;Étape 5 : vérifier&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Confirmez que l&apos;agent externe tourne (&lt;code class=&quot;language-text&quot;&gt;sudo python simple-agent.py NoInputNoOutput&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Lancez votre application. &lt;code class=&quot;language-text&quot;&gt;Pair()&lt;/code&gt; devrait réussir.&lt;/li&gt;
&lt;li&gt;Surveillez les logs de polling -- vous devriez voir des requêtes de propriétés D-Bus pour les transitions d&apos;état.&lt;/li&gt;
&lt;li&gt;Si &lt;code class=&quot;language-text&quot;&gt;Pair()&lt;/code&gt; échoue encore, vérifiez votre version de BlueZ (&lt;code class=&quot;language-text&quot;&gt;bluetoothd --version&lt;/code&gt;) et votre version de noyau -- le problème est peut-être plus profond.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Ce que cela vous coûte&lt;/h2&gt;
&lt;p&gt;Je ne vais pas prétendre que c&apos;est une solution propre. Ça ne l&apos;est pas :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Dépendance externe&lt;/strong&gt; -- votre application a maintenant besoin d&apos;un processus Python séparé en cours d&apos;exécution.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Plus de complexité&lt;/strong&gt; -- logique de polling dans la boucle principale, en plus des gestionnaires de signaux.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Moins autonome&lt;/strong&gt; -- le rêve d&apos;un seul binaire a disparu.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Mais ça marche. De façon fiable. Et quand vous avez fixé &lt;code class=&quot;language-text&quot;&gt;AuthenticationFailed&lt;/code&gt; pendant deux jours, « ça marche » est ce qui compte.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Références&lt;/h3&gt;
&lt;p&gt;&lt;a id=&quot;ref1&quot;&gt;&lt;/a&gt;1. &lt;a href=&quot;https://github.com/bluez/bluez/issues/55&quot;&gt;BlueZ GitHub Issue #55: Device characteristics and pairing timing&lt;/a&gt; -- &lt;em&gt;Échecs d&apos;appairage intermittents liés au timing de l&apos;agent.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref2&quot;&gt;&lt;/a&gt;2. &lt;a href=&quot;https://forums.raspberrypi.com/viewtopic.php?t=324225&quot;&gt;Bluetooth Auto Pairing with NoInputNoOutput Agent Issues&lt;/a&gt; -- &lt;em&gt;Discussion de forum sur les défis de l&apos;appairage headless.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref3&quot;&gt;&lt;/a&gt;3. &lt;a href=&quot;https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/test/simple-agent&quot;&gt;BlueZ Source: test/simple-agent&lt;/a&gt; -- &lt;em&gt;L&apos;agent Python standard.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref4&quot;&gt;&lt;/a&gt;4. &lt;a href=&quot;https://github.com/bluez/bluez/issues/605&quot;&gt;BlueZ GitHub Issue #605: Pairing regression in 5.70+&lt;/a&gt; -- &lt;em&gt;Échecs documentés avec les versions récentes de BlueZ.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref5&quot;&gt;&lt;/a&gt;5. &lt;a href=&quot;https://github.com/hbldh/bleak/issues/1434&quot;&gt;Bleak Issue #1434: Pairing requires active agent&lt;/a&gt; -- &lt;em&gt;Preuve que la disponibilité de l&apos;agent est la cause racine.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref6&quot;&gt;&lt;/a&gt;6. &lt;a href=&quot;https://bugzilla.redhat.com/show_bug.cgi?id=1905671&quot;&gt;Red Hat Bug #1905671: Kernel-related pairing failures&lt;/a&gt; -- &lt;em&gt;Ce n&apos;est pas toujours BlueZ -- parfois c&apos;est le noyau.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref7&quot;&gt;&lt;/a&gt;7. &lt;a href=&quot;https://github.com/bluez/bluez/issues/650&quot;&gt;BlueZ GitHub Issue #650: Agent capability mismatch&lt;/a&gt; -- &lt;em&gt;Échecs de négociation SMP avec NoInputNoOutput.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref8&quot;&gt;&lt;/a&gt;8. &lt;a href=&quot;https://bluez.readthedocs.io/en/latest/agent-api/&quot;&gt;BlueZ Agent API Documentation&lt;/a&gt; -- &lt;em&gt;Référence officielle de l&apos;interface agent.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref9&quot;&gt;&lt;/a&gt;9. &lt;a href=&quot;https://technotes.kynetics.com/2018/pairing_agents_bluez/&quot;&gt;Kynetics: Pairing Agents in the BlueZ Stack&lt;/a&gt; -- &lt;em&gt;Analyse technique détaillée de l&apos;enregistrement des agents.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Articles liés&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/huawei-watch-d2-proprietary-protocol-vendor-lockin/&quot;&gt;Appairage BLE de la Huawei Watch D2 : protocole et verrouillage fournisseur&lt;/a&gt; -- le projet qui a déclenché cette enquête. La Watch D2 exige une poignée de main propriétaire au niveau applicatif, par-dessus l&apos;appairage BLE standard, ce qui explique pourquoi nous avions besoin que l&apos;appairage automatisé fonctionne dès le départ.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/m1-mac-android-emulator-bluetooth-passthrough-bumble/&quot;&gt;Corriger le Bluetooth de l&apos;émulateur Android sur Mac M1 avec Bumble et API 32&lt;/a&gt; -- une autre bataille d&apos;intégration Bluetooth, cette fois pour faire passer la radio physique d&apos;un Mac dans l&apos;émulateur Android.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Guide dev : mises à jour des classes et namespaces dans Shopware 6.5/6.6]]></title><description><![CDATA[Shopware 6.5 et 6.6 ont introduit plusieurs changements importants dans les classes, les namespaces, les attributs data et les mécanismes de…]]></description><link>https://bdteo.com/fr/understanding-class-namespace-changes-shopware-6-5-developers-guide/</link><guid isPermaLink="false">https://bdteo.com/fr/understanding-class-namespace-changes-shopware-6-5-developers-guide/</guid><pubDate>Sat, 30 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Shopware 6.5 et 6.6 ont introduit plusieurs changements importants dans les classes, les namespaces, les attributs data et les mécanismes de sécurité dont les développeurs doivent tenir compte lorsqu’ils mettent à jour ou maintiennent leurs projets Shopware. Cet article propose un aperçu concis mais complet de ces changements, avec quelques observations sur leur impact et sur la manière d’adapter votre code.&lt;/p&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;À mesure que Shopware évolue, les mises à jour apportent souvent des améliorations, des optimisations et de nouvelles fonctionnalités. Elles peuvent toutefois aussi introduire des changements qui affectent les bases de code existantes. Comprendre ces changements est essentiel pour assurer une transition fluide et exploiter efficacement les nouvelles capacités.&lt;/p&gt;
&lt;p&gt;Cet article se concentre sur les points suivants :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Migration du namespace Elasticsearch&lt;/li&gt;
&lt;li&gt;Mises à jour de la gestion des chemins média&lt;/li&gt;
&lt;li&gt;Changements de méthode dans &lt;code class=&quot;language-text&quot;&gt;AvailableCombinationLoader&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Mise à niveau du framework Symfony vers la version 6&lt;/li&gt;
&lt;li&gt;Mises à jour de la gestion du stock&lt;/li&gt;
&lt;li&gt;Mise à niveau de Bootstrap dans le Storefront&lt;/li&gt;
&lt;li&gt;Changements d’attribut data pour l’Offcanvas Cart&lt;/li&gt;
&lt;li&gt;Changements de protection CSRF&lt;/li&gt;
&lt;li&gt;Améliorations du Rule Builder&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Entrons dans le détail de chacun de ces changements.&lt;/p&gt;
&lt;h2&gt;1. Migration du namespace Elasticsearch&lt;/h2&gt;
&lt;h3&gt;Aperçu du changement&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Namespace précédent :&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;ONGR\ElasticsearchDSL&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Nouveau namespace :&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;OpenSearchDSL&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Impact&lt;/h3&gt;
&lt;p&gt;Toutes les classes et méthodes qui interagissent avec Elasticsearch doivent mettre à jour leurs namespaces afin de refléter la migration de &lt;code class=&quot;language-text&quot;&gt;ONGR\ElasticsearchDSL&lt;/code&gt; vers &lt;code class=&quot;language-text&quot;&gt;OpenSearchDSL&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Action requise&lt;/h3&gt;
&lt;p&gt;Mettez à jour les instructions d’import et les références dans votre code pour utiliser le nouveau namespace.&lt;/p&gt;
&lt;h3&gt;Exemple&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;php&quot;&gt;&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Before&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token package&quot;&gt;ONGR&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;ElasticsearchDSL&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;Query&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;MatchQuery&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;$query&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MatchQuery&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;field&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;value&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// After&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token package&quot;&gt;OpenSearchDSL&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;Query&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;MatchQuery&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;$query&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MatchQuery&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;field&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&apos;value&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Observations&lt;/h3&gt;
&lt;p&gt;Ce changement s’aligne sur le mouvement plus large de l’industrie vers OpenSearch, un fork communautaire d’Elasticsearch. Passer au nouveau namespace garantit la compatibilité avec les développements et le support à venir.&lt;/p&gt;
&lt;h2&gt;2. Gestion des chemins média&lt;/h2&gt;
&lt;h3&gt;Aperçu du changement&lt;/h3&gt;
&lt;p&gt;Les chemins média sont désormais stockés directement en base de données au lieu d’être générés dynamiquement.&lt;/p&gt;
&lt;h3&gt;Impact&lt;/h3&gt;
&lt;p&gt;Les classes et services qui s’appuyaient auparavant sur la génération dynamique des chemins doivent lire les chemins média depuis la base de données.&lt;/p&gt;
&lt;h3&gt;Action requise&lt;/h3&gt;
&lt;p&gt;Adaptez votre code pour utiliser la méthode &lt;code class=&quot;language-text&quot;&gt;getPath()&lt;/code&gt; de &lt;code class=&quot;language-text&quot;&gt;MediaEntity&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Exemple&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;php&quot;&gt;&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Before (dynamic path generation)&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$mediaPath&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$mediaService&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$mediaId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// After (database-stored path)&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$mediaPath&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$mediaEntity&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Observations&lt;/h3&gt;
&lt;p&gt;Stocker les chemins média en base de données améliore les performances en réduisant le coût de calcul. Cela apporte aussi plus de cohérence et de fiabilité dans la gestion des médias.&lt;/p&gt;
&lt;h2&gt;3. Mise à jour de méthode dans &lt;code class=&quot;language-text&quot;&gt;AvailableCombinationLoader&lt;/code&gt;&lt;/h2&gt;
&lt;h3&gt;Aperçu du changement&lt;/h3&gt;
&lt;p&gt;La méthode &lt;code class=&quot;language-text&quot;&gt;load()&lt;/code&gt; dans &lt;code class=&quot;language-text&quot;&gt;AbstractAvailableCombinationLoader&lt;/code&gt; a été remplacée par &lt;code class=&quot;language-text&quot;&gt;loadCombinations()&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Impact&lt;/h3&gt;
&lt;p&gt;Toute classe personnalisée qui étend &lt;code class=&quot;language-text&quot;&gt;AbstractAvailableCombinationLoader&lt;/code&gt; doit implémenter la nouvelle méthode &lt;code class=&quot;language-text&quot;&gt;loadCombinations()&lt;/code&gt; au lieu de l’ancienne méthode &lt;code class=&quot;language-text&quot;&gt;load()&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Action requise&lt;/h3&gt;
&lt;p&gt;Renommez ou refactorez vos implémentations de méthode pour les aligner sur le nouveau nom et la nouvelle signature.&lt;/p&gt;
&lt;h3&gt;Exemple&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;php&quot;&gt;&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Before&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$combinations&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$combinationLoader&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$productId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// After&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$combinations&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$combinationLoader&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;loadCombinations&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$productId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Observations&lt;/h3&gt;
&lt;p&gt;Ce changement renforce la clarté avec un nom de méthode plus descriptif. Il peut aussi impliquer des paramètres ou des types de retour supplémentaires ; il est donc essentiel de relire la signature de la méthode.&lt;/p&gt;
&lt;h2&gt;4. Mise à niveau du framework Symfony vers la version 6&lt;/h2&gt;
&lt;h3&gt;Aperçu du changement&lt;/h3&gt;
&lt;p&gt;Shopware a mis à niveau ses composants Symfony vers la version 6.&lt;/p&gt;
&lt;h3&gt;Impact&lt;/h3&gt;
&lt;p&gt;Cette mise à niveau introduit quelques breaking changes liés à des fonctionnalités dépréciées et à des changements de signatures de méthodes. Le code personnalisé qui dépend d’anciennes fonctionnalités Symfony peut casser ou produire des avertissements.&lt;/p&gt;
&lt;h3&gt;Action requise&lt;/h3&gt;
&lt;p&gt;Relisez votre code pour identifier les fonctionnalités Symfony dépréciées et mettez-les à jour afin qu’elles soient compatibles avec Symfony 6.&lt;/p&gt;
&lt;h3&gt;Observations&lt;/h3&gt;
&lt;p&gt;Rester à jour avec la dernière version de Symfony apporte de meilleures performances, une meilleure sécurité et l’accès à de nouvelles fonctionnalités. Cela demande toutefois une revue attentive du code et des tests pour garantir la compatibilité.&lt;/p&gt;
&lt;h2&gt;5. Mises à jour de la gestion du stock&lt;/h2&gt;
&lt;h3&gt;Aperçu du changement&lt;/h3&gt;
&lt;p&gt;Une nouvelle Stock API a été introduite, disponible derrière le feature flag &lt;code class=&quot;language-text&quot;&gt;STOCK_HANDLING&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Impact&lt;/h3&gt;
&lt;p&gt;Les classes et services liés à la gestion du stock peuvent devoir s’adapter à la nouvelle structure de l’API, surtout s’ils interagissent directement avec les données de stock.&lt;/p&gt;
&lt;h3&gt;Action requise&lt;/h3&gt;
&lt;p&gt;Utilisez les nouvelles méthodes de gestion du stock fournies par l’API et assurez-vous que toute logique liée au stock s’aligne sur la structure mise à jour.&lt;/p&gt;
&lt;h3&gt;Exemple&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;php&quot;&gt;&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Before&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$stock&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$productEntity&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getStock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// After&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$stock&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$stockService&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getStock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$productId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Observations&lt;/h3&gt;
&lt;p&gt;La nouvelle Stock API fournit une manière plus robuste et plus flexible de gérer le stock, ce qui peut simplifier les personnalisations et les intégrations avec des systèmes externes.&lt;/p&gt;
&lt;h2&gt;6. Mise à niveau de Bootstrap dans le Storefront&lt;/h2&gt;
&lt;h3&gt;Aperçu du changement&lt;/h3&gt;
&lt;p&gt;Le Storefront est passé de Bootstrap 4 à Bootstrap 5, et jQuery a été retiré comme dépendance.&lt;/p&gt;
&lt;h3&gt;Impact&lt;/h3&gt;
&lt;p&gt;Le code JavaScript personnalisé et les templates qui s’appuient sur jQuery ou sur des composants Bootstrap 4 doivent être refactorés pour s’aligner sur Bootstrap 5 et utiliser du JavaScript natif lorsque c’est nécessaire.&lt;/p&gt;
&lt;h3&gt;Action requise&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Remplacez l’usage de jQuery par du JavaScript natif ou par les utilitaires Bootstrap 5.&lt;/li&gt;
&lt;li&gt;Mettez à jour les classes et composants Bootstrap pour correspondre au nommage et à la structure de Bootstrap 5.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Observations&lt;/h3&gt;
&lt;p&gt;Bootstrap 5 apporte de meilleures performances, moins de dépendances et des composants modernisés. La mise à jour peut prendre du temps, mais elle offre des bénéfices durables en maintenabilité et en expérience utilisateur.&lt;/p&gt;
&lt;h2&gt;7. Changements d’attribut data pour l’Offcanvas Cart (Shopware 6.6)&lt;/h2&gt;
&lt;h3&gt;Aperçu du changement&lt;/h3&gt;
&lt;p&gt;Dans Shopware 6.6, un changement discret mais important a été introduit dans l’attribut data utilisé pour déclencher la fonctionnalité d’offcanvas cart.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Attribut data précédent :&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;data-offcanvas-cart&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Nouvel attribut data :&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;data-off-canvas-cart&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Impact&lt;/h3&gt;
&lt;p&gt;Les templates ou thèmes personnalisés qui utilisent l’attribut &lt;code class=&quot;language-text&quot;&gt;data-offcanvas-cart&lt;/code&gt; sans traits d’union peuvent constater que l’offcanvas cart ne fonctionne plus comme prévu, car le listener JavaScript de Shopware 6.6 cherche la version avec traits d’union.&lt;/p&gt;
&lt;h3&gt;Action requise&lt;/h3&gt;
&lt;p&gt;Mettez à jour l’attribut &lt;code class=&quot;language-text&quot;&gt;data-offcanvas-cart&lt;/code&gt; dans vos templates vers &lt;code class=&quot;language-text&quot;&gt;data-off-canvas-cart&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Exemple&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Before --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;header-cart&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;data-offcanvas-cart&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Cart content --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- After --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;header-cart&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;data-off-canvas-cart&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Cart content --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Observations&lt;/h3&gt;
&lt;p&gt;Ce changement est mal documenté dans les notes de version officielles de Shopware 6.6, mais il est crucial pour le bon fonctionnement de l’offcanvas cart. Le JavaScript chargé d’initialiser la fonctionnalité du panier s’appuie sur l’attribut &lt;code class=&quot;language-text&quot;&gt;data-off-canvas-cart&lt;/code&gt;, et tout écart peut empêcher le panier de fonctionner.&lt;/p&gt;
&lt;h3&gt;Notes supplémentaires&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;La cohérence est essentielle :&lt;/strong&gt; assurez-vous que toutes les occurrences de l’attribut offcanvas cart sont mises à jour.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Testez soigneusement :&lt;/strong&gt; après le changement, testez la fonctionnalité du panier pour confirmer qu’elle se comporte comme attendu.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cherchez les changements similaires :&lt;/strong&gt; d’autres attributs data ou listeners d’événements ont peut-être subi des mises à jour comparables ; relisez vos templates personnalisés en conséquence.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;8. Changements de protection CSRF&lt;/h2&gt;
&lt;h3&gt;Aperçu du changement&lt;/h3&gt;
&lt;p&gt;Shopware 6.5 et les versions ultérieures ont supprimé la gestion explicite des tokens CSRF dans les templates, au profit d’une stratégie de cookies SameSite pour la protection CSRF.&lt;/p&gt;
&lt;h3&gt;Impact&lt;/h3&gt;
&lt;p&gt;Les templates et formulaires qui incluaient auparavant des tokens CSRF avec la fonction &lt;code class=&quot;language-text&quot;&gt;sw_csrf&lt;/code&gt; rencontreront des erreurs, car cette fonction n’existe plus.&lt;/p&gt;
&lt;h3&gt;Action requise&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Supprimez les fonctions de token CSRF :&lt;/strong&gt; éliminez l’usage de &lt;code class=&quot;language-text&quot;&gt;{{ sw_csrf(&apos;route_name&apos;) }}&lt;/code&gt; dans vos templates.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Appuyez-vous sur les cookies SameSite :&lt;/strong&gt; faites confiance à la stratégie SameSite intégrée pour la protection CSRF, qui ne requiert pas de tokens explicites dans les formulaires.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ajustez les attributs de formulaire :&lt;/strong&gt; assurez-vous que les formulaires et les requêtes AJAX sont correctement configurés pour fonctionner avec le nouveau mécanisme de protection CSRF.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Exemple&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;twig&quot;&gt;&lt;pre class=&quot;language-twig&quot;&gt;&lt;code class=&quot;language-twig&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Before --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;form&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token twig language-twig&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{{&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&apos;&lt;/span&gt;frontend.checkout.line-item.add&lt;span class=&quot;token punctuation&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;}}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;post&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token twig language-twig&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{{&lt;/span&gt; sw_csrf&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&apos;&lt;/span&gt;frontend.checkout.line-item.add&lt;span class=&quot;token punctuation&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;}}&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Form fields --&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;submit&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Add to Cart&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;form&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- After --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;form&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token twig language-twig&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{{&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&apos;&lt;/span&gt;frontend.checkout.line-item.add&lt;span class=&quot;token punctuation&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;}}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;post&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Form fields --&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;submit&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Add to Cart&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;form&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Observations&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Résolution de l’erreur :&lt;/strong&gt; supprimer la fonction &lt;code class=&quot;language-text&quot;&gt;sw_csrf&lt;/code&gt; résout l’erreur &quot;Unknown &apos;sw_csrf&apos; function&quot;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Maintien de la sécurité :&lt;/strong&gt; la stratégie de cookies SameSite continue de protéger contre les attaques CSRF sans tokens supplémentaires.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Templates simplifiés :&lt;/strong&gt; les formulaires deviennent plus propres et légèrement plus simples sans tokens CSRF.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Notes supplémentaires&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Les tests sont essentiels :&lt;/strong&gt; après ces changements, testez soigneusement les soumissions de formulaires pour vérifier qu’elles fonctionnent correctement.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Comprenez le nouveau mécanisme :&lt;/strong&gt; familiarisez-vous avec le fonctionnement de la stratégie SameSite pour conserver une application sécurisée.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mettez la documentation à jour :&lt;/strong&gt; assurez-vous que toute documentation interne reflète ce changement afin d’éviter de futures confusions.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;9. Gérer les problèmes avec l’Offcanvas Cart&lt;/h2&gt;
&lt;h3&gt;Aperçu du scénario&lt;/h3&gt;
&lt;p&gt;Après avoir mis à jour les templates et supprimé la fonction &lt;code class=&quot;language-text&quot;&gt;sw_csrf&lt;/code&gt;, les développeurs peuvent encore rencontrer des problèmes : cliquer sur le bouton &quot;Add to Cart&quot; ouvre l’offcanvas cart, mais celui-ci apparaît vide.&lt;/p&gt;
&lt;h3&gt;Cause racine&lt;/h3&gt;
&lt;p&gt;L’offcanvas cart peut ne pas afficher les articles ajoutés à cause de paramètres manquants ou incorrects dans la soumission du formulaire, en particulier l’absence du champ d’entrée &lt;code class=&quot;language-text&quot;&gt;redirectTo&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Action requise&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Ajoutez le paramètre &lt;code class=&quot;language-text&quot;&gt;redirectTo&lt;/code&gt; :&lt;/strong&gt; incluez dans vos formulaires d’ajout au panier un champ caché nommé &lt;code class=&quot;language-text&quot;&gt;redirectTo&lt;/code&gt; avec la valeur &lt;code class=&quot;language-text&quot;&gt;frontend.cart.offcanvas&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Assurez-vous que les attributs data sont corrects :&lt;/strong&gt; vérifiez que tous les attributs data nécessaires sont présents et correctement nommés.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Exemple&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;twig&quot;&gt;&lt;pre class=&quot;language-twig&quot;&gt;&lt;code class=&quot;language-twig&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;form&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token twig language-twig&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{{&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&apos;&lt;/span&gt;frontend.checkout.line-item.add&lt;span class=&quot;token punctuation&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;}}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;post&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;hidden&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;redirectTo&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;frontend.cart.offcanvas&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;hidden&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lineItems[&lt;span class=&quot;token twig language-twig&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{{&lt;/span&gt; product&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id &lt;span class=&quot;token delimiter punctuation&quot;&gt;}}&lt;/span&gt;&lt;/span&gt;][id]&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token twig language-twig&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{{&lt;/span&gt; product&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id &lt;span class=&quot;token delimiter punctuation&quot;&gt;}}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Other form fields --&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;submit&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Add to Cart&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;form&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Observations&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Restauration de la fonctionnalité :&lt;/strong&gt; ajouter le paramètre &lt;code class=&quot;language-text&quot;&gt;redirectTo&lt;/code&gt; indique à Shopware de charger l’offcanvas cart lors de l’ajout d’un article, ce qui garantit que le panier s’affiche correctement.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Attention au détail :&lt;/strong&gt; de petites omissions, comme des champs d’entrée manquants, peuvent provoquer des problèmes fonctionnels importants ; cela rappelle l’importance d’une revue de code attentive.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Notes supplémentaires&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Cohérence des attributs data :&lt;/strong&gt; vérifiez deux fois que les attributs data comme &lt;code class=&quot;language-text&quot;&gt;data-product-id&lt;/code&gt; sont correctement définis.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Relisez les dépendances JavaScript :&lt;/strong&gt; assurez-vous que tous les plugins ou composants JavaScript liés au panier sont bien chargés et initialisés.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Videz le cache :&lt;/strong&gt; après les changements, videz le cache Shopware et celui de votre navigateur pour éviter que des fichiers obsolètes ne causent des problèmes.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;10. Améliorations du Rule Builder&lt;/h2&gt;
&lt;h3&gt;Aperçu du changement&lt;/h3&gt;
&lt;p&gt;L’API Rule Builder a été étendue pour prendre en charge une logique conditionnelle plus complexe.&lt;/p&gt;
&lt;h3&gt;Impact&lt;/h3&gt;
&lt;p&gt;Les règles et conditions personnalisées peuvent nécessiter des ajustements afin de s’aligner sur les nouvelles interfaces ou méthodes fournies par le Rule Builder amélioré.&lt;/p&gt;
&lt;h3&gt;Action requise&lt;/h3&gt;
&lt;p&gt;Relisez la documentation du Rule Builder et mettez à jour les implémentations de règles personnalisées pour garantir leur compatibilité.&lt;/p&gt;
&lt;h3&gt;Observations&lt;/h3&gt;
&lt;p&gt;Des capacités de règles plus avancées permettent un ciblage et une personnalisation plus précis dans Shopware. Tirer parti de ces nouvelles fonctionnalités peut mener à une meilleure adaptabilité et à des expériences plus personnalisées pour les utilisateurs finaux.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Shopware 6.5 et 6.6 introduisent plusieurs changements importants dans les classes, les namespaces, les attributs data et les mécanismes de sécurité que les développeurs doivent prendre en compte pour maintenir la compatibilité et profiter des nouvelles fonctionnalités. Mettre à jour votre base de code exige une revue attentive et des tests, mais offre aussi l’occasion d’améliorer les performances, la sécurité et la fonctionnalité.&lt;/p&gt;
&lt;h2&gt;Recommandations&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Planifiez en amont :&lt;/strong&gt; avant la mise à jour, relisez les notes de version officielles et les guides de mise à niveau de Shopware pour obtenir une vue complète.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Testez soigneusement :&lt;/strong&gt; appliquez les changements dans un environnement de staging et effectuez des tests approfondis pour identifier et corriger les problèmes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Appuyez-vous sur la documentation :&lt;/strong&gt; utilisez la documentation Shopware et les forums de la communauté pour trouver des indications sur les changements spécifiques.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Restez informé :&lt;/strong&gt; suivez les futures mises à jour afin d’anticiper les changements à venir et de vous y préparer.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Observations supplémentaires&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Attention au détail :&lt;/strong&gt; de petits changements, comme des traits d’union dans les attributs data ou la suppression de fonctions, peuvent avoir un impact important. Relisez toujours les mises à jour de templates avec soin.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Soutien de la communauté :&lt;/strong&gt; la communauté Shopware est active et collaborative. Échanger avec d’autres développeurs peut apporter des idées et des solutions à des problèmes courants.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bonnes pratiques :&lt;/strong&gt; adopter les bonnes pratiques mises à jour, comme l’usage de JavaScript natif à la place de jQuery et le recours à des stratégies de sécurité modernes, conduit à un code plus propre et plus efficace.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Surveillance des dépréciations :&lt;/strong&gt; suivre les avis de dépréciation et se préparer aux suppressions futures peut réduire les interruptions pendant les mises à niveau.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;En comprenant et en traitant les changements de classes, de namespaces, d’attributs data et de sécurité dans Shopware 6.5 et 6.6, les développeurs peuvent assurer une transition fluide et continuer à construire des solutions e-commerce robustes et durables.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Évolution de Docker Compose : ce qui a changé et pourquoi c’est important]]></title><description><![CDATA[TL;DR : Docker Compose v1 () a été entièrement supprimé en avril 2025. Le champ  dans votre YAML est mort. La clé  est maintenant simplement…]]></description><link>https://bdteo.com/fr/docker-compose-major-changes-since-october-2023/</link><guid isPermaLink="false">https://bdteo.com/fr/docker-compose-major-changes-since-october-2023/</guid><pubDate>Wed, 20 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR :&lt;/strong&gt; Docker Compose v1 (&lt;code class=&quot;language-text&quot;&gt;docker-compose&lt;/code&gt;) a été entièrement supprimé en avril 2025. Le champ &lt;code class=&quot;language-text&quot;&gt;version&lt;/code&gt; dans votre YAML est mort. La clé &lt;code class=&quot;language-text&quot;&gt;x-develop&lt;/code&gt; est maintenant simplement &lt;code class=&quot;language-text&quot;&gt;develop&lt;/code&gt;. Le mode watch est prêt pour la production avec &lt;code class=&quot;language-text&quot;&gt;initial_sync&lt;/code&gt;. Il existe une CVE critique de traversée de chemins (CVE-2025-62725) si vous utilisez &lt;code class=&quot;language-text&quot;&gt;include&lt;/code&gt; avec des artefacts OCI : mettez à jour vers v2.40.2 ou plus récent. Et oui, Compose est passé de v2 à v5. Les détails ci-dessous.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Publié initialement en novembre 2024. Mis à jour en mars 2026 avec Compose v5, CVE-2025-62725, la suppression de v1, et les nouvelles fonctionnalités de la spécification.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Si vous utilisez Docker Compose depuis un moment, vous avez probablement remarqué que des choses cassaient ou changeaient sous vos pieds. Les deux dernières années ont été l’évolution la plus agressive que Compose ait jamais connue -- et tout n’était pas évident.&lt;/p&gt;
&lt;p&gt;J’utilise Compose tous les jours. La plupart de mes &lt;a href=&quot;/laravel-sail-vs-laradock-choosing-right-docker-solution/&quot;&gt;environnements de développement&lt;/a&gt; tournent dessus. Quand les choses changent, je le remarque. Voici ce qui compte vraiment.&lt;/p&gt;
&lt;h2&gt;Ce qui a cassé&lt;/h2&gt;
&lt;h3&gt;docker-compose est mort&lt;/h3&gt;
&lt;p&gt;Pas déprécié. Pas en mode maintenance. &lt;strong&gt;Mort.&lt;/strong&gt; Le binaire autonome &lt;code class=&quot;language-text&quot;&gt;docker-compose&lt;/code&gt; (la v1 basée sur Python) a été supprimé des runners GitHub Actions et des images Docker officielles en avril 2025 &lt;small&gt;&lt;a href=&quot;#ref1&quot;&gt;[1]&lt;/a&gt;&lt;/small&gt;. Si vos pipelines CI/CD référencent encore &lt;code class=&quot;language-text&quot;&gt;docker-compose&lt;/code&gt; avec un trait d’union, ils sont cassés ou sur le point de l’être.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# This no longer works&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;docker-compose&lt;/span&gt; up &lt;span class=&quot;token parameter variable&quot;&gt;-d&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# This is the only way now&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; compose up &lt;span class=&quot;token parameter variable&quot;&gt;-d&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Le &lt;code class=&quot;language-text&quot;&gt;docker compose&lt;/code&gt; basé sur Go (v2, maintenant v5) est la vraie implémentation depuis 2022. La CLI v1 était sous assistance respiratoire pour compatibilité. Cette assistance est terminée.&lt;/p&gt;
&lt;h3&gt;Le champ version a disparu&lt;/h3&gt;
&lt;p&gt;Arrêtez de mettre &lt;code class=&quot;language-text&quot;&gt;version: &quot;3.8&quot;&lt;/code&gt; en haut de vos fichiers Compose. Cela ne fait rien. Il est ignoré depuis v2 et il est maintenant officiellement déprécié. Les fichiers Compose modernes commencent par &lt;code class=&quot;language-text&quot;&gt;services:&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Stop doing this&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;3.8&quot;&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;web&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx

&lt;span class=&quot;token comment&quot;&gt;# Just do this&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;web&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Si vous voyez &lt;code class=&quot;language-text&quot;&gt;version:&lt;/code&gt; dans un tutoriel, ce tutoriel est obsolète.&lt;/p&gt;
&lt;h3&gt;Autres dépréciations&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;links&lt;/code&gt;&lt;/strong&gt; -- utilisez les réseaux Docker. Les links sont legacy depuis le lancement de Compose v2.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;container_name&lt;/code&gt;&lt;/strong&gt; -- laissez Docker gérer les noms. Les noms codés en dur cassent le scaling et provoquent des conflits.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Syntaxe courte des volumes pour les montages complexes&lt;/strong&gt; -- utilisez la syntaxe longue avec &lt;code class=&quot;language-text&quot;&gt;type&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;source&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;target&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Ce qui est nouveau et réellement utile&lt;/h2&gt;
&lt;h3&gt;Mode watch (maintenant prêt pour la production)&lt;/h3&gt;
&lt;p&gt;C’est la plus grosse amélioration de confort depuis des années. La section &lt;code class=&quot;language-text&quot;&gt;develop&lt;/code&gt; (anciennement &lt;code class=&quot;language-text&quot;&gt;x-develop&lt;/code&gt; -- retirez le préfixe &lt;code class=&quot;language-text&quot;&gt;x-&lt;/code&gt;, ce n’est plus expérimental) permet de définir des règles de surveillance de fichiers qui synchronisent ou reconstruisent automatiquement quand les fichiers changent :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;web&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; .
    &lt;span class=&quot;token key atrule&quot;&gt;develop&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;watch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ./src
          &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; sync
          &lt;span class=&quot;token key atrule&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; /app/src
        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ./package.json
          &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; rebuild&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Trois actions disponibles (depuis v2.32.0) :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;sync&lt;/code&gt;&lt;/strong&gt; -- copie les fichiers modifiés dans le conteneur sans reconstruire&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;restart&lt;/code&gt;&lt;/strong&gt; -- redémarre le service quand les fichiers changent&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;rebuild&lt;/code&gt;&lt;/strong&gt; -- déclenche une reconstruction complète&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Depuis septembre 2025, il existe aussi &lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;initial_sync&lt;/code&gt;&lt;/strong&gt; : cela synchronise tous les fichiers immédiatement au démarrage de &lt;code class=&quot;language-text&quot;&gt;docker compose watch&lt;/code&gt;, pour que vous n’ayez pas à attendre le premier changement avant de déclencher la synchronisation. Cela a été un point de friction pendant longtemps.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; compose &lt;span class=&quot;token function&quot;&gt;watch&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Plus besoin de reconstructions manuelles pendant le développement. Cela a vraiment changé mon workflow.&lt;/p&gt;
&lt;h3&gt;Include avec des artefacts OCI&lt;/h3&gt;
&lt;p&gt;La directive &lt;code class=&quot;language-text&quot;&gt;include&lt;/code&gt; peut maintenant récupérer des fragments Compose depuis des registres OCI :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;include&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; oci&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;//docker.io/username/my&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;compose&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;fragment&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;latest&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Il existe aussi une prise en charge expérimentale des dépôts Git. C’est utile pour partager des définitions de services communes entre projets -- configurations de bases de données, stacks de monitoring, etc.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Mais lisez d’abord la section sécurité ci-dessous.&lt;/strong&gt; Il y a une CVE.&lt;/p&gt;
&lt;h3&gt;Prise en charge des GPU&lt;/h3&gt;
&lt;p&gt;Le passthrough GPU est devenu plus propre. Il existe maintenant une syntaxe plus courte &lt;code class=&quot;language-text&quot;&gt;gpus:&lt;/code&gt; (v2.30.0+) en plus de l’approche verbeuse &lt;code class=&quot;language-text&quot;&gt;deploy.resources.reservations.devices&lt;/code&gt;. La prise en charge des GPU AMD a été officiellement intégrée en 2025 -- ce n’est plus seulement NVIDIA.&lt;/p&gt;
&lt;h3&gt;Élément models&lt;/h3&gt;
&lt;p&gt;La spécification Compose inclut maintenant un élément &lt;code class=&quot;language-text&quot;&gt;models&lt;/code&gt; pour définir des modèles IA/ML comme artefacts OCI. Vous pouvez empaqueter des LLMs et des runtimes d’inférence directement dans votre configuration Compose. C’est de niche, mais intéressant si vous faites du travail IA en local.&lt;/p&gt;
&lt;h3&gt;Dépendances améliorées&lt;/h3&gt;
&lt;p&gt;Les conditions de &lt;code class=&quot;language-text&quot;&gt;depends_on&lt;/code&gt; sont devenues plus flexibles :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;web&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;depends_on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;condition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; service_healthy
        &lt;span class=&quot;token key atrule&quot;&gt;restart&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;      &lt;span class=&quot;token comment&quot;&gt;# restart web if db restarts&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;false&lt;/span&gt;     &lt;span class=&quot;token comment&quot;&gt;# web can start even if db isn&apos;t ready&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Les options &lt;code class=&quot;language-text&quot;&gt;restart: true&lt;/code&gt; et &lt;code class=&quot;language-text&quot;&gt;required: false&lt;/code&gt; sont vraiment utiles pour des environnements de développement locaux plus résilients.&lt;/p&gt;
&lt;h2&gt;Ce que vous devez savoir&lt;/h2&gt;
&lt;h3&gt;CVE-2025-62725 : traversée de chemins avec include&lt;/h3&gt;
&lt;p&gt;Si vous utilisez la directive &lt;code class=&quot;language-text&quot;&gt;include&lt;/code&gt; avec des artefacts OCI, &lt;strong&gt;mettez à jour vers v2.40.2 ou plus récent immédiatement&lt;/strong&gt; &lt;small&gt;&lt;a href=&quot;#ref2&quot;&gt;[2]&lt;/a&gt;&lt;/small&gt;. Une vulnérabilité de traversée de chemins permet à un attaquant de sortir du répertoire de cache pendant la résolution de l’artefact. Même un &lt;code class=&quot;language-text&quot;&gt;docker compose ps&lt;/code&gt; apparemment inoffensif peut la déclencher si votre fichier Compose inclut une référence OCI malveillante.&lt;/p&gt;
&lt;p&gt;Docker a corrigé cela avec une vérification &lt;code class=&quot;language-text&quot;&gt;validatePathInBase()&lt;/code&gt;, mais vous devez être sur la version corrigée.&lt;/p&gt;
&lt;h3&gt;Compose v5&lt;/h3&gt;
&lt;p&gt;Docker est passé de v2 à v5 (en sautant 3 et 4 pour éviter la confusion avec les anciennes versions du format de fichier) &lt;small&gt;&lt;a href=&quot;#ref3&quot;&gt;[3]&lt;/a&gt;&lt;/small&gt;. Fonctionnellement, v5 est identique aux dernières versions v2, mais elle inclut un &lt;strong&gt;SDK Go&lt;/strong&gt; officiel pour l’accès programmatique -- ce qui signifie que vous pouvez intégrer des fonctionnalités Compose directement dans des applications Go sans appeler la CLI via le shell.&lt;/p&gt;
&lt;p&gt;Vérifiez votre version :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; compose version
&lt;span class=&quot;token comment&quot;&gt;# Docker Compose version v5.1.0&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Bake est l’outil de build par défaut&lt;/h3&gt;
&lt;p&gt;Docker Bake (via BuildKit) est maintenant l’outil par défaut pour &lt;code class=&quot;language-text&quot;&gt;docker compose build&lt;/code&gt;. Il gère mieux que le builder legacy les builds multi-cibles, la compilation multi-plateforme, et les stratégies avancées de cache. Si vous n’avez pas encore regardé les fichiers &lt;code class=&quot;language-text&quot;&gt;docker-bake.hcl&lt;/code&gt;, cela vaut la peine de comprendre le sujet -- surtout pour les builds multi-services complexes.&lt;/p&gt;
&lt;h3&gt;Améliorations des healthchecks&lt;/h3&gt;
&lt;p&gt;Le champ &lt;code class=&quot;language-text&quot;&gt;start_interval&lt;/code&gt; permet de définir un intervalle de vérification plus rapide pendant la période de grâce au démarrage :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;healthcheck&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;CMD&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;pg_isready&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;start_period&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 30s
      &lt;span class=&quot;token key atrule&quot;&gt;start_interval&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2s    &lt;span class=&quot;token comment&quot;&gt;# check every 2s during startup&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;interval&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 30s         &lt;span class=&quot;token comment&quot;&gt;# then every 30s after&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;retries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Cela signifie que vos services dépendants démarrent plus vite sans compromettre les intervalles de healthcheck en production.&lt;/p&gt;
&lt;h2&gt;Checklist de migration&lt;/h2&gt;
&lt;p&gt;Si vous n’avez pas mis à jour votre setup Compose depuis un moment :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Supprimez &lt;code class=&quot;language-text&quot;&gt;version:&lt;/code&gt;&lt;/strong&gt; de tous les fichiers Compose.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Remplacez &lt;code class=&quot;language-text&quot;&gt;docker-compose&lt;/code&gt;&lt;/strong&gt; par &lt;code class=&quot;language-text&quot;&gt;docker compose&lt;/code&gt; dans tous les scripts et configurations CI.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Renommez &lt;code class=&quot;language-text&quot;&gt;x-develop&lt;/code&gt;&lt;/strong&gt; en &lt;code class=&quot;language-text&quot;&gt;develop&lt;/code&gt; dans les configurations watch.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mettez à jour vers v2.40.2+&lt;/strong&gt; si vous utilisez &lt;code class=&quot;language-text&quot;&gt;include&lt;/code&gt; (CVE-2025-62725).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Remplacez &lt;code class=&quot;language-text&quot;&gt;links&lt;/code&gt;&lt;/strong&gt; par des réseaux Docker si vous les utilisez encore, d’une manière ou d’une autre.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Testez votre CI&lt;/strong&gt; -- GitHub Actions a mis à jour ses runners vers Compose v2.40.3 en février 2026 &lt;small&gt;&lt;a href=&quot;#ref4&quot;&gt;[4]&lt;/a&gt;&lt;/small&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3&gt;Références&lt;/h3&gt;
&lt;p&gt;&lt;a id=&quot;ref1&quot;&gt;&lt;/a&gt;1. &lt;a href=&quot;https://github.com/actions/runner-images/issues/9557&quot;&gt;Docker Compose v1 removed from runner images (April 2025)&lt;/a&gt; -- &lt;em&gt;Annonce GitHub Actions de la suppression de v1.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref2&quot;&gt;&lt;/a&gt;2. &lt;a href=&quot;https://www.imperva.com/blog/cve-2025-62725-from-docker-compose-ps-to-system-compromise/&quot;&gt;CVE-2025-62725: From &quot;docker compose ps&quot; to System Compromise&lt;/a&gt; -- &lt;em&gt;Analyse détaillée d’Imperva sur la vulnérabilité de traversée de chemins avec include.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref3&quot;&gt;&lt;/a&gt;3. &lt;a href=&quot;https://github.com/docker/compose/releases&quot;&gt;Docker Compose Releases&lt;/a&gt; -- &lt;em&gt;Historique officiel des releases, y compris v5.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref4&quot;&gt;&lt;/a&gt;4. &lt;a href=&quot;https://github.blog/changelog/2026-01-30-docker-and-docker-compose-version-upgrades-on-hosted-runners/&quot;&gt;Docker and Docker Compose version upgrades on hosted runners&lt;/a&gt; -- &lt;em&gt;Mise à jour des runners GitHub en février 2026.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref5&quot;&gt;&lt;/a&gt;5. &lt;a href=&quot;https://docs.docker.com/compose/compose-file/&quot;&gt;Compose Specification&lt;/a&gt; -- &lt;em&gt;Référence officielle des fichiers Compose.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref6&quot;&gt;&lt;/a&gt;6. &lt;a href=&quot;https://docs.docker.com/compose/how-tos/file-watch/&quot;&gt;Use Compose Watch&lt;/a&gt; -- &lt;em&gt;Documentation Docker du mode watch.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref7&quot;&gt;&lt;/a&gt;7. &lt;a href=&quot;https://docs.docker.com/compose/how-tos/gpu-support/&quot;&gt;Enable GPU Support in Docker Compose&lt;/a&gt; -- &lt;em&gt;Documentation du passthrough GPU, y compris la prise en charge AMD.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref8&quot;&gt;&lt;/a&gt;8. &lt;a href=&quot;https://docs.docker.com/compose/how-tos/multiple-compose-files/include/&quot;&gt;Docker Compose Include&lt;/a&gt; -- &lt;em&gt;Directive include avec prise en charge OCI et Git.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Articles liés&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/laravel-sail-vs-laradock-choosing-right-docker-solution/&quot;&gt;Laravel Sail vs Laradock: Choosing the Right Docker Solution&lt;/a&gt; -- comparaison d’environnements de développement PHP basés sur Docker.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Déverrouiller la puissance de 'git grep' pour rechercher efficacement dans le code]]></title><description><![CDATA[Dans un vaste royaume rempli d’innombrables rouleaux et manuscrits vivait un érudit nommé Alaric. Sa bibliothèque était immense : un…]]></description><link>https://bdteo.com/fr/unlocking-the-power-of-git-grep/</link><guid isPermaLink="false">https://bdteo.com/fr/unlocking-the-power-of-git-grep/</guid><pubDate>Wed, 13 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Dans un vaste royaume rempli d’innombrables rouleaux et manuscrits vivait un érudit nommé Alaric. Sa bibliothèque était immense : un labyrinthe de savoir où les textes anciens se mêlaient aux écrits contemporains, et où les secrets se cachaient entre les lignes. Alaric se retrouvait souvent à chercher une seule phrase insaisissable au milieu de cette mer d’informations, une tâche qui devenait plus intimidante de jour en jour.&lt;/p&gt;
&lt;p&gt;Un matin, tandis que le soleil projetait ses rayons dorés sur les tomes poussiéreux, Alaric partit retrouver un concept particulier mentionné dans ses archives, connu seulement sous le nom de « The Whispering Sigil ». Il parcourut des volumes entiers, utilisant ses méthodes habituelles pour passer les pages au crible — des méthodes qui semblaient désormais lentes et imprécises. Plus il avançait, plus il s’empêtrait dans des passages sans rapport, des doublons et des références trompeuses. La frustration monta tandis que les heures devenaient des jours, avec peu de progrès.&lt;/p&gt;
&lt;p&gt;Puis un vieux sage rendit visite à Alaric et remarqua sa détresse. Avec un sourire entendu, le sage dit : « Peut-être cherches-tu par le chemin difficile. Il existe une voie cachée, connue seulement de ceux qui organisent leur savoir avec soin. » Intrigué, Alaric écouta le sage lui expliquer une méthode qui resserrait sa recherche, traversait le désordre et menait directement aux textes qu’il cherchait.&lt;/p&gt;
&lt;p&gt;Armé de cette nouvelle approche, Alaric essaya de nouveau. Cette fois, le bruit inutile s’effaça. Le chemin vers « The Whispering Sigil » devint clair, et il trouva ce qu’il cherchait avec une rapidité stupéfiante. C’était comme s’il avait déverrouillé une porte secrète dans son labyrinthe, lui donnant un accès immédiat au savoir exact dont il avait besoin.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pouf !&lt;/strong&gt; Le secret était révélé : la puissance de &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Ce qu’est réellement &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Un simple &lt;code class=&quot;language-text&quot;&gt;grep -r&lt;/code&gt; parcourt le système de fichiers. Il lit consciencieusement tout ce qui se trouve sur son chemin : code source, fichiers de log, sorties de build, ce fichier dump perdu de 4 Mo que votre collègue a oublié de supprimer, l’arbre &lt;code class=&quot;language-text&quot;&gt;node_modules&lt;/code&gt; tout entier. &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; fait quelque chose de plus étroit : il cherche dans les fichiers que Git connaît déjà. C’est de ce choix de conception que vient l’essentiel de sa valeur.&lt;/p&gt;
&lt;h3&gt;Là où &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; est bon&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Il cherche dans les fichiers suivis, pas dans le système de fichiers.&lt;/strong&gt; Git garde une liste de chaque fichier que vous avez déjà staged ou commit — l’index. &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; lit depuis cette liste. Les déchets non suivis n’y sont tout simplement pas. Pas de &lt;code class=&quot;language-text&quot;&gt;node_modules/&lt;/code&gt;, pas de &lt;code class=&quot;language-text&quot;&gt;dist/&lt;/code&gt;, pas de rapports de couverture, pas de fichier de log aléatoire — parce que Git n’a jamais été informé de leur existence.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Il est plus rapide que &lt;code class=&quot;language-text&quot;&gt;grep -r&lt;/code&gt; sur les gros dépôts.&lt;/strong&gt; Il possède déjà la liste des fichiers, donc il évite de parcourir le système de fichiers. Il lance plusieurs threads en parallèle. Le gain est réel, mais ce n’est pas de la magie. &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; itère sur les mêmes blobs que &lt;code class=&quot;language-text&quot;&gt;grep&lt;/code&gt;, simplement avec moins de cérémonie. Il n’y a pas d’index de recherche de contenu impliqué — « l’index Git » est une liste de chemins de fichiers et de hashes de blobs, pas un index inversé à la Lucene.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Il peut chercher dans n’importe quelle ref sans checkout.&lt;/strong&gt; C’est la fonctionnalité qui tue. Un tag, une branche, un commit, un objet tree : pointez &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; directement dessus. Pas de &lt;code class=&quot;language-text&quot;&gt;git checkout&lt;/code&gt;, pas de danse autour du stash, pas de détour loin de ce que vous étiez en train de faire.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Exemples pratiques&lt;/h3&gt;
&lt;h4&gt;Recherche de base&lt;/h4&gt;
&lt;p&gt;Pour chercher un terme précis, comme &lt;code class=&quot;language-text&quot;&gt;&quot;initializeSettings&quot;&lt;/code&gt;, dans votre dépôt :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;initializeSettings&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Cela scanne tous les fichiers suivis dans la branche courante pour trouver la correspondance exacte.&lt;/p&gt;
&lt;h4&gt;Recherche insensible à la casse&lt;/h4&gt;
&lt;p&gt;Pour une recherche insensible à la casse, utile quand vous n’êtes pas sûr de la capitalisation :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;initializesettings&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Cela trouvera les correspondances quelles que soient les différences de casse.&lt;/p&gt;
&lt;h4&gt;Chercher dans une branche précise&lt;/h4&gt;
&lt;p&gt;Pour chercher dans une autre branche sans basculer dessus, par exemple dans &lt;code class=&quot;language-text&quot;&gt;feature/login&lt;/code&gt; :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;validateUser&quot;&lt;/span&gt; feature/login&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;C’est le geste difficile à battre. Pas de checkout, pas de stash, juste la réponse.&lt;/p&gt;
&lt;h4&gt;Chercher dans toutes les branches&lt;/h4&gt;
&lt;p&gt;Pour chercher un terme dans toutes les branches, y compris les remotes :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; branch &lt;span class=&quot;token parameter variable&quot;&gt;-a&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;xargs&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;configureDatabase&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Pour chercher dans chaque commit dont Git a entendu parler, pas seulement à la pointe des branches :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;configureDatabase&quot;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; rev-list &lt;span class=&quot;token parameter variable&quot;&gt;--all&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Cela trouve les correspondances dans n’importe quel blob, n’importe où dans votre historique. Sur un dépôt actif, cela peut prendre un moment : il parcourt littéralement chaque commit.&lt;/p&gt;
&lt;h4&gt;Chercher dans l’historique des commits&lt;/h4&gt;
&lt;p&gt;Pour trouver quand une chaîne particulière a été ajoutée ou supprimée, utilisez :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; log &lt;span class=&quot;token parameter variable&quot;&gt;-S&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;optimizePerformance&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Cela affiche les commits qui ont introduit ou supprimé le terme &lt;code class=&quot;language-text&quot;&gt;&quot;optimizePerformance&quot;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Pour voir les diffs réels où le terme a été ajouté ou supprimé :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; log &lt;span class=&quot;token parameter variable&quot;&gt;-G&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;optimizePerformance&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-p&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;Utiliser des expressions régulières&lt;/h4&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; prend en charge les expressions régulières pour des recherches plus avancées :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-E&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;def\s+\w+\(&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Cela correspond aux définitions de fonctions Python : &lt;code class=&quot;language-text&quot;&gt;def&lt;/code&gt;, un espace blanc, un nom de fonction, puis une parenthèse ouvrante littérale. (En regex étendue, &lt;code class=&quot;language-text&quot;&gt;\(&lt;/code&gt; est une parenthèse littérale et &lt;code class=&quot;language-text&quot;&gt;(&lt;/code&gt; signifierait un groupe, d’où la barre oblique inverse.)&lt;/p&gt;
&lt;h3&gt;Ce que &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; lit, et ne lit pas&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; parcourt l’index. C’est tout. Il ne parse pas &lt;code class=&quot;language-text&quot;&gt;.gitignore&lt;/code&gt;. Beaucoup de gens, y compris une version précédente de cet article, affirment qu’il le fait — et l’affirmation est presque vraie, de la même manière que « la Terre est plate » est presque vraie si vous ne regardez jamais qu’un parking.&lt;/p&gt;
&lt;p&gt;Les deux ne s’alignent que parce que les fichiers gitignorés sont généralement aussi non suivis. Dès qu’un fichier est &lt;em&gt;à la fois&lt;/em&gt; gitignoré &lt;em&gt;et&lt;/em&gt; suivi — quelqu’un a lancé &lt;code class=&quot;language-text&quot;&gt;git add -f&lt;/code&gt;, ou le fichier a été commité avant que la règle existe — &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; le cherchera volontiers. &lt;code class=&quot;language-text&quot;&gt;rg&lt;/code&gt;, lui, ne le fera pas.&lt;/p&gt;
&lt;p&gt;Vous pouvez le prouver en vingt secondes :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; demo &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; demo
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; init &lt;span class=&quot;token parameter variable&quot;&gt;-q&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;*.log&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; .gitignore
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;the secret phrase&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; tracked.log
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; tracked.log .gitignore
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; commit &lt;span class=&quot;token parameter variable&quot;&gt;-qm&lt;/span&gt; init

&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;secret phrase&quot;&lt;/span&gt;   &lt;span class=&quot;token comment&quot;&gt;# finds it - the file is tracked, ignore rule notwithstanding&lt;/span&gt;
rg &lt;span class=&quot;token string&quot;&gt;&quot;secret phrase&quot;&lt;/span&gt;         &lt;span class=&quot;token comment&quot;&gt;# finds nothing - rg actually reads .gitignore&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;La formulation précise est donc : &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; cherche dans les fichiers suivis. Cela saute par hasard &lt;em&gt;la plupart&lt;/em&gt; de ce que &lt;code class=&quot;language-text&quot;&gt;.gitignore&lt;/code&gt; sauterait, mais le mécanisme est différent et le cas limite compte — surtout quand vous traquez une chaîne qui finit par vivre dans un fichier généré que quelqu’un a ajouté de force il y a des années.&lt;/p&gt;
&lt;p&gt;Le mécanisme &lt;code class=&quot;language-text&quot;&gt;.gitignore&lt;/code&gt; n’entre dans &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; que par deux modes explicites :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;--untracked&lt;/code&gt; — cherche aussi dans les fichiers non suivis. &lt;em&gt;Dans ce mode&lt;/em&gt;, &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; respecte &lt;code class=&quot;language-text&quot;&gt;.gitignore&lt;/code&gt; par défaut et saute les fichiers ignorés (utilisez &lt;code class=&quot;language-text&quot;&gt;--no-exclude-standard&lt;/code&gt; pour les chercher eux aussi).&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;--no-index&lt;/code&gt; — cherche dans le répertoire courant en ignorant Git entièrement. Utile dans un dépôt quand vous voulez la sémantique de grep tout court. Dans ce mode, &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; ne consulte &lt;em&gt;pas&lt;/em&gt; &lt;code class=&quot;language-text&quot;&gt;.gitignore&lt;/code&gt; par défaut — activez &lt;code class=&quot;language-text&quot;&gt;--exclude-standard&lt;/code&gt; si vous le voulez.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Le &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; par défaut, sans flags, n’ouvre jamais votre fichier &lt;code class=&quot;language-text&quot;&gt;.gitignore&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Quand choisir plutôt &lt;code class=&quot;language-text&quot;&gt;rg&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; et &lt;code class=&quot;language-text&quot;&gt;rg&lt;/code&gt; (ripgrep) ne sont pas vraiment des concurrents. Ils parcourent des choses différentes, et une boîte à outils sérieuse contient les deux.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; parcourt &lt;strong&gt;l’index&lt;/strong&gt; : les fichiers suivis, plus toute ref ou tout objet tree que vous lui indiquez.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;rg&lt;/code&gt; parcourt &lt;strong&gt;le système de fichiers&lt;/strong&gt; : chaque fichier sous le répertoire courant, moins ce que votre &lt;code class=&quot;language-text&quot;&gt;.gitignore&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;.ignore&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;.rgignore&lt;/code&gt; et vos exclusions globales lui disent de sauter.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Chacun fait quelque chose que l’autre ne peut pas faire.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; gagne quand vous voulez chercher dans l’historique sans checkout :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;deprecated_api&quot;&lt;/span&gt; v2.3.0          &lt;span class=&quot;token comment&quot;&gt;# search a tag&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;deprecated_api&quot;&lt;/span&gt; HEAD~50         &lt;span class=&quot;token comment&quot;&gt;# 50 commits ago&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;deprecated_api&quot;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; rev-list &lt;span class=&quot;token parameter variable&quot;&gt;--all&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;   &lt;span class=&quot;token comment&quot;&gt;# every commit, ever&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;rg&lt;/code&gt; gagne quand vous voulez réellement la sémantique du système de fichiers avec une bonne gestion de gitignore — y compris un sous-dossier fraîchement cloné que vous n’avez pas encore &lt;code class=&quot;language-text&quot;&gt;git add&lt;/code&gt;, des fichiers générés dont Git n’a jamais entendu parler, ou un répertoire qui n’est pas un dépôt Git du tout :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;rg &lt;span class=&quot;token string&quot;&gt;&quot;deprecated_api&quot;&lt;/span&gt;                &lt;span class=&quot;token comment&quot;&gt;# respects .gitignore by default&lt;/span&gt;
rg --no-ignore &lt;span class=&quot;token string&quot;&gt;&quot;deprecated_api&quot;&lt;/span&gt;    &lt;span class=&quot;token comment&quot;&gt;# opt back into ignored files&lt;/span&gt;
rg &lt;span class=&quot;token parameter variable&quot;&gt;--hidden&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;deprecated_api&quot;&lt;/span&gt;       &lt;span class=&quot;token comment&quot;&gt;# include dotfiles&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;rg&lt;/code&gt; est aussi le moteur derrière la recherche de projet de VS Code, ce qui explique pourquoi « Find in Files » ressemble exactement à un &lt;code class=&quot;language-text&quot;&gt;rg&lt;/code&gt; lancé dans un terminal. Il gère solidement Unicode, et sur la plupart des corpus modernes, il est au moins aussi rapide que &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt;, souvent plus rapide — le &lt;a href=&quot;https://github.com/BurntSushi/ripgrep/blob/master/README.md&quot;&gt;benchmark du noyau Linux dans le README de ripgrep&lt;/a&gt; montre ripgrep battant &lt;code class=&quot;language-text&quot;&gt;git grep -P&lt;/code&gt; d’environ 3x sur la même requête. (Astuce : si vous voulez le comportement « sensible à la casse seulement quand le motif contient une majuscule », passez &lt;code class=&quot;language-text&quot;&gt;-S&lt;/code&gt; pour smart-case — c’est opt-in, pas le défaut.)&lt;/p&gt;
&lt;p&gt;Si vous n’avez pas encore installé &lt;code class=&quot;language-text&quot;&gt;rg&lt;/code&gt;, corrigez ça :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;brew &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; ripgrep   &lt;span class=&quot;token comment&quot;&gt;# macOS&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;apt&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; ripgrep    &lt;span class=&quot;token comment&quot;&gt;# Debian/Ubuntu&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;cargo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; ripgrep  &lt;span class=&quot;token comment&quot;&gt;# anywhere with Rust&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Mettez &lt;code class=&quot;language-text&quot;&gt;rg&lt;/code&gt; à côté de &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; dans votre boîte à outils. Ils couvrent des tâches différentes.&lt;/p&gt;
&lt;h3&gt;Avantages de &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pertinence.&lt;/strong&gt; Il cherche seulement dans ce que vous suivez. Les sorties de build, caches et &lt;code class=&quot;language-text&quot;&gt;node_modules&lt;/code&gt; ne sont pas sur votre chemin — parce que Git ne les a jamais vus.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Vitesse sur les gros dépôts.&lt;/strong&gt; Multi-threadé, sans parcours du système de fichiers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Portée historique.&lt;/strong&gt; Toute branche, tout tag ou tout commit, sans quitter votre working tree. C’est la partie que &lt;code class=&quot;language-text&quot;&gt;rg&lt;/code&gt; ne peut pas faire.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Moins de bruit binaire.&lt;/strong&gt; Comme &lt;code class=&quot;language-text&quot;&gt;grep&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; signale les binaires avec « Binary file X matches » au lieu de déverser des octets — mais comme il parcourt les fichiers suivis, il en rencontre généralement moins dès le départ. Passez &lt;code class=&quot;language-text&quot;&gt;-I&lt;/code&gt; pour ignorer entièrement les binaires.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Conseils supplémentaires&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Paginer les résultats :&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;searchTerm&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;less&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Compter les correspondances par fichier :&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-c&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;searchTerm&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Afficher les numéros de ligne :&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;searchTerm&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Ouvrir chaque correspondance dans votre éditeur :&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-l&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;searchTerm&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;xargs&lt;/span&gt; code&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Remplacez &lt;code class=&quot;language-text&quot;&gt;code&lt;/code&gt; par &lt;code class=&quot;language-text&quot;&gt;nvim&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;subl&lt;/code&gt;, ou ce que vous utilisez.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Tout comme Alaric a trouvé un chemin caché dans sa bibliothèque labyrinthique, &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; trace une ligne nette à travers une base de code suivie : rapide, conscient des branches, et dégagé de tout ce dont Git n’a jamais entendu parler. Ce n’est pas un remplacement universel de &lt;code class=&quot;language-text&quot;&gt;grep&lt;/code&gt;, et ce n’est pas un remplacement de &lt;code class=&quot;language-text&quot;&gt;rg&lt;/code&gt;. C’est l’outil qui connaît &lt;em&gt;l’index&lt;/em&gt; de votre dépôt, et une fois que vous l’atteignez, le labyrinthe devient beaucoup plus petit.&lt;/p&gt;
&lt;p&gt;Utilisez &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; quand la question est « où dans cette base de code, y compris dans son historique ». Utilisez &lt;code class=&quot;language-text&quot;&gt;rg&lt;/code&gt; quand la question est « où sur le disque, en respectant mes règles d’ignore ». La plupart des jours, vous voudrez les deux à portée de main.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Mis à jour le 2026-04-27 : correction d’une affirmation précédente selon laquelle &lt;code class=&quot;language-text&quot;&gt;git grep&lt;/code&gt; respecte &lt;code class=&quot;language-text&quot;&gt;.gitignore&lt;/code&gt; (ce n’est pas le cas directement), assouplissement de l’explication sur « l’indexation interne », correction d’un exemple de regex, et ajout d’une section sur le moment où utiliser plutôt &lt;code class=&quot;language-text&quot;&gt;rg&lt;/code&gt;.&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[PHP 8.3.6 + IMAP sur macOS avec phpenv : guide d'installation]]></title><description><![CDATA[Important (mise à jour 2024-11) : PHP 8.4 a retiré ext-imap du cœur. L'extension a été déplacée vers PECL et elle est, dans les faits,…]]></description><link>https://bdteo.com/fr/installing-php-8-3-6-with-imap-on-macos-using-phpenv/</link><guid isPermaLink="false">https://bdteo.com/fr/installing-php-8-3-6-with-imap-on-macos-using-phpenv/</guid><pubDate>Sun, 01 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important (mise à jour 2024-11) :&lt;/strong&gt; PHP 8.4 a &lt;strong&gt;retiré ext-imap&lt;/strong&gt; du cœur. L&apos;extension a été déplacée vers PECL et elle est, dans les faits, dépréciée -- la bibliothèque C sous-jacente (libc-client) n&apos;a pas été mise à jour depuis 2018. Si vous démarrez un nouveau projet ou si vous êtes sur PHP 8.4+, allez directement à &lt;a href=&quot;#avez-vous-vraiment-besoin-de-ext-imap&quot;&gt;Avez-vous vraiment besoin de ext-imap ?&lt;/a&gt; pour des alternatives modernes. Si vous êtes sur PHP 8.3 ou une version antérieure et que vous avez besoin de l&apos;extension native, ce guide fonctionne toujours.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;La correction rapide&lt;/h2&gt;
&lt;p&gt;Si vous voulez simplement les commandes, sans vous soucier du pourquoi -- voici tout, dans l&apos;ordre. Vous devez déjà avoir installé Homebrew et phpenv.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# 1. Install all required libraries&lt;/span&gt;
brew &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; tidy-html5 openssl@3 zlib &lt;span class=&quot;token function&quot;&gt;bzip2&lt;/span&gt; libedit readline &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  gettext libiconv libsodium krb5 imap-uw &lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# 2. Set build configuration (add to ~/.zshrc or run before install)&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;PHP_BUILD_CONFIGURE_OPTS&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;--with-openssl=&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;brew &lt;span class=&quot;token parameter variable&quot;&gt;--prefix&lt;/span&gt; openssl@3&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt; \
  --with-zlib=&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;brew &lt;span class=&quot;token parameter variable&quot;&gt;--prefix&lt;/span&gt; zlib&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt; \
  --with-bz2=&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;brew &lt;span class=&quot;token parameter variable&quot;&gt;--prefix&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;bzip2&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt; \
  --with-curl \
  --with-libedit \
  --with-readline=&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;brew &lt;span class=&quot;token parameter variable&quot;&gt;--prefix&lt;/span&gt; readline&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt; \
  --with-gettext=&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;brew &lt;span class=&quot;token parameter variable&quot;&gt;--prefix&lt;/span&gt; gettext&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt; \
  --with-iconv=&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;brew &lt;span class=&quot;token parameter variable&quot;&gt;--prefix&lt;/span&gt; libiconv&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt; \
  --with-sodium=&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;brew &lt;span class=&quot;token parameter variable&quot;&gt;--prefix&lt;/span&gt; libsodium&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt; \
  --with-kerberos=&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;brew &lt;span class=&quot;token parameter variable&quot;&gt;--prefix&lt;/span&gt; krb5&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt; \
  --with-imap=&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;brew &lt;span class=&quot;token parameter variable&quot;&gt;--prefix&lt;/span&gt; imap-uw&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt; \
  --with-imap-ssl=&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;brew &lt;span class=&quot;token parameter variable&quot;&gt;--prefix&lt;/span&gt; openssl@3&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt; \
  --with-tidy=&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;brew &lt;span class=&quot;token parameter variable&quot;&gt;--prefix&lt;/span&gt; tidy-html5&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt; \
  --enable-mbstring \
  --enable-intl&quot;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# 3. Help the compiler find headers&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;CPPFLAGS&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;-I&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;brew &lt;span class=&quot;token parameter variable&quot;&gt;--prefix&lt;/span&gt; openssl@3&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;/include -I&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;brew &lt;span class=&quot;token parameter variable&quot;&gt;--prefix&lt;/span&gt; imap-uw&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;/include &lt;span class=&quot;token variable&quot;&gt;$CPPFLAGS&lt;/span&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# 4. Build and install&lt;/span&gt;
phpenv &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8.3&lt;/span&gt;.6
phpenv global &lt;span class=&quot;token number&quot;&gt;8.3&lt;/span&gt;.6

&lt;span class=&quot;token comment&quot;&gt;# 5. Verify&lt;/span&gt;
php &lt;span class=&quot;token parameter variable&quot;&gt;-v&lt;/span&gt;
php &lt;span class=&quot;token parameter variable&quot;&gt;-m&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; imap&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Si &lt;code class=&quot;language-text&quot;&gt;imap&lt;/code&gt; apparaît dans la sortie, c&apos;est terminé. Sinon, continuez.&lt;/p&gt;
&lt;h2&gt;Avez-vous vraiment besoin de ext-imap ?&lt;/h2&gt;
&lt;p&gt;Question sérieuse. Avant de vous battre avec des bibliothèques C et des flags de compilation, demandez-vous si vous avez vraiment besoin de l&apos;extension IMAP native.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;PHP 8.4 a retiré ext-imap du cœur.&lt;/strong&gt; Elle a été déplacée vers PECL, et la bibliothèque C sous-jacente (&lt;code class=&quot;language-text&quot;&gt;libc-client&lt;/code&gt; / UW-IMAP) n&apos;a pas reçu de mise à jour depuis 2018. Elle a des problèmes de thread-safety, pas de support XAUTH, et des bugs POP. Elle ne reviendra pas.&lt;/p&gt;
&lt;p&gt;L&apos;alternative moderne est &lt;a href=&quot;https://github.com/Webklex/php-imap&quot;&gt;&lt;strong&gt;Webklex/php-imap&lt;/strong&gt;&lt;/a&gt; -- une implémentation IMAP en pur PHP :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;composer&lt;/span&gt; require webklex/php-imap&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;C&apos;est tout. Pas de dépendances brew, pas de flags de compilation, pas de chasse aux fichiers d&apos;en-tête. Elle fonctionne sur PHP 8.0.2+ (y compris 8.4 et 8.5), prend en charge IMAP IDLE et OAuth, et compte plus de 5 millions d&apos;installations sur Packagist. Il existe aussi une &lt;a href=&quot;https://github.com/Webklex/laravel-imap&quot;&gt;intégration Laravel&lt;/a&gt; si c&apos;est votre stack.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;N&apos;utilisez ext-imap que si&lt;/strong&gt; vous maintenez une base de code legacy sur PHP 8.3 ou une version antérieure, qui dépend déjà des fonctions &lt;code class=&quot;language-text&quot;&gt;imap_*&lt;/code&gt;, et que vous ne pouvez pas encore migrer.&lt;/p&gt;
&lt;h2&gt;Ce que fait chaque étape&lt;/h2&gt;
&lt;p&gt;Si la correction rapide a fonctionné, vous pouvez arrêter de lire. Si elle n&apos;a pas fonctionné -- ou si vous voulez comprendre ce qui se passe -- voici le détail.&lt;/p&gt;
&lt;h3&gt;Les dépendances Brew&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;brew &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; tidy-html5 openssl@3 zlib &lt;span class=&quot;token function&quot;&gt;bzip2&lt;/span&gt; libedit readline &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  gettext libiconv libsodium krb5 imap-uw &lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;macOS fournit certaines d&apos;entre elles, mais phpenv a besoin des versions Homebrew, avec les bons headers et fichiers pkg-config. La dépendance critique est &lt;code class=&quot;language-text&quot;&gt;imap-uw&lt;/code&gt; -- c&apos;est la bibliothèque UW-IMAP qui fournit &lt;code class=&quot;language-text&quot;&gt;libc-client&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;PHP_BUILD_CONFIGURE_OPTS&lt;/h3&gt;
&lt;p&gt;phpenv utilise php-build sous le capot, qui lance &lt;code class=&quot;language-text&quot;&gt;./configure&lt;/code&gt; sur les sources de PHP. La variable &lt;code class=&quot;language-text&quot;&gt;PHP_BUILD_CONFIGURE_OPTS&lt;/code&gt; passe des flags directement à configure. Chaque flag &lt;code class=&quot;language-text&quot;&gt;--with-*&lt;/code&gt; indique à PHP où trouver une bibliothèque précise.&lt;/p&gt;
&lt;p&gt;Les plus importants pour IMAP :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;--with-imap=$(brew --prefix imap-uw)&lt;/code&gt; -- pointe vers la bibliothèque IMAP&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;--with-imap-ssl=$(brew --prefix openssl@3)&lt;/code&gt; -- active IMAP via SSL&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;--with-kerberos=$(brew --prefix krb5)&lt;/code&gt; -- requis pour l&apos;authentification IMAP&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;La correction CPPFLAGS&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;CPPFLAGS&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;-I&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;brew &lt;span class=&quot;token parameter variable&quot;&gt;--prefix&lt;/span&gt; openssl@3&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;/include -I&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;brew &lt;span class=&quot;token parameter variable&quot;&gt;--prefix&lt;/span&gt; imap-uw&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;/include &lt;span class=&quot;token variable&quot;&gt;$CPPFLAGS&lt;/span&gt;&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Même avec les flags configure, le préprocesseur C n&apos;arrive parfois pas à trouver les fichiers d&apos;en-tête. Cela arrive parce que macOS ne met pas les headers Homebrew dans le chemin de recherche par défaut. Les deux headers qui échouent le plus souvent :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;openssl/ssl.h&lt;/code&gt; -- depuis OpenSSL&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;imap/imap.h&lt;/code&gt; -- depuis imap-uw&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;La correction PATH&lt;/h3&gt;
&lt;p&gt;Si &lt;code class=&quot;language-text&quot;&gt;php -v&lt;/code&gt; affiche la mauvaise version après l&apos;installation, les shims de phpenv ne sont pas dans votre PATH (ou autre chose les masque). Ajoutez ceci à &lt;code class=&quot;language-text&quot;&gt;~/.zshrc&lt;/code&gt; :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;PHPENV_ROOT&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token environment constant&quot;&gt;$HOME&lt;/span&gt;/.phpenv&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;${PHPENV_ROOT}&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
  &lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;&lt;span class=&quot;token environment constant&quot;&gt;PATH&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;${PHPENV_ROOT}&lt;/span&gt;/shims:&lt;span class=&quot;token variable&quot;&gt;${PHPENV_ROOT}&lt;/span&gt;/bin:&lt;span class=&quot;token environment constant&quot;&gt;$PATH&lt;/span&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;token builtin class-name&quot;&gt;eval&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;phpenv init -&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Puis lancez &lt;code class=&quot;language-text&quot;&gt;source ~/.zshrc&lt;/code&gt; et réessayez.&lt;/p&gt;
&lt;h3&gt;L&apos;erreur utf8_mime2text&lt;/h3&gt;
&lt;p&gt;Si vous voyez ceci :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;configure: error: utf8_mime2text() has new signature, but U8T_CANONICAL is missing.
This should not happen.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Votre bibliothèque &lt;code class=&quot;language-text&quot;&gt;imap-uw&lt;/code&gt; est obsolète ou cassée. Corrigez-la avec :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;brew upgrade imap-uw&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Puis relancez l&apos;installation.&lt;/p&gt;
&lt;h2&gt;L&apos;avenir de PHP + IMAP&lt;/h2&gt;
&lt;p&gt;Tout est déjà annoncé. ext-imap est dépréciée, la bibliothèque C sous-jacente est abandonnée, et PHP 8.4 l&apos;a déjà retirée du cœur. Si vous lisez ceci parce que vous avez besoin d&apos;IMAP dans un projet PHP, commencez à planifier votre migration vers &lt;code class=&quot;language-text&quot;&gt;webklex/php-imap&lt;/code&gt;. L&apos;extension native vit sur du temps emprunté.&lt;/p&gt;
&lt;p&gt;Pour celles et ceux d&apos;entre nous qui maintiennent des bases de code legacy -- ce guide continuera à fonctionner pour PHP 8.3 et les versions antérieures. Mais ne démarrez pas de nouveaux projets avec ext-imap. Il n&apos;y a aucune raison de se battre avec la compilation d&apos;une bibliothèque C quand une solution en pur PHP existe.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Références&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://php.watch/versions/8.4/imap-unbundled&quot;&gt;PHP 8.4: IMAP Extension Unbundled&lt;/a&gt; -- &lt;em&gt;Documentation PHP.Watch sur le retrait de ext-imap.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/Webklex/php-imap&quot;&gt;Webklex/php-imap on GitHub&lt;/a&gt; -- &lt;em&gt;Implémentation IMAP en pur PHP (5M+ installations Packagist).&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.php.net/manual/en/book.imap.php&quot;&gt;PHP Manual: IMAP Extension&lt;/a&gt; -- &lt;em&gt;Documentation officielle avec avis de dépréciation.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/shivammathur/homebrew-extensions&quot;&gt;shivammathur/extensions: IMAP for PHP 8.3+&lt;/a&gt; -- &lt;em&gt;Tap Homebrew pour les extensions PHP, dont IMAP.&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3&gt;Articles connexes&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/fr/php-8-5-new-features-pipe-operator-guide/&quot;&gt;PHP 8.5 : tour des fonctionnalités à venir&lt;/a&gt; -- ce qui arrive ensuite dans PHP.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/fr/understanding-class-namespace-changes-shopware-6-5-developers-guide/&quot;&gt;Guide dev : changements de classes et namespaces Shopware 6.5/6.6&lt;/a&gt; -- un autre guide de migration PHP pour les développeurs Shopware.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/laravel-sail-vs-laradock-choosing-right-docker-solution/&quot;&gt;Laravel Sail vs Laradock&lt;/a&gt; -- choisir la bonne configuration Docker pour le développement PHP.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Laravel Sail vs Laradock : comparaison pour développeurs PHP avec Docker]]></title><description><![CDATA[TL;DR : Pour la plupart des développeurs Laravel en 2026 : utilisez Laravel Herd si vous voulez zéro friction (natif, sans Docker, installé…]]></description><link>https://bdteo.com/fr/laravel-sail-vs-laradock-choosing-right-docker-solution/</link><guid isPermaLink="false">https://bdteo.com/fr/laravel-sail-vs-laradock-choosing-right-docker-solution/</guid><pubDate>Thu, 08 Aug 2024 12:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR :&lt;/strong&gt; Pour la plupart des développeurs Laravel en 2026 : utilisez &lt;strong&gt;Laravel Herd&lt;/strong&gt; si vous voulez zéro friction (natif, sans Docker, installé en quelques secondes). Utilisez &lt;strong&gt;Sail&lt;/strong&gt; si votre équipe a besoin d&apos;environnements identiques ou si vous dépendez de services comme Redis/Meilisearch. Utilisez &lt;strong&gt;Laradock&lt;/strong&gt; si vous travaillez avec plusieurs frameworks PHP. Utilisez une configuration &lt;strong&gt;Docker Compose personnalisée&lt;/strong&gt; si vous avez dépassé les abstractions. Et si la performance est absolument centrale, regardez &lt;strong&gt;FrankenPHP + Octane&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Publié initialement en août 2024. Mis à jour en mars 2026 avec Laravel 12/Herd/FrankenPHP et l&apos;état actuel de l&apos;écosystème.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;La question était autrefois : « Sail ou Laradock ? » Ce cadrage est trop étroit maintenant. La vraie question est : &lt;strong&gt;comment devrais-je configurer mon environnement de développement Laravel en 2026 ?&lt;/strong&gt; Il y a plus d&apos;options que jamais, et le meilleur choix dépend de ce dont vous avez réellement besoin.&lt;/p&gt;
&lt;p&gt;J&apos;ai utilisé la plupart de ces outils. Aujourd&apos;hui, j&apos;utilise une configuration Docker Compose personnalisée parce que je veux garder un contrôle complet sur mes conteneurs, sans abstractions qui cachent ce qui se passe. Mais c&apos;est ma préférence, pas une recommandation universelle. Voyons ce que chaque option vous apporte.&lt;/p&gt;
&lt;h2&gt;Les concurrents&lt;/h2&gt;
&lt;h3&gt;Laravel Herd&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://herd.laravel.com/&quot;&gt;Herd&lt;/a&gt; est l&apos;option la plus récente et, pour beaucoup de développeurs, la bonne. C&apos;est une application native (macOS et Windows -- pas encore Linux) qui vous donne PHP, Nginx, Node.js et Dnsmasq sans Docker. La version Pro ajoute MySQL, PostgreSQL, Redis et des outils de débogage.&lt;/p&gt;
&lt;p&gt;La fonctionnalité décisive : changer de version PHP en quelques secondes (de 7.4 à 8.4), router automatiquement les domaines &lt;code class=&quot;language-text&quot;&gt;*.test&lt;/code&gt;, et n&apos;avoir aucun surcoût Docker. Si vous construisez une application Laravel standard et que vous n&apos;avez pas besoin de services exotiques, Herd vous fait écrire du code en moins d&apos;une minute.&lt;/p&gt;
&lt;h3&gt;Laravel Sail&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://laravel.com/docs/12.x/sail&quot;&gt;Sail&lt;/a&gt; est l&apos;environnement de développement officiel de Laravel basé sur Docker. Il enveloppe Docker Compose avec une CLI &lt;code class=&quot;language-text&quot;&gt;sail&lt;/code&gt; qui abstrait les commandes courantes (&lt;code class=&quot;language-text&quot;&gt;sail up&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;sail artisan&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;sail php&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Depuis Laravel 12, Sail arrive avec PHP 8.5 par défaut, utilise &lt;code class=&quot;language-text&quot;&gt;compose.yaml&lt;/code&gt; (le nom de fichier moderne, pas &lt;code class=&quot;language-text&quot;&gt;docker-compose.yml&lt;/code&gt;) et inclut Swoole pour Octane prêt à l&apos;emploi. Il prend aussi en charge la génération de devcontainer via &lt;code class=&quot;language-text&quot;&gt;--devcontainer&lt;/code&gt; pour l&apos;intégration VS Code/GitHub Codespaces.&lt;/p&gt;
&lt;p&gt;Services par défaut : PHP, MySQL, Redis, Meilisearch, Mailpit et Selenium.&lt;/p&gt;
&lt;h3&gt;Laradock&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://laradock.io/&quot;&gt;Laradock&lt;/a&gt; est le couteau suisse. C&apos;est un environnement Docker open source qui prend en charge n&apos;importe quel projet PHP -- pas seulement Laravel. Il propose plus de 70 services préconfigurés et peut être configuré pour la production.&lt;/p&gt;
&lt;p&gt;Il est toujours activement maintenu en décembre 2025 (mises à jour récentes des images PHP-FPM et workspace). Le compromis, c&apos;est la complexité : l&apos;installation prend plus de temps, la configuration implique de modifier plusieurs fichiers, et il faut une vraie connaissance de Docker.&lt;/p&gt;
&lt;h3&gt;FrankenPHP + Octane&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://frankenphp.dev/&quot;&gt;FrankenPHP&lt;/a&gt; est un serveur d&apos;applications PHP moderne construit sur Caddy. Combiné à Laravel Octane, il atteint un temps de démarrage du framework de 4 à 6 ms par requête -- un développeur a rapporté une latence passée de 7 secondes à 66 ms en basculant vers le Worker Mode &lt;small&gt;&lt;a href=&quot;#ref1&quot;&gt;[1]&lt;/a&gt;&lt;/small&gt;.&lt;/p&gt;
&lt;p&gt;Laravel Cloud utilise FrankenPHP dans son runtime Octane en production &lt;small&gt;&lt;a href=&quot;#ref2&quot;&gt;[2]&lt;/a&gt;&lt;/small&gt;. La dernière version (v1.11.2, février 2026) a apporté un CGO 30 % plus rapide et un ramasse-miettes 40 % plus rapide grâce à Go 1.26.&lt;/p&gt;
&lt;p&gt;Ce n&apos;est pas un environnement de développement au sens traditionnel -- c&apos;est un runtime PHP de niveau production que vous pouvez aussi utiliser en développement. Sail inclut une intégration pour exécuter Octane avec FrankenPHP ou Swoole.&lt;/p&gt;
&lt;h2&gt;Quand utiliser quoi&lt;/h2&gt;
&lt;p&gt;Voici mon avis honnête, basé sur l&apos;utilisation réelle de ces outils :&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Utilisez Herd si&lt;/strong&gt; vous êtes seul ou dans une petite équipe, que vous construisez des applications Laravel standard, et que vous voulez passer zéro temps sur l&apos;infrastructure. C&apos;est le chemin le plus rapide entre « j&apos;ai une idée » et « j&apos;écris du code ». La limite, c&apos;est qu&apos;il n&apos;existe que pour macOS/Windows et que la version gratuite n&apos;inclut pas les bases de données.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Utilisez Sail si&lt;/strong&gt; votre équipe a besoin de parité d&apos;environnement, que vous dépendez de versions précises de services (Redis 7, MySQL 8, PostgreSQL 15), ou que vous travaillez dans une chaîne CI/CD qui a besoin de Docker. La commande &lt;code class=&quot;language-text&quot;&gt;sail:publish&lt;/code&gt; de Sail vous permet de personnaliser la configuration Docker quand vous dépassez les valeurs par défaut.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Utilisez Laradock si&lt;/strong&gt; vous travaillez avec plusieurs frameworks PHP (Symfony, Shopware, PHP vanilla), que vous avez besoin de services exotiques (Aerospike, RethinkDB, Manticore), ou que vous voulez un seul environnement Docker pour plusieurs projets. La courbe d&apos;apprentissage est plus raide, mais la flexibilité est inégalée.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Utilisez une configuration Docker Compose personnalisée si&lt;/strong&gt; vous avez dépassé Sail et Laradock et que vous voulez un contrôle complet. C&apos;est ce que je fais. Je maintiens mon propre &lt;code class=&quot;language-text&quot;&gt;compose.yaml&lt;/code&gt; avec exactement les services dont j&apos;ai besoin, sans couche d&apos;abstraction, et avec des alias Docker Compose pour garder les commandes courtes. Cela demande plus de travail au départ, mais il n&apos;y a pas de magie -- tout est explicite.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Utilisez FrankenPHP + Octane si&lt;/strong&gt; vous construisez une API à haute performance ou si votre application est sensible à la latence. L&apos;écart de performance n&apos;est pas marginal -- c&apos;est un ordre de grandeur. Cela vaut la peine de l&apos;explorer, même si vous utilisez un autre outil pour le développement général.&lt;/p&gt;
&lt;h2&gt;Les détails qui comptent&lt;/h2&gt;
&lt;h3&gt;Temps d&apos;installation&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Outil&lt;/th&gt;
&lt;th&gt;Temps avant la première requête&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Herd&lt;/td&gt;
&lt;td&gt;Moins d&apos;une minute&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sail&lt;/td&gt;
&lt;td&gt;5-10 minutes (téléchargement des images)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compose personnalisé&lt;/td&gt;
&lt;td&gt;30-60 minutes (configuration initiale)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Laradock&lt;/td&gt;
&lt;td&gt;1-2 heures (configuration complète)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;Personnalisation&lt;/h3&gt;
&lt;p&gt;Sail est volontairement limité. Vous obtenez les services dont Laravel a besoin, et pas beaucoup plus. Vous &lt;em&gt;pouvez&lt;/em&gt; personnaliser en lançant &lt;code class=&quot;language-text&quot;&gt;sail:publish&lt;/code&gt; et en modifiant les Dockerfiles, mais à ce stade vous maintenez une configuration Docker personnalisée avec les abstractions de Sail par-dessus -- le pire des deux mondes.&lt;/p&gt;
&lt;p&gt;Laradock vous donne tout, mais exige que vous compreniez ce que vous activez. Activer un service signifie modifier &lt;code class=&quot;language-text&quot;&gt;.env&lt;/code&gt; et peut-être &lt;code class=&quot;language-text&quot;&gt;docker-compose.yml&lt;/code&gt;, et certains services ont leurs propres répertoires de configuration.&lt;/p&gt;
&lt;p&gt;Compose personnalisé vous donne exactement ce que vous écrivez. Rien de plus, rien de moins.&lt;/p&gt;
&lt;h3&gt;Prêt pour la production&lt;/h3&gt;
&lt;p&gt;Sail n&apos;est explicitement pas destiné à la production. Laradock peut être configuré pour la production, mais vous devez savoir ce que vous faites en matière de durcissement de sécurité, de limites de ressources et de réseau correct. FrankenPHP est prêt pour la production par conception -- il est construit pour ça.&lt;/p&gt;
&lt;h3&gt;Support multi-projets&lt;/h3&gt;
&lt;p&gt;Sail : un projet par environnement. Vous pouvez lancer plusieurs instances Sail, mais elles vont se battre pour les ports.&lt;/p&gt;
&lt;p&gt;Laradock : conçu pour les configurations multi-projets. Un environnement, plusieurs projets, services partagés.&lt;/p&gt;
&lt;p&gt;Compose personnalisé : ce que vous décidez d&apos;architecturer. Je garde des fichiers compose séparés par projet, avec des définitions de réseau partagées.&lt;/p&gt;
&lt;h2&gt;Ce que j&apos;utilise vraiment&lt;/h2&gt;
&lt;p&gt;Docker Compose personnalisé. J&apos;ai des alias pour tout -- &lt;code class=&quot;language-text&quot;&gt;dcu&lt;/code&gt; pour &lt;code class=&quot;language-text&quot;&gt;docker compose up -d&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;dce&lt;/code&gt; pour exec, &lt;code class=&quot;language-text&quot;&gt;dcefpm&lt;/code&gt; pour l&apos;accès shell PHP-FPM, et une fonction &lt;code class=&quot;language-text&quot;&gt;sail&lt;/code&gt; qui découvre automatiquement la racine du projet. La configuration est dans mes &lt;a href=&quot;/fr/docker-compose-major-changes-since-october-2023/&quot;&gt;notes d&apos;évolution Docker Compose&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;J&apos;ai commencé avec Laradock il y a des années, je suis passé à Sail à son lancement, puis j&apos;ai fini par me poser sur une configuration personnalisée parce que je voulais comprendre exactement ce qui tournait et pourquoi. Chaque abstraction cache des décisions. Parfois c&apos;est très bien. Parfois ces décisions cachées causent des problèmes difficiles à déboguer parce qu&apos;on ne peut pas les voir.&lt;/p&gt;
&lt;p&gt;Cela dit, si je démarrais aujourd&apos;hui un nouveau projet Laravel avec une équipe qui ne se soucie pas des entrailles de Docker, j&apos;utiliserais Sail. Et si j&apos;accompagnais quelqu&apos;un qui débute avec Laravel, je lui dirais d&apos;installer Herd et de commencer à écrire du code immédiatement.&lt;/p&gt;
&lt;h2&gt;Autres options à mentionner&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://ddev.com/&quot;&gt;DDEV&lt;/a&gt;&lt;/strong&gt; -- basé sur Docker, bon support Laravel, feuille de route 2026 active avec intégration Gitpod prévue. À évaluer si vous l&apos;utilisez déjà pour d&apos;autres projets CMS (WordPress, Drupal).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://lando.dev/&quot;&gt;Lando&lt;/a&gt;&lt;/strong&gt; -- une autre couche d&apos;abstraction Docker avec un plugin Laravel (v1.10.0, janvier 2026). Philosophie similaire à Sail, mais agnostique côté framework.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Valet&lt;/strong&gt; -- le prédécesseur de Herd. Il fonctionne toujours, mais Herd l&apos;a supplanté pour la plupart des usages.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;Références&lt;/h3&gt;
&lt;p&gt;&lt;a id=&quot;ref1&quot;&gt;&lt;/a&gt;1. &lt;a href=&quot;https://medium.com/@danarcahyaa/setup-and-boost-your-laravel-app-with-frankenphp-worker-mode-c0228f44f71b&quot;&gt;Configurer et accélérer Laravel avec FrankenPHP Worker Mode&lt;/a&gt; -- &lt;em&gt;Comparaison de performance en conditions réelles : latence de 7 s à 66 ms.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref2&quot;&gt;&lt;/a&gt;2. &lt;a href=&quot;https://devconf.net/talk/florian-beer-how-laravel-cloud-uses-frankenphp-in-production&quot;&gt;Comment Laravel Cloud utilise FrankenPHP en production&lt;/a&gt; -- &lt;em&gt;Conférence DevConf sur le runtime Octane de Laravel Cloud.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref3&quot;&gt;&lt;/a&gt;3. &lt;a href=&quot;https://laravel.com/docs/12.x/sail&quot;&gt;Documentation Laravel 12.x Sail&lt;/a&gt; -- &lt;em&gt;Documentation officielle de Sail avec les changements PHP 8.5 et compose.yaml.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref4&quot;&gt;&lt;/a&gt;4. &lt;a href=&quot;https://herd.laravel.com/&quot;&gt;Laravel Herd&lt;/a&gt; -- &lt;em&gt;Site officiel de l&apos;environnement de développement Laravel natif.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref5&quot;&gt;&lt;/a&gt;5. &lt;a href=&quot;https://laravel-news.com/frankenphp-v1112-released-with-30-faster-cgo-40-faster-gc-and-security-patches&quot;&gt;Publication de FrankenPHP v1.11.2&lt;/a&gt; -- &lt;em&gt;Version de février 2026 avec améliorations de performance et correctifs de sécurité.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref6&quot;&gt;&lt;/a&gt;6. &lt;a href=&quot;https://github.com/laradock/laradock&quot;&gt;Laradock sur GitHub&lt;/a&gt; -- &lt;em&gt;Toujours maintenu, mises à jour de décembre 2025.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref7&quot;&gt;&lt;/a&gt;7. &lt;a href=&quot;https://aschmelyun.com/blog/the-current-state-of-local-laravel-development/&quot;&gt;L&apos;état actuel du développement Laravel local&lt;/a&gt; -- &lt;em&gt;Vue d&apos;ensemble de l&apos;écosystème par Andrew Schmelyun.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Articles liés&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/fr/docker-compose-major-changes-since-october-2023/&quot;&gt;Évolution de Docker Compose : ce qui a changé et pourquoi c&apos;est important&lt;/a&gt; -- les changements Docker Compose qui affectent tous ces outils.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/fr/php-8-5-new-features-pipe-operator-guide/&quot;&gt;PHP 8.5 : tour d&apos;horizon des nouvelles fonctionnalités&lt;/a&gt; -- ce qui arrive dans la version de PHP que Laravel 12 utilise par défaut.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/installing-php-8-3-6-with-imap-on-macos-using-phpenv/&quot;&gt;PHP 8.3.6 + IMAP sur macOS avec phpenv&lt;/a&gt; -- quand vous avez besoin d&apos;une configuration PHP spécifique en dehors de Docker.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Le pire hypocrite : conte d'amour de soi d'un canard en caoutchouc]]></title><description><![CDATA[De Boris à une amie chère :
~ La personne que j'ai toujours eu du mal à comprendre, mais la plus compréhensive de toutes. ~ Salut toi…]]></description><link>https://bdteo.com/fr/worst-hypocrite-rubber-duck-tale/</link><guid isPermaLink="false">https://bdteo.com/fr/worst-hypocrite-rubber-duck-tale/</guid><pubDate>Wed, 17 Jul 2024 12:00:00 GMT</pubDate><content:encoded>&lt;p&gt;De Boris à une amie chère :
~ La personne que j&apos;ai toujours eu du mal à comprendre, mais la plus compréhensive de toutes. ~&lt;/p&gt;
&lt;p&gt;Salut toi. Rebonjour. Est-ce qu&apos;on peut se rencontrer de nouveau pour la première fois ?
Oui. Je crois que cette fois ce sera plus cool. Je serai plus mièvre que jamais 🧀, j&apos;essaierai de te faire rire davantage mais j&apos;échouerai encore parfois, ou la plupart du temps, ou peu importe...
Ce sera presque comme la dernière fois mais pas comme la dernière fois. Note : tu peux faire semblant que ma dernière phrase a du sens, juste pour le raffinement 🥸&lt;/p&gt;
&lt;p&gt;Laisse-moi commencer par fournir un petit dictionnaire, ou une intro, ou une petite base de connaissances :&lt;/p&gt;
&lt;h2&gt;Un « commencement » à l&apos;envers, ça fait « tnemecnemmoc »&lt;/h2&gt;
&lt;p&gt;J&apos;avais juste besoin d&apos;un nouveau titre pour que mon paragraphe soit plus joli. Bref, nouveau moi. Nouvelle toi. Vieux vêtements et quelques vieilles mauvaises habitudes. Ouf !&lt;/p&gt;
&lt;h2&gt;Un miroir sur le mur&lt;/h2&gt;
&lt;p&gt;Tu m&apos;as beaucoup manqué et j&apos;ai longtemps repensé à mes erreurs. Les miroirs existent bien pour nous rendre plus jolis et moins sales.&lt;/p&gt;
&lt;h2&gt;Un canard en caoutchouc&lt;/h2&gt;
&lt;p&gt;C&apos;est moi. Je suis un canard en caoutchouc. Je suis fait de caoutchouc, et ça veut dire que ma tête aussi est faite de caoutchouc.&lt;/p&gt;
&lt;h2&gt;Les canards en caoutchouc ne font pas « coin-coin ». Ou si ?&lt;/h2&gt;
&lt;p&gt;Dans mon humble expérience de vie de canard en caoutchouc, je fais bien « coin-coin » et je le fais assez souvent. 😆&lt;/p&gt;
&lt;h3&gt;Un canard en caoutchouc qui fait coin-coin fait coin-coin&lt;/h3&gt;
&lt;p&gt;Coin ! Coin ! Coin ! - dit le canard, et il fit encore coin-coin. Quel son incroyable ! N&apos;est-ce pas ?&lt;/p&gt;
&lt;h3&gt;Un canard en caoutchouc qui a du caractère&lt;/h3&gt;
&lt;p&gt;Je suis le plus gentil canard en caoutchouc de tous, dit le canard en caoutchouc en faisant coin-coin avec enthousiasme ! Coin !&lt;/p&gt;
&lt;h3&gt;La vraie chute des canards en caoutchouc&lt;/h3&gt;
&lt;p&gt;Faire coin-coin peut être assez épuisant, tu sais. On ne peut faire coin-coin gentiment qu&apos;un nombre limité de fois avec une bonne bouffée d&apos;air frais.
Mais tu peux quand même essayer de faire coin-coin gentiment quand tu es vide ? N&apos;est-ce pas ?
Bien sûr que tu peux ! Tu peux essayer de faire coin-coin ! Tu peux même donner le meilleur quickity-quack de ton caoutchouc pour aider les autres canards en caoutchouc vides à reprendre une bouffée d&apos;air.&lt;/p&gt;
&lt;h2&gt;Une courte histoire pour deux canards en caoutchouc à moitié vides&lt;/h2&gt;
&lt;p&gt;« Tu sais quoi ? » - dit Duckitty
« Quoi ? » - dit Duckleton
« Quand je fais trop coin-coin, je ne peux plus faire coin-coin, et je veux faire coin-coin, et ça me rend triste. » - répondit Duckitty
Duckleton prit un air très sérieux, fronça les sourcils et dit d&apos;une voix sage :
« Oui. Je ressens la même chose ! »&lt;/p&gt;
&lt;h2&gt;Le canard en caoutchouc qui détestait être silencieux&lt;/h2&gt;
&lt;p&gt;Duckleton détestait être silencieux et poussait un grand coin-coin dès qu&apos;il le pouvait. Parfois, il était assez vide et se demandait s&apos;il était assez jaune pour faire coin-coin.
Mais tu vois, l&apos;usine qui avait fabriqué Ducleton et Duckitty les avait faits tous les deux parfaitement jaunes.
Duckleton était un canard en caoutchouc qui avait du caractère. Il pensait qu&apos;il pouvait et qu&apos;il devait surpasser tous les autres canards en coin-coin pour les sauver de la honte de ne pas faire coin-coin quand ils étaient vides.
Duckitty avait parfois des pensées semblables, ou du moins Duckleton pensait ainsi quand il avait toute sa tête. (Parfois, ce n&apos;était pas le cas 😆)
Mais Duckleton était un fier canard en caoutchouc. Un canard en caoutchouc fabriqué dans la meilleure usine du Monde, celle qui fabriquait depuis des siècles les meilleurs canards en caoutchouc jaunes qui faisaient coin-coin. En tout cas, Duckleton pensait ainsi jusqu&apos;à ce qu&apos;il rencontre Duckitty.
Une fois qu&apos;il l&apos;eut rencontrée, il fut tellement émerveillé par sa voix qu&apos;il pensa qu&apos;elle devait avoir été fabriquée dans une usine extraterrestre, bien meilleure que l&apos;usine terrestre d&apos;où il venait.
Il était heureux d&apos;avoir la chance d&apos;admirer simplement cette merveilleuse et parfaite canarde en caoutchouc, bien meilleure que lui (pensait-il), nommée Duckitty.
« Eh bien, c&apos;est tellement agréable de la regarder. Elle est si jaune. Elle est si gentille. Elle a une voix bien plus jolie que la mienne. Je suis un canard en caoutchouc chanceux. » - pensait Duckleton.&lt;/p&gt;
&lt;h2&gt;Une histoire courte mais un peu triste&lt;/h2&gt;
&lt;p&gt;Duckleton et Duckitty étaient tous les deux heureux et faisaient coin-coin comme si c&apos;était le meilleur concours de coin-coin du Monde et de l&apos;espace intersidéral.
Un jour, un enfant humain arriva et pressa Duckleton. Il fut incapable de faire coin-coin pendant quelque temps, mais il en avait toujours très envie ; alors il resta silencieux sans se plaindre, prit une grande inspiration, et ils continuèrent tous les deux à faire coin-coin joyeusement.
Un peu de temps passa, et Duckitty fut elle aussi prise par un méchant enfant humain. L&apos;enfant humain expulsa tout l&apos;air de Duckitty en la pressant, mais Duckleton était trop occupé à faire coin-coin pour le remarquer.
Duckitty allait bien, mais elle devait reprendre un peu d&apos;air.
Duckleton était triste au-delà du désespoir. Il pensait que sa canarde préférée était cassée et qu&apos;il serait de nouveau seul pour toujours jusqu&apos;à la fin de l&apos;Univers. Il avait simplement oublié que le même enfant l&apos;avait pressé quelque temps auparavant.
Duckleton était fier mais bête ; après tout, il avait une petite tête en caoutchouc. Quand Duckitty fit coin-coin, Duckleton fut furieux. Il était à la fois heureux qu&apos;elle soit en vie, mais il était aussi un peu découragé, parce qu&apos;il pensait que Duckitty pouvait mieux survivre que lui à la pression d&apos;un enfant humain.
Elle était tellement plus parfaite que lui, après tout, non ?&lt;/p&gt;
&lt;h2&gt;Le voyage de culpabilité de Duckleton&lt;/h2&gt;
&lt;p&gt;Duckleton était le pire hypocrite de tous et il finit par l&apos;apprendre. Il était triste, mais il pensa : Allons présenter mes excuses à Duckitty et lui montrer un peu de gratitude pour être aussi parfaite qu&apos;elle l&apos;est.&lt;/p&gt;
&lt;h2&gt;Épilogue&lt;/h2&gt;
&lt;p&gt;Ma chère amie, tu as été parfaite pendant les 8 dernières années. Tu as été parfaite chaque jour. Je suis vraiment désolé d&apos;avoir été un tel hypocrite, mais les mauvaises choses arrivent, tu sais. Et on continue quand même.
S&apos;il te plaît, aime-toi beaucoup plus. Je suis bête. Tu es intelligente. Ou peu importe... Mais aime-toi beaucoup plus, parce que tu es parfaite telle que tu es !
En fait, tu es la canarde en caoutchouc jaune la plus parfaite du Monde. Du moins, c&apos;est ce que je pense. Ma tête en caoutchouc est petite. Mais, youpi ! Je peux encore penser. 🫶
Coin !&lt;/p&gt;
&lt;p&gt;Coin, Coin, Coin
~ Duckleton&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Représentations discrètes en RL : les enseignements de la recherche d'Edan Meyer]]></title><description><![CDATA[Vous êtes-vous déjà demandé comment les agents d'IA apprennent à comprendre des environnements complexes et à interagir avec eux ? Edan…]]></description><link>https://bdteo.com/fr/discrete-representations-reinforcement-learning-insights/</link><guid isPermaLink="false">https://bdteo.com/fr/discrete-representations-reinforcement-learning-insights/</guid><pubDate>Mon, 15 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Vous êtes-vous déjà demandé comment les agents d&apos;IA apprennent à comprendre des environnements complexes et à interagir avec eux ? Edan Meyer, chercheur dans le domaine de l&apos;apprentissage par renforcement (RL), explore une approche intrigante qui pourrait bien changer notre manière de penser l&apos;apprentissage de l&apos;IA. Plongeons dans son travail fascinant sur les représentations discrètes en RL !&lt;/p&gt;
&lt;h2&gt;Le pouvoir de la représentation&lt;/h2&gt;
&lt;p&gt;Imaginez que vous essayiez d&apos;apprendre à un ordinateur à jouer à un jeu vidéo. Comment représenteriez-vous l&apos;état du jeu d&apos;une manière que l&apos;ordinateur puisse comprendre et dont il puisse apprendre ? C&apos;est là qu&apos;intervient l&apos;apprentissage de représentations, et c&apos;est un élément crucial pour créer des agents d&apos;IA efficaces.&lt;/p&gt;
&lt;p&gt;Edan Meyer, dont vous pouvez découvrir le travail sur sa &lt;a href=&quot;https://www.youtube.com/@EdanMeyer&quot;&gt;chaîne YouTube&lt;/a&gt;, étudie un type particulier de représentation appelé représentations discrètes. Ses recherches, détaillées dans un &lt;a href=&quot;https://arxiv.org/abs/2312.01203&quot;&gt;article disponible sur arXiv&lt;/a&gt;, éclairent pourquoi ces représentations pourraient être particulièrement utiles dans certains scénarios de RL.&lt;/p&gt;
&lt;h2&gt;Deux ans de recherche en 13 minutes&lt;/h2&gt;
&lt;p&gt;Edan a condensé deux années de recherche de master dans une vidéo captivante de 13 minutes intitulée &lt;a href=&quot;https://www.youtube.com/watch?v=s8RqGlU5HEs&quot;&gt;&quot;2 Years of My Research Explained in 13 Minutes&quot;&lt;/a&gt;. Dans cette vidéo, il décompose des concepts complexes en explications assimilables, rendant son travail accessible à un public plus large.&lt;/p&gt;
&lt;p&gt;Comme Edan l&apos;écrit dans la description de sa vidéo :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Voici mes recherches sur l&apos;apprentissage de représentations et l&apos;apprentissage de modèles dans le cadre de l&apos;apprentissage par renforcement. Deux ans de travail, et je peux enfin parler de ma recherche de master ! L&apos;article a été accepté à la Reinforcement Learning Conference (RLC) 2024.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Cette vidéo offre un excellent point de départ pour quiconque veut comprendre les bases de ses recherches sans se plonger dans l&apos;article académique complet.&lt;/p&gt;
&lt;h2&gt;Que sont les représentations discrètes ?&lt;/h2&gt;
&lt;p&gt;Traditionnellement, beaucoup de systèmes de RL utilisent des représentations continues : pensez à des vecteurs de nombres décimaux qui peuvent prendre n&apos;importe quelle valeur. Les représentations discrètes, à l&apos;inverse, ressemblent davantage à une série de questions à choix multiples. Chaque &quot;emplacement&quot; de la représentation ne peut prendre qu&apos;une valeur parmi un nombre fixe de possibilités.&lt;/p&gt;
&lt;p&gt;Comme Edan l&apos;explique dans sa vidéo, cela peut sembler limitant au premier abord. Après tout, une valeur continue peut représenter une infinité d&apos;états, tandis qu&apos;une valeur discrète est beaucoup plus contrainte. Alors pourquoi utiliser des représentations discrètes ?&lt;/p&gt;
&lt;h2&gt;Les bénéfices surprenants&lt;/h2&gt;
&lt;p&gt;Les recherches d&apos;Edan ont mis au jour plusieurs avantages fascinants à l&apos;utilisation de représentations discrètes :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;De meilleurs world models avec moins de capacité&lt;/strong&gt; : Lorsqu&apos;une IA essaie d&apos;apprendre un modèle de son environnement (un &quot;world model&quot;), les représentations discrètes lui permettent de capturer une information plus précise avec moins de puissance de calcul. C&apos;est particulièrement vrai lorsque le modèle n&apos;a pas assez de capacité pour représenter parfaitement tout ce qui concerne l&apos;environnement, un scénario courant dans les problèmes complexes du monde réel.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Une adaptation plus rapide&lt;/strong&gt; : Dans des expériences où l&apos;environnement changeait au fil du temps, les agents utilisant des représentations discrètes ont pu s&apos;adapter plus rapidement à ces changements. Cela pourrait être crucial pour des systèmes d&apos;IA qui doivent fonctionner dans des environnements dynamiques et imprévisibles.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Un apprentissage efficace&lt;/strong&gt; : Même si les représentations discrètes peuvent demander plus de temps à apprendre au départ, une fois établies, elles permettent un apprentissage et une adaptation plus rapides, aussi bien dans les tâches de world modeling que dans l&apos;apprentissage de politiques.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Pourquoi est-ce important ?&lt;/h2&gt;
&lt;p&gt;Les implications du travail d&apos;Edan vont bien au-delà de simples expériences en grid-world. Comme il le souligne dans sa vidéo, le monde réel est infiniment plus complexe que n&apos;importe quelle simulation que nous pouvons créer. Dans de tels environnements, il est impossible pour une IA de tout apprendre ; la clé, c&apos;est l&apos;adaptation.&lt;/p&gt;
&lt;p&gt;Les représentations discrètes semblent offrir un outil puissant pour créer des systèmes d&apos;IA capables de s&apos;adapter rapidement à de nouvelles situations, même lorsqu&apos;ils ne peuvent pas modéliser tous les aspects de leur environnement. Cela pourrait changer la donne pour des applications allant de la robotique aux jeux de stratégie complexes, et bien au-delà.&lt;/p&gt;
&lt;h2&gt;Aller plus loin&lt;/h2&gt;
&lt;p&gt;Pour celles et ceux qui s&apos;intéressent aux détails techniques, l&apos;article d&apos;Edan explore des aspects fascinants de ce qui rend les représentations discrètes si efficaces. Par exemple, il a constaté que toutes les représentations discrètes ne se valent pas : des facteurs comme la parcimonie et la binarité jouent un rôle important dans leur efficacité.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Le travail d&apos;Edan Meyer sur les représentations discrètes en apprentissage par renforcement offre des pistes passionnantes sur la manière dont nous pourrions créer des systèmes d&apos;IA plus adaptables et plus efficaces. En remettant en question les idées reçues sur la façon de représenter l&apos;information pour l&apos;IA, ses recherches ouvrent de nouvelles possibilités pour créer des agents capables de prospérer dans des environnements complexes et dynamiques.&lt;/p&gt;
&lt;p&gt;Que vous soyez chercheur en IA, étudiant en machine learning, ou simplement fasciné par les frontières de la technologie, le travail d&apos;Edan offre un aperçu convaincant de l&apos;avenir de l&apos;intelligence artificielle. N&apos;hésitez pas à consulter sa &lt;a href=&quot;https://www.youtube.com/@EdanMeyer&quot;&gt;chaîne YouTube&lt;/a&gt;, sa &lt;a href=&quot;https://www.youtube.com/watch?v=s8RqGlU5HEs&quot;&gt;vidéo&lt;/a&gt; explicative et son &lt;a href=&quot;https://arxiv.org/abs/2312.01203&quot;&gt;article&lt;/a&gt; pour explorer ces idées plus en profondeur !&lt;/p&gt;
&lt;p&gt;Souvenez-vous : dans le monde très rapide de la recherche en IA, les techniques expérimentales d&apos;aujourd&apos;hui pourraient devenir les technologies de rupture de demain. Les représentations discrètes pourraient bien être la clé qui débloquera des systèmes d&apos;IA plus capables et plus adaptables dans un avenir proche.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Histoire de l'IA chez Google : promesses, bourse et impact]]></title><description><![CDATA[Le parcours de Google dans le développement et la commercialisation de technologies d'IA telles
que la série Gemini offre une étude de cas…]]></description><link>https://bdteo.com/fr/google-ai-ambitions-historical-analysis-promises-stock-market-impact/</link><guid isPermaLink="false">https://bdteo.com/fr/google-ai-ambitions-historical-analysis-promises-stock-market-impact/</guid><pubDate>Sat, 11 May 2024 12:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Le parcours de Google dans le développement et la commercialisation de technologies d&apos;IA telles
que la série Gemini offre une étude de cas fascinante sur les rapports entre promesses
d&apos;entreprise, performance boursière et innovation technologique. Cette analyse examine des moments
historiques précis où les promesses de Google en matière d&apos;IA ont fortement influencé le cours de
son action, tout en revenant sur ses réussites et ses échecs.&lt;/p&gt;
&lt;p&gt;L&apos;entrée du géant technologique dans l&apos;IA a été marquée par des projets ambitieux et des
affirmations audacieuses. Depuis l&apos;introduction de TensorFlow en 2015, qui a installé Google comme
un leader de la recherche et du développement en IA, jusqu&apos;au lancement de Google Assistant en
2016, qui a renforcé sa compétitivité face à des rivaux comme Alexa d&apos;Amazon et Siri d&apos;Apple,
Google a constamment cherché à repousser les limites des capacités de l&apos;IA &lt;small&gt;&lt;a href=&quot;#ref1&quot;&gt;[1]&lt;/a&gt;&lt;/small&gt;.&lt;/p&gt;
&lt;h3&gt;Jalons historiques clés dans le développement de l&apos;IA chez Google&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Introduction de TensorFlow (2015)&lt;/strong&gt; : Google a rendu TensorFlow open source, son framework de
machine learning, qui est devenu largement populaire. Cette décision a contribué à établir
Google comme un leader de la recherche et du développement en IA, avec un effet positif sur sa
perception par le marché &lt;small&gt;&lt;a href=&quot;#ref1&quot;&gt;[1]&lt;/a&gt;&lt;/small&gt;. TensorFlow a été utilisé dans diverses applications, de
l&apos;amélioration des résultats de recherche à l&apos;alimentation des voitures autonomes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Lancement de Google Assistant (2016)&lt;/strong&gt; : L&apos;introduction de Google Assistant a renforcé la
compétitivité de Google dans l&apos;IA face à des rivaux comme Alexa d&apos;Amazon et Siri d&apos;Apple. Le
marché l&apos;a bien accueilli, y voyant un potentiel de croissance dans les interfaces utilisateur
pilotées par l&apos;IA &lt;small&gt;&lt;a href=&quot;#ref2&quot;&gt;[2]&lt;/a&gt;&lt;/small&gt;. Google Assistant a été intégré aux smartphones, aux appareils
domotiques et aux voitures, ce qui en a fait un acteur clé du marché des assistants vocaux.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Progrès en informatique quantique (2019)&lt;/strong&gt; : Google a annoncé une percée de suprématie
quantique, affirmant que son ordinateur quantique pouvait effectuer des calculs hors de portée
des supercalculateurs traditionnels. Cette annonce a provoqué une brève hausse du cours de
l&apos;action, illustrant l&apos;enthousiasme des investisseurs pour les capacités technologiques de pointe
de Google &lt;small&gt;&lt;a href=&quot;#ref3&quot;&gt;[3]&lt;/a&gt;&lt;/small&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Performance boursière et jalons de l&apos;IA&lt;/h3&gt;
&lt;p&gt;Le marché boursier a réagi de façon variable aux avancées de Google en IA. Les annonces
importantes, comme les percées en informatique quantique et les lancements de nouveaux produits
d&apos;IA, entraînent généralement des hausses de cours à court terme. Cependant, l&apos;impact à long terme
sur le cours de l&apos;action est resté plus étroitement lié au déploiement effectif et au succès
commercial de ces technologies.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Jalons du S&amp;#x26;P 500&lt;/strong&gt; : Les grandes annonces de Google en IA ont souvent coïncidé avec des
tendances de marché plus larges. Par exemple, le marché haussier général de 2021 a vu Google
atteindre de nouveaux sommets boursiers en même temps que des avancées significatives en IA,
reflétant une forte confiance des investisseurs dans la croissance portée par la technologie
&lt;small&gt;&lt;a href=&quot;#ref4&quot;&gt;[4]&lt;/a&gt;&lt;/small&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;
  &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;&gt;
      &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;https://bdteo.com/static/ab6f0e497ea3ee100966d0f875eab63b/56fb7/google_stock_milestones.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;
    &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 49.36708860759494%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAC4jAAAuIwF4pT92AAABh0lEQVR42l2SyW7jQAxE9f/fNqc55RDADgwjE41kLb1vqhQpSxOPgAYFNvm6uHQhBMzzgr7vsSwLSinIOatNKWH422MYB8bMGMeRdkKMmTFFY8oRSxtiQifAYRhxuVxwv99hjNGA1hq2bUPLCbkkyCc++VKq/N+Qa4NPWX2TSzAuoPM+nIBaq6oSK0d8JUbEFM77bWtUUsEf3B4Ov2+TPvzrOsKS1TnnFChOVfRUtifTUmHiOXwkIVNhylVVvX0u+Bgt+tWjktM9Ho9/5Z0qfgDTK1AU1tJgQlbg6pOqKyxf+tlN03Qm/w9stEWBUUusck87mYT3L4NIlbVV2LS3TIapQzl6+BO46QD4CIGRwMDkG0u7DgbXL4vBRL0/Hn4B1lr0EtpHQjlBx9Vw7NU4W/yZDWafYWVdCIhRhvba8xMoU/UhYnURs5UTsLAvC1dgJWQ1FiH4vWQdHqccZPeeW/Cs7tjbTujOe67GvpjWeQZQBSGNajJXRoCyXt57TRa7Lz9zQnjxfQN54Q2UCayA8gAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Jalons boursiers de Google&quot; title=&quot;&quot; src=&quot;https://bdteo.com/static/ab6f0e497ea3ee100966d0f875eab63b/f058b/google_stock_milestones.png&quot; srcset=&quot;https://bdteo.com/static/ab6f0e497ea3ee100966d0f875eab63b/c26ae/google_stock_milestones.png 158w,
https://bdteo.com/static/ab6f0e497ea3ee100966d0f875eab63b/6bdcf/google_stock_milestones.png 315w,
https://bdteo.com/static/ab6f0e497ea3ee100966d0f875eab63b/f058b/google_stock_milestones.png 630w,
https://bdteo.com/static/ab6f0e497ea3ee100966d0f875eab63b/40601/google_stock_milestones.png 945w,
https://bdteo.com/static/ab6f0e497ea3ee100966d0f875eab63b/78612/google_stock_milestones.png 1260w,
https://bdteo.com/static/ab6f0e497ea3ee100966d0f875eab63b/56fb7/google_stock_milestones.png 4169w&quot; sizes=&quot;(max-width: 630px) 100vw, 630px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;
  &lt;/a&gt;
    &lt;/span&gt;
  &lt;figcaption&gt;
    Fig1. - Jalons boursiers de Google.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3&gt;Réussites et échecs&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Réussites&lt;/strong&gt; : L&apos;IA de Google a obtenu des succès considérables dans des domaines comme la
traduction automatique, la reconnaissance d&apos;images et les technologies de conduite autonome avec
Waymo. Ces réussites ont contribué à consolider la réputation de Google comme innovateur
technologique &lt;small&gt;&lt;a href=&quot;#ref5&quot;&gt;[5]&lt;/a&gt;&lt;/small&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Échecs&lt;/strong&gt; : Toutes les initiatives de Google en IA n&apos;ont pas répondu aux attentes du marché. Par
exemple, le très attendu projet Google Glass n&apos;a pas trouvé d&apos;écho chez les consommateurs, ce qui
a conduit à son abandon. Le produit a suscité des inquiétudes en matière de vie privée et n&apos;a pas
offert de cas d&apos;usage convaincant pour le consommateur moyen. De même, les retards et les
promesses excessives autour de Gemini 1.5 Pro ont provoqué de l&apos;insatisfaction chez les
utilisateurs et du scepticisme &lt;small&gt;&lt;a href=&quot;#ref6&quot;&gt;[6]&lt;/a&gt;&lt;/small&gt;. Google a tiré des leçons de ces échecs, en se
concentrant sur des applications d&apos;IA plus pragmatiques et en améliorant sa communication avec
les utilisateurs.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;
  &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;&gt;
      &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;https://bdteo.com/static/74c512a4ea459157d8319ebeced56698/f154a/tech_companies_ai_milestones.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;
    &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 55.69620253164557%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAC4jAAAuIwF4pT92AAABuklEQVR42n2S65LaMAyFef/X21+Flg67O4QEkjg4iW35cnrkANPZndaDR8iRPx3J2uWcMQwjzuczxnFETgl2tuiHgf5E22Pg+UjfMK7vb9wDpskgBGF8RIwRwYXq70QEl7bF4bDH6XRCc27gxaMUgLHI/JPU4S/HjJwjEpPqUc56nqAfR7PA+fAAXhrs93tCD+gI98HxUoZIRuRlYRWZfpJENYGkhF+dxZGbDuMjfrwP8E/gfLcsW8uYEEMgRDZl6bvCUlieRDTG4Wdrq8Jjd8fvxtREOwUVvfBYhf0QqkhUFWPZLHdheVFSBfZ2RSD8eLU4XAyG2SN5YTx7eL1eK1AfR+3/gFsPBc24sIpSz98+etysA2UzIYHGmA32D+DfJUtI+BzuaPkAuvRRHEESEzL7VxUG7RkhFfYAauMVpMBpDWipoLcen7cZH/30it+ExGqT+wJ8rsiMi/e4O8HtHtBNDh2Bgw18xQiv6uvYfAE+e6gwla1KppUQ9qczFitHwQf2rORXyTo2Kcl3hUzwUqjZ5mXB7DwhgkB13q+8GOvkr84RHDDPC9aFrxs8dNS0Ms/YRIj6jt/V/gFlSFmPIFDCcAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Jalons IA des entreprises technologiques&quot; title=&quot;&quot; src=&quot;https://bdteo.com/static/74c512a4ea459157d8319ebeced56698/f058b/tech_companies_ai_milestones.png&quot; srcset=&quot;https://bdteo.com/static/74c512a4ea459157d8319ebeced56698/c26ae/tech_companies_ai_milestones.png 158w,
https://bdteo.com/static/74c512a4ea459157d8319ebeced56698/6bdcf/tech_companies_ai_milestones.png 315w,
https://bdteo.com/static/74c512a4ea459157d8319ebeced56698/f058b/tech_companies_ai_milestones.png 630w,
https://bdteo.com/static/74c512a4ea459157d8319ebeced56698/40601/tech_companies_ai_milestones.png 945w,
https://bdteo.com/static/74c512a4ea459157d8319ebeced56698/78612/tech_companies_ai_milestones.png 1260w,
https://bdteo.com/static/74c512a4ea459157d8319ebeced56698/f154a/tech_companies_ai_milestones.png 4769w&quot; sizes=&quot;(max-width: 630px) 100vw, 630px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;
  &lt;/a&gt;
    &lt;/span&gt;
  &lt;figcaption&gt;Fig2. - Jalons IA des entreprises technologiques.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3&gt;Impact sur le marché et perspectives&lt;/h3&gt;
&lt;p&gt;L&apos;impact de l&apos;IA de Google sur le marché a été significatif, influençant non seulement le cours de
son action, mais aussi l&apos;orientation plus large de l&apos;industrie technologique. Le secteur de l&apos;IA
reste un centre d&apos;intérêt majeur pour les investisseurs, comme le montre la performance du S&amp;#x26;P 500,
où les valeurs technologiques jouent un rôle substantiel &lt;small&gt;&lt;a href=&quot;#ref4&quot;&gt;[4]&lt;/a&gt;&lt;/small&gt;.&lt;/p&gt;
&lt;p&gt;Pour l&apos;avenir, la capacité de Google à tenir ses promesses en matière d&apos;IA et à surmonter les
défis du déploiement technologique sera cruciale. Sa stratégie visant à renforcer la transparence
et à mieux gérer les attentes des consommateurs pourrait déterminer sa position future sur le
marché et la confiance des investisseurs.&lt;/p&gt;
&lt;p&gt;Un point clé à surveiller est la progression de Google avec sa série de modèles d&apos;IA Gemini.
Gemini 1.5 Pro, annoncé en février 2024, promet un bond important dans les capacités de l&apos;IA, mais
son déploiement a rencontré des difficultés &lt;small&gt;&lt;a href=&quot;#ref6&quot;&gt;[6]&lt;/a&gt;&lt;/small&gt;. La manière dont Google affrontera ces
défis et tiendra ses promesses aura probablement un impact notable sur sa réputation en IA et sur
sa position de marché.&lt;/p&gt;
&lt;p&gt;Les avancées ambitieuses de Google en intelligence artificielle, en particulier à travers sa série
Gemini, marquent une ère transformatrice pour la technologie de l&apos;IA. L&apos;introduction de Gemini 1.5
Pro dans cette série souligne l&apos;engagement de Google à repousser les limites des capacités de
l&apos;IA. Cependant, le déploiement de Gemini 1.5 Pro n&apos;a pas été sans difficultés, lesquelles sont
essentielles pour comprendre à la fois le potentiel et les limites de modèles d&apos;IA aussi avancés.&lt;/p&gt;
&lt;h3&gt;Avis d&apos;experts sur Gemini 1.5 Pro&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Performance et capacités&lt;/strong&gt; : Gemini 1.5 Pro, construit sur une architecture Mixture-of-Experts
(MoE), apporte des améliorations significatives par rapport aux modèles précédents, notamment une
augmentation massive de la fenêtre de contexte, jusqu&apos;à 10 millions de tokens. Cette amélioration
permet de mieux traiter des tâches complexes impliquant de grands volumes de données dans divers
formats — texte, code, vision et audio &lt;small&gt;&lt;a href=&quot;#ref7&quot;&gt;[7]&lt;/a&gt;&lt;/small&gt;. Selon Stephen Oladele d&apos;Encord, &quot;Gemini 1.5
Pro conserve un rappel presque parfait sur l&apos;ensemble du contexte et utilise une architecture
mixture-of-experts pour un entraînement plus efficace et des réponses de meilleure qualité&quot;
&lt;small&gt;&lt;a href=&quot;#ref7&quot;&gt;[7]&lt;/a&gt;&lt;/small&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Difficultés du déploiement&lt;/strong&gt; : Malgré ses capacités avancées, le déploiement de Gemini 1.5 Pro a
rencontré plusieurs difficultés. Le modèle est actuellement en private preview, et sa disponibilité
générale est prévue plus tard, ce qui indique une approche de déploiement par étapes
&lt;small&gt;&lt;a href=&quot;#ref7&quot;&gt;[7]&lt;/a&gt;&lt;/small&gt;. Cette stratégie prudente tient peut-être à la nécessité d&apos;affiner encore le modèle et
de s&apos;assurer qu&apos;il réponde aux attentes élevées fixées par ses prédécesseurs et par le marché.&lt;/p&gt;
&lt;h3&gt;Points de vue des analystes du secteur&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Impact sur l&apos;industrie de l&apos;IA&lt;/strong&gt; : Les analystes du secteur voient la série Gemini comme une
avancée importante pour Google, susceptible d&apos;établir de nouveaux standards pour ce que les modèles
d&apos;IA peuvent accomplir. La série Gemini, et en particulier le modèle 1.5 Pro, devrait renforcer la
compétitivité de Google face à d&apos;autres géants technologiques comme OpenAI et Microsoft, qui ont
eux aussi progressé agressivement dans leurs capacités d&apos;IA &lt;small&gt;&lt;a href=&quot;#ref8&quot;&gt;[8]&lt;/a&gt;&lt;/small&gt; &lt;small&gt;&lt;a href=&quot;#ref7&quot;&gt;[7]&lt;/a&gt;&lt;/small&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Implications pour le marché&lt;/strong&gt; : Les progrès de Gemini 1.5 Pro sont susceptibles d&apos;influencer
divers secteurs, dont la santé, la finance et d&apos;autres encore, en permettant des applications d&apos;IA
plus sophistiquées. Cela pourrait entraîner des changements dans la dynamique du marché, où les
entreprises capables d&apos;intégrer efficacement de telles technologies d&apos;IA avancées pourraient
obtenir un avantage concurrentiel significatif &lt;small&gt;&lt;a href=&quot;#ref7&quot;&gt;[7]&lt;/a&gt;&lt;/small&gt;.&lt;/p&gt;
&lt;h3&gt;Éclairages de dirigeants de Google&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Plans futurs et considérations éthiques&lt;/strong&gt; : Les dirigeants de Google, dont Sundar Pichai, ont
souligné l&apos;engagement de l&apos;entreprise en faveur d&apos;un développement responsable de l&apos;IA. Pichai a
mis en avant l&apos;importance d&apos;aligner les avancées de l&apos;IA avec des lignes directrices éthiques et de
veiller à ce que ces technologies soient utilisées au bénéfice de la société &lt;small&gt;&lt;a href=&quot;#ref2&quot;&gt;[2]&lt;/a&gt;&lt;/small&gt;
&lt;small&gt;&lt;a href=&quot;#ref9&quot;&gt;[9]&lt;/a&gt;&lt;/small&gt;. Cette approche est cruciale alors que l&apos;IA devient de plus en plus capable et
s&apos;intègre aux applications du quotidien.&lt;/p&gt;
&lt;h3&gt;Naviguer entre difficultés et opportunités&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Répondre aux défis techniques&lt;/strong&gt; : Pour affronter les défis à venir, Google doit continuer
d&apos;investir dans la recherche et le développement afin de traiter des questions comme la fiabilité
des modèles et les préoccupations éthiques. L&apos;approche de l&apos;entreprise, qui consiste à déployer
progressivement Gemini 1.5 Pro, suggère une stratégie axée sur l&apos;atténuation des risques avant
qu&apos;ils ne deviennent des problèmes importants &lt;small&gt;&lt;a href=&quot;#ref7&quot;&gt;[7]&lt;/a&gt;&lt;/small&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Élargir les opportunités de marché&lt;/strong&gt; : Google peut exploiter les capacités de Gemini 1.5 Pro pour
créer de nouvelles opportunités de marché, en particulier dans les secteurs qui exigent le
traitement de grands ensembles de données et de scénarios complexes de résolution de problèmes. En
fournissant des outils qui simplifient l&apos;intégration de l&apos;IA dans les processus métier, Google peut
aider les industries à transformer leurs opérations et à gagner en efficacité &lt;small&gt;&lt;a href=&quot;#ref10&quot;&gt;[10]&lt;/a&gt;&lt;/small&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Développement éthique de l&apos;IA&lt;/strong&gt; : À mesure que les technologies d&apos;IA deviennent plus puissantes,
leurs implications éthiques prennent davantage d&apos;importance. L&apos;engagement continu de Google en
faveur d&apos;un développement responsable de l&apos;IA, tel qu&apos;il se manifeste dans ses principes d&apos;IA et
ses cadres de gouvernance, sera crucial pour maintenir la confiance du public et la conformité
réglementaire &lt;small&gt;&lt;a href=&quot;#ref11&quot;&gt;[11]&lt;/a&gt;&lt;/small&gt;.&lt;/p&gt;
&lt;p&gt;En conclusion, si le déploiement de Gemini 1.5 Pro présente des difficultés, il offre aussi à
Google des opportunités substantielles de prendre la tête dans le domaine de l&apos;IA. En continuant à
se concentrer sur l&apos;amélioration des performances, le développement éthique de l&apos;IA et l&apos;expansion
du marché, Google peut relever efficacement ces défis et établir de nouveaux standards pour ce que
l&apos;IA peut accomplir dans différents secteurs.&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Le parcours de Google dans l&apos;IA illustre les dynamiques complexes de l&apos;innovation technologique,
des attentes du marché et de la stratégie d&apos;entreprise. Si l&apos;entreprise a connu à la fois des
réussites notables et des revers, ses efforts continus dans l&apos;IA continuent d&apos;attirer une
attention importante du marché. Les investisseurs et les observateurs du marché garderont
probablement un oeil attentif sur la capacité de Google à transformer ses capacités d&apos;IA en
croissance durable et en leadership de marché.&lt;/p&gt;
&lt;p&gt;Alors que le paysage de l&apos;IA continue d&apos;évoluer rapidement, la position de Google comme pionnier de
l&apos;IA sera mise à l&apos;épreuve. Sa capacité à équilibrer innovation ambitieuse et exécution réaliste,
communication transparente et gestion efficace des attentes sera un facteur critique pour
déterminer son succès à long terme dans le domaine de l&apos;IA et sa performance globale sur le
marché.&lt;/p&gt;
&lt;h3&gt;Références&lt;/h3&gt;
&lt;p&gt;&lt;a id=&quot;ref1&quot;&gt;&lt;/a&gt;1. &lt;a href=&quot;https://emeritus.org/blog/ai-strategy-google/&quot;&gt;Emeritus - Stratégie IA de Google&lt;/a&gt;&lt;br&gt;
&lt;a id=&quot;ref2&quot;&gt;&lt;/a&gt;2. &lt;a href=&quot;https://blog.google/products/google-one/google-one-gemini-ai-gmail-docs-sheets/&quot;&gt;Blog Google - Gemini AI&lt;/a&gt;&lt;br&gt;
&lt;a id=&quot;ref3&quot;&gt;&lt;/a&gt;3. &lt;a href=&quot;https://finance.yahoo.com/quote/GOOGL/&quot;&gt;Yahoo Finance - Cours GOOGL&lt;/a&gt;&lt;br&gt;
&lt;a id=&quot;ref4&quot;&gt;&lt;/a&gt;4. &lt;a href=&quot;https://en.wikipedia.org/wiki/Closing_milestones_of_the_S%26P_500&quot;&gt;Wikipedia - Jalons du S&amp;#x26;P 500&lt;/a&gt;&lt;br&gt;
&lt;a id=&quot;ref5&quot;&gt;&lt;/a&gt;5. &lt;a href=&quot;https://www.thinkwithgoogle.com/intl/en-emea/marketing-strategies/automation/using-google-ai-tools/&quot;&gt;Think with Google - Outils d&apos;IA&lt;/a&gt;&lt;br&gt;
&lt;a id=&quot;ref6&quot;&gt;&lt;/a&gt;6. &lt;a href=&quot;https://timesofindia.indiatimes.com/gadgets-news/google-releases-gemini-15-pro-ai-model-heres-what-company-ceo-sundar-pichai-has-to-say/articleshow/107732867.cms&quot;&gt;Times of India - Sortie du modèle IA Gemini 15 Pro&lt;/a&gt;&lt;br&gt;
&lt;a id=&quot;ref7&quot;&gt;&lt;/a&gt;7. &lt;a href=&quot;https://encord.com/blog/google-gemini-1-5-generative-ai-model-with-mixture-of-experts/&quot;&gt;Encord - Modèle d&apos;IA générative Google Gemini 1.5 avec Mixture of Experts&lt;/a&gt;&lt;br&gt;
&lt;a id=&quot;ref8&quot;&gt;&lt;/a&gt;8. &lt;a href=&quot;https://builtin.com/articles/google-gemini&quot;&gt;Built In - Google Gemini&lt;/a&gt;&lt;br&gt;
&lt;a id=&quot;ref9&quot;&gt;&lt;/a&gt;9. &lt;a href=&quot;https://www.cbsnews.com/news/google-artificial-intelligence-future-60-minutes-transcript-2023-04-16/&quot;&gt;CBS News - L&apos;avenir de l&apos;IA chez Google&lt;/a&gt;&lt;br&gt;
&lt;a id=&quot;ref10&quot;&gt;&lt;/a&gt;10. &lt;a href=&quot;https://cloud.google.com/ai/generative-ai&quot;&gt;Google Cloud - IA générative&lt;/a&gt;&lt;br&gt;
&lt;a id=&quot;ref11&quot;&gt;&lt;/a&gt;11. &lt;a href=&quot;https://blog.google/technology/ai/responsible-ai-looking-back-at-2022-and-to-the-future/&quot;&gt;Blog Google - IA responsable&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Photoréalisme avec Stable Diffusion : réglages et limites GPU]]></title><description><![CDATA[Mis à jour en mars 2026. Cet article a été écrit à l'origine en mai 2023, quand SD 1.5 en 512x512 était la norme et que la RTX 3090…]]></description><link>https://bdteo.com/fr/pushing-the-stable-diffussion-limits/</link><guid isPermaLink="false">https://bdteo.com/fr/pushing-the-stable-diffussion-limits/</guid><pubDate>Thu, 04 May 2023 23:45:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Mis à jour en mars 2026.&lt;/strong&gt; Cet article a été écrit à l&apos;origine en mai 2023, quand SD 1.5 en 512x512 était la norme et que la RTX 3090 représentait le sommet du matériel. Tout a changé. Flux 2, les fine-tunes SDXL, SD 3.5, ControlNet et la RTX 5090 ont complètement redéfini ce qui est possible. Voici l&apos;état actuel des choses.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;L&apos;écart entre les images générées par IA et les vraies photographies s&apos;est presque refermé. En 2023, « photoréaliste » voulait dire « presque convaincant si l&apos;on plisse les yeux ». En 2026, les meilleurs modèles produisent des images qu&apos;il est réellement difficile de distinguer d&apos;une photographie professionnelle.&lt;/p&gt;
&lt;p&gt;Voici comment y arriver.&lt;/p&gt;
&lt;h2&gt;Le paysage actuel du photoréalisme&lt;/h2&gt;
&lt;p&gt;Le modèle que vous choisissez compte plus que n&apos;importe quel réglage que vous ajustez. Voici où nous en sommes :&lt;/p&gt;
&lt;h3&gt;Flux 2 -- Le nouveau roi&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://bfl.ai/models/flux-2&quot;&gt;Flux 2&lt;/a&gt;, de Black Forest Labs (sorti en novembre 2025), est probablement le meilleur modèle open-weight pour le photoréalisme en 2026 &lt;small&gt;&lt;a href=&quot;#ref1&quot;&gt;[1]&lt;/a&gt;&lt;/small&gt;. Il produit des images avec une lumière naturelle, des textures de peau précises et une composition cohérente qui rivalise avec la photographie professionnelle. Adobe a intégré Flux (Kontext Pro) dans Photoshop en septembre 2025 &lt;small&gt;&lt;a href=&quot;#ref2&quot;&gt;[2]&lt;/a&gt;&lt;/small&gt; -- cela dit assez où se situe la confiance de l&apos;industrie.&lt;/p&gt;
&lt;p&gt;Flux possède aussi une excellente compréhension du langage naturel. Vous pouvez décrire ce que vous voulez en anglais courant, sans la soupe de mots-clés qu&apos;exigeait SD 1.5.&lt;/p&gt;
&lt;h3&gt;Fine-tunes SDXL -- Les chevaux de trait&lt;/h3&gt;
&lt;p&gt;Pour le photoréalisme basé sur SDXL, voici les leaders actuels :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Juggernaut XL v9/v10&lt;/strong&gt; -- le choix évident pour un rendu cinématographique et photographique. Le plus populaire chez les photographes et les cinéastes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Realistic Vision&lt;/strong&gt; -- fine-tuné spécifiquement pour les textures réalistes, l&apos;éclairage et la précision des visages.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;EpicRealism&lt;/strong&gt; -- détail fin exceptionnel et lumière naturelle.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ces modèles disposent d&apos;un énorme soutien communautaire, de bibliothèques LoRA très riches et d&apos;un comportement prévisible. Si Flux vous semble trop récent ou si votre workflow repose sur SDXL, ce sont d&apos;excellents choix.&lt;/p&gt;
&lt;h3&gt;SD 3.5 Large&lt;/h3&gt;
&lt;p&gt;Le modèle phare de Stability AI utilise la nouvelle architecture Multimodal Diffusion Transformer (MMDiT) -- une approche fondamentalement différente de SDXL. Il est techniquement impressionnant, mais son écosystème est plus petit. SD 3.0 a été déprécié en avril 2025, donc assurez-vous d&apos;être sur 3.5 &lt;small&gt;&lt;a href=&quot;#ref3&quot;&gt;[3]&lt;/a&gt;&lt;/small&gt;.&lt;/p&gt;
&lt;h2&gt;Retour à la réalité côté GPU&lt;/h2&gt;
&lt;p&gt;Les exigences matérielles ont fortement augmenté.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;GPU&lt;/th&gt;
&lt;th&gt;VRAM&lt;/th&gt;
&lt;th&gt;Capacité photoréaliste&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;RTX 3060 12GB&lt;/td&gt;
&lt;td&gt;12GB&lt;/td&gt;
&lt;td&gt;Photoréalisme SD 1.5 seulement. SDXL est serré&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RTX 4070 Ti&lt;/td&gt;
&lt;td&gt;12GB&lt;/td&gt;
&lt;td&gt;SDXL en 1024x1024. Flux est possible avec des optimisations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RTX 4090&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;24GB&lt;/td&gt;
&lt;td&gt;Le bon équilibre. Gère confortablement SDXL, Flux et SD 3.5 en 1024x1024+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RTX 5090&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;32GB&lt;/td&gt;
&lt;td&gt;Tout, y compris la génération 4K et les workflows en lot. 32GB GDDR7, bus 512 bits &lt;small&gt;&lt;a href=&quot;#ref4&quot;&gt;[4]&lt;/a&gt;&lt;/small&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cartes 8GB&lt;/td&gt;
&lt;td&gt;8GB&lt;/td&gt;
&lt;td&gt;Minimum viable avec la gestion VRAM de ComfyUI. Pas confortable&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Le bon compromis de 2023, « 512x512 sur une RTX 3080 », appartient à l&apos;histoire ancienne. &lt;strong&gt;1024x1024 est maintenant la résolution standard&lt;/strong&gt;, et il faut au moins 16GB de VRAM pour travailler sans frustration permanente. À 24GB, les choses deviennent confortables.&lt;/p&gt;
&lt;p&gt;Pour le photoréalisme en particulier, plus de VRAM signifie que vous pouvez exécuter simultanément des modèles plus grands, des résolutions plus élevées et ControlNet sans délestage vers le CPU.&lt;/p&gt;
&lt;h2&gt;Réglages pour le photoréalisme&lt;/h2&gt;
&lt;h3&gt;Sampler&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;DPM++ 2M Karras&lt;/strong&gt; à 25-30 steps. C&apos;est le consensus établi pour le photoréalisme SDXL -- le meilleur rapport vitesse/qualité. Si vous voulez un peu plus de détail avec peu de steps, passez à &lt;strong&gt;DPM++ SDE Karras&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Pour Flux : utilisez le sampler par défaut à 20-30 steps.&lt;/p&gt;
&lt;h3&gt;CFG&lt;/h3&gt;
&lt;p&gt;Pour le photoréalisme SDXL : &lt;strong&gt;7-9&lt;/strong&gt;. Cela donne une forte adhérence au prompt sans l&apos;aspect sursaturé et trop cuit qui apparaît au-dessus de 10.&lt;/p&gt;
&lt;p&gt;Pour SD 3.5 : commencez plus bas (&lt;strong&gt;3-5&lt;/strong&gt;) -- le mécanisme de guidance fonctionne différemment.&lt;/p&gt;
&lt;p&gt;Pour Flux : suivez les recommandations propres au modèle, mais en général plus bas que SDXL.&lt;/p&gt;
&lt;h3&gt;Résolution&lt;/h3&gt;
&lt;p&gt;Générez à la résolution native du modèle (1024x1024 pour SDXL/SD 3.5/Flux), puis &lt;strong&gt;upscalez&lt;/strong&gt; pour obtenir une résolution supérieure. N&apos;essayez pas de générer directement en 2048x2048 -- vous obtiendrez des artefacts, des éléments dupliqués et des problèmes de composition.&lt;/p&gt;
&lt;p&gt;Options d&apos;upscaling : hi-res fix dans A1111, ou des nodes d&apos;upscaling dédiés dans ComfyUI (4x-UltraSharp, ESRGAN).&lt;/p&gt;
&lt;h3&gt;Prompter pour le photoréalisme&lt;/h3&gt;
&lt;p&gt;Le grand changement depuis 2023 : &lt;strong&gt;écrivez naturellement, pas en mots-clés&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;SD 1.5 avait besoin de prompts comme :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;portrait of a woman, photorealistic, 8k, ultra detailed, sharp focus,
professional photography, Fujifilm X-T4, 85mm f/1.4&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;SDXL et Flux comprennent :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;A portrait of a woman in soft afternoon light, photographed with a shallow
depth of field. She&apos;s looking slightly off-camera with a natural expression.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;L&apos;approche en soupe de mots-clés fonctionne encore sur SDXL, mais le langage naturel produit des résultats plus cohérents. Flux, en particulier, excelle avec des prompts descriptifs et conversationnels.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Negative prompts :&lt;/strong&gt; Gardez-les minimaux. Commencez sans rien, puis ajoutez des corrections précises. « cartoon, illustration, painting » suffit généralement à garder les choses photoréalistes. Voir la &lt;a href=&quot;/stable-difussion-cheat-sheet/&quot;&gt;cheat sheet&lt;/a&gt; pour le changement complet de philosophie autour des negative prompts.&lt;/p&gt;
&lt;h2&gt;ControlNet change tout&lt;/h2&gt;
&lt;p&gt;Si vous êtes sérieux au sujet de la composition photoréaliste, ControlNet est non négociable. Il vous permet de contrôler la structure de votre image avec :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Depth maps&lt;/strong&gt; -- maintenir les relations spatiales et la perspective&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Canny edge detection&lt;/strong&gt; -- préserver les contours et les formes&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;OpenPose&lt;/strong&gt; -- contrôler la pose humaine et les proportions du corps&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Surface normals&lt;/strong&gt; -- interaction réaliste de la lumière avec les surfaces&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Des modèles ControlNet sont maintenant disponibles pour SDXL, Flux et SD 3.5 &lt;small&gt;&lt;a href=&quot;#ref5&quot;&gt;[5]&lt;/a&gt;&lt;/small&gt;. Multi-ControlNet (empiler plusieurs contrôles) vous donne un contrôle précis de la composition que le prompt engineering seul ne peut pas atteindre.&lt;/p&gt;
&lt;p&gt;Le workflow : prenez une photo de référence, extrayez une depth map ou une pose, utilisez-la comme entrée ControlNet, puis générez une image photoréaliste avec la même composition.&lt;/p&gt;
&lt;h2&gt;Vitesse contre qualité&lt;/h2&gt;
&lt;p&gt;Si vous avez besoin d&apos;itérations rapides (travail de concept, tests de prompts), utilisez &lt;strong&gt;SDXL Lightning&lt;/strong&gt; -- il génère des images 1024px de qualité en 2-8 steps &lt;small&gt;&lt;a href=&quot;#ref6&quot;&gt;[6]&lt;/a&gt;&lt;/small&gt;. La qualité est meilleure que SDXL Turbo aux résolutions plus élevées.&lt;/p&gt;
&lt;p&gt;Pour le rendu final, revenez à SDXL complet ou à Flux avec 25-30 steps. La différence se voit.&lt;/p&gt;
&lt;h2&gt;Le workflow pratique&lt;/h2&gt;
&lt;p&gt;Voici ce qui fonctionne réellement pour une sortie photoréaliste en 2026 :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Choisissez votre modèle&lt;/strong&gt; -- Flux 2 pour le meilleur photoréalisme, Juggernaut XL pour l&apos;écosystème SDXL&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Écrivez un prompt en langage naturel&lt;/strong&gt; qui décrit ce que vous voyez&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Générez en 1024x1024&lt;/strong&gt;, DPM++ 2M Karras, CFG 7-9, 25-30 steps&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Utilisez ControlNet&lt;/strong&gt; si vous avez besoin d&apos;une composition précise (depth ou pose)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Itérez sur le prompt&lt;/strong&gt; -- générez 4-8 images, choisissez la meilleure&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Upscalez&lt;/strong&gt; la gagnante vers votre résolution cible&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Inpaintez&lt;/strong&gt; les zones problématiques (mains, yeux, petits détails)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;C&apos;est le même workflow, que vous soyez dans ComfyUI ou dans A1111. Les outils diffèrent, pas le pipeline.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Références&lt;/h3&gt;
&lt;p&gt;&lt;a id=&quot;ref1&quot;&gt;&lt;/a&gt;1. &lt;a href=&quot;https://bfl.ai/models/flux-2&quot;&gt;Modèles Flux 2 -- Black Forest Labs&lt;/a&gt; -- &lt;em&gt;Page officielle du modèle Flux 2.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref2&quot;&gt;&lt;/a&gt;2. &lt;a href=&quot;https://blogs.nvidia.com/blog/rtx-ai-garage-flux-2-comfyui/&quot;&gt;FLUX.2 and NVIDIA RTX AI Garage&lt;/a&gt; -- &lt;em&gt;Intégration de Flux 2 avec ComfyUI et adoption par l&apos;industrie.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref3&quot;&gt;&lt;/a&gt;3. &lt;a href=&quot;https://platform.stability.ai/docs/release-notes&quot;&gt;Stability AI Release Notes&lt;/a&gt; -- &lt;em&gt;Dépréciation de SD 3.0 et détails de la sortie 3.5.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref4&quot;&gt;&lt;/a&gt;4. &lt;a href=&quot;https://www.bestgpusforai.com/gpu-comparison/5090-vs-4090&quot;&gt;RTX 5090 vs 4090 for AI Workloads&lt;/a&gt; -- &lt;em&gt;Comparaison matérielle pour la génération d&apos;images.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref5&quot;&gt;&lt;/a&gt;5. &lt;a href=&quot;https://stable-diffusion-art.com/controlnet/&quot;&gt;ControlNet Complete Guide&lt;/a&gt; -- &lt;em&gt;Documentation ControlNet mise à jour pour plusieurs architectures.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref6&quot;&gt;&lt;/a&gt;6. &lt;a href=&quot;https://huggingface.co/ByteDance/SDXL-Lightning&quot;&gt;SDXL-Lightning by ByteDance&lt;/a&gt; -- &lt;em&gt;Modèle de génération en 2-8 steps.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref7&quot;&gt;&lt;/a&gt;7. &lt;a href=&quot;https://www.cubix.co/blog/best-model-for-stable-diffusion/&quot;&gt;Best Stable Diffusion Models for Photorealism 2026&lt;/a&gt; -- &lt;em&gt;Paysage actuel des modèles.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref8&quot;&gt;&lt;/a&gt;8. &lt;a href=&quot;https://civitai.com/articles/2115/top-5-photorealistic-stable-diffusion-models-reviewed&quot;&gt;Top Photorealistic Stable Diffusion Models&lt;/a&gt; -- &lt;em&gt;Revues communautaires Civitai.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Articles liés&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/stable-difussion-cheat-sheet/&quot;&gt;Stable Diffusion Cheat Sheet : dépannage et optimisation&lt;/a&gt; -- référence rapide pour les paramètres, les samplers et le dépannage.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Aide-mémoire Stable Diffusion : dépannage et optimisation]]></title><description><![CDATA[Mis à jour en mars 2026. La version originale de cet aide-mémoire a été écrite pour SD 1.5 en mai 2023. Presque tout a changé depuis…]]></description><link>https://bdteo.com/fr/stable-difussion-cheat-sheet/</link><guid isPermaLink="false">https://bdteo.com/fr/stable-difussion-cheat-sheet/</guid><pubDate>Thu, 04 May 2023 23:30:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Mis à jour en mars 2026.&lt;/strong&gt; La version originale de cet aide-mémoire a été écrite pour SD 1.5 en mai 2023. Presque tout a changé depuis -- nouvelles architectures (SDXL, SD 3.5, Flux), nouvelles interfaces (ComfyUI), nouveau matériel (RTX 5090) et renversement complet de la philosophie des prompts négatifs. Voici la version actuelle.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Ceci est ma référence de travail pour les paramètres de Stable Diffusion. Pas un tutoriel -- seulement les réglages que je vais chercher quand quelque chose ne marche pas ou quand je veux pousser la qualité.&lt;/p&gt;
&lt;h2&gt;Quel modèle utiliser&lt;/h2&gt;
&lt;p&gt;C&apos;est maintenant la première décision, et elle compte plus que n&apos;importe quel ajustement de paramètre.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Modèle&lt;/th&gt;
&lt;th&gt;Idéal pour&lt;/th&gt;
&lt;th&gt;Résolution&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Flux 2&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Photoréalisme, respect du prompt&lt;/td&gt;
&lt;td&gt;1024x1024+&lt;/td&gt;
&lt;td&gt;Meilleur modèle open-weight pour le photoréalisme en 2026. Intégré à Adobe Photoshop &lt;small&gt;&lt;a href=&quot;#ref1&quot;&gt;[1]&lt;/a&gt;&lt;/small&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SDXL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Usage général&lt;/td&gt;
&lt;td&gt;1024x1024&lt;/td&gt;
&lt;td&gt;Énorme écosystème de fine-tunes. Juggernaut XL, Realistic Vision, DreamShaper&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SD 3.5 Large&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Qualité maximale (le fleuron de Stability)&lt;/td&gt;
&lt;td&gt;1024x1024&lt;/td&gt;
&lt;td&gt;Architecture MMDiT. SD 3.0 a été déprécié en avril 2025 &lt;small&gt;&lt;a href=&quot;#ref2&quot;&gt;[2]&lt;/a&gt;&lt;/small&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SDXL Lightning&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Vitesse&lt;/td&gt;
&lt;td&gt;1024x1024&lt;/td&gt;
&lt;td&gt;Génération en 2 à 8 étapes. Meilleure qualité que Turbo à haute résolution &lt;small&gt;&lt;a href=&quot;#ref3&quot;&gt;[3]&lt;/a&gt;&lt;/small&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SD 1.5&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Workflows hérités&lt;/td&gt;
&lt;td&gt;512x512&lt;/td&gt;
&lt;td&gt;Immense bibliothèque de fine-tunes, mais en voie d&apos;abandon. SD 2.0/2.1 officiellement dépréciés&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Si vous repartez de zéro : &lt;strong&gt;Flux 2 pour le photoréalisme, SDXL pour tout le reste.&lt;/strong&gt; SD 3.5 est bon, mais son écosystème est plus petit.&lt;/p&gt;
&lt;h2&gt;Quelle interface utiliser&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Interface&lt;/th&gt;
&lt;th&gt;Idéale pour&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ComfyUI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Utilisateurs avancés. Basée sur des nœuds, meilleure gestion de la VRAM, 15 % plus rapide, meilleur support de Flux. Standard industriel pour le travail sérieux depuis 2025 &lt;small&gt;&lt;a href=&quot;#ref4&quot;&gt;[4]&lt;/a&gt;&lt;/small&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Automatic1111&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Débutants. Interface plus simple, immense bibliothèque d&apos;extensions. Fonctionne encore très bien pour SDXL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fooocus&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Génération en un clic. Configuration minimale. Bien pour des résultats rapides&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;J&apos;utilise ComfyUI. La courbe d&apos;apprentissage est plus raide (comptez 10 à 20 heures pour être à l&apos;aise), mais la gestion de la VRAM vaut à elle seule l&apos;effort -- il fait tourner SDXL sur 8 Go là où A1111 plante.&lt;/p&gt;
&lt;h2&gt;Samplers&lt;/h2&gt;
&lt;p&gt;Le débat sur les samplers est plus ou moins réglé.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Choix par défaut :&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;DPM++ 2M Karras&lt;/strong&gt; -- meilleur rapport vitesse/qualité. C&apos;est mon choix par défaut pour presque tout.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DPM++ SDE Karras&lt;/strong&gt; -- légèrement meilleur avec peu d&apos;étapes. Bon quand vous itérez vite.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Euler a&lt;/strong&gt; -- toujours fiable. Plus de variété dans les sorties, utile pour explorer.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Quand changer :&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Manque de diversité dans les sorties ? Essayez DPM++ SDE ou Euler a.&lt;/li&gt;
&lt;li&gt;Artefacts ou sursaturation ? Essayez DPM++ 2M Karras ou Euler simple.&lt;/li&gt;
&lt;li&gt;Besoin de vitesse avant tout ? Euler a ou DPM++ 2M (non-Karras).&lt;/li&gt;
&lt;li&gt;Vous voulez la qualité maximale ? DPM++ 3M SDE Karras ou UniPC.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Nombre d&apos;étapes :&lt;/strong&gt; 20 à 30 étapes pour la plupart des samplers. Les modèles Lightning n&apos;en demandent que 2 à 8.&lt;/p&gt;
&lt;h2&gt;CFG (Classifier Free Guidance)&lt;/h2&gt;
&lt;p&gt;À quel point le modèle suit strictement votre prompt plutôt que sa propre interprétation.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Plage&lt;/th&gt;
&lt;th&gt;Effet&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1-4&lt;/td&gt;
&lt;td&gt;Très créatif, interprétation lâche. Souvent incohérent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;5-7&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Bon équilibre pour la plupart des travaux&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;7-10&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Forte adhérence au prompt. Zone idéale pour le photoréalisme avec SDXL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10-15&lt;/td&gt;
&lt;td&gt;Risque d&apos;artefacts et de couleurs trop cuites&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;15+&lt;/td&gt;
&lt;td&gt;Presque toujours trop. Artefacts garantis&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Note :&lt;/strong&gt; SD 3.5 utilise un mécanisme de guidance différent. Le concept de CFG s&apos;applique encore, mais l&apos;échelle se comporte autrement -- commencez plus bas (3-5) et ajustez.&lt;/p&gt;
&lt;h2&gt;Résolution&lt;/h2&gt;
&lt;p&gt;L&apos;époque du 512x512 est finie.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Modèle&lt;/th&gt;
&lt;th&gt;Résolution native&lt;/th&gt;
&lt;th&gt;Plage pratique&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SD 1.5&lt;/td&gt;
&lt;td&gt;512x512&lt;/td&gt;
&lt;td&gt;512x512 à 768x768&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SDXL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1024x1024&lt;/td&gt;
&lt;td&gt;1024x1024 (standard), 1024x768, 768x1024&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SD 3.5&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1024x1024&lt;/td&gt;
&lt;td&gt;1024x1024+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Flux&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1024x1024&lt;/td&gt;
&lt;td&gt;1024x1024+, 4K possible sur GPU haut de gamme&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Dépasser la résolution native risque de produire des artefacts et des problèmes de composition. Utilisez plutôt hi-res fix ou l&apos;upscaling au lieu de générer directement en 2048x2048.&lt;/p&gt;
&lt;h2&gt;Clip Skip&lt;/h2&gt;
&lt;p&gt;Moins pertinent qu&apos;avant.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;SD 1.5 :&lt;/strong&gt; Clip skip 1-2 compte beaucoup. Les modèles anime utilisent souvent clip skip 2.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SDXL :&lt;/strong&gt; Utilise deux encodeurs de texte (CLIP + OpenCLIP). Clip skip est largement ignoré -- l&apos;architecture le gère différemment.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SD 3.5 / Flux :&lt;/strong&gt; Pas applicable de la même manière. Ces modèles utilisent un encodage de texte basé sur des transformers.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Si vous êtes sur SDXL ou plus récent : ne vous préoccupez pas de clip skip. Si vous êtes sur SD 1.5 : gardez-le à 1 pour le photoréalisme, 2 pour l&apos;anime.&lt;/p&gt;
&lt;h2&gt;Prompts négatifs&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;La philosophie s&apos;est retournée.&lt;/strong&gt; En 2023, le conseil était d&apos;utiliser de longues listes de prompts négatifs. En 2026, le consensus est : &lt;strong&gt;commencez sans rien et n&apos;ajoutez que ce dont vous avez besoin pour corriger.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Pourquoi ce changement :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SDXL et Flux comprennent le langage naturel bien mieux que SD 1.5&lt;/li&gt;
&lt;li&gt;Les longs prompts négatifs peuvent en fait &lt;em&gt;restreindre la créativité&lt;/em&gt; et produire de moins bons résultats&lt;/li&gt;
&lt;li&gt;&quot;bad anatomy&quot; est trop vague pour être utile. &quot;ugly&quot; ne fonctionne pas parce que SD n&apos;a pas été entraîné sur des images étiquetées &quot;ugly&quot;&lt;/li&gt;
&lt;li&gt;Certains modèles donnent des résultats nettement pires avec de longs négatifs &lt;small&gt;&lt;a href=&quot;#ref5&quot;&gt;[5]&lt;/a&gt;&lt;/small&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Approche actuelle :&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Générez d&apos;abord sans aucun prompt négatif.&lt;/li&gt;
&lt;li&gt;Si vous voyez un problème précis (doigts en trop, arrière-plan flou), ajoutez un négatif ciblé pour celui-ci.&lt;/li&gt;
&lt;li&gt;Utilisez la pondération d&apos;emphase : &lt;code class=&quot;language-text&quot;&gt;(blurry:1.3)&lt;/code&gt; au lieu de simplement &lt;code class=&quot;language-text&quot;&gt;blurry&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Gardez-le court -- 5 à 10 termes au maximum.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Référence rapide GPU&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;GPU&lt;/th&gt;
&lt;th&gt;VRAM&lt;/th&gt;
&lt;th&gt;Bien pour&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;RTX 3060 12GB&lt;/td&gt;
&lt;td&gt;12GB&lt;/td&gt;
&lt;td&gt;SD 1.5, SDXL de base&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RTX 4070 Ti&lt;/td&gt;
&lt;td&gt;12GB&lt;/td&gt;
&lt;td&gt;SDXL, un peu de Flux&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RTX 4090&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;24GB&lt;/td&gt;
&lt;td&gt;Tout. La machine de travail&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RTX 5090&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;32GB&lt;/td&gt;
&lt;td&gt;Tout, y compris la 4K et la génération par lots&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cartes 8GB&lt;/td&gt;
&lt;td&gt;8GB&lt;/td&gt;
&lt;td&gt;Minimum viable. ComfyUI aide à gérer la VRAM&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Le seuil des 24 Go est celui où les choses deviennent confortables pour SDXL et Flux sans jongler constamment avec la VRAM.&lt;/p&gt;
&lt;h2&gt;Correctifs rapides de dépannage&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Problème&lt;/th&gt;
&lt;th&gt;À essayer&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sortie floue&lt;/td&gt;
&lt;td&gt;Augmentez les étapes. Vérifiez que la résolution correspond à la résolution native du modèle&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Doigts/membres en trop&lt;/td&gt;
&lt;td&gt;Ajoutez &lt;code class=&quot;language-text&quot;&gt;extra fingers, extra limbs&lt;/code&gt; au prompt négatif. Ou utilisez ControlNet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Couleurs sursaturées&lt;/td&gt;
&lt;td&gt;Baissez le CFG. Passez à DPM++ 2M Karras&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;La composition est mauvaise&lt;/td&gt;
&lt;td&gt;Utilisez ControlNet (depth, canny, pose) au lieu de vous battre avec le prompt&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;La génération est lente&lt;/td&gt;
&lt;td&gt;Utilisez un modèle Lightning, réduisez les étapes, utilisez ComfyUI pour une meilleure VRAM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Manque de VRAM&lt;/td&gt;
&lt;td&gt;Passez à ComfyUI, réduisez la taille du batch, utilisez fp16&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h3&gt;Références&lt;/h3&gt;
&lt;p&gt;&lt;a id=&quot;ref1&quot;&gt;&lt;/a&gt;1. &lt;a href=&quot;https://blogs.nvidia.com/blog/rtx-ai-garage-flux-2-comfyui/&quot;&gt;Flux 2 and NVIDIA RTX AI Integration&lt;/a&gt; -- &lt;em&gt;Article NVIDIA sur Flux 2 avec ComfyUI.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref2&quot;&gt;&lt;/a&gt;2. &lt;a href=&quot;https://platform.stability.ai/docs/release-notes&quot;&gt;Stability AI Release Notes&lt;/a&gt; -- &lt;em&gt;Dépréciation de SD 3.0 et sortie de 3.5.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref3&quot;&gt;&lt;/a&gt;3. &lt;a href=&quot;https://huggingface.co/ByteDance/SDXL-Lightning&quot;&gt;SDXL-Lightning by ByteDance&lt;/a&gt; -- &lt;em&gt;Génération en 2 à 8 étapes à 1024 px.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref4&quot;&gt;&lt;/a&gt;4. &lt;a href=&quot;https://wiki.shakker.ai/en/comfyui-vs-automatic1111&quot;&gt;ComfyUI vs Automatic1111 2026 Comparison&lt;/a&gt; -- &lt;em&gt;Comparaison des performances et des fonctionnalités.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref5&quot;&gt;&lt;/a&gt;5. &lt;a href=&quot;https://stable-diffusion-art.com/how-to-use-negative-prompts/&quot;&gt;How to Use Negative Prompts Effectively&lt;/a&gt; -- &lt;em&gt;Guide mis à jour sur la philosophie des prompts négatifs minimaux.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref6&quot;&gt;&lt;/a&gt;6. &lt;a href=&quot;https://civitai.com/articles/7484/understanding-stable-diffusion-samplers-beyond-image-comparisons&quot;&gt;Understanding Stable Diffusion Samplers&lt;/a&gt; -- &lt;em&gt;Guide de comparaison et de sélection des samplers.&lt;/em&gt;&lt;br&gt;
&lt;a id=&quot;ref7&quot;&gt;&lt;/a&gt;7. &lt;a href=&quot;https://www.cubix.co/blog/best-model-for-stable-diffusion/&quot;&gt;Best Stable Diffusion Models for 2026&lt;/a&gt; -- &lt;em&gt;Panorama actuel des modèles.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Articles liés&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/pushing-the-stable-diffussion-limits/&quot;&gt;Stable Diffusion Photorealism: Settings &amp;#x26; GPU Limits Guide&lt;/a&gt; -- analyse approfondie pour obtenir des résultats photoréalistes avec les modèles actuels.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[À propos]]></title><description><![CDATA[Les pages À propos sont une chose étrange à écrire. On finit par se décrire comme un étranger, et cela me rend méfiant. Donc, simplement. Je…]]></description><link>https://bdteo.com/fr/about/</link><guid isPermaLink="false">https://bdteo.com/fr/about/</guid><pubDate>Thu, 04 May 2023 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;Les pages À propos sont une chose étrange à écrire. On finit par se décrire comme un étranger, et cela me rend méfiant.&lt;/p&gt;
&lt;p&gt;Donc, simplement. Je suis Boris. J&apos;écris du logiciel depuis un peu plus de quatorze ans. Le travail paie le loyer, nourrit les chiens et me donne presque chaque matin un problème à ronger — ce qui, honnêtement, est déjà plus que ce que font la plupart des métiers. Ces jours-ci, je suis la moitié backend d&apos;une petite équipe produit, quelque part entre Laravel, Docker et Kubernetes, surtout préoccupé par des files d&apos;attente qui devraient se vider plus vite qu&apos;elles ne se remplissent et par des index qui grossissent plus vite que je ne voudrais.&lt;/p&gt;
&lt;p&gt;Avant le logiciel, j&apos;ai étudié les mathématiques à l&apos;université. Je n&apos;ai jamais vraiment arrêté. Les mathématiques sont l&apos;endroit où je vais quand le monde devient trop bruyant. La théorie des nombres surtout — il y a quelque chose d&apos;honnête dans un nombre premier que je ne retrouve pas dans beaucoup d&apos;autres endroits. Une grande partie de ce que j&apos;écris ici part de là. Une petite démangeaison mathématique. Un article médical à moitié compris. Une phrase française qu&apos;il m&apos;a fallu lire quatre fois. Puis je tire dessus et je regarde ce qui vient avec.&lt;/p&gt;
&lt;p&gt;Le blog est l&apos;endroit où cela se passe. Rien de tout cela n&apos;est un travail d&apos;expert. Je lis de la médecine que je n&apos;ai aucune raison de lire. Je lis des romans bulgares et français et je fais semblant de suivre. J&apos;apprends l&apos;allemand lentement, surtout parce que la grammaire m&apos;amuse. Il m&apos;arrive d&apos;écrire du C++ à la main. Pas pour le travail — simplement parce que la discipline est une chose en soi. Les essais sortent de tout cela — de tranquilles tentatives de noter ce que j&apos;ai remarqué avant de l&apos;oublier.&lt;/p&gt;
&lt;p&gt;Je vis à Sofia. Deux chiens errants recueillis — Кожухка et Еклер — veillent à ce que je ne me prenne pas trop au sérieux. Ils ont déjà échoué. Ils continuent d&apos;essayer.&lt;/p&gt;
&lt;p&gt;C&apos;est à peu près tout. Je garde une petite enseigne de freelance sous le nom de Percepticus, pour les travaux qui arrivent par bouche à oreille. Sinon, les essais sont ici, et cela me convient de rester discret sur le reste.&lt;/p&gt;</content:encoded></item></channel></rss>