<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="fr">
    <title>Meta Lambda</title>
    <subtitle>Cédric Brancourt, architecte logiciel indépendant.</subtitle>
    <link rel="self" type="application/atom+xml" href="https://meta-lambda.com/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://meta-lambda.com"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-10-12T00:00:00+00:00</updated>
    <id>https://meta-lambda.com/atom.xml</id>
    <entry xml:lang="fr">
        <title>Intégrer l&#x27;IA sans jeter l&#x27;argent par la fenêtre de contexte</title>
        <published>2026-10-12T00:00:00+00:00</published>
        <updated>2026-10-12T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://meta-lambda.com/integrer-ia-sans-jeter-argent-fenetre-contexte/"/>
        <id>https://meta-lambda.com/integrer-ia-sans-jeter-argent-fenetre-contexte/</id>
        
        <content type="html" xml:base="https://meta-lambda.com/integrer-ia-sans-jeter-argent-fenetre-contexte/">&lt;p&gt;Dans un &lt;em&gt;pipeline&lt;&#x2F;em&gt; qui enchaîne des appels à un LLM, l&#x27;étape qui fait le plus d&#x27;appels n&#x27;est pas forcément celle qui coûte le plus cher.
Et chaque jeton envoyé dans la fenêtre de contexte a un coût : en argent chez un fournisseur d&#x27;API, en latence, et en calcul, donc indirectement en énergie.
Ce coût grimpe vite avec la taille des données.&lt;&#x2F;p&gt;
&lt;p&gt;Or une partie de cette dépense peut être évitée : des contenus renvoyés à l&#x27;identique, des étapes rejouées, des questions que du code aurait réglées seul.&lt;&#x2F;p&gt;
&lt;p&gt;Repérer et supprimer ce gaspillage n&#x27;est pas une affaire de bonnes intentions : la frugalité est de l&#x27;ingénierie de performance, avec les mêmes réflexes que d&#x27;habitude.
Voici ceux que j&#x27;ai retenus en développant &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Electron-libre&#x2F;RetroDoc&quot;&gt;RetroDoc&lt;&#x2F;a&gt;, un outil qui reconstruit la documentation d&#x27;un dépôt Git à partir de son code (j&#x27;en parlais dans &lt;a href=&quot;&#x2F;la-dette-de-documentation&#x2F;&quot;&gt;La dette de documentation&lt;&#x2F;a&gt;).
Les principes valent pour d&#x27;autres projets de ce genre, mais les gains que je cite dépendent de mon projet, de mon modèle et de mes données : RetroDoc ne sert qu&#x27;à les illustrer.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;avant-d-optimiser-trouvez-ce-qui-pese-vraiment&quot;&gt;Avant d&#x27;optimiser, trouvez ce qui pèse vraiment&lt;&#x2F;h2&gt;
&lt;p&gt;Ne vous fiez pas à l&#x27;intuition.
Appels, jetons et temps ne se répartissent pas de la même façon entre les étapes d&#x27;un &lt;em&gt;pipeline&lt;&#x2F;em&gt;, et on est toujours tenté de s&#x27;attaquer à celle qui fait le plus d&#x27;appels.&lt;&#x2F;p&gt;
&lt;p&gt;Chez moi, une étape de résumé fait 45 à 55 % des appels sur une application d&#x27;environ 180 fichiers. La cible idéale !
Sauf que ses &lt;em&gt;prompts&lt;&#x2F;em&gt; sont petits : elle ne pèse que 9 à 17 % du temps.
L&#x27;essentiel se passe dans une autre étape : 61 % du temps en local (72,6 minutes sur 118,4), 72 % avec DeepSeek.
La supprimer aurait économisé des appels, pas des heures.&lt;&#x2F;p&gt;
&lt;figure&gt;
  &lt;img src=&quot;&#x2F;assets&#x2F;img&#x2F;frugalite-mesure.svg&quot; alt=&quot;Deux étapes d&#x27;un pipeline : le résumé des fichiers fait 49 % des appels mais 17 % du temps, les use cases font 39 % des appels mais 72 % du temps.&quot;&gt;
  &lt;figcaption&gt;Le nombre d&#x27;appels ne dit pas où passe le temps.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Knuth le disait déjà en 1974, bien avant les LLM :&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;It is often a mistake to make a priori judgments about what parts of a program are really critical, since the universal experience of programmers who have been using measurement tools has been that their intuitive guesses fail.
— Donald Knuth, &lt;em&gt;Structured Programming with go to Statements&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Pour ne pas avoir à deviner, faites afficher cette répartition à chaque exécution : un récapitulatif des appels, des jetons et du temps, étape par étape et modèle par modèle.
Si votre fournisseur les expose, séparez les jetons d&#x27;entrée des jetons de sortie (ils ne pèsent pas pareil), repérez ceux qu&#x27;un cache de &lt;em&gt;prompt&lt;&#x2F;em&gt; a déjà traités, et comptez à part les appels échoués ou rejoués.
Une étape peut en effet consommer beaucoup de jetons sans être lente, ou l&#x27;inverse.&lt;&#x2F;p&gt;
&lt;p&gt;Deux précautions rendent ce récapitulatif fiable.
Ne comptez que les jetons que le serveur rapporte, sans les estimer à partir du nombre de caractères, quitte à signaler que le total est sous-estimé.
Et si vous affichez un prix, datez-le : les tarifs changent plus vite que votre code, et une seule grille ne vaut pas pour plusieurs modèles.
À défaut, donnez au moins les volumes de jetons, qui permettent de le calculer.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;reutilisez-ce-qui-est-deja-fait&quot;&gt;Réutilisez ce qui est déjà fait&lt;&#x2F;h2&gt;
&lt;p&gt;Si les données qu&#x27;un traitement reçoit n&#x27;ont pas changé depuis la dernière exécution, inutile de rappeler le modèle : on réutilise le résultat déjà obtenu.
Associez à chaque résultat une clé calculée sur ce dont il dépend (le contenu des documents lus, le modèle utilisé, la version du &lt;em&gt;prompt&lt;&#x2F;em&gt;) et sautez ce qui est à jour.
La clé doit couvrir tout ce qui peut changer le résultat : selon votre système, cela peut inclure aussi les paramètres de génération ou le format de sortie attendu.&lt;&#x2F;p&gt;
&lt;p&gt;Enregistrez après chaque appel, pas à la fin : une exécution interrompue reprend alors où elle s&#x27;était arrêtée, au lieu de jeter le travail déjà fait.&lt;&#x2F;p&gt;
&lt;p&gt;Un cache n&#x27;est utile que si ses clés sont stables.
Dans mon cas, une étape intermédiaire n&#x27;était pas déterministe : ses identifiants changeaient à chaque exécution, toutes les clés en aval aussi, et une relance « sans changement » refaisait tout. Le tonneau des Danaïdes !
Cherchez ce qui, dans votre &lt;em&gt;pipeline&lt;&#x2F;em&gt;, rend vos clés instables, et décidez à l&#x27;avance ce qui invalide le cache : si changer un &lt;em&gt;prompt&lt;&#x2F;em&gt; ne le vide pas, vous servirez de vieux résultats sans le savoir.
Et gardez en tête qu&#x27;avec un modèle non déterministe, réutiliser le cache, c&#x27;est accepter la réponse de la première génération : une mauvaise réponse reste figée tant que l&#x27;entrée ne change pas, ou que vous ne videz pas le cache.&lt;&#x2F;p&gt;
&lt;figure&gt;
  &lt;img src=&quot;&#x2F;assets&#x2F;img&#x2F;frugalite-cache.svg&quot; alt=&quot;Une clé calculée sur le contenu, le modèle et la version du prompt. Si elle a déjà été vue, le résultat est réutilisé sans appel ; sinon on appelle le modèle et on enregistre.&quot;&gt;
  &lt;figcaption&gt;Un cache n&#x27;économise que si sa clé est stable.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;lisez-chaque-document-une-seule-fois&quot;&gt;Lisez chaque document une seule fois&lt;&#x2F;h2&gt;
&lt;p&gt;Dans un &lt;em&gt;pipeline&lt;&#x2F;em&gt; à plusieurs étapes (extraction, classification, résumé, vérification), le même document finit souvent dans plusieurs &lt;em&gt;prompts&lt;&#x2F;em&gt;, un par question.
Le modèle relit donc plusieurs fois le même texte.&lt;&#x2F;p&gt;
&lt;p&gt;Deux remèdes.
Poser les questions ensemble dans un seul appel, avec une réponse structurée qui les couvre toutes.
Ou garder le contenu commun en tête de &lt;em&gt;prompt&lt;&#x2F;em&gt; et profiter du cache de &lt;em&gt;prompt&lt;&#x2F;em&gt; si votre fournisseur en propose (il évite de retraiter un début de &lt;em&gt;prompt&lt;&#x2F;em&gt; déjà envoyé).
Ils n&#x27;ont pas les mêmes gains : le premier change la façon d&#x27;interroger le modèle, le second dépend de ce que votre fournisseur propose et facture.&lt;&#x2F;p&gt;
&lt;p&gt;Pour savoir si le jeu en vaut la chandelle, mesurez d&#x27;abord le recouvrement : un petit script qui compte, pour chaque étape, les documents qu&#x27;elle lit, et ce que les étapes ont en commun.
Chez moi, deux étapes de RetroDoc lisaient les mêmes fichiers : 36 % des caractères qu&#x27;elles lisaient portaient sur des fichiers lus deux fois.
En les fusionnant, j&#x27;ai supprimé un tiers des appels et des jetons sur une application réelle.&lt;&#x2F;p&gt;
&lt;p&gt;Mais fusionner n&#x27;est pas gratuit pour la qualité.
L&#x27;ordre des questions compte : en plaçant la question sur les entités en premier, le modèle ne retrouvait plus qu&#x27;un seul point d&#x27;entrée de l&#x27;application (route, commande…) sur une quinzaine.
Et sur une petite bibliothèque de neuf fichiers, la lecture fusionnée retrouvait moins de points d&#x27;entrée que les deux lectures séparées.
Gardez donc l&#x27;ancien chemin disponible, et comparez.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;reservez-le-modele-a-ce-qui-est-ambigu&quot;&gt;Réservez le modèle à ce qui est ambigu&lt;&#x2F;h2&gt;
&lt;p&gt;Si du code peut répondre avec certitude, qu&#x27;il réponde.
Beaucoup de contrôles qu&#x27;on délègue à un LLM relèvent de la vérification mécanique : la source citée existe-t-elle, la valeur annoncée apparaît-elle dans le texte, le JSON respecte-t-il son schéma, le chemin existe-t-il ?
Passez chaque réponse du modèle par ces contrôles, et ne le rappelez que pour les cas douteux.
Un tel contrôle ne doit valider que ce qu&#x27;il peut établir mécaniquement : il conclut « valide », « invalide » ou « indéterminé », et ce dernier cas part au modèle.&lt;&#x2F;p&gt;
&lt;p&gt;Chez moi, chaque étape d&#x27;un &lt;em&gt;use case&lt;&#x2F;em&gt; (un scénario fonctionnel décrit pas à pas) cite les lignes de code qui la justifient, et les faire vérifier par un LLM représentait un appel sur cinq.
Une simple recherche de texte règle une bonne part des cas : le contrôle vérifie que les lignes citées existent et que les noms mentionnés par l&#x27;étape s&#x27;y trouvent, et n&#x27;envoie au modèle que le reste.&lt;&#x2F;p&gt;
&lt;p&gt;Sur une petite bibliothèque, 80 % des étapes passent seules.
Mais un &lt;em&gt;use case&lt;&#x2F;em&gt; n&#x27;est réglé que si toutes ses étapes passent : 6 sur 13, ce qui ramène cette vérification de 8 à 4 appels.
Sur une application plus grande et plus dynamique, seule la moitié des étapes passent seules, et le reste part encore au modèle.
Le gain dépend donc de votre code : annoncez le résultat moyen, pas celui de votre meilleur cas.&lt;&#x2F;p&gt;
&lt;p&gt;Ce contrôle a aussi sa limite : trouver un nom dans le code ne prouve pas que la fonction fait ce que la documentation lui attribue.
Ses scores sont donc un peu optimistes (quand on demande aussi son avis au modèle, il est plus sévère), et on fait relire un échantillon par le modèle pour garder la mesure honnête.&lt;&#x2F;p&gt;
&lt;figure&gt;
  &lt;img src=&quot;&#x2F;assets&#x2F;img&#x2F;frugalite-controles.svg&quot; alt=&quot;Deux barres : sur une petite bibliothèque, 80 % des étapes sont validées par les contrôles seuls et 20 % partent au modèle ; sur linkding, environ 50 % et 50 %.&quot;&gt;
  &lt;figcaption&gt;Part des étapes réglées sans appel au modèle.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Le cas « indéterminé » est le plus facile à oublier.
Mon contrôle comptait d&#x27;abord comme valide une affirmation qui ne nommait rien de vérifiable, puisque rien ne la contredisait : 93 % de réussite, ramenés à 80 % après correction.
Un thermomètre qui affiche toujours 37 °C ne signale jamais de fièvre.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;produisez-une-fois-consultez-autant-que-vous-voulez&quot;&gt;Produisez une fois, consultez autant que vous voulez&lt;&#x2F;h2&gt;
&lt;p&gt;Séparez la production de la consommation.
Si le résultat d&#x27;un traitement lourd sert à de nombreuses questions, exposez-le par un chemin de lecture léger (fichiers statiques, index, API en lecture seule) plutôt que de reposer la question au modèle à chaque fois.
Le travail se fait une fois, à la génération.&lt;&#x2F;p&gt;
&lt;p&gt;Dans mon cas, les documents générés sont servis aux agents de code par un serveur en lecture seule, sans clé d&#x27;API ni modèle : une recherche lexicale écrite à la main suffit, sans &lt;em&gt;embeddings&lt;&#x2F;em&gt; tant qu&#x27;on n&#x27;a pas montré qu&#x27;elle échoue sur le vocabulaire du domaine.
Pour le montrer, testez-la sur quelques questions représentatives, avec les synonymes et les formulations indirectes que vos utilisateurs emploieront vraiment, et n&#x27;ajoutez des &lt;em&gt;embeddings&lt;&#x2F;em&gt; que si elle échoue de façon durable.
Les réponses sont déterministes.
Elles préviennent aussi quand un fichier cité a été modifié depuis la génération : un résultat produit une fois peut devenir faux, et le lecteur doit le savoir.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;laissez-l-utilisateur-plafonner-et-dites-ce-qui-reste-dehors&quot;&gt;Laissez l&#x27;utilisateur plafonner, et dites ce qui reste dehors&lt;&#x2F;h2&gt;
&lt;p&gt;Un &lt;em&gt;pipeline&lt;&#x2F;em&gt; LLM doit pouvoir être borné : un plafond sur le nombre de documents, un échantillon pour les vérifications lourdes, une estimation affichée avant de lancer.
Savoir « ça va faire 300 appels » avant de commencer change la décision de lancer ou non.&lt;&#x2F;p&gt;
&lt;p&gt;Mais la limite doit rester un choix visible.
Chez moi, l&#x27;utilisateur peut limiter l&#x27;analyse aux N fichiers les mieux classés, et le rapport liste tout ce qui n&#x27;a pas été analysé.
Attention : plafonner le périmètre ne plafonne pas le travail demandé au modèle, car un fichier long demande plus d&#x27;appels qu&#x27;un fichier court.
Je n&#x27;ai que le plafond de périmètre et l&#x27;estimation avant lancement ; si votre budget est une contrainte ferme, plafonnez aussi les appels ou les jetons.&lt;&#x2F;p&gt;
&lt;p&gt;J&#x27;ai refusé un plafond automatique : abandonner des entrées sans le dire fausse le résultat, et masquerait ici de la dette de documentation, exactement ce que l&#x27;outil doit révéler.
Une borne silencieuse est une dégradation, pas une optimisation.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;mesurez-avant-d-activer-une-optimisation&quot;&gt;Mesurez avant d&#x27;activer une optimisation&lt;&#x2F;h2&gt;
&lt;p&gt;C&#x27;est le conseil qui m&#x27;a le plus servi.
Une optimisation dont on ne connaît pas l&#x27;effet sur le résultat n&#x27;en est pas encore une : un &lt;em&gt;prompt&lt;&#x2F;em&gt; plus court ou un appel en moins qui dégrade silencieusement le résultat déplace le problème, il ne le supprime pas.
Mesurez donc la qualité en même temps que le gain, avec une référence si vous en avez une, et sur plusieurs exécutions, car la variance d&#x27;un LLM peut être aussi grande que l&#x27;effet que vous cherchez.&lt;&#x2F;p&gt;
&lt;p&gt;Un exemple.
Pour alléger les &lt;em&gt;prompts&lt;&#x2F;em&gt;, j&#x27;ai essayé de ne montrer au modèle que la signature et les premières lignes de chaque méthode, au lieu du code complet.
Résultat : des &lt;em&gt;prompts&lt;&#x2F;em&gt; 42 % plus petits. Sur le papier, c&#x27;est un gain net.&lt;&#x2F;p&gt;
&lt;p&gt;Mais en regardant aussi le résultat, le tableau change : le modèle retrouve moins d&#x27;entités (les objets métier du code, comme un utilisateur ou une commande), 77 à 94 par exécution contre 103 à 132 avec le code complet, dans les trois exécutions.
Sans cette seconde mesure, j&#x27;aurais activé l&#x27;optimisation par défaut et perdu de l&#x27;information sans m&#x27;en apercevoir.
Elle reste donc une option, et c&#x27;est un avantage : chaque projet règle le curseur selon ses moyens, la qualité de ses données, la puissance de son modèle, le temps dont il dispose.
Un projet aux données propres et au modèle puissant n&#x27;a pas les mêmes besoins qu&#x27;un projet qui tourne sur un petit modèle local.&lt;&#x2F;p&gt;
&lt;p&gt;La suite logique est de mesurer ce qu&#x27;on perd exactement : compter des entités donne une quantité, pas une qualité.
Avec une liste annotée à la main, on peut calculer la précision (ce que le modèle a trouvé est-il juste ?) et le rappel (a-t-il trouvé tout ce qu&#x27;on attendait ?).&lt;&#x2F;p&gt;
&lt;p&gt;J&#x27;ai appliqué la même démarche à une autre approche, qui réduit de 55 % le nombre d&#x27;appels sur une grosse application mais donne de moins bons résultats sur une petite.
Là aussi, c&#x27;est un réglage plutôt qu&#x27;un choix imposé : à chacun de l&#x27;adapter à la taille de son projet.&lt;&#x2F;p&gt;
&lt;figure&gt;
  &lt;img src=&quot;&#x2F;assets&#x2F;img&#x2F;frugalite-gain-perte.svg&quot; alt=&quot;À gauche, les prompts passent de 100 % à 58 % de leur taille. À droite, les entités retrouvées passent de 103-132 à 77-94 selon les exécutions.&quot;&gt;
  &lt;figcaption&gt;Un gain de 42 % sur les prompts, et une perte dans le résultat.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Ces chiffres viennent d&#x27;un modèle local, de peu de dépôts et de trois exécutions par réglage : ils valent pour mon projet, pas forcément pour le vôtre.
C&#x27;est précisément pourquoi il faut les mesurer chez soi.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;concevoir-juste-ce-qu-il-faut&quot;&gt;Concevoir juste ce qu&#x27;il faut&lt;&#x2F;h2&gt;
&lt;p&gt;Rien de spectaculaire là-dedans : des compteurs, des clés de cache stables, des contrôles qui évitent un appel, des limites choisies par l&#x27;utilisateur, et l&#x27;habitude de noter ce qu&#x27;on a mesuré avec ce qu&#x27;on n&#x27;a pas pu mesurer.
Chaque appel évité est un choix qu&#x27;on peut justifier, et chaque appel gardé aussi.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Il semble que la perfection soit atteinte non quand il n&#x27;y a plus rien à ajouter, mais quand il n&#x27;y a plus rien à retrancher.
— Antoine de Saint-Exupéry, &lt;em&gt;Terre des hommes&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;À condition de savoir ce qu&#x27;on retranche. Sans mesure, on coupe à l&#x27;aveugle.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="fr">
        <title>L&#x27;agentic coding ne réinvente pas l&#x27;ingénierie logicielle, il la rend obligatoire</title>
        <published>2026-10-05T00:00:00+00:00</published>
        <updated>2026-10-05T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://meta-lambda.com/agentic-coding-ingenierie-obligatoire/"/>
        <id>https://meta-lambda.com/agentic-coding-ingenierie-obligatoire/</id>
        
        <content type="html" xml:base="https://meta-lambda.com/agentic-coding-ingenierie-obligatoire/">&lt;p&gt;Des tests fiables, une architecture explicite, des builds reproductibles, des changements petits et réversibles, une documentation qui sert vraiment.
Je défends ces idées depuis des années, et elles passaient parfois pour du perfectionnisme. (Quand on est poli.)&lt;&#x2F;p&gt;
&lt;p&gt;Avec les agents de code, la discussion est close : ce ne sont plus des préférences d&#x27;équipe, c&#x27;est la condition pour que l&#x27;outil fonctionne.&lt;&#x2F;p&gt;
&lt;p&gt;C&#x27;est ce que je constate sur le terrain, et c&#x27;est ce que raconte aussi la communauté qui utilise ces outils intensivement.
Voici ce qui en ressort, avec les désaccords qui restent ouverts.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;un-agent-c-est-d-abord-une-boucle&quot;&gt;Un agent, c&#x27;est d&#x27;abord une boucle&lt;&#x2F;h2&gt;
&lt;p&gt;Un assistant classique propose du code, on le relit, on le corrige. Un agent agit : il lit le dépôt, lance des commandes, exécute les tests, observe le résultat et corrige. Un modèle qui n&#x27;exécute jamais son code reste dans le plausible. Un agent qui peut vérifier ce qu&#x27;il produit entre dans un cycle d&#x27;hypothèse, d&#x27;action, d&#x27;observation et de correction.&lt;&#x2F;p&gt;
&lt;p&gt;C&#x27;est un peu le rémoulage d&#x27;une lame : on dégrossit, on contrôle le fil, on corrige, et on recommence. Sans contrôle du fil, on affûte à l&#x27;aveugle.&lt;&#x2F;p&gt;
&lt;p&gt;Simon Willison, qui documente ces pratiques sous le nom d&#x27;&lt;em&gt;&lt;a href=&quot;https:&#x2F;&#x2F;simonwillison.net&#x2F;2026&#x2F;Feb&#x2F;23&#x2F;agentic-engineering-patterns&#x2F;&quot;&gt;agentic engineering&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;, en fait le critère qui distingue l&#x27;agent : il exécute le code qu&#x27;il écrit. Le prompt compte toujours, mais la qualité du résultat dépend surtout de ce que l&#x27;agent peut observer.&lt;&#x2F;p&gt;
&lt;figure&gt;
  &lt;img src=&quot;&#x2F;assets&#x2F;img&#x2F;agentic-boucle.svg&quot; alt=&quot;Un cycle en quatre étapes : hypothèse, action, observation, correction. L&#x27;observation s&#x27;appuie sur le compilateur, le linter, les tests, les logs, le navigateur et les métriques.&quot;&gt;
  &lt;figcaption&gt;Hypothèse, action, observation, correction : la boucle ne vaut que par ce qu&#x27;elle observe.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;donner-a-l-agent-quelque-chose-a-observer&quot;&gt;Donner à l&#x27;agent quelque chose à observer&lt;&#x2F;h2&gt;
&lt;p&gt;La question à se poser est simple : si l&#x27;agent modifie quelque chose, comment sait-il que ça marche ? Compilateur, tests, logs, navigateur, métriques : plus ces signaux sont fiables et rapides, plus la boucle est efficace.&lt;&#x2F;p&gt;
&lt;p&gt;Un compilateur strict et un linter intransigeant, c&#x27;est du feedback gratuit. Et ils ne se lassent jamais de répéter la même remarque. Sur un projet Rust, trois commandes suffisent à cadrer un agent :&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;cargo fmt --check
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;cargo clippy&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt; --&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt; -D warnings
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;cargo test
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Un développeur humain en tire le même bénéfice. Ce qui change, c&#x27;est qu&#x27;un projet sans ce filet ne fait plus seulement perdre du temps à l&#x27;équipe : il rend l&#x27;agent dangereux.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;le-depot-est-une-interface-et-la-documentation-aussi&quot;&gt;Le dépôt est une interface, et la documentation aussi&lt;&#x2F;h2&gt;
&lt;p&gt;Un dépôt a toujours été une interface pour les humains qui arrivent dans l&#x27;équipe. C&#x27;est maintenant aussi une interface pour les agents : comment démarrer, comment lancer les tests, où est la logique métier, quelles conventions comptent vraiment. Un humain compense une documentation médiocre avec son expérience et ses collègues. Un agent, lui, compense avec des suppositions, et avec beaucoup d&#x27;aplomb. (J&#x27;en parlais déjà dans &lt;a href=&quot;&#x2F;la-dette-de-documentation&#x2F;&quot;&gt;La dette de documentation&lt;&#x2F;a&gt;.)&lt;&#x2F;p&gt;
&lt;p&gt;Le fichier d&#x27;instructions (&lt;code&gt;AGENTS.md&lt;&#x2F;code&gt;, &lt;code&gt;CLAUDE.md&lt;&#x2F;code&gt;) illustre bien cette convergence. Une &lt;a href=&quot;https:&#x2F;&#x2F;research.google&#x2F;pubs&#x2F;from-black-box-to-toolbox-human-centered-explainability-in-developer-authored-agent-rules&#x2F;&quot;&gt;étude qualitative de Google&lt;&#x2F;a&gt; auprès de 12 développeurs montre qu&#x27;il joue un double rôle : instructions pour l&#x27;IA et documentation d&#x27;onboarding pour les humains. Elle montre aussi que les 12 valident leurs règles « au feeling », sans évaluation formelle. L&#x27;échantillon est petit, mais cela rejoint la mise en garde de Birgitta Böckeler (Thoughtworks) : le &lt;em&gt;&lt;a href=&quot;https:&#x2F;&#x2F;martinfowler.com&#x2F;articles&#x2F;exploring-gen-ai&#x2F;context-engineering-coding-agents.html&quot;&gt;context engineering&lt;&#x2F;a&gt;&lt;&#x2F;em&gt; n&#x27;a pas de tests unitaires et reste probabiliste. Son conseil : construire ces règles progressivement plutôt que de tout déverser d&#x27;un coup, et ne jamais parler de « garantie » quand on écrit une instruction.&lt;&#x2F;p&gt;
&lt;p&gt;Autrement dit, on retrouve ce qu&#x27;on disait de la documentation humaine : courte, vivante, maintenue.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ce-qui-compte-doit-etre-verifiable-par-une-machine&quot;&gt;Ce qui compte doit être vérifiable par une machine&lt;&#x2F;h2&gt;
&lt;p&gt;Une règle qui doit toujours être respectée ne devrait pas vivre uniquement dans un fichier Markdown. « Le module &lt;code&gt;billing&lt;&#x2F;code&gt; ne dépend jamais de &lt;code&gt;web&lt;&#x2F;code&gt; » peut s&#x27;écrire dans les instructions, ou faire échouer la CI. Seule la deuxième solution est fiable, pour les agents comme pour les humains pressés un vendredi soir.&lt;&#x2F;p&gt;
&lt;p&gt;L&#x27;idée est ancienne en clean architecture : les règles d&#x27;architecture s&#x27;automatisent. Les agents nous obligent simplement à le faire.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;les-tests-d-abord-mais-sans-ceremonie&quot;&gt;Les tests d&#x27;abord, mais sans cérémonie&lt;&#x2F;h2&gt;
&lt;p&gt;Le TDD&#x2F;BDD fait partie de mon hygiène de développement depuis longtemps. Avec un agent, je n&#x27;y ai pas renoncé, mais j&#x27;ai changé le rythme : beaucoup moins de petits cycles RED&#x2F;GREEN&#x2F;REFACTOR, des cycles plus larges et plus directs. Je continue pourtant à faire écrire les tests en premier, pour trois raisons :&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;c&#x27;est une &lt;strong&gt;reformulation du problème&lt;&#x2F;strong&gt; : le test dit autrement ce que le prompt demandait, et les écarts de compréhension apparaissent tôt ;&lt;&#x2F;li&gt;
&lt;li&gt;cela &lt;strong&gt;évite les trous de couverture&lt;&#x2F;strong&gt; : on n&#x27;écrit pas les tests après coup en fonction de ce que le code fait déjà ;&lt;&#x2F;li&gt;
&lt;li&gt;cela donne à l&#x27;agent un moyen de &lt;strong&gt;vérifier lui-même&lt;&#x2F;strong&gt; son code.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Les avis divergent. Willison recommande le &lt;em&gt;&lt;a href=&quot;https:&#x2F;&#x2F;simonwillison.net&#x2F;guides&#x2F;agentic-engineering-patterns&#x2F;red-green-tdd&#x2F;&quot;&gt;red&#x2F;green TDD&lt;&#x2F;a&gt;&lt;&#x2F;em&gt; : écrire le test, le voir échouer, puis implémenter. Böckeler, à l&#x27;inverse, a &lt;a href=&quot;https:&#x2F;&#x2F;martinfowler.com&#x2F;articles&#x2F;exploring-gen-ai&#x2F;tdd-in-the-agent-loop.html&quot;&gt;mesuré l&#x27;effet&lt;&#x2F;a&gt; sur des tâches simples : pas de différence nette avec ou sans TDD, des solutions sans TDD plutôt mieux classées, et au moins trois fois plus de tokens. Elle a arrêté de demander le test-first à ses agents.&lt;&#x2F;p&gt;
&lt;p&gt;Son raisonnement se tient : le red&#x2F;green ne prouve rien quand c&#x27;est l&#x27;agent qui écrit le test et constate l&#x27;échec, puisque personne ne vérifie que l&#x27;échec a lieu pour la bonne raison. Et une bonne partie des bénéfices du TDD (gérer la peur, avancer à petits pas, subir la friction de la conception) est propre à l&#x27;humain. Son expérience reste réduite (des tâches Python greenfield, une qualité jugée par un modèle), et elle n&#x27;a pas testé le cas où l&#x27;humain écrit lui-même les scénarios. Je n&#x27;y vois donc pas la preuve que j&#x27;ai tort, mais de quoi préciser ce que je garde : pas la cérémonie, ses fonctions.&lt;&#x2F;p&gt;
&lt;p&gt;Sa conclusion rejoint d&#x27;ailleurs mon expérience : mieux vaut surveiller les résultats que dicter la méthode. Elle propose les tests de mutation, qui montrent si un test détecterait réellement une régression, plutôt que d&#x27;imposer un rituel en espérant.&lt;&#x2F;p&gt;
&lt;p&gt;C&#x27;est aussi pourquoi je relis les tests avant le code. Ils sont la partie du diff qui dit ce que le système est censé faire.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;le-vrai-sujet-la-dette-cognitive&quot;&gt;Le vrai sujet : la dette cognitive&lt;&#x2F;h2&gt;
&lt;p&gt;C&#x27;est la partie la plus discutée par les praticiens, et la moins visible dans les guides.&lt;&#x2F;p&gt;
&lt;p&gt;Willison le résume ainsi : &lt;a href=&quot;https:&#x2F;&#x2F;simonwillison.net&#x2F;2026&#x2F;Feb&#x2F;23&#x2F;agentic-engineering-patterns&#x2F;&quot;&gt;écrire du code ne coûte presque plus rien&lt;&#x2F;a&gt;, ce qui bouleverse nos intuitions. Mais la compréhension, elle, n&#x27;est pas devenue gratuite. Un fil &lt;a href=&quot;https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=47196582&quot;&gt;Hacker News&lt;&#x2F;a&gt; décrit des équipes qui reçoivent chaque semaine des quantités de code qui marche à peu près, sans pouvoir le relire sérieusement. Une &lt;a href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;pdf&#x2F;2607.10856&quot;&gt;étude qualitative récente&lt;&#x2F;a&gt; parle de « dette de compréhension » : les agents produisent des changements plus vite et en lots plus gros que les relecteurs ne peuvent les inspecter.&lt;&#x2F;p&gt;
&lt;p&gt;Tout le monde n&#x27;est pas d&#x27;accord. Pour certains, « dette cognitive » est un mot-valise qui désigne un workflow mal construit (documentation absente, tests manquants, onboarding raté). Pour d&#x27;autres, le vrai problème est la qualité du code généré, donc la dette technique classique.&lt;&#x2F;p&gt;
&lt;p&gt;Mon avis : les deux ont raison, et c&#x27;est la discussion que j&#x27;ai avec mes clients depuis des années. Une dette cognitive est une dette technique que personne n&#x27;a encore identifiée. Les agents ne changent pas sa nature, seulement la vitesse à laquelle elle s&#x27;accumule.&lt;&#x2F;p&gt;
&lt;p&gt;Addy Osmani (qui travaille sur Claude Code chez Anthropic) donne une image qui fait mouche : avec un agent, le relecteur est souvent « le premier humain à voir ce code », car le raisonnement de l&#x27;agent est jeté une fois le diff produit. Sa piste : capturer l&#x27;intention et les alternatives écartées dans un journal de décisions attaché à la PR. Steve Yegge va dans le même sens avec &lt;a href=&quot;https:&#x2F;&#x2F;steve-yegge.medium.com&#x2F;gas-town-from-clown-show-to-v1-0-c239d9a407ec&quot;&gt;Beads&lt;&#x2F;a&gt;, un journal versionné dans Git qui garde le « pourquoi » des changements (un projet qu&#x27;il promeut lui-même, donc à prendre comme une illustration). Les ADR font la même chose depuis longtemps.&lt;&#x2F;p&gt;
&lt;p&gt;Les remèdes restent donc classiques : des changements petits et liés à une intention claire, des décisions écrites, une revue centrée sur l&#x27;architecture et le comportement, et une règle simple, ne pas fusionner ce qu&#x27;on ne sait pas expliquer.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;la-revue-devient-le-goulot&quot;&gt;La revue devient le goulot&lt;&#x2F;h2&gt;
&lt;p&gt;Avant, écrire coûtait plus cher que lire. Ce rapport s&#x27;inverse. Osmani rassemble des mesures récentes, avec une réserve qu&#x27;il signale lui-même : elles viennent surtout d&#x27;éditeurs d&#x27;outils. GitClear observe environ quatre fois plus de code produit par les utilisateurs quotidiens d&#x27;IA, pour un gain réel d&#x27;environ 12 %. Faros, sur 22 000 développeurs, relève 31 % de PR fusionnées sans aucune revue en plus. Personne n&#x27;a décidé d&#x27;arrêter de relire : le volume a décidé à notre place.&lt;&#x2F;p&gt;
&lt;figure&gt;
  &lt;img src=&quot;&#x2F;assets&#x2F;img&#x2F;agentic-goulot-revue.svg&quot; alt=&quot;Beaucoup de changements produits par les agents se heurtent à une capacité de revue humaine inchangée : peu en sortent relus et compris.&quot;&gt;
  &lt;figcaption&gt;Le volume a changé de côté, la capacité de relecture pas.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Ses recommandations ressemblent à du bon sens d&#x27;ingénieur :&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Trier par risque&lt;&#x2F;strong&gt; : un changement de configuration mérite un linter, un chemin de paiement mérite le grand jeu.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Garder les PR petites&lt;&#x2F;strong&gt;, car un diff énorme est refusé ou approuvé sans lecture.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Lire les tests modifiés d&#x27;abord&lt;&#x2F;strong&gt; : un agent peut « réparer » un test en réécrivant l&#x27;assertion pour coller au comportement cassé.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Mettre l&#x27;humain « sur la boucle »&lt;&#x2F;strong&gt; plutôt que « dans la boucle » : échantillonner et auditer, pas lire chaque ligne.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;La revue par IA est utile pour dégrossir, mais elle ne remplace pas la compréhension. Quand un modèle relit le code d&#x27;un autre modèle, ils ont tendance à se tromper de la même façon : ce que l&#x27;un ne voit pas, l&#x27;autre ne le voit pas non plus. Son « looks good » est sans doute sincère, il ne vaut pas celui d&#x27;un humain qui a compris.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;tout-doit-pouvoir-se-defaire&quot;&gt;Tout doit pouvoir se défaire&lt;&#x2F;h2&gt;
&lt;p&gt;Pour savoir quelle autonomie laisser à un agent, je me pose une seule question : que se passe-t-il si c&#x27;est faux ? Si la réponse est « je jette la branche », il peut travailler sans surveillance. Si c&#x27;est « une migration est partie en production », certainement pas. La branche dédiée est le minimum, et &lt;code&gt;git worktree&lt;&#x2F;code&gt; va plus loin : chaque agent reçoit son propre répertoire de travail, une paillasse jetable sans risque pour le reste.&lt;&#x2F;p&gt;
&lt;figure&gt;
  &lt;img src=&quot;&#x2F;assets&#x2F;img&#x2F;agentic-autonomie.svg&quot; alt=&quot;De gauche à droite, du réversible à l&#x27;irréversible : worktree jetable, branche relue, merge sur main, migration en production. L&#x27;autonomie laissée à l&#x27;agent diminue au fil de l&#x27;échelle.&quot;&gt;
  &lt;figcaption&gt;Plus c&#x27;est difficile à défaire, moins on laisse d&#x27;autonomie.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;C&#x27;est ce qui rend le parallélisme possible, mais il a sa limite. Un praticien qui fait tourner trois à cinq agents depuis des mois conclut que la décomposition des tâches compte plus que leur nombre. Des consignes vagues donnent autant d&#x27;interprétations que d&#x27;agents, et deux agents dans le même répertoire finissent par s&#x27;écraser. Le multi-agent sert à paralléliser des problèmes indépendants, pas à remplacer une conception qu&#x27;on n&#x27;a pas faite.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;securite-des-tests-verts-ne-suffisent-pas&quot;&gt;Sécurité : des tests verts ne suffisent pas&lt;&#x2F;h2&gt;
&lt;p&gt;Des chercheurs ont montré que des agents peuvent produire des &lt;a href=&quot;https:&#x2F;&#x2F;research.google&#x2F;pubs&#x2F;when-correct-is-not-safe-can-we-trust-functionally-correct-patches-generated-by-code-agents-2&#x2F;&quot;&gt;patchs qui passent tous les tests tout en contenant une faille&lt;&#x2F;a&gt;, que la faille ait été glissée volontairement par un attaquant ou introduite sans le vouloir. L&#x27;étude porte sur des scénarios précis, mais le principe est général : les tests ne prouvent que ce qu&#x27;ils couvrent.&lt;&#x2F;p&gt;
&lt;p&gt;Willison propose un cadre utile, la &lt;em&gt;lethal trifecta&lt;&#x2F;em&gt;. Un agent qui a accès à des données privées, qui lit du contenu non fiable et qui peut communiquer vers l&#x27;extérieur peut être manipulé par injection de prompt pour exfiltrer ces données. Dès que les trois sont réunis, le risque est structurel. La réponse est celle de tout système sensible : &lt;em&gt;sandbox&lt;&#x2F;em&gt;, permissions minimales, secrets isolés, réseau restreint, journalisation. Et l&#x27;autonomie reste proportionnelle au risque : refactorer une fonction pure ne demande pas les mêmes garde-fous que toucher à l&#x27;authentification.&lt;&#x2F;p&gt;
&lt;figure&gt;
  &lt;img src=&quot;&#x2F;assets&#x2F;img&#x2F;agentic-trifecta.svg&quot; alt=&quot;Trois cercles qui se recoupent : données privées, contenu non fiable, communication vers l&#x27;extérieur. Là où ils se rejoignent, une injection de prompt peut exfiltrer les données.&quot;&gt;
  &lt;figcaption&gt;La lethal trifecta : le risque apparaît quand les trois se rejoignent.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;ce-que-ca-dit-de-notre-metier&quot;&gt;Ce que ça dit de notre métier&lt;&#x2F;h2&gt;
&lt;p&gt;Avec les agents, les bonnes pratiques sont restées les mêmes. Ce qui a disparu, c&#x27;est la possibilité de s&#x27;en passer. Une équipe sans tests fiables, sans architecture explicite et sans discipline de revue pouvait survivre avec des humains qui compensent avec leurs petites mains. Avec des agents capables de produire dix fois plus de code, elle accumule dix fois plus vite sa dette.&lt;&#x2F;p&gt;
&lt;p&gt;La bonne question n&#x27;est plus « quel est mon meilleur prompt ? » mais « quel environnement ai-je construit pour que mon agent, et mes collègues, puissent réussir ? ».
Ça ressemble beaucoup plus à de l&#x27;ingénierie qu&#x27;à de la magie.&lt;&#x2F;p&gt;
&lt;p&gt;Boileau le disait déjà, et j&#x27;en parlais dans &lt;a href=&quot;&#x2F;j-ai-une-dette-technique-et-c-est-mon-choix&#x2F;&quot;&gt;J&#x27;ai une dette technique et c&#x27;est mon choix&lt;&#x2F;a&gt; :&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hâtez-vous lentement, et sans perdre courage,
Vingt fois sur le métier remettez votre ouvrage
— Boileau, &lt;em&gt;L&#x27;Art poétique&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Avec des agents qui vont dix fois plus vite, c&#x27;est devenu un conseil d&#x27;ingénieur.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;&lt;em&gt;Sources : &lt;a href=&quot;https:&#x2F;&#x2F;simonwillison.net&#x2F;guides&#x2F;agentic-engineering-patterns&#x2F;&quot;&gt;Simon Willison, Agentic Engineering Patterns&lt;&#x2F;a&gt; · Birgitta Böckeler, &lt;a href=&quot;https:&#x2F;&#x2F;martinfowler.com&#x2F;articles&#x2F;exploring-gen-ai&#x2F;tdd-in-the-agent-loop.html&quot;&gt;TDD inside the agent loop&lt;&#x2F;a&gt; et &lt;a href=&quot;https:&#x2F;&#x2F;martinfowler.com&#x2F;articles&#x2F;exploring-gen-ai&#x2F;context-engineering-coding-agents.html&quot;&gt;Context Engineering for Coding Agents&lt;&#x2F;a&gt; · Addy Osmani, &lt;a href=&quot;https:&#x2F;&#x2F;addyosmani.com&#x2F;blog&#x2F;agentic-code-review&#x2F;&quot;&gt;Agentic Code Review&lt;&#x2F;a&gt; · Google Research, &lt;a href=&quot;https:&#x2F;&#x2F;research.google&#x2F;pubs&#x2F;from-black-box-to-toolbox-human-centered-explainability-in-developer-authored-agent-rules&#x2F;&quot;&gt;règles d&#x27;agents&lt;&#x2F;a&gt; et &lt;a href=&quot;https:&#x2F;&#x2F;research.google&#x2F;pubs&#x2F;when-correct-is-not-safe-can-we-trust-functionally-correct-patches-generated-by-code-agents-2&#x2F;&quot;&gt;patchs corrects mais vulnérables&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="fr">
        <title>La dette de documentation : le passif caché qui plombe vos projets</title>
        <published>2026-09-28T00:00:00+00:00</published>
        <updated>2026-09-28T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://meta-lambda.com/la-dette-de-documentation/"/>
        <id>https://meta-lambda.com/la-dette-de-documentation/</id>
        
        <content type="html" xml:base="https://meta-lambda.com/la-dette-de-documentation/">&lt;p&gt;Il y a une dette dont on parle beaucoup en 2026 : la dette technique. Refactoring repoussé, tests manquants, architecture qui grince. Mais il existe une dette cousine, tout aussi coûteuse et bien moins visible sur un dashboard de qualité de code : la &lt;strong&gt;dette de documentation&lt;&#x2F;strong&gt;, fonctionnelle autant que technique.&lt;&#x2F;p&gt;
&lt;p&gt;J&#x27;ai vu ce cas de figure se répéter, sous des formes différentes mais avec la même mécanique de fond.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;comment-on-en-arrive-la&quot;&gt;Comment on en arrive là&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;« Shipping first time code is like going into debt »&lt;&#x2F;em&gt; — Ward Cunningham, qui a inventé le terme « dette technique » en 1992, posait déjà le problème : livrer du code non mûri, c&#x27;est emprunter. Reste à savoir qui paiera les intérêts, et avec quoi.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;strong&gt;La solution grandit sans cadrage produit formel.&lt;&#x2F;strong&gt; Une PME lance un MVP, une fonctionnalité en entraîne une autre, un client important demande une exception, puis une autre. Le produit avance à la vitesse du besoin, pas à celle de la documentation. Personne n&#x27;a jamais écrit noir sur blanc &lt;em&gt;pourquoi&lt;&#x2F;em&gt; telle règle métier existe — elle vit uniquement dans la mémoire de ceux qui l&#x27;ont codée, ou pire, uniquement dans le code lui-même, sous forme de &lt;code&gt;if&lt;&#x2F;code&gt; imbriqués sans commentaire.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Les sachants quittent l&#x27;entreprise.&lt;&#x2F;strong&gt; Et avec eux part la doc fonctionnelle &lt;em&gt;et&lt;&#x2F;em&gt; technique qui n&#x27;a jamais existé ailleurs que dans leur tête. Ce n&#x27;est pas une négligence isolée : c&#x27;est souvent la conséquence directe du premier point. Quand il n&#x27;y a jamais eu de cadrage formel, il n&#x27;y a rien à transmettre au départ d&#x27;un sachant — sinon un debrief de dernière minute, forcément incomplet.&lt;&#x2F;p&gt;
&lt;p&gt;Ce phénomène n&#x27;a rien d&#x27;anecdotique. Selon l&#x27;enquête 2022 de Stack Overflow menée auprès de plus de 70 000 développeurs, 68 % des développeurs tombent au moins une fois par semaine sur une information dont ils ont besoin... mais qui n&#x27;existe nulle part, sauf dans la tête de quelqu&#x27;un d&#x27;autre. Un chiffre qui grimpe à 73 % chez les managers techniques — souvent les plus expérimentés, donc les plus proches du départ.&lt;&#x2F;p&gt;
&lt;figure&gt;
  &lt;img src=&quot;&#x2F;assets&#x2F;img&#x2F;dette-doc-silo.svg&quot; alt=&quot;68 % des développeurs et 73 % des managers techniques tombent au moins une fois par semaine sur une information dont ils ont besoin mais qui n&#x27;existe nulle part, sauf dans la tête de quelqu&#x27;un d&#x27;autre.&quot;&gt;
  &lt;figcaption&gt;Une information indispensable, absente de partout sauf d&#x27;une tête.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;les-impacts-au-dela-de-l-inconfort&quot;&gt;Les impacts, au-delà de l&#x27;inconfort&lt;&#x2F;h2&gt;
&lt;p&gt;Le manque de documentation n&#x27;est pas qu&#x27;une gêne pour les nouveaux arrivants. Il a des conséquences concrètes et mesurables :&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Onboarding qui s&#x27;éternise&lt;&#x2F;strong&gt; : un nouveau développeur met des semaines à devenir autonome, en reconstituant par archéologie ce qu&#x27;une doc aurait transmis en heures.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Régressions fonctionnelles&lt;&#x2F;strong&gt; : sans trace des règles métier, on réintroduit des bugs déjà corrigés, ou on casse un comportement qui avait une raison d&#x27;être oubliée depuis longtemps.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Décisions produit à l&#x27;aveugle&lt;&#x2F;strong&gt; : impossible de savoir si une fonctionnalité existe déjà, sous quelle forme, ni pourquoi elle a été conçue ainsi.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Dépendance critique à quelques individus&lt;&#x2F;strong&gt; : le bus factor devient un risque business réel, pas juste une private joke d&#x27;ingénieur.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Un coût qui s&#x27;accumule avec le temps&lt;&#x2F;strong&gt; : chaque fonctionnalité ajoutée sans être documentée alourdit un peu plus la dette suivante — comme des intérêts qui s&#x27;empilent sur un capital jamais remboursé.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Le temps perdu se chiffre facilement. Toujours selon la même enquête, 62 % des développeurs passent plus de 30 minutes par jour à chercher des réponses ou des solutions, et 25 % y consacrent plus d&#x27;une heure — ce qui représente, pour une équipe de 50 développeurs, entre 333 et 651 heures perdues chaque semaine. À l&#x27;échelle macro, le Consortium for Information &amp;amp; Software Quality (CISQ) a estimé, dans son rapport 2022 sur la qualité logicielle aux États-Unis, que la mauvaise qualité logicielle coûtait 2 410 milliards de dollars, dont 1 520 milliards imputables à la seule dette technique. La documentation manquante n&#x27;est évidemment pas l&#x27;unique cause de ce chiffre, mais elle en est un contributeur direct et documenté.&lt;&#x2F;p&gt;
&lt;figure&gt;
  &lt;img src=&quot;&#x2F;assets&#x2F;img&#x2F;dette-doc-couts.svg&quot; alt=&quot;62 % des développeurs perdent plus de 30 minutes par jour à chercher des réponses, 25 % plus d&#x27;une heure, soit 333 à 651 heures perdues chaque semaine pour une équipe de 50 développeurs. Sur les 2410 milliards de dollars que coûte la mauvaise qualité logicielle aux États-Unis, 1520 milliards sont imputables à la dette technique.&quot;&gt;
  &lt;figcaption&gt;Le manque de documentation se chiffre, en heures perdues comme en dollars.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;blockquote&gt;
&lt;p&gt;Comme le résume Damian Conway, auteur de &lt;em&gt;Perl Best Practices&lt;&#x2F;em&gt; : &lt;em&gt;« Documentation is a love letter that you write to your future self »&lt;&#x2F;em&gt; — une documentation, c&#x27;est une lettre d&#x27;amour qu&#x27;on s&#x27;écrit à soi-même dans le futur. Faute de l&#x27;avoir écrite, c&#x27;est toute l&#x27;équipe suivante qui en paie l&#x27;absence.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;meme-a-l-heure-de-l-ia-la-doc-reste-indispensable&quot;&gt;Même à l&#x27;heure de l&#x27;IA, la doc reste indispensable&lt;&#x2F;h2&gt;
&lt;p&gt;On pourrait penser que l&#x27;essor des agents de code change la donne, ou même rend la doc obsolète puisque l&#x27;IA « comprend » le code. C&#x27;est l&#x27;inverse qui se produit.&lt;&#x2F;p&gt;
&lt;p&gt;Un agent de code ou de QA n&#x27;a pas d&#x27;intuition métier. Il ne sait pas &lt;em&gt;pourquoi&lt;&#x2F;em&gt; une règle existe, seulement ce que le code fait littéralement. Une documentation fonctionnelle et technique de qualité, passée en contexte ou indexée en RAG, améliore considérablement l&#x27;efficacité de ces agents : moins d&#x27;hallucinations sur l&#x27;intention métier, des suggestions de code plus pertinentes, une revue de QA capable de distinguer un vrai bug d&#x27;un comportement voulu. La doc n&#x27;a pas perdu de valeur avec l&#x27;IA — elle en a gagné, parce qu&#x27;elle devient la mémoire structurée que l&#x27;agent consulte à chaque tâche.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;retrodoc-reconstruire-la-doc-manquante&quot;&gt;RetroDoc : reconstruire la doc manquante&lt;&#x2F;h2&gt;
&lt;p&gt;J&#x27;interviens régulièrement chez mes clients pour résoudre leurs problèmes de dette technique, mais aussi pour les accompagner dans l&#x27;intégration efficace de l&#x27;IA à leur métier. Et sur le terrain, le constat est sans appel : le manque de documentation ne freine pas seulement leurs équipes, il a un impact direct sur mes propres missions. Chaque heure passée à reconstituer manuellement le contexte fonctionnel d&#x27;une application est une heure en moins pour résoudre le vrai problème.&lt;&#x2F;p&gt;
&lt;p&gt;C&#x27;est ce constat qui m&#x27;a poussé à créer &lt;strong&gt;RetroDoc&lt;&#x2F;strong&gt;, et plus largement une panoplie d&#x27;outils pensés pour ça, en m&#x27;appuyant sur ma longue expérience du développement logiciel et de la dette technique, combinée à mon savoir-faire en intégration d&#x27;IA agentique. RetroDoc s&#x27;appuie sur le code source, l&#x27;historique des tickets et les quelques documents épars encore disponibles pour &lt;strong&gt;reconstruire a posteriori&lt;&#x2F;strong&gt; la documentation fonctionnelle et technique qui n&#x27;a jamais été écrite — ou qui s&#x27;est perdue avec le départ des sachants.&lt;&#x2F;p&gt;
&lt;p&gt;L&#x27;idée n&#x27;est pas de remplacer la discipline de documenter au fil de l&#x27;eau, mais de traiter le passif existant : redonner à une équipe une base de connaissance exploitable, à la fois pour les humains qui rejoignent le projet et pour les agents IA qui vont désormais y travailler à leurs côtés.&lt;&#x2F;p&gt;
&lt;figure&gt;
  &lt;img src=&quot;&#x2F;assets&#x2F;img&#x2F;dette-doc-retrodoc.svg&quot; alt=&quot;Code source, historique des tickets et documents épars alimentent RetroDoc, qui reconstruit une base de connaissance exploitable à la fois par les humains et par les agents IA.&quot;&gt;
  &lt;figcaption&gt;RetroDoc reconstruit a posteriori ce qui n&#x27;a jamais été écrit.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Je partagerai l&#x27;avancement de RetroDoc dans un prochain article.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;&lt;em&gt;Sources : &lt;a href=&quot;https:&#x2F;&#x2F;stackoverflow.blog&#x2F;2022&#x2F;06&#x2F;22&#x2F;asked-and-answered-the-results-for-the-2022-developer-survey-are-here&#x2F;&quot;&gt;Stack Overflow Developer Survey 2022&lt;&#x2F;a&gt; · &lt;a href=&quot;https:&#x2F;&#x2F;www.it-cisq.org&#x2F;the-cost-of-poor-quality-software&#x2F;&quot;&gt;CISQ, Cost of Poor Software Quality in the US: A 2022 Report&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="fr">
        <title>Gloire aux Soft Skills !!!</title>
        <published>2026-09-24T00:00:00+00:00</published>
        <updated>2026-09-24T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://meta-lambda.com/gloire-aux-soft-skills/"/>
        <id>https://meta-lambda.com/gloire-aux-soft-skills/</id>
        
        <content type="html" xml:base="https://meta-lambda.com/gloire-aux-soft-skills/">&lt;p&gt;On l&#x27;entend partout en ce moment : le métier de codeur serait en train de mourir. Pour les juniors, c&#x27;est encore plus rude. À quoi bon apprendre à coder quand une IA générative le fait à leur place, souvent mieux, et surtout beaucoup plus vite ?&lt;&#x2F;p&gt;
&lt;p&gt;Résultat, les &lt;em&gt;soft skills&lt;&#x2F;em&gt; ont le vent en poupe. Communication, collaboration, empathie… On les retrouve martelées dans chaque offre d&#x27;emploi et chaque conférence. On pourrait croire qu&#x27;elles ont soudainement pris de la valeur. En réalité, c&#x27;est surtout le code qui en a perdu, et on se raccroche à ce qui reste.&lt;&#x2F;p&gt;
&lt;p&gt;Gloire aux &lt;em&gt;soft skills&lt;&#x2F;em&gt;, donc ! Ou presque.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;le-code-devient-jetable&quot;&gt;Le code devient jetable&lt;&#x2F;h2&gt;
&lt;p&gt;L&#x27;IA générative a changé la donne assez brutalement : écrire du code n&#x27;est plus un acte rare, ni particulièrement précieux.
Une IA en recrache à la chaîne, avec un niveau tout à fait honorable, sur des tâches qui exigeaient un vrai savoir-faire il y a encore deux ou trois ans.&lt;&#x2F;p&gt;
&lt;p&gt;Ajoutez à ça un marché du travail qui se tend (les développeurs le sentent bien sur le terrain), et les candidats cherchent logiquement à se différencier ailleurs. Puisque le code ne suffit plus à faire la différence, on se rabat sur ce que l&#x27;IA ne sait pas encore bien faire : les &lt;em&gt;soft skills&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Plus qu&#x27;une montée en puissance des &lt;em&gt;soft skills&lt;&#x2F;em&gt;, c&#x27;est un repli stratégique face à la dévaluation du code brut.&lt;&#x2F;p&gt;
&lt;figure&gt;
  &lt;img src=&quot;&#x2F;assets&#x2F;img&#x2F;soft-skills-devaluation.svg&quot; alt=&quot;Avant l&#x27;IA générative, le code vaut bien plus que les soft skills. Aujourd&#x27;hui, la valeur du code s&#x27;est effondrée, celle des soft skills n&#x27;a pas bougé.&quot;&gt;
  &lt;figcaption&gt;Les soft skills n&#x27;ont pas pris de valeur : c&#x27;est le code qui en a perdu.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;ce-que-ce-glissement-libere&quot;&gt;Ce que ce glissement libère&lt;&#x2F;h2&gt;
&lt;p&gt;Vu comme ça, l&#x27;histoire est un peu triste : celle d&#x27;un métier qui perd son cœur technique. Je crois au contraire que c&#x27;est une bonne nouvelle !&lt;&#x2F;p&gt;
&lt;p&gt;Réduire un développeur à la production de code a toujours été une vision étriquée du métier.
L&#x27;IA générative nous donne l&#x27;occasion de sortir de ce rôle d&#x27;exécutant pour devenir un vrai contributeur à l&#x27;avancée du produit. Un meilleur architecte, un meilleur communicant, quelqu&#x27;un qui pense système plutôt que syntaxe.&lt;&#x2F;p&gt;
&lt;p&gt;Rien de bien neuf d&#x27;ailleurs, le MIT l&#x27;enseignait déjà en 1985 :&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;We believe that the essential material to be addressed by a subject at this level is not the syntax of particular programming-language constructs, nor clever algorithms for computing particular functions efficiently, nor even the mathematical analysis of algorithms and the foundations of computing, but rather the techniques used to control the intellectual complexity of large software systems.
— H. Abelson &amp;amp; G. J. Sussman, &lt;a href=&quot;https:&#x2F;&#x2F;mitp-content-server.mit.edu&#x2F;books&#x2F;content&#x2F;sectbyfn&#x2F;books_pres_0&#x2F;6515&#x2F;sicp.zip&#x2F;full-text&#x2F;book&#x2F;book-Z-H-7.html&quot;&gt;préface de &lt;em&gt;Structure and Interpretation of Computer Programs&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Il y a même une dimension managériale qui émerge, et qui me semble sous-estimée.
Orchestrer plusieurs agents IA pour atteindre un résultat, identifier les points forts de chacun, cadrer leur travail, vérifier le rendu… Ça ressemble à s&#x27;y méprendre au quotidien d&#x27;un manager d&#x27;équipe. (La machine à café et les entretiens annuels en moins.)&lt;&#x2F;p&gt;
&lt;p&gt;On ne code plus seul dans son coin : on délègue, on arbitre, on supervise.&lt;&#x2F;p&gt;
&lt;figure&gt;
  &lt;img src=&quot;&#x2F;assets&#x2F;img&#x2F;soft-skills-orchestration.svg&quot; alt=&quot;Le développeur au centre, relié à quatre agents IA qu&#x27;il cadre, à qui il délègue, entre lesquels il arbitre et dont il vérifie le rendu.&quot;&gt;
  &lt;figcaption&gt;Orchestrer des agents, c&#x27;est déjà un peu manager une équipe.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;la-fin-d-une-aberration-du-recrutement&quot;&gt;La fin d&#x27;une aberration du recrutement&lt;&#x2F;h2&gt;
&lt;p&gt;Autre effet de bord, plus concret celui-ci : on pourrait enfin en finir avec une absurdité qui traîne dans notre métier depuis toujours. Les intitulés de poste hyper-spécifiques ! « Développeur langage X, framework Y, version Z », qui enferment les profils dans une case technique précise… et périssable.&lt;&#x2F;p&gt;
&lt;p&gt;Si la valeur d&#x27;un développeur se déplace vers l&#x27;architecture, la communication et l&#x27;orchestration, ces intitulés n&#x27;ont plus grand sens.
On devrait voir émerger des profils bien plus transverses, capables de passer d&#x27;une &lt;em&gt;stack&lt;&#x2F;em&gt; à l&#x27;autre sans en faire un drame. Parce que ce qui compte, ce n&#x27;est plus la maîtrise pointue d&#x27;une syntaxe, mais un savoir-faire de fond, transposable.&lt;&#x2F;p&gt;
&lt;figure&gt;
  &lt;img src=&quot;&#x2F;assets&#x2F;img&#x2F;soft-skills-profils.svg&quot; alt=&quot;Hier, un développeur enfermé dans des cases emboîtées : langage X, framework Y, version Z. Demain, un profil en T capable de passer d&#x27;une stack à l&#x27;autre.&quot;&gt;
  &lt;figcaption&gt;De la case technique au profil transverse.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;et-la-releve-dans-tout-ca&quot;&gt;Et la relève, dans tout ça ?&lt;&#x2F;h2&gt;
&lt;p&gt;Reste une question qui me tient particulièrement à cœur : qu&#x27;est-ce que tout ça signifie pour la formation des jeunes développeurs ?&lt;&#x2F;p&gt;
&lt;p&gt;Si le code perd sa place centrale, former quelqu&#x27;un uniquement à coder devient une impasse. Montaigne, lui, avait déjà tranché :&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Je voudrois aussi qu&#x27;on fut soigneux de luy choisir un conducteur qui eust plustost la teste bien faicte que bien pleine, et qu&#x27;on y requit tous les deux, mais plus les meurs et l&#x27;entendement que la science.
— Montaigne, &lt;a href=&quot;https:&#x2F;&#x2F;fr.wikisource.org&#x2F;wiki&#x2F;Essais&#x2F;Livre_I&#x2F;Chapitre_26&quot;&gt;&lt;em&gt;Essais&lt;&#x2F;em&gt;, I, 26&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;figure&gt;
  &lt;img src=&quot;&#x2F;assets&#x2F;img&#x2F;soft-skills-tetes.svg&quot; alt=&quot;À gauche, une tête encombrée de fragments de syntaxe en vrac. À droite, une tête qui contient un arbre de concepts reliés entre eux.&quot;&gt;
  &lt;figcaption&gt;Une tête bien pleine… ou bien faite.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Il faudra sans doute repenser en profondeur ce qu&#x27;on transmet, et dans quel ordre. Des têtes bien faites, plutôt que bien pleines de syntaxe !&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="fr">
        <title>La dette technique n&#x27;est que la partie émergée</title>
        <published>2026-09-21T00:00:00+00:00</published>
        <updated>2026-09-21T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://meta-lambda.com/dette-technique-et-le-reste/"/>
        <id>https://meta-lambda.com/dette-technique-et-le-reste/</id>
        
        <content type="html" xml:base="https://meta-lambda.com/dette-technique-et-le-reste/">&lt;p&gt;Quand on me mandate en tant que freelance, la demande initiale ressemble presque toujours à la même chose : « l&#x27;équipe n&#x27;arrive pas à produire assez vite, il faut accélérer ». C&#x27;est un cadrage confortable : il pointe vers un problème technique, donc vers une solution technique. Sauf que dans la plupart des missions, ce que je découvre en creusant n&#x27;a que partiellement à voir avec le code.&lt;&#x2F;p&gt;
&lt;p&gt;La dette technique existe, bien sûr. Mais elle n&#x27;est souvent que la partie visible d&#x27;un ensemble plus large : dette organisationnelle, dette de documentation, dette de compétences. Et tant qu&#x27;on ne traite que la première, on ne fait parfois que déplacer le problème, ou pire, l&#x27;aggraver.&lt;&#x2F;p&gt;
&lt;figure&gt;
  &lt;img src=&quot;&#x2F;assets&#x2F;img&#x2F;dette-iceberg.svg&quot; alt=&quot;La dette technique, partie émergée d&#x27;un iceberg : dessous se trouvent les dettes organisationnelle, de documentation et de compétences.&quot;&gt;
  &lt;figcaption&gt;La dette technique n&#x27;est que la partie visible du problème.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Je voudrais raconter ici une mission qui illustre bien ce mécanisme, parce qu&#x27;elle m&#x27;a appris quelque chose que je n&#x27;avais pas anticipé : améliorer le code peut, dans certaines conditions, empirer la situation globale d&#x27;une équipe.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;un-goulot-d-etranglement-humain&quot;&gt;Un goulot d&#x27;étranglement humain&lt;&#x2F;h2&gt;
&lt;p&gt;Dès mes premiers entretiens avec les développeurs, quelques heures après mon arrivée, un &lt;em&gt;pattern&lt;&#x2F;em&gt; est apparu clairement : une seule personne assurait toute la QA de l&#x27;application. Toute. Chaque fonctionnalité, chaque correctif, chaque évolution passait par elle avant de pouvoir être livré.&lt;&#x2F;p&gt;
&lt;p&gt;Ce n&#x27;était pas qu&#x27;un problème de capacité. En discutant avec elle, j&#x27;ai perçu une vraie fatigue, un débit de parole élevé, des hésitations, une personne qui se perdait dans ses explications. Je lui ai dit, assez directement, qu&#x27;elle devait se ménager, parce qu&#x27;à ce rythme elle n&#x27;allait pas tenir. C&#x27;est exactement ce qui s&#x27;est passé peu de temps après.&lt;&#x2F;p&gt;
&lt;p&gt;Au-delà de l&#x27;aspect humain, c&#x27;était aussi un risque business direct : cette personne était la seule à connaître l&#x27;application dans son ensemble. Un classique &lt;em&gt;bus factor&lt;&#x2F;em&gt; de un. Si elle s&#x27;arrêtait, ce n&#x27;était pas juste la QA qui s&#x27;arrêtait, c&#x27;était potentiellement l&#x27;activité de l&#x27;entreprise qui se retrouvait menacée.&lt;&#x2F;p&gt;
&lt;p&gt;J&#x27;ai identifié deux priorités immédiates : mettre en place des suites de tests &lt;em&gt;end-to-end&lt;&#x2F;em&gt; pour sécuriser les &lt;em&gt;workflows&lt;&#x2F;em&gt; complexes de l&#x27;application, et lui trouver un binôme pour répartir la connaissance métier. La première action a bien avancé pendant ma mission, même si le périmètre de l&#x27;application était large et que le travail se poursuit encore aujourd&#x27;hui par l&#x27;équipe. La seconde s&#x27;est heurtée à un mur : l&#x27;absence totale de documentation du comportement attendu de l&#x27;application. Sans elle, impossible de former qui que ce soit, développeur comme QA.&lt;&#x2F;p&gt;
&lt;p&gt;Et malgré mon insistance, le binômage n&#x27;a jamais eu lieu pendant ma mission. Je pense que l&#x27;impact réel de cette situation n&#x27;a pas été pleinement mesuré côté client.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;les-effets-en-cascade&quot;&gt;Les effets en cascade&lt;&#x2F;h2&gt;
&lt;p&gt;Ce goulot d&#x27;étranglement ne se contentait pas de ralentir les livraisons, il générait des effets secondaires qui, eux, ressemblaient à s&#x27;y méprendre à de la dette technique classique.&lt;&#x2F;p&gt;
&lt;p&gt;Une fonctionnalité prête attendait parfois longtemps avant d&#x27;être prise en charge par la QA. Pendant ce temps, sa branche vieillissait, et chaque développeur devait la rebaser régulièrement sur &lt;code&gt;master&lt;&#x2F;code&gt; pour ne pas perdre le fil. C&#x27;est un travail invisible, répétitif, qui ne produit aucune valeur, mais qui devient nécessaire uniquement parce que la file d&#x27;attente en aval n&#x27;avance pas. Une dette organisationnelle qui se transforme en charge technique.&lt;&#x2F;p&gt;
&lt;figure&gt;
  &lt;img src=&quot;&#x2F;assets&#x2F;img&#x2F;dette-cascade.svg&quot; alt=&quot;Enchaînement : QA saturée, branches qui vieillissent, allers-retours, deadlines qui dérapent, livraisons inabouties, moral de l&#x27;équipe.&quot;&gt;
  &lt;figcaption&gt;Un goulot d&#x27;étranglement unique, et ses effets en chaîne.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Les allers-retours entre développement et QA prenaient eux aussi énormément de temps, ce qui faisait mécaniquement déraper les &lt;em&gt;deadlines&lt;&#x2F;em&gt;. Et en bout de chaîne, la pression du calendrier poussait parfois à livrer des choses qui n&#x27;étaient pas techniquement abouties. Un comble, pour un &lt;em&gt;process&lt;&#x2F;em&gt; censé garantir la qualité.&lt;&#x2F;p&gt;
&lt;p&gt;Cette pression ne restait pas cantonnée à la QA. Elle se diffusait à toute l&#x27;équipe de développement, qui sentait bien que le rythme des livraisons ne suivait pas, sans toujours pouvoir agir dessus. Ce genre de tension continue, même quand elle reste discrète au quotidien, finit par peser sur le moral collectif : le sentiment de courir après un retard qu&#x27;on ne rattrape jamais est usant, bien au-delà de la seule personne en première ligne.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;le-paradoxe-de-l-optimisation-locale&quot;&gt;Le paradoxe de l&#x27;optimisation locale&lt;&#x2F;h2&gt;
&lt;p&gt;Comment un chantier technique qui a parfaitement fonctionné a-t-il pu, au final, aggraver la situation ? C&#x27;est le point que je trouve le plus intéressant, et le plus contre-intuitif, de cette mission.&lt;&#x2F;p&gt;
&lt;p&gt;J&#x27;ai mis en place de la CI&#x2F;CD et des suites de tests automatisés, avec un objectif précis : détecter les défauts en amont, avant qu&#x27;ils n&#x27;atteignent la QA, pour la décharger des livraisons de mauvaise qualité. Sur ce plan-là, ça a très bien fonctionné. Ce n&#x27;est pas la partie technique qui a échoué.&lt;&#x2F;p&gt;
&lt;p&gt;Le problème est venu d&#x27;ailleurs. En fluidifiant le &lt;em&gt;pipeline&lt;&#x2F;em&gt; de développement, ces améliorations ont aussi accéléré le rythme de production. Nous avons ensuite accompagné les développeurs sur l&#x27;usage de l&#x27;IA pour les aider dans leurs tâches. Mais l&#x27;effet net n&#x27;a pas été &lt;em&gt;du meilleur code&lt;&#x2F;em&gt;, il a été &lt;em&gt;plus de code&lt;&#x2F;em&gt;. Plus de fonctionnalités livrées, plus vite, avec une qualité de base qui ne s&#x27;améliorait pas vraiment.&lt;&#x2F;p&gt;
&lt;p&gt;Résultat : encore plus de volume à faire passer par le même goulot d&#x27;étranglement humain, désormais sous une pression accrue. Chaque optimisation locale que j&#x27;apportais au système ne faisait que déplacer la contrainte, et parfois l&#x27;intensifier, plutôt que de résoudre le vrai problème.&lt;&#x2F;p&gt;
&lt;figure&gt;
  &lt;img src=&quot;&#x2F;assets&#x2F;img&#x2F;dette-optimisation-locale.svg&quot; alt=&quot;Avant et après CI&#x2F;CD et IA : la production augmente, la QA garde la même capacité et la file d&#x27;attente grossit.&quot;&gt;
  &lt;figcaption&gt;Accélérer en amont du goulot ne fait qu&#x27;allonger la file d&#x27;attente.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Bref, c&#x27;est une illustration assez nette d&#x27;un principe qu&#x27;on retrouve dans la théorie des contraintes : optimiser une étape qui n&#x27;est pas le goulot d&#x27;étranglement du système n&#x27;améliore pas le débit global, ça déplace simplement la pression vers le vrai point de blocage.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ce-qu-un-freelance-peut-et-ne-peut-pas-resoudre-seul&quot;&gt;Ce qu&#x27;un freelance peut, et ne peut pas, résoudre seul&lt;&#x2F;h2&gt;
&lt;p&gt;Cette mission m&#x27;a laissé une conviction assez claire. La dette technique, un freelance peut la traiter directement : refactoring, tests, CI&#x2F;CD, ce sont des leviers qui dépendent largement de sa propre action. La dette organisationnelle, en revanche, demande une reconnaissance et une volonté qui appartiennent au client. On peut alerter, documenter le risque, proposer des solutions concrètes, mais on ne peut pas décider à la place de l&#x27;entreprise de réorganiser une équipe ou de recruter un binôme.&lt;&#x2F;p&gt;
&lt;p&gt;Ce n&#x27;est pas un constat pessimiste. C&#x27;est plutôt une invitation à élargir le diagnostic dès le départ d&#x27;une mission. Quand on me parle de vitesse de production, je sais d&#x27;expérience qu&#x27;il faut regarder au-delà du code : où est le vrai goulot d&#x27;étranglement, qui porte seul une connaissance critique, quelle documentation manque pour que cette connaissance puisse être transmise.&lt;&#x2F;p&gt;
&lt;p&gt;La dette technique se corrige avec du temps et de la rigueur. La dette organisationnelle, elle, ne se corrige qu&#x27;avec une décision. Et celle-ci n&#x27;appartient jamais au freelance.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="fr">
        <title>Ports and Adapters at compile-time for Rust</title>
        <published>2023-08-28T15:00:00+00:00</published>
        <updated>2023-08-28T15:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://meta-lambda.com/Ports-and-Adapters-at-compile-time-for-Rust/"/>
        <id>https://meta-lambda.com/Ports-and-Adapters-at-compile-time-for-Rust/</id>
        
        <content type="html" xml:base="https://meta-lambda.com/Ports-and-Adapters-at-compile-time-for-Rust/">&lt;p&gt;Dans &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Electron-libre&#x2F;Rust-compile-time-port-and-adapter&quot;&gt;ce dépôt&lt;&#x2F;a&gt;, je propose une façon simple et efficace de mettre en œuvre une inversion de dépendances avec Rust.
Plus particulièrement lorsque le besoin est de substituer les dépendances lors de la compilation et d&#x27;éviter le &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;reference&#x2F;types&#x2F;trait-object.html&quot;&gt;Dynamic Dispatch&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Très utile pour les tests unitaires et le TDD, mais aussi pour proposer des binaires avec des variantes d&#x27;implémentation.
Par exemple pour livrer un webservice dans 2 versions , une pour Postgres et l&#x27;autre pour SqlLite.&lt;&#x2F;p&gt;
&lt;p&gt;Le code fait appel à la &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;reference&#x2F;conditional-compilation.html&quot;&gt;compilation conditionnelle&lt;&#x2F;a&gt;, il est simple et lisible, mais destiné à des développeurs avancés qui ont déjà quelque notions de ces concepts.&lt;&#x2F;p&gt;
&lt;p&gt;J&#x27;insiste cependant sur le fait qu&#x27;il vaut mieux ne pas introduire d&#x27;abstraction tant qu&#x27;elle n&#x27;est pas indispensable, ce serait contre-productif.&lt;&#x2F;p&gt;
&lt;p&gt;Cadeau !!!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="fr">
        <title>Elixir amqp 1.2 et confirm handler</title>
        <published>2019-05-15T13:25:00+00:00</published>
        <updated>2019-05-15T13:25:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://meta-lambda.com/Elixir-amqp-et-confirm-handler/"/>
        <id>https://meta-lambda.com/Elixir-amqp-et-confirm-handler/</id>
        
        <content type="html" xml:base="https://meta-lambda.com/Elixir-amqp-et-confirm-handler/">&lt;p&gt;RabbitMq permet de recevoir des confirmations de ventilation des messages par l&#x27;&lt;em&gt;exchange&lt;&#x2F;em&gt;.
Le client Elixir, &lt;code&gt;amqp (&amp;gt; 1.2)&lt;&#x2F;code&gt; permet d&#x27;attendre de manière bloquante ces confirmations, mais ne propose pas d&#x27;API pour enregistrer un &lt;em&gt;handler&lt;&#x2F;em&gt;.
Ce qui m&#x27;a donné l&#x27;opportunité de faire cette contribution, qui fait maintenant partie de la version 1.2.&lt;&#x2F;p&gt;
&lt;p&gt;Cet article résume l&#x27;histoire de cette contribution à des fins didactiques.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;le-contexte&quot;&gt;Le contexte&lt;&#x2F;h2&gt;
&lt;p&gt;La fonctionnalité de &lt;a href=&quot;https:&#x2F;&#x2F;www.rabbitmq.com&#x2F;confirms.html#publisher-confirms&quot;&gt;publisher confirm&lt;&#x2F;a&gt; proposée par RabbitMq, permet en sacrifiant un peu de performances, d&#x27;obtenir une confirmation que le message publié à bien été accepté par les queues destinataires.&lt;&#x2F;p&gt;
&lt;p&gt;Imagine que tu publie un message dans le bus AMQP, ce que tu obtiens de manière synchrone lors d&#x27;un &lt;em&gt;publish&lt;&#x2F;em&gt;, c&#x27;est une confirmation que l&#x27;&lt;em&gt;exchange&lt;&#x2F;em&gt; à bien reçu le message.
Cela ne garanti pas que le message est bien ventilé vers les queues enregistrées comme destinataires.&lt;&#x2F;p&gt;
&lt;p&gt;Une option serait d&#x27;utiliser les transactions du protocol &lt;code&gt;AMQP 0-9-1&lt;&#x2F;code&gt;, mais elle ont un énorme impacte sur le débit de messages.&lt;&#x2F;p&gt;
&lt;p&gt;L&#x27;autre option est d&#x27;utiliser une extension du protocole proposée par RabbitMq: &lt;em&gt;publisher confirm&lt;&#x2F;em&gt;.
Cette extension te permet de d&#x27;activer le mode confirmation  pour un &lt;em&gt;channel&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Ce qui a pour effet de reproduire le sémantique de l&#x27;acquittement de message, au niveau de l&#x27;&lt;em&gt;exchange&lt;&#x2F;em&gt;.
Quand celui-ci parvient a router un message, il retourne un message d&#x27;acquittement.&lt;&#x2F;p&gt;
&lt;p&gt;Le paquet Hex &lt;code&gt;amqp &amp;lt; 1.2&lt;&#x2F;code&gt; expose une fonction &lt;code&gt;AMQP.Confirm.select&#x2F;1&lt;&#x2F;code&gt; qui permet de d&#x27;activer les confirmations sur le &lt;em&gt;channel&lt;&#x2F;em&gt;.
Et une suite de fonctions &lt;a href=&quot;https:&#x2F;&#x2F;hexdocs.pm&#x2F;amqp&#x2F;AMQP.Confirm.html#wait_for_confirms&#x2F;2&quot;&gt;&lt;code&gt;wait_for_confirms*(&#x2F;1|&#x2F;2)&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; qui permettent d&#x27;attendre de manière synchrone la confirmation des derniers messages envoyés.&lt;&#x2F;p&gt;
&lt;p&gt;Dans mon système je conserve un état des messages à envoyer, et des messages envoyés avec succès.
Mais je ne souhaite pas bloquer l&#x27;acteur en charge de publier les messages pour valider la confirmation de l&#x27;envoi de ceux-ci. J&#x27;aimerais plutôt exploiter au mieux l&#x27;outillage en déléguant la confirmation de l&#x27;envoi à un autre acteur.&lt;&#x2F;p&gt;
&lt;p&gt;En me documentant sur le fonctionnement du publisher confirm et des recommandations pour RabbitMq, et en lisant le code source d&#x27;autres clients, je me suis rendu compte que plusieurs clients implémentaient un fonction &lt;code&gt;register_confirm_handler&lt;&#x2F;code&gt; et &lt;code&gt;next_publish_seqno&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Ces fonctions permettent respectivement de déclarer un handler pour traiter les confirmations et d&#x27;obtenir l&#x27;identifiant du message publié pour un &lt;em&gt;channel&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;C&#x27;est justement ce dont j&#x27;avais besoin.&lt;&#x2F;p&gt;
&lt;p&gt;Le client Elixir s&#x27;appuyant sur le client Erlang, qui lui implémement ces fonctions, il n&#x27;y avait plus qu&#x27;à se pencher pour récolter, permettant de fermer quelque &lt;em&gt;issues&lt;&#x2F;em&gt; au passage.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;le-developpement&quot;&gt;Le développement&lt;&#x2F;h2&gt;
&lt;p&gt;Je vais ré-exposer les fonctions proposée par Erlang, dans la continuité de la &lt;em&gt;codebase&lt;&#x2F;em&gt; actuelle.
Habituellement je consulte donc avec la plus grande précaution les consignes de contribution.
Mais dans le cas présent elles brillaient par leur absence.&lt;&#x2F;p&gt;
&lt;p&gt;Dans ce cas, je m&#x27;imprègne des pratiques des mainteneurs précédents en explorant le code source.&lt;&#x2F;p&gt;
&lt;p&gt;Je trouve que c&#x27;est une étape indispensable, open source, ou pas. Cela contribue à rendre le code homogène et compréhensible.&lt;&#x2F;p&gt;
&lt;p&gt;Ensuite j&#x27;écris un test simple.
Je n&#x27;argumente plus sur la &lt;strong&gt;nécéssité&lt;&#x2F;strong&gt; d&#x27;écrire les tests en premier.&lt;&#x2F;p&gt;
&lt;p&gt;Dans un premier temps le setup.&lt;&#x2F;p&gt;
&lt;p&gt;Il me faut :&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;une &lt;em&gt;connection&lt;&#x2F;em&gt; pour obtenir un &lt;em&gt;channel&lt;&#x2F;em&gt;&lt;&#x2F;li&gt;
&lt;li&gt;un &lt;em&gt;channel&lt;&#x2F;em&gt; pour selectionner le mode confirmation.&lt;&#x2F;li&gt;
&lt;li&gt;un &lt;em&gt;callback&lt;&#x2F;em&gt; pour fermer la &lt;em&gt;connection&lt;&#x2F;em&gt; proprement à la fin du test&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Puisque toute les opérations se font au niveau du &lt;em&gt;channel&lt;&#x2F;em&gt;, je l&#x27;ajoute au contexte qui sera passé en argument du test.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;elixir&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-elixir &quot;&gt;&lt;code class=&quot;language-elixir&quot; data-lang=&quot;elixir&quot;&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt; test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;confirm.exs
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;defmodule &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;ConfirmTest &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;ExUnit&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Case
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;alias &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;AMQP&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Connection
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;alias &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;AMQP&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Channel
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;alias &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;AMQP&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Confirm
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  setup &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;    {&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:ok&lt;&#x2F;span&gt;&lt;span&gt;, conn} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Connection&lt;&#x2F;span&gt;&lt;span&gt;.open
&lt;&#x2F;span&gt;&lt;span&gt;    {&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:ok&lt;&#x2F;span&gt;&lt;span&gt;, chan} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Channel&lt;&#x2F;span&gt;&lt;span&gt;.open(conn)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:ok &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Confirm&lt;&#x2F;span&gt;&lt;span&gt;.select(chan)
&lt;&#x2F;span&gt;&lt;span&gt;    on_exit &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;-&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:ok &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Connection&lt;&#x2F;span&gt;&lt;span&gt;.close(conn)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;    {&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:ok&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;chan:&lt;&#x2F;span&gt;&lt;span&gt; chan}
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;  
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;&#x2F;&#x2F; ..&lt;&#x2F;span&gt;&lt;span&gt;.
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Ensuite le premier test, simple, récupérer le numéro de séquence du message.&lt;&#x2F;p&gt;
&lt;p&gt;En consultant la documentation de RabbitMq et du client Erlang, je comprend que le premier numéro de séquence d&#x27;un channel vide est 1 et que la &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rabbitmq&#x2F;rabbitmq-erlang-client&#x2F;blob&#x2F;master&#x2F;src&#x2F;amqp_channel.erl#L213&quot;&gt;fonction Erlang&lt;&#x2F;a&gt; attend l&#x27;identifiant du process &lt;em&gt;channel&lt;&#x2F;em&gt; en argument.&lt;&#x2F;p&gt;
&lt;p&gt;Ensuite je décide du nom et module de la fonction, en m&#x27;inspirant des pratiques du code source actuel, qui est de reprendre celui de la fonction Erlang.&lt;&#x2F;p&gt;
&lt;p&gt;Voci le test:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;elixir&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-elixir &quot;&gt;&lt;code class=&quot;language-elixir&quot; data-lang=&quot;elixir&quot;&gt;&lt;span&gt;  describe &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;next_publish_seqno&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;    test &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;returns 1 whe no messages where sent&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, ctx &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;      assert &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;1 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Confirm&lt;&#x2F;span&gt;&lt;span&gt;.next_publish_seqno(ctx[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:chan&lt;&#x2F;span&gt;&lt;span&gt;])
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Il ne reste plus qu&#x27;a le lancer pour le regarder échouer, puis écrire le code pour le faire passer.&lt;&#x2F;p&gt;
&lt;p&gt;Dans le module &lt;code&gt;AMQP.Confirm&lt;&#x2F;code&gt;, j&#x27;ajoute une fonction qui:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Prend une structure &lt;code&gt;Channel&lt;&#x2F;code&gt; en argument&lt;&#x2F;li&gt;
&lt;li&gt;Appelle la fonction Erlang&lt;&#x2F;li&gt;
&lt;li&gt;Retourne le résultat sous forme d&#x27;entier positif&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;elixir&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-elixir &quot;&gt;&lt;code class=&quot;language-elixir&quot; data-lang=&quot;elixir&quot;&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;# lib&#x2F;amqp&#x2F;confirm.ex
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;@doc &amp;quot;&amp;quot;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;  On channel with confirm activated, return the next message sequence number.
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;  To use in combination with `register_handler&#x2F;2`
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;  &amp;quot;&amp;quot;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;spec&lt;&#x2F;span&gt;&lt;span&gt; next_publish_seqno(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Channel&lt;&#x2F;span&gt;&lt;span&gt;.t) :: non_neg_integer
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;next_publish_seqno&lt;&#x2F;span&gt;&lt;span&gt;(%&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Channel&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;pid:&lt;&#x2F;span&gt;&lt;span&gt; pid}) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:amqp_channel&lt;&#x2F;span&gt;&lt;span&gt;.next_publish_seqno(pid&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;**
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Ensuite je regarde le test passer, &lt;em&gt;easy&lt;&#x2F;em&gt; :)
Deux points très importants ici:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;puisqu&#x27;il s&#x27;agit d&#x27;ajouter cette fonction a l&#x27;API publique de la bibliothèque, j&#x27;écris une spécification de sa signature en bonne et due forme.&lt;&#x2F;li&gt;
&lt;li&gt;Pour les mêmes raisons, je me fends d&#x27;une documentation d&#x27;utilisation.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;L&#x27;étape suivante c&#x27;est d&#x27;ajouter la fonction qui permet d&#x27;enregistrer un &lt;em&gt;handler&lt;&#x2F;em&gt;.
Tous ce que qui a été dit avant reste vrai (tests, documentation, conventions ...).&lt;&#x2F;p&gt;
&lt;p&gt;La fonction Erlang &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rabbitmq&#x2F;rabbitmq-erlang-client&#x2F;blob&#x2F;master&#x2F;src&#x2F;amqp_channel.erl#L295&quot;&gt;&lt;code&gt;register_confirm_handler&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; attend en argument les identifiants du &lt;em&gt;channel&lt;&#x2F;em&gt; et du &lt;em&gt;handler&lt;&#x2F;em&gt; et retourne un &lt;code&gt;ok&lt;&#x2F;code&gt;. La documentation précise également que le &lt;em&gt;handler&lt;&#x2F;em&gt; recevra une structure en guise de message &lt;code&gt;#basic.ack{}&lt;&#x2F;code&gt; ou &lt;code&gt;#basic.nack{}&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Ce ne sont pas des structures valides pour Elixir, ce sont des &lt;em&gt;Erlang Records&lt;&#x2F;em&gt;.
Comme la sémantique de confirmation est la même que celle de l&#x27;acquittement de messages par le consommateur, la transposition des Records Erlang vers Elixir existe déjà.&lt;&#x2F;p&gt;
&lt;p&gt;En explorant le fichier &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Electron-libre&#x2F;amqp&#x2F;blob&#x2F;confirm-handler&#x2F;lib&#x2F;amqp&#x2F;core.ex#L19&quot;&gt;&lt;code&gt;core.ex&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; de la bibliothèque j&#x27;apprends que le process recevra un message &lt;code&gt;:basic_ack&lt;&#x2F;code&gt; ou &lt;code&gt;:basic_nack&lt;&#x2F;code&gt;.
Il sera accompagné d&#x27;un ou plusieurs numéros de séquence. Si il y en a plusieurs, le troisième argument
du message vaudra &lt;code&gt;true&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Cette fois-ci le test consiste à:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;obtenir le prochain numéro de séquence&lt;&#x2F;li&gt;
&lt;li&gt;envoyer un message&lt;&#x2F;li&gt;
&lt;li&gt;déclarer le module test comme handler de confirmation&lt;&#x2F;li&gt;
&lt;li&gt;s&#x27;assurer que je reçoive bien la confirmation&lt;&#x2F;li&gt;
&lt;li&gt;s&#x27;assurer que la confirmation porte bien numéro de séquence attendu&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;elixir&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-elixir &quot;&gt;&lt;code class=&quot;language-elixir&quot; data-lang=&quot;elixir&quot;&gt;&lt;span&gt;  describe &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;register_handler&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;    test &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;handler receive confirm with message seqno&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, ctx &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;      seq_no &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Confirm&lt;&#x2F;span&gt;&lt;span&gt;.next_publish_seqno(ctx[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:chan&lt;&#x2F;span&gt;&lt;span&gt;])
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:ok &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;AMQP&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Basic&lt;&#x2F;span&gt;&lt;span&gt;.publish(ctx[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:chan&lt;&#x2F;span&gt;&lt;span&gt;], &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;foo&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:ok &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Confirm&lt;&#x2F;span&gt;&lt;span&gt;.register_handler(ctx[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:chan&lt;&#x2F;span&gt;&lt;span&gt;], self())
&lt;&#x2F;span&gt;&lt;span&gt;      assert_receive {&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:basic_ack&lt;&#x2F;span&gt;&lt;span&gt;, seq_no, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:ok &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Confirm&lt;&#x2F;span&gt;&lt;span&gt;.unregister_handler(ctx[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:chan&lt;&#x2F;span&gt;&lt;span&gt;])
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Faire échouer les tests et passer à l&#x27;implémentation.&lt;&#x2F;p&gt;
&lt;p&gt;La fonction &lt;code&gt;register_handler&lt;&#x2F;code&gt; prend en argument le &lt;em&gt;channel&lt;&#x2F;em&gt; et l&#x27;identifiant du handler.&lt;&#x2F;p&gt;
&lt;p&gt;Elle &lt;em&gt;spawn&lt;&#x2F;em&gt; un &lt;em&gt;process&lt;&#x2F;em&gt; qui va servir d&#x27;&lt;em&gt;adapter&lt;&#x2F;em&gt; entre les messages de Erlang vers Elixir.
Ce &lt;em&gt;process&lt;&#x2F;em&gt; est supervisé ainsi que celui du &lt;em&gt;handler&lt;&#x2F;em&gt; pour &lt;a href=&quot;https:&#x2F;&#x2F;hexdocs.pm&#x2F;elixir&#x2F;Process.html#monitor&#x2F;1&quot;&gt;réagir à la mort des process enfants&lt;&#x2F;a&gt; avec un message &lt;code&gt;{:DOWN}&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Puis appelle la fonction privée &lt;code&gt;handle_confirm&lt;&#x2F;code&gt; qui attend la réception d&#x27;un message Erlang et se charge de le transposer et de l&#x27;envoyer au &lt;em&gt;handler&lt;&#x2F;em&gt;.
Pour finir elle utilise la récursion pour rester à l&#x27;écoute.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;elixir&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-elixir &quot;&gt;&lt;code class=&quot;language-elixir&quot; data-lang=&quot;elixir&quot;&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;@doc &amp;quot;&amp;quot;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;  Register a handler for confirms on channel.
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;  The handler will receive either:
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;  * `{:basic_ack, seqno, multiple}`
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;  * `{:basic_nack, seqno, multiple}`
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;  The `seqno` (delivery_tag) is an integer, the sequence number of the message.
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;  `multiple` is a boolean, when `true` means multiple messages confirm, upto `seqno`.
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;  see https:&#x2F;&#x2F;www.rabbitmq.com&#x2F;confirms.html
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;  &amp;quot;&amp;quot;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;spec&lt;&#x2F;span&gt;&lt;span&gt; register_handler(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Channel&lt;&#x2F;span&gt;&lt;span&gt;.t, pid) :: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:ok
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;register_handler&lt;&#x2F;span&gt;&lt;span&gt;(%&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Channel&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;pid:&lt;&#x2F;span&gt;&lt;span&gt; pid}, handler_pid) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;    adapter_pid &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; spawn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;-&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Process&lt;&#x2F;span&gt;&lt;span&gt;.flag(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:trap_exit&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Process&lt;&#x2F;span&gt;&lt;span&gt;.monitor(handler_pid)
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Process&lt;&#x2F;span&gt;&lt;span&gt;.monitor(pid)
&lt;&#x2F;span&gt;&lt;span&gt;      handle_confirm(handler_pid)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:amqp_channel&lt;&#x2F;span&gt;&lt;span&gt;.register_confirm_handler(pid, adapter_pid)
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;  
&lt;&#x2F;span&gt;&lt;span&gt;   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;defp &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;handle_confirm&lt;&#x2F;span&gt;&lt;span&gt;(handler_pid) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;receive do
&lt;&#x2F;span&gt;&lt;span&gt;      basic_ack(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;delivery_tag:&lt;&#x2F;span&gt;&lt;span&gt; delivery_tag, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;multiple:&lt;&#x2F;span&gt;&lt;span&gt; multiple) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;-&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        send(handler_pid, {&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:basic_ack&lt;&#x2F;span&gt;&lt;span&gt;, delivery_tag, multiple})
&lt;&#x2F;span&gt;&lt;span&gt;        handle_confirm(handler_pid)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      basic_nack(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;delivery_tag:&lt;&#x2F;span&gt;&lt;span&gt; delivery_tag, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;multiple:&lt;&#x2F;span&gt;&lt;span&gt; multiple) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;-&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        send(handler_pid, {&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:basic_nack&lt;&#x2F;span&gt;&lt;span&gt;, delivery_tag, multiple})
&lt;&#x2F;span&gt;&lt;span&gt;        handle_confirm(handler_pid)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      {&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:DOWN&lt;&#x2F;span&gt;&lt;span&gt;, _ref, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;:process&lt;&#x2F;span&gt;&lt;span&gt;, _pid, reason} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;-&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;exit&lt;&#x2F;span&gt;&lt;span&gt;(reason)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Les tests passent, il ne reste qu&#x27;à ajouter la fonction &lt;code&gt;unregister_hander&#x2F;1&lt;&#x2F;code&gt;.
Je ne la commenterais pas, car tu n&#x27;y apprendra rien de plus ce qui a été dit plus haut.&lt;&#x2F;p&gt;
&lt;p&gt;Tu trouvera la commit dans son intégralité sur &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Electron-libre&#x2F;amqp&#x2F;commit&#x2F;97f7e3f98b179793bd98fb687d59b03a338c9af3&quot;&gt;github&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;pour-finir&quot;&gt;Pour finir&lt;&#x2F;h2&gt;
&lt;p&gt;La PR a été intégrée et fait partie des &lt;em&gt;features&lt;&#x2F;em&gt; de la version 1.2.&lt;&#x2F;p&gt;
&lt;p&gt;Le fait d&#x27;avoir adopté les conventions des mainteneurs du projet leur ont permis d&#x27;enchaîner un &lt;em&gt;refactoring&lt;&#x2F;em&gt; sans en souffrir.&lt;&#x2F;p&gt;
&lt;p&gt;Et finalement je peux traiter les confirmations de manière asynchrone sans avoir à utiliser l&#x27;API du client Erlang dans mon application.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="fr">
        <title>Du dosage de la rouille</title>
        <published>2019-04-17T13:25:00+00:00</published>
        <updated>2019-04-17T13:25:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://meta-lambda.com/du-dosage-de-la-rouille/"/>
        <id>https://meta-lambda.com/du-dosage-de-la-rouille/</id>
        
        <content type="html" xml:base="https://meta-lambda.com/du-dosage-de-la-rouille/">&lt;p&gt;Parlons de la rouille, cette délicieuse sauce qui accompagne nos soupes de poisson.
Le bon dosage des ingrédients ravira le palais vos convives !&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;static.openfoodfacts.org&#x2F;images&#x2F;products&#x2F;339&#x2F;461&#x2F;000&#x2F;0237&#x2F;front_fr.13.400.jpg&quot; alt=&quot;Bouillabaisse et Rouille&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Pour faire une portion de rouille les ingrédients sont :&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#282828;color:#fdf4c1aa;&quot;&gt;&lt;code&gt;&lt;span&gt;1&#x2F;2 gousse d&amp;#39;ail dégermée
&lt;&#x2F;span&gt;&lt;span&gt;1&#x2F;8 petit piment rouge
&lt;&#x2F;span&gt;&lt;span&gt;1&#x2F;8 jaune d&amp;#39;oeuf
&lt;&#x2F;span&gt;&lt;span&gt;1&#x2F;8 tranche de pain
&lt;&#x2F;span&gt;&lt;span&gt;1&#x2F;32 L d&amp;#39;huile d&amp;#39;olive
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Plus on est nombreux autour de la table et plus on rit.&lt;&#x2F;p&gt;
&lt;p&gt;Il va donc falloir multiplier la proportion des ingrédients pour atteindre un dosage équilibré.
Miam !&lt;&#x2F;p&gt;
&lt;p&gt;Je profite de cet article dédié à la recette de la rouille pour évoquer Rust au passage.&lt;&#x2F;p&gt;
&lt;p&gt;Non, pas le jeu vidéo pour survivaliste virtuel frustré (je plaisante, bisous !).
&lt;a href=&quot;https:&#x2F;&#x2F;www.rust-lang.org&#x2F;&quot;&gt;Rust(2018)&lt;&#x2F;a&gt;: Le langage de programmation qui fait peur.&lt;&#x2F;p&gt;
&lt;p&gt;{:style=&quot;float: left&quot;}
&lt;img src=&quot;https:&#x2F;&#x2F;meta-lambda.com&#x2F;du-dosage-de-la-rouille&#x2F;assets&#x2F;img&#x2F;Rust_programming_language_black_logo.svg&quot; alt=&quot;Logo Rust&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Je ne sais plus par quelle association d&#x27;idée j&#x27;en suis arrivé là.
Mais je vais vous expliquer comment je calcule le dosage des ingrédients de ma rouille avec Rust.&lt;&#x2F;p&gt;
&lt;p&gt;En espérant qu&#x27;a l&#x27;issu de cet article Rust ne vous intimidera plus et titillera votre palais.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;pourquoi-autant-d-interet-pour-la-rouille&quot;&gt;Pourquoi autant d&#x27;intérêt pour la rouille ?&lt;&#x2F;h2&gt;
&lt;p&gt;Le premier aspect séduisant de Rust est l&#x27;absence de &lt;em&gt;garbage collector&lt;&#x2F;em&gt;, évitant beaucoup de problèmes de latence.&lt;&#x2F;p&gt;
&lt;p&gt;Son système de gestion de mémoire, qui martyrise les nouveaux venus, apporte un gros avantage en termes de sécurité. (Mais ce n&#x27;est &lt;a href=&quot;https:&#x2F;&#x2F;medium.com&#x2F;@shnatsel&#x2F;how-rusts-standard-library-was-vulnerable-for-years-and-nobody-noticed-aebf0503c3d6&quot;&gt;pas le pays des Bisounours&lt;&#x2F;a&gt; non plus)&lt;&#x2F;p&gt;
&lt;p&gt;Rust est idéal pour l&#x27;embarqué, les outils système, mais pas que. WebAssembly, protocoles réseau, concurrence, portabilité ...&lt;&#x2F;p&gt;
&lt;p&gt;C&#x27;est à chacun de choisir ses arguments.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;la-recette&quot;&gt;La recette&lt;&#x2F;h2&gt;
&lt;p&gt;Pour générer un nouveau &lt;code&gt;crate&lt;&#x2F;code&gt; (&lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;book&#x2F;ch07-01-packages-and-crates-for-making-libraries-and-executables.html&quot;&gt;un package Rust&lt;&#x2F;a&gt;), il est possible d&#x27;utiliser &lt;code&gt;cargo&lt;&#x2F;code&gt; (l&#x27;outil de build).&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;cargo new doser --bin
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;L&#x27;option &lt;code&gt;--bin&lt;&#x2F;code&gt; précise qu&#x27;il s&#x27;agit d&#x27;un exécutable et non d&#x27;une bibliothèque.&lt;&#x2F;p&gt;
&lt;p&gt;Le point d&#x27;entrée d&#x27;un &lt;code&gt;crate&lt;&#x2F;code&gt;, sans surprise, est le fichier &lt;code&gt;main.rs&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Le doseur de rouille a besoin de la recette unitaire.
Profitons-en pour introduire quelques notions.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;&#x2F;&#x2F; main.rs
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;RECIPE&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;str &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;1&#x2F;2 gousse d&amp;#39;ail dégermée
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;1&#x2F;8 piment rouge
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;1&#x2F;8 jaune d&amp;#39;œuf
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;1&#x2F;8 tranche de pain
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;1&#x2F;32 L d&amp;#39;huile d&amp;#39;olive
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;C&#x27;est une constante &lt;code&gt;const&lt;&#x2F;code&gt;, nommée &lt;code&gt;RECIPE&lt;&#x2F;code&gt; dont on définit le type &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;str&#x2F;&quot;&gt;&lt;code&gt;&amp;amp;str&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;.
&lt;code&gt;&amp;amp;str&lt;&#x2F;code&gt; est une &lt;a href=&quot;https:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;24158114&#x2F;what-are-the-differences-between-rusts-string-and-str&#x2F;24159933#24159933&quot;&gt;référence vers une chaîne de caractères UTF-8 immuable&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;La ligne se terminant par un &lt;code&gt;;&lt;&#x2F;code&gt; il s&#x27;agit d&#x27;une &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;reference&#x2F;statements.html&quot;&gt;déclaration&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;les-ingredients&quot;&gt;Les Ingrédients&lt;&#x2F;h2&gt;
&lt;p&gt;Pour modéliser les ingrédients de la recette et leurs proportions utilisons  une structure dans son propre module.&lt;&#x2F;p&gt;
&lt;p&gt;Un &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;book&#x2F;ch07-02-modules-and-use-to-control-scope-and-privacy.html?highlight=modules#the-module-system-to-control-scope-and-privacy&quot;&gt;module Rust&lt;&#x2F;a&gt; peut être déclaré grâce à &lt;code&gt;mod&lt;&#x2F;code&gt; suivi d&#x27;accolades &lt;code&gt;{}&lt;&#x2F;code&gt;, le contenu des accolades sera alors la définition du module.&lt;&#x2F;p&gt;
&lt;p&gt;Le contenu du module peut aussi être déporté dans un autre fichier qui porte le nom du module en question.
Dans ce cas on omet les parenthèses.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;&#x2F;&#x2F; main.rs
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span&gt; ingredient
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;&#x2F;&#x2F; ingredient.rs
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;num_rational::Ratio;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;pub struct &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Ingredient &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;label&lt;&#x2F;span&gt;&lt;span&gt;: String,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;quantity&lt;&#x2F;span&gt;&lt;span&gt;: Ratio&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;u32&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Dans notre fichier &lt;code&gt;ingredient.rs&lt;&#x2F;code&gt; est défini une structure (&lt;code&gt;struct&lt;&#x2F;code&gt;) que le module expose publiquement.
Par défaut la visibilité est privée le mot clé &lt;code&gt;pub&lt;&#x2F;code&gt; rend publique la structure et ses champs.&lt;&#x2F;p&gt;
&lt;p&gt;Nos ingrédients sont composés:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;d&#x27;un &lt;code&gt;label&lt;&#x2F;code&gt; qui contient la désignation, qui est une donnée de type &lt;code&gt;String&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;d&#x27;une &lt;code&gt;quantity&lt;&#x2F;code&gt; qui contient une fraction &lt;code&gt;Ratio&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;code&gt;use num_rational::Ratio&lt;&#x2F;code&gt; est la syntaxe d&#x27;import de Rust. Par ce biais nous indiquons que nous utilisons la structure &lt;code&gt;Ratio&lt;&#x2F;code&gt; de la bibliothèque &lt;code&gt;num_rational&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;La philosophie de Rust est d&#x27;embarquer le strict minimum dans le noyau du langage et d&#x27;utiliser des &lt;code&gt;crates&lt;&#x2F;code&gt; pour ajouter des fonctions ou structures.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;num_rational&lt;&#x2F;code&gt; ne fait pas partie du langage, c&#x27;est une dépendance externe.&lt;&#x2F;p&gt;
&lt;p&gt;Pour gérer les dépendances il faut utiliser le fichier &lt;code&gt;Cargo.toml&lt;&#x2F;code&gt; qui est crée à la racine du projet.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;toml&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-toml &quot;&gt;&lt;code class=&quot;language-toml&quot; data-lang=&quot;toml&quot;&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;package&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;name &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;doser&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;version &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;0.1.0&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;authors &lt;&#x2F;span&gt;&lt;span&gt;= [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;Cedric &amp;lt;cedric.brancourt@gmail.com&amp;gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;edition &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;2018&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;dependencies&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span style=&quot;font-weight:bold;color:#8ec07c;&quot;&gt;num-rational &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;0.2&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;La section &lt;code&gt;dependencies&lt;&#x2F;code&gt; permet de référencer les dépendances à récupérer lors du &lt;code&gt;cargo build&lt;&#x2F;code&gt; ou &lt;code&gt;cargo run&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Le type &lt;code&gt;Ratio&lt;&#x2F;code&gt; prend un paramètre si l&#x27;on regarde la &lt;a href=&quot;https:&#x2F;&#x2F;rust-num.github.io&#x2F;num&#x2F;num_rational&#x2F;struct.Ratio.html&quot;&gt;définition de ce type&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;La définition du constructeur est &lt;code&gt;pub fn new(numer: T, denom: T) -&amp;gt; Ratio&amp;lt;T&amp;gt;&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Puisqu&#x27;il existe plusieurs types de numériques suivant leur taille, signés ou pas, la bibliothèque utilise un &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;1.30.0&#x2F;book&#x2F;second-edition&#x2F;ch10-01-syntax.html&quot;&gt;type générique&lt;&#x2F;a&gt; &lt;code&gt;T&lt;&#x2F;code&gt;. Qui donnera un &lt;code&gt;Ratio&lt;&#x2F;code&gt; dérivé de ce type.&lt;&#x2F;p&gt;
&lt;p&gt;Dans le cadre de notre recette, les quantités négatives n&#x27;étant pas possible, il ne s&#x27;agira que d&#x27;entiers non signés de 32 bits: &lt;code&gt;u32&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;À présent définissons une méthode &lt;code&gt;scale&lt;&#x2F;code&gt; qui sera en charge de retourner un nouvel ingrédient multiplié par la quantité.&lt;&#x2F;p&gt;
&lt;p&gt;Un nouvel ingrédient, car il n&#x27;est pas question ici de changer la valeur de l&#x27;ingrédient d&#x27;origine, mais d&#x27;en obtenir un nouveau à l&#x27;échelle de la recette.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;&#x2F;&#x2F; ingredient.rs
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;impl &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Ingredient &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;pub fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;scale&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;mult&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;u32&lt;&#x2F;span&gt;&lt;span&gt;) -&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;Self &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;Self &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;            label: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.label.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;clone&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;            quantity: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.quantity &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; mult,
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;impl&lt;&#x2F;code&gt; nous permet de définir des méthodes pour la structure &lt;code&gt;Ingredient&lt;&#x2F;code&gt;.
Nous définissons une méthode publique (&lt;code&gt;pub&lt;&#x2F;code&gt;) nommée &lt;code&gt;scale&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Elle prend en argument :&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;amp;self&lt;&#x2F;code&gt; qui est une &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;book&#x2F;ch04-02-references-and-borrowing.html#references-and-borrowing&quot;&gt;référence&lt;&#x2F;a&gt; à l&#x27;objet courant.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;mult: u32&lt;&#x2F;code&gt; qui est notre multiplicateur de type &lt;code&gt;u32&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Elle retourne &lt;code&gt;Self&lt;&#x2F;code&gt; qui est un alias vers le type courant, donc vers &lt;code&gt;Ingredient&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Le corps de la fonction se contente de retourner une nouvelle instance d&#x27;ingrédient dont :&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;la &lt;code&gt;quantity&lt;&#x2F;code&gt; est multipliée par &lt;code&gt;mult&lt;&#x2F;code&gt;. Ce qui retourne un nouveau &lt;code&gt;Ratio&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;le label est lui explicitement cloné.
Sinon nous aurions transféré la propriété du label au nouvel ingrédient. Ce qui produirait une erreur de compilation car leur cycle de vie est différent.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;tests-unitaires&quot;&gt;Tests unitaires&lt;&#x2F;h3&gt;
&lt;p&gt;Rust permet d&#x27;écrire des tests unitaires à proximité du code source sans les embarquer dans le build de production.&lt;&#x2F;p&gt;
&lt;p&gt;Beaucoup de tests qui auraient été écrits dans un langage interprété et non typé sont évités car le compilateur sert de garde-fou.&lt;&#x2F;p&gt;
&lt;p&gt;L&#x27;unique test à écrire dans notre cas est de vérifier que la logique implémentée est la bonne.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;&#x2F;&#x2F; ingredient.rs
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;#[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;cfg&lt;&#x2F;span&gt;&lt;span&gt;(test)]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;mod &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;tests &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;use crate&lt;&#x2F;span&gt;&lt;span&gt;::Ingredient;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;num_rational::Ratio;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    #[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;it_scale_quantity&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; quantity &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;Ratio::new(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; ingredient &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Ingredient {
&lt;&#x2F;span&gt;&lt;span&gt;            label: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;to_string&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;            quantity,
&lt;&#x2F;span&gt;&lt;span&gt;        };
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;assert_eq!&lt;&#x2F;span&gt;&lt;span&gt;(ingredient.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;scale&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;).quantity, Ratio::from_integer(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;#[cfg(test)]&lt;&#x2F;code&gt; est une annotation destinée au compilateur.
Elle indique que la section qui suit est destinée à l&#x27;environnement de test et donc ne sera pas embarquée dans le binaire de production.&lt;&#x2F;p&gt;
&lt;p&gt;Ensuite est défini un module de tests avec &lt;code&gt;mod tests {}&lt;&#x2F;code&gt;.
Ce module importe ses dépendances à &lt;code&gt;Ratio&lt;&#x2F;code&gt; et &lt;code&gt;Ingredient&lt;&#x2F;code&gt; avec &lt;code&gt;use&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Viens ensuite notre test qui n&#x27;est autre qu&#x27;une fonction précédée de l&#x27;annotation &lt;code&gt;#[test]&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Dans ce test on crée un ingrédient avec une &lt;code&gt;quantity&lt;&#x2F;code&gt; de 1&#x2F;2.&lt;&#x2F;p&gt;
&lt;p&gt;Puis on vérifie que la &lt;code&gt;quantity&lt;&#x2F;code&gt; du résultat d&#x27;une multiplication par 2 nous donne un &lt;code&gt;Ratio&lt;&#x2F;code&gt; équivalent à 1.&lt;&#x2F;p&gt;
&lt;p&gt;À ce stade vous pouvez jouer avec le fichier &lt;a href=&quot;https:&#x2F;&#x2F;play.rust-lang.org&#x2F;?version=stable&amp;amp;mode=debug&amp;amp;edition=2018&amp;amp;gist=e67b5aa7da325847e612275ff584639a&quot;&gt;ingredient.rs dans le Playground&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;le-parser&quot;&gt;Le parser&lt;&#x2F;h2&gt;
&lt;p&gt;Notre recette est une chaîne de caractères multi-lignes.
Nous allons l&#x27;analyser pour retourner une collection d&#x27;ingrédients.&lt;&#x2F;p&gt;
&lt;p&gt;Notre analyseur est lui aussi un module nommé &lt;code&gt;parser&lt;&#x2F;code&gt;.
Il expose une fonction publique:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;pub fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;parse&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;input&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;) -&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;Vec&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;Ingredient&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    input.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;lines&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;filter_map&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;parse_line).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;collect&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Cette fonction prend en paramètre un &lt;code&gt;&amp;amp;str&lt;&#x2F;code&gt; que nous avons déjà rencontré plus haut.&lt;&#x2F;p&gt;
&lt;p&gt;Elle renvoi une collection de type &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;1.30.0&#x2F;book&#x2F;second-edition&#x2F;ch08-01-vectors.html&quot;&gt;vecteur&lt;&#x2F;a&gt; &lt;code&gt;Vec&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Le type &lt;code&gt;Vec&lt;&#x2F;code&gt; attend lui aussi un paramètre, qui est le type de ses éléments (&lt;code&gt;Ingredient&lt;&#x2F;code&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;lines()&lt;&#x2F;code&gt; appelé sur &lt;code&gt;input&lt;&#x2F;code&gt; renvoi un &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;iter&#x2F;trait.Iterator.html&quot;&gt;itérateur&lt;&#x2F;a&gt; sur les lignes du &lt;code&gt;&amp;amp;str&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Ensuite est appelé &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;iter&#x2F;trait.Iterator.html#method.filter_map&quot;&gt;&lt;code&gt;filter_map&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; qui fonctionne comme un &lt;code&gt;map&lt;&#x2F;code&gt; classique à quelque détails près, nous y reviendrons plus loin.&lt;&#x2F;p&gt;
&lt;p&gt;L&#x27;argument &lt;code&gt;&amp;amp;parse_line&lt;&#x2F;code&gt; est une référence à la fonction &lt;code&gt;parse_line&lt;&#x2F;code&gt; qui sera appliquée sur chaque ligne.&lt;&#x2F;p&gt;
&lt;p&gt;Le tout renvoi un &lt;code&gt;iterator&lt;&#x2F;code&gt; de nouveau.&lt;&#x2F;p&gt;
&lt;p&gt;Pour terminer nous appelons &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;iter&#x2F;trait.Iterator.html#method.collect&quot;&gt;&lt;code&gt;collect&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; qui s&#x27;applique aux itérateurs pour renvoyer une collection (&lt;code&gt;Vec&lt;&#x2F;code&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;Revenons à notre fonction &lt;code&gt;parse_line&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;&#x2F;&#x2F; parser.rs
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;parse_line&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;line&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;) -&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;Option&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;Ingredient&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; splits: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;Vec&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; line.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;splitn&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39; &amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;collect&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;match&lt;&#x2F;span&gt;&lt;span&gt; splits[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;..&lt;&#x2F;span&gt;&lt;span&gt;] {
&lt;&#x2F;span&gt;&lt;span&gt;        [quantity, label] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;Some&lt;&#x2F;span&gt;&lt;span&gt;(Ingredient {
&lt;&#x2F;span&gt;&lt;span&gt;            quantity: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;str_to_ratio&lt;&#x2F;span&gt;&lt;span&gt;(quantity),
&lt;&#x2F;span&gt;&lt;span&gt;            label: label.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;to_string&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;        }),
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;_ =&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;None&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Cette fonction prend en argument un &lt;code&gt;&amp;amp;str&lt;&#x2F;code&gt; qui représente une ligne de texte et l&#x27;analyse pour en ressortir un &lt;code&gt;Ingredient&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Cependant il est possible que la ligne ne soit pas formatée correctement.&lt;&#x2F;p&gt;
&lt;p&gt;Dans ce cas quel est le retour de la fonction ?&lt;&#x2F;p&gt;
&lt;p&gt;Rust n&#x27;a pas de &lt;code&gt;nil&lt;&#x2F;code&gt; ou &lt;code&gt;null&lt;&#x2F;code&gt;, comme d&#x27;autres langages il dispose de valeurs mises en boites, &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;option&#x2F;index.html&quot;&gt;ici &lt;code&gt;Option&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; qui dénote l&#x27;éventualité qu&#x27;il y ait une valeur.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;Option&lt;&#x2F;code&gt; est une énumération de valeurs possibles:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Some(Ingredient)&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;None&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;code&gt;line.splitn(2, &#x27; &#x27;).collect()&lt;&#x2F;code&gt; divise la ligne en 2 parties à partir du premier espace, et retourne une collection.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;let splits: Vec&amp;lt;&amp;amp;str&amp;gt;&lt;&#x2F;code&gt; permet de préciser le type de la collection attendue qui ne peut pas être toujours déduit par le compilateur.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;splits&lt;&#x2F;code&gt; est ensuite destructuré avec &lt;code&gt;match&lt;&#x2F;code&gt; qui permet de faire des branches en fonction des motifs (&lt;em&gt;pattern matching&lt;&#x2F;em&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;Soit nous obtenons 2 éléments pour construire &lt;code&gt;Some(Ingredient)&lt;&#x2F;code&gt; soit la ligne est mal formatée et donc &lt;code&gt;None&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Lors de la construction de &lt;code&gt;Ingredient&lt;&#x2F;code&gt;, &lt;code&gt;to_string&lt;&#x2F;code&gt; est appelé sur &lt;code&gt;label&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;label&lt;&#x2F;code&gt; localement est de type &lt;code&gt;&amp;amp;str&lt;&#x2F;code&gt;(référence empruntée) alors que la structure &lt;code&gt;Ingredient&lt;&#x2F;code&gt; attend un &lt;code&gt;String&lt;&#x2F;code&gt; (valeur possédée), &lt;code&gt;to_string&lt;&#x2F;code&gt; opère la copie comme vu précédemment avec &lt;code&gt;clone&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Puisque nous obtenons un résultat de type &lt;code&gt;Option&lt;&#x2F;code&gt; , dans la fonction &lt;code&gt;parse&lt;&#x2F;code&gt; nous obtenons un &lt;code&gt;Iterator&amp;lt;Option&amp;gt;&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Pour obtenir un &lt;code&gt;Iterator&amp;lt;Ingredient&amp;gt;&lt;&#x2F;code&gt; il suffit d&#x27;utiliser &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;iter&#x2F;struct.FilterMap.html&quot;&gt;&lt;code&gt;filter_map&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; qui va ignorer les résultats de type &lt;code&gt;None&lt;&#x2F;code&gt; et déballer l&#x27;ingrédient du &lt;code&gt;Some(Ingredient)&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Pour convertir &lt;code&gt;quantity&lt;&#x2F;code&gt; qui est aussi de type &lt;code&gt;&amp;amp;str&lt;&#x2F;code&gt; en &lt;code&gt;Ratio&lt;&#x2F;code&gt;, nous utilisons la fonction &lt;code&gt;str_to_ratio&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;&#x2F;&#x2F; parser.rs
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;&#x2F;&#x2F;...
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;str_to_ratio&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;s&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;) -&amp;gt; Ratio&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;u32&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; numerics: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;Vec&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;u32&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; s
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;splitn&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;#39;&#x2F;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;::parse::&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;u32&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;filter_map&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;Result&lt;&#x2F;span&gt;&lt;span&gt;::ok)
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;collect&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;match&lt;&#x2F;span&gt;&lt;span&gt; numerics[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;..&lt;&#x2F;span&gt;&lt;span&gt;] {
&lt;&#x2F;span&gt;&lt;span&gt;        [numer] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;Ratio::from_integer(numer),
&lt;&#x2F;span&gt;&lt;span&gt;        [numer, denom] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;Ratio::new(numer, denom),
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;_ =&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;panic!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;Malformed recipe quantity: {:?}&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, numerics),
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Cette fonction sépare le &lt;code&gt;&amp;amp;str&lt;&#x2F;code&gt; en 2 parties à partir du &lt;code&gt;&#x2F;&lt;&#x2F;code&gt; puis transforme les résultats en &lt;code&gt;u32&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;filter_map&lt;&#x2F;code&gt; est de nouveau utilisé, cette fois pour n&#x27;obtenir que le contenu des boites &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;result&#x2F;index.html&quot;&gt;&lt;code&gt;Result::ok&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Ensuite le résultat est décomposé avec &lt;code&gt;match&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;une seule valeur c&#x27;est un &lt;code&gt;Ratio&lt;&#x2F;code&gt; depuis un entier.&lt;&#x2F;li&gt;
&lt;li&gt;deux valeurs c&#x27;est un &lt;code&gt;Ratio&lt;&#x2F;code&gt; à partir d&#x27;un numérateur et d&#x27;un dénominateur.&lt;&#x2F;li&gt;
&lt;li&gt;aucun des deux c&#x27;est une erreur.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;macro.panic.html&quot;&gt;&lt;code&gt;panic!&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; est une macro comme tout ce qui se termine par un &lt;code&gt;!&lt;&#x2F;code&gt;.
Elle termine le programme comme une exception non interceptée.&lt;&#x2F;p&gt;
&lt;p&gt;En lecteur attentif, vous noterez qu&#x27;il aurait été préférable de renvoyer un &lt;code&gt;Option&amp;lt;Ratio&amp;gt;&lt;&#x2F;code&gt; pour sécuriser les erreurs d&#x27;analyse de la chaîne de caractères.
Ce qui n&#x27;est pas fait par besoin de démontrer &lt;code&gt;panic!&lt;&#x2F;code&gt; dans l&#x27;article.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;la-chanson-de-la-main&quot;&gt;La chanson de la main&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=tvanSC9FlkE&quot;&gt;Elle vient de loin ...&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;De retour dans notre fichier &lt;code&gt;main.rs&lt;&#x2F;code&gt; voyons comment combiner tout ça.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;&#x2F;&#x2F; main.rs
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;mod &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;ingredient&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;mod &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;parser&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Cette forme de déclaration de module précise que les modules &lt;code&gt;ingredient&lt;&#x2F;code&gt; et &lt;code&gt;parser&lt;&#x2F;code&gt; se trouvent dans les fichiers du même nom.&lt;&#x2F;p&gt;
&lt;p&gt;La fonction &lt;code&gt;main&lt;&#x2F;code&gt; du fichier &lt;code&gt;main.rs&lt;&#x2F;code&gt; est, sans surprise, celle qui est exécutée lors du lancement du programme.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;&#x2F;&#x2F; main.rs
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;scale_ingredients&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;ins&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;Vec&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;Ingredient&amp;gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;mult&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;u32&lt;&#x2F;span&gt;&lt;span&gt;) -&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;Vec&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;Ingredient&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    ins.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;iter&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(|&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;e&lt;&#x2F;span&gt;&lt;span&gt;| e.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;scale&lt;&#x2F;span&gt;&lt;span&gt;(mult)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;collect&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; scale: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;u32 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d3869b;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; base_ingredients &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;parser::parse(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;RECIPE&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt; i &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;in&lt;&#x2F;span&gt;&lt;span&gt; base_ingredients.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;iter&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;println!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, i.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;scale&lt;&#x2F;span&gt;&lt;span&gt;(scale))
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Cette fonction déclare une variable locale &lt;code&gt;scale&lt;&#x2F;code&gt; qui est le multiplicateur de la recette.
Puis utilise la fonction &lt;code&gt;parse&lt;&#x2F;code&gt; du module &lt;code&gt;parser&lt;&#x2F;code&gt; pour obtenir les ingrédients depuis &lt;code&gt;RECIPE&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Ensuite nous itérons sur les ingrédients pour afficher l&#x27;ingrédient multiplié par l&#x27;échelle.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;primitive.slice.html#method.iter&quot;&gt;&lt;code&gt;iter()&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; Nous permet d&#x27;obtenir un &lt;code&gt;Iterator&lt;&#x2F;code&gt; depuis un &lt;code&gt;Vec&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;macro.println.html&quot;&gt;&lt;code&gt;println!&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; est la macro qui permet d&#x27;afficher sur la sortie standard.&lt;&#x2F;p&gt;
&lt;p&gt;Pour qu&#x27;une valeur puisse être formatée pour la sortie texte, elle doit implémenter le &lt;code&gt;Trait&lt;&#x2F;code&gt; &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;fmt&#x2F;trait.Display.html&quot;&gt;&lt;code&gt;Display&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Un &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;book&#x2F;ch10-02-traits.html&quot;&gt;&lt;code&gt;Trait&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; s&#x27;apparente à une interface pour un comportement implémenté sur plusieurs types.&lt;&#x2F;p&gt;
&lt;p&gt;L&#x27;implémentation de &lt;code&gt;Display&lt;&#x2F;code&gt; pour notre ingrédient se déclare ainsi:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#282828;color:#fdf4c1aa;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;font-style:italic;color:#928374;&quot;&gt;&#x2F;&#x2F; ingredient.rs
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;impl &lt;&#x2F;span&gt;&lt;span&gt;std::fmt::Display &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;Ingredient &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8ec07c;&quot;&gt;fmt&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fe8019;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fa5c4b;&quot;&gt;mut &lt;&#x2F;span&gt;&lt;span&gt;std::fmt::Formatter) -&amp;gt; std::fmt::Result {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fabd2f;&quot;&gt;write!&lt;&#x2F;span&gt;&lt;span&gt;(f, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;{} {}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b8bb26;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.quantity, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdf4c1;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.label)
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Elle prend en argument la structure courante et une &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;fmt&#x2F;struct.Formatter.html&quot;&gt;fonction de formatage&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;La macro &lt;code&gt;write!&lt;&#x2F;code&gt; pousse les données formatées dans un tampon.
Le format choisi est tout simplement d&#x27;afficher la quantité suivie du libellé.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;pour-aller-plus-loin&quot;&gt;Pour aller plus loin&lt;&#x2F;h2&gt;
&lt;p&gt;L&#x27;ensemble du programme est disponible est &lt;a href=&quot;https:&#x2F;&#x2F;play.rust-lang.org&#x2F;?version=stable&amp;amp;mode=debug&amp;amp;edition=2018&amp;amp;gist=d714874cb965fc559a8cf993283a3733&quot;&gt;disponible dans le Playground Rust&lt;&#x2F;a&gt;, qui permet de jouer avec le code sans avoir à installer Rust.&lt;&#x2F;p&gt;
&lt;p&gt;Le code est également disponible sur &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Electron-libre&#x2F;doser&quot;&gt;github&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;cargo test&lt;&#x2F;code&gt; permettra de lancer les tests.
&lt;code&gt;cargo run&lt;&#x2F;code&gt; lancera l&#x27;application (en environnement de développement).&lt;&#x2F;p&gt;
&lt;p&gt;Pour continuer d&#x27;expérimenter avec Rust je recommande de tenter ces exercices :&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;sécuriser la fonction &lt;code&gt;parser::str_to_ratio&lt;&#x2F;code&gt; pour supprimer le &lt;code&gt;panic!&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;faire en sorte de pouvoir &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;book&#x2F;ch12-01-accepting-command-line-arguments.html&quot;&gt;passer le multiplicateur&lt;&#x2F;a&gt; à l&#x27;invocation du programme&lt;&#x2F;li&gt;
&lt;li&gt;paralléliser l&#x27;exécution des &lt;code&gt;parse_line&lt;&#x2F;code&gt; avec &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;book&#x2F;ch16-00-concurrency.html&quot;&gt;l&#x27;outillage du langage&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Un dernier conseil pour la faim et le dosage de la rouille :&lt;&#x2F;p&gt;
&lt;p&gt;Rust permet beaucoup d&#x27;optimisations de gestion de la mémoire. Cependant, la maîtrise de la recette est dans le dosage !&lt;&#x2F;p&gt;
&lt;p&gt;Utiliser des références à tout bout de chandelles vous obligera à affronter le &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;1.30.0&#x2F;book&#x2F;second-edition&#x2F;ch10-03-lifetime-syntax.html?highlight=borrow,checker#the-borrow-checker&quot;&gt;bourreau shaker&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Dans un premier temps abusez de &lt;code&gt;clone&lt;&#x2F;code&gt;, puis optimisez progressivement.&lt;&#x2F;p&gt;
&lt;p&gt;Toutes bonnes choses ayant une fin, il est temps de nous quitter.
Au revoir lecteur, &lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=6287anhoGCw&quot;&gt;je pars&lt;&#x2F;a&gt;...&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="fr">
        <title>Droits et rôles dans une architecture orientée domaine</title>
        <published>2018-11-08T13:25:00+00:00</published>
        <updated>2018-11-08T13:25:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://meta-lambda.com/droits-et-roles-dans-une-architecture-orientee-domaine/"/>
        <id>https://meta-lambda.com/droits-et-roles-dans-une-architecture-orientee-domaine/</id>
        
        <content type="html" xml:base="https://meta-lambda.com/droits-et-roles-dans-une-architecture-orientee-domaine/">&lt;p&gt;Le chemin à emprunter en suivant la direction Domain Driven Design (DDD) n’est pas si complexe qu’il n’y paraît. Il requiert, entre autres, de se débarrasser de certains réflexes et idées préconçues. Surtout de la fâcheuse tendance qu’on a de se projeter dans l’implémentation et la technique en zappant l’analyse du domaine. (Si vous n’êtes pas familier avec le DDD, je vous recommande de vous familiariser avec le sujet avant la lecture de cet article.)&lt;&#x2F;p&gt;
&lt;p&gt;C’est vrai aussi pour la question des permissions dans le système. Cette question revient très régulièrement. Comme toujours il n’y a pas de réponse universelle, mais il y a des orientations qu’il est bon de connaître l’heure du choix venue.&lt;&#x2F;p&gt;
&lt;p&gt;Comment faire cohabiter la notion de permission avec la &lt;a href=&quot;https:&#x2F;&#x2F;fr.wikipedia.org&#x2F;wiki&#x2F;Mod%C3%A8le_de_domaine&quot;&gt;modélisation du domaine&lt;&#x2F;a&gt; ? C’est la question à laquelle je me propose de répondre dans cet article.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;questions-reponses&quot;&gt;Questions &#x2F; réponses&lt;&#x2F;h2&gt;
&lt;p&gt;Pour introduire la question et la réponse, je vous rapporte une conversation que j&#x27;ai eue avec un architecte logiciel externe à l&#x27;œil perçant (monsieur X).&lt;&#x2F;p&gt;
&lt;p&gt;Je venais de présenter un diagramme qui détaille le flux de contrôle d&#x27;une commande (ci-dessous).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;(Image manquante : base_service_arch.png — perdue avec la fermeture du blog Synbioz)&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Voici les questions et réponses qui suivirent cette présentation.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;x:&lt;&#x2F;p&gt;
&lt;p&gt;Dans ton modèle de flux pour l&#x27;exécution des commandes, tu places où le contrôle d&#x27;autorisation ?&lt;&#x2F;p&gt;
&lt;p&gt;cb:&lt;&#x2F;p&gt;
&lt;p&gt;Bonne question :)
Je distingue 2 choses : le rôle et la propriété.
Je suppose que tu parles du rôle.
Pour moi les rôles d&#x27;un utilisateur sont liés à son identité.&lt;&#x2F;p&gt;
&lt;p&gt;x:&lt;&#x2F;p&gt;
&lt;p&gt;Typiquement un utilisateur doit avoir le rôle X, Y ou Z pour effectuer une action donnée.&lt;&#x2F;p&gt;
&lt;p&gt;cb:&lt;&#x2F;p&gt;
&lt;p&gt;En règle générale je le mettrais dans les interactors&#x2F;handlers.&lt;&#x2F;p&gt;
&lt;p&gt;x:&lt;&#x2F;p&gt;
&lt;p&gt;Du point de vue métier, c&#x27;est généralement exprimé comme précondition à un &lt;em&gt;use case&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;cb:&lt;&#x2F;p&gt;
&lt;p&gt;Oui, en effet c&#x27;est une précondition ou un argument requis. Parfois même une règle du domaine.&lt;&#x2F;p&gt;
&lt;p&gt;x:&lt;&#x2F;p&gt;
&lt;p&gt;Il y a quelque chose qui me chiffonne à l&#x27;idée de passer un objet &quot;current user&quot; à toutes mes commandes&lt;&#x2F;p&gt;
&lt;p&gt;cb:&lt;&#x2F;p&gt;
&lt;p&gt;Oui, tu remarqueras que je ne parle pas de current user. C&#x27;est plus qualifié que ça.
Le rôle fait partie du jargon du domaine.
Dans le cadre de la revue de document par exemple :&lt;&#x2F;p&gt;
&lt;p&gt;Dans mon review handler je transforme mon user en &lt;code&gt;reviewer_uid&lt;&#x2F;code&gt; et je le passe a mon business tout simplement.
Donc si je peux pas transformer mon user en &lt;code&gt;reviewer_uid&lt;&#x2F;code&gt;, c&#x27;est qu&#x27;une condition de permission n&#x27;est pas satisfaite. La réponse sera un échec pour cause de permission.&lt;&#x2F;p&gt;
&lt;p&gt;x:&lt;&#x2F;p&gt;
&lt;p&gt;Et ton business va chercher les rôles ?&lt;&#x2F;p&gt;
&lt;p&gt;cb:&lt;&#x2F;p&gt;
&lt;p&gt;Non, si j&#x27;avais besoin de plus d&#x27;informations sur le &lt;em&gt;reviewer&lt;&#x2F;em&gt; dans mon domaine j&#x27;aurai un modèle &lt;em&gt;reviewer&lt;&#x2F;em&gt; en relation avec l&#x27;UID de l&#x27;utilisateur.&lt;&#x2F;p&gt;
&lt;p&gt;x:&lt;&#x2F;p&gt;
&lt;p&gt;Oui, d&#x27;office je ne comptais pas passer un objet avec des token oauth etc … :)&lt;&#x2F;p&gt;
&lt;p&gt;cb:&lt;&#x2F;p&gt;
&lt;p&gt;Si le rôle doit interagir avec le domaine au-delà d&#x27;une simple référence (UID), c&#x27;est le rôle que je passe (entité).
Je trouve ça élégant car ça concilie la gestion du rôle et le domain model.
La transformation du user en reviewer se fait dans la couche appli. Tandis que l&#x27;exploitation du reviewer se fait dans la couche domaine.&lt;&#x2F;p&gt;
&lt;p&gt;x:&lt;&#x2F;p&gt;
&lt;p&gt;Mais donc la responsabilité de vérifier l&#x27;autorisation revient au use case… ce qui peut élargir significativement le nombre de cas à tester :-\&lt;&#x2F;p&gt;
&lt;p&gt;cb:&lt;&#x2F;p&gt;
&lt;p&gt;Non, je dirais plutôt dans la &lt;em&gt;boundary&lt;&#x2F;em&gt;.
Son rôle est de transformer les objets du dehors vers ceux du domaine, ça te permet de &quot;fail early&quot; en validant les entrées.&lt;&#x2F;p&gt;
&lt;p&gt;x:&lt;&#x2F;p&gt;
&lt;p&gt;Oui ça fonctionne tant que la décision ne dépend pas des données qui vont être chargées dans le use case (typiquement une vérification de propriété)&lt;&#x2F;p&gt;
&lt;p&gt;cb:&lt;&#x2F;p&gt;
&lt;p&gt;Il n&#x27;y pas de &quot;silver bullet&quot; désolé :)
La propriété c&#x27;est radicalement différent. En tout cas selon moi.
Elle serait plutôt un scope d&#x27;accès aux ressources.&lt;&#x2F;p&gt;
&lt;p&gt;x:&lt;&#x2F;p&gt;
&lt;p&gt;Oui t&#x27;as raison je pense que ce problème-là doit être réglé via le scoping, ça devrait plus souvent se terminer en &quot;record not found&quot; que &quot;unauthorized&quot;
voilà :)&lt;&#x2F;p&gt;
&lt;p&gt;cb:&lt;&#x2F;p&gt;
&lt;p&gt;J&#x27;ai fait &lt;a href=&quot;&#x2F;droits-d-acces-et-securite&quot;&gt;un article&lt;&#x2F;a&gt; là-dessus y a longtemps.
Où j&#x27;oriente vers des Activity Access control&lt;&#x2F;p&gt;
&lt;p&gt;x:&lt;&#x2F;p&gt;
&lt;p&gt;Ok je regarderai ça&lt;&#x2F;p&gt;
&lt;p&gt;cb:&lt;&#x2F;p&gt;
&lt;p&gt;Si tu le permets, je vais garder notre conversation pour que je puisse éclaircir le sujet plus tard dans le document.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Ce monsieur X a le don de poser les bonnes questions.
Ce qui en fait un collaborateur à forte valeur ajoutée.&lt;&#x2F;p&gt;
&lt;p&gt;Il paraît que j&#x27;ai un don aussi : Quand quelqu&#x27;un me demande quelque chose, je lui explique comment s&#x27;en passer.
C&#x27;est moisi comme super pouvoir.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;exemple-concret&quot;&gt;Exemple concret&lt;&#x2F;h2&gt;
&lt;p&gt;Par expérience nous utilisons des abstractions pour qualifier les composants logiciels lors de nos échanges. Mais cette pratique a la contrainte de n&#x27;être accessible qu&#x27;à ceux qui ont déjà l&#x27;expérience de l&#x27;abstraction en question.&lt;&#x2F;p&gt;
&lt;p&gt;Il faut donc du concret, de l&#x27;exemple. J&#x27;aime beaucoup les exemples, mais je redoute leur principal effet pervers : ils sont souvent pris au pied de la lettre.
N&#x27;oublions pas de nous inspirer des exemples, mais gardons-nous de les reproduire.&lt;&#x2F;p&gt;
&lt;p&gt;Posons le contexte de l&#x27;exemple : Il s&#x27;agit d&#x27;un service qui permet de rédiger et publier des &lt;abbr title=&quot;General Terms and Conditions&quot;&gt;GTC&lt;&#x2F;abbr&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Plusieurs processus sont inclus : rédiger un brouillon, publier un nouveau document à partir d&#x27;un brouillon, définir la version courante…&lt;&#x2F;p&gt;
&lt;p&gt;Nous allons nous intéresser seulement au cas de l&#x27;ajout d&#x27;un nouveau brouillon.&lt;&#x2F;p&gt;
&lt;p&gt;Puisque tout doit commencer par une analyse du domaine, parlons-en brièvement pour faire apparaître la notion de rôle dans le domaine.&lt;&#x2F;p&gt;
&lt;p&gt;Notre jargon du domaine pour l&#x27;instant se résume à &quot;ajouter&quot; et &quot;brouillon&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;Mais qui ? Dans notre contexte métier le brouillon est ajouté par son auteur.&lt;&#x2F;p&gt;
&lt;p&gt;Nous avons maintenant un &lt;em&gt;use case&lt;&#x2F;em&gt; complet.
Un &lt;strong&gt;auteur&lt;&#x2F;strong&gt; ajoute un &lt;strong&gt;brouillon&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Le contexte technique dans notre exemple est déjà en place, car l&#x27;intégration se fait dans un système existant.&lt;&#x2F;p&gt;
&lt;p&gt;Bien qu&#x27;il ne soit pas essentiel au principe de base, le contexte du système l&#x27;est pour l&#x27;exemple.
Je vais donc planter le décor.&lt;&#x2F;p&gt;
&lt;p&gt;Les services sont exposés au travers d&#x27;une &lt;em&gt;API gateway&lt;&#x2F;em&gt;. Elle est en charge de vérifier l&#x27;identité de l&#x27;utilisateur et d&#x27;enrichir la requête HTTP avec les propriétés liées.&lt;&#x2F;p&gt;
&lt;p&gt;Les requêtes qui parviennent aux services contiennent déjà l&#x27;UID de l&#x27;utilisateur, ainsi que ses rôles dans l&#x27;ensemble du système d&#x27;information.
Ces derniers viennent d&#x27;une brique d&#x27;authentification historique et n&#x27;ont pas de portée dans le domaine actuel.
Par exemple, le rôle &quot;admin&quot; n&#x27;a aucun sens dans le contexte du domaine de la publication de conditions contractuelles.&lt;&#x2F;p&gt;
&lt;p&gt;Un module rack (nommé MountApi) est en charge de filtrer et valider les paramètres reçus via HTTP. Ce qui nous affranchit de beaucoup de programmation défensive.&lt;&#x2F;p&gt;
&lt;p&gt;Voilà pour le &lt;em&gt;background&lt;&#x2F;em&gt;, revenons à nos moutons.&lt;&#x2F;p&gt;
&lt;p&gt;Nous avons une source d&#x27;authentification dont le rôle &quot;admin&quot; peut être un auteur de GTC. Faisons en sorte que notre code exprime le domaine.&lt;&#x2F;p&gt;
&lt;p&gt;Notre &lt;em&gt;aggregate&lt;&#x2F;em&gt; &lt;code&gt;Draft&lt;&#x2F;code&gt; (ligne 10 ci-dessous) ne pourra pas s&#x27;initialiser sans &lt;code&gt;author_uid&lt;&#x2F;code&gt;. Ce qui évite de transiger avec les règles du domaine. Surtout dans le cadre d&#x27;un &lt;em&gt;aggregate&lt;&#x2F;em&gt;.
Il en va de même pour toutes les méthodes du genre &lt;code&gt;change_content(author_uid:, content:)&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;De ce fait notre couche domaine dispose de la notion d&#x27;auteur, ce dernier est indispensable à l&#x27;écriture d&#x27;un brouillon.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;(Image manquante : domain_draft.png — perdue avec la fermeture du blog Synbioz)&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Si nous remontons dans la couche applicative (ligne 9 ci-dessous), notre service (&lt;em&gt;command&lt;&#x2F;em&gt;, &lt;em&gt;interactor&lt;&#x2F;em&gt;, &lt;em&gt;use case&lt;&#x2F;em&gt;) aura lui aussi besoin de ce paramètre qui &lt;strong&gt;doit&lt;&#x2F;strong&gt; lui être fourni, puisqu&#x27;il est une précondition au &lt;em&gt;use case&lt;&#x2F;em&gt; :&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;(Image manquante : app_service.png — perdue avec la fermeture du blog Synbioz)&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Pour finir c&#x27;est dans notre couche infrastructure que nous transformons les données externes en données internes.&lt;&#x2F;p&gt;
&lt;p&gt;Puisque par le canal HTTP nous obtenons des rôles avec la requête, notre &lt;em&gt;handler&lt;&#x2F;em&gt; est en charge de vérifier ceux-ci.
Il agit comme une couche d&#x27;anti-corruption pour préserver les autres couches (&lt;code&gt;App&lt;&#x2F;code&gt; et &lt;code&gt;Domain&lt;&#x2F;code&gt;) et les garde centrées sur le domaine et isolées du reste du système.&lt;&#x2F;p&gt;
&lt;p&gt;C&#x27;est une fonctionnalité partagée à travers un &lt;em&gt;mixin&lt;&#x2F;em&gt; (&lt;code&gt;BaseHandler&lt;&#x2F;code&gt; ci-dessous).&lt;&#x2F;p&gt;
&lt;p&gt;La stratégie est simple :&lt;&#x2F;p&gt;
&lt;p&gt;Avec l&#x27;outil de gestion de permissions (ici &lt;a href=&quot;https:&#x2F;&#x2F;rubygems.org&#x2F;gems&#x2F;activity_permission_engine&quot;&gt;activity_permission_engine&lt;&#x2F;a&gt;), on enregistre le &lt;em&gt;handler&lt;&#x2F;em&gt; comme une activité (ligne 15).&lt;&#x2F;p&gt;
&lt;p&gt;Puis on autorise le rôle &lt;code&gt;admin&lt;&#x2F;code&gt; à effectuer cette activité (ligne 17).&lt;&#x2F;p&gt;
&lt;p&gt;Ensuite nous vérifions que l&#x27;utilisateur courant correspond bien à un des rôles pour lesquels l&#x27;activité est autorisée (lignes 11, et 22 à 27).
S&#x27;il ne l&#x27;est pas une réponse appropriée est retournée sans aller plus loin.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;(Image manquante : infra_base_handler.png — perdue avec la fermeture du blog Synbioz)&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;À présent notre &lt;em&gt;handler&lt;&#x2F;em&gt; qui utilise le &lt;em&gt;mixin&lt;&#x2F;em&gt; &lt;code&gt;BaseHandler&lt;&#x2F;code&gt; peut se contenter de passer l&#x27;UID de l&#x27;utilisateur courant comme celui de l&#x27;auteur.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;(Image manquante : handler.png — perdue avec la fermeture du blog Synbioz)&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Dans notre exemple le contrôle de droits d&#x27;accès se fait au niveau de la couche infrastructure car les rôles n&#x27;ont rien de local à l&#x27;application puisqu&#x27;un contexte borné (&lt;a href=&quot;https:&#x2F;&#x2F;martinfowler.com&#x2F;bliki&#x2F;BoundedContext.html&quot;&gt;&lt;em&gt;bounded context&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;) lui est dédié. Cependant dans le contexte borné du domaine de l&#x27;authentification ils seront partie prenante de l&#x27;application, voire du domaine.&lt;&#x2F;p&gt;
&lt;p&gt;Ces rôles externes ne franchissent jamais la frontière de la couche applicative.
Et cette dernière n&#x27;utilise que des concepts qui font partie de son domaine d&#x27;application (auteur).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;this-is-the-end-my-only-friend&quot;&gt;This is the end, my only friend…&lt;&#x2F;h2&gt;
&lt;p&gt;Au-delà de l&#x27;exemple fourni il y a beaucoup de manières d&#x27;arriver au même résultat.&lt;&#x2F;p&gt;
&lt;p&gt;L&#x27;important c&#x27;est de bien identifier les zones de responsabilité de chaque couche (infrastructure, domaine, application).
Éviter que l&#x27;implémentation des sessions (dans le giron de l&#x27;infrastructure) ne se répande partout dans les autres couches créant du couplage et brouillant la lisibilité (comme un &lt;code&gt;current_user&lt;&#x2F;code&gt; qui se balade dans les modèles).
Et rendre visible, les rôles dans la couche domaine.&lt;&#x2F;p&gt;
&lt;p&gt;Il ne faudra surtout pas hésiter à implémenter le rôle comme un modèle local du domaine si les règles du métier rendent nécessaire l&#x27;interaction avec le domaine.&lt;&#x2F;p&gt;
&lt;p&gt;Tous ces principes peuvent être transposés, quel que soit le langage ou le &lt;em&gt;framework&lt;&#x2F;em&gt;. Ils peuvent nécessiter quelques ajustements mineurs en fonction de la nature du domaine (pour la brique d&#x27;authentification par exemple).
Ils sont la pierre de Rosette de l&#x27;architecture du système.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="fr">
        <title>La métaphysique des systèmes proudhoniens</title>
        <published>2018-05-17T13:25:00+00:00</published>
        <updated>2018-05-17T13:25:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://meta-lambda.com/metaphysique-des-systemes-proudhoniens/"/>
        <id>https://meta-lambda.com/metaphysique-des-systemes-proudhoniens/</id>
        
        <content type="html" xml:base="https://meta-lambda.com/metaphysique-des-systemes-proudhoniens/">&lt;p&gt;Libertaire, Anarchiste, Utopiste, ZADiste, Étudiant (68) … C’est la saison ! Quelle étiquette coller à un système dessiné pour privilégier l’autonomie et l’indépendance de ses composants ?&lt;&#x2F;p&gt;
&lt;p&gt;C’est une promenade philosophique, sans conclusion, qui fait le parallèle entre une « Utopie » sociale et le design d’un système.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;proudhonien&quot;&gt;Proudhonien ?&lt;&#x2F;h2&gt;
&lt;p&gt;Ces deux dernières années mes recherches documentaires professionnelles ont porté principalement sur la composition de systèmes distribués, plus particulièrement sur la capacité à diviser ces systèmes en sous-systèmes découplés.&lt;&#x2F;p&gt;
&lt;p&gt;L&#x27;objectif est de devenir plus efficace sur des systèmes dont l&#x27;éventail fonctionnel est large, et donc d&#x27;éviter le couplage entre les différentes fonctionnalités offertes par le système. (voir &lt;a href=&quot;&#x2F;peregrinations-vers-une-architecture-decouplee&quot;&gt;Pérégrinations vers une architecture découplée&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
&lt;p&gt;Ces recherches ont fait écho à certaines de mes lectures sur les possibles alternatives de société et plus particulièrement la pensée de &lt;a href=&quot;https:&#x2F;&#x2F;fr.wikipedia.org&#x2F;wiki&#x2F;Pierre-Joseph_Proudhon&quot;&gt;Pierre Joseph Proudhon&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;(Image manquante : Pierre-Joseph_Proudhon_thumb_650.png — perdue avec la fermeture du blog Synbioz)&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Aujourd&#x27;hui quand je regarde ces systèmes avec une interprétation personnelle, ils présentent en effet des qualités et similitudes avec les aspirations de P.J. Proudhon.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;de-l-utopie-idealiste&quot;&gt;De l&#x27;Utopie idéaliste ?&lt;&#x2F;h2&gt;
&lt;p&gt;On peut se laisser porter par le courant, nager à contre courant, mais aussi rejoindre la berge et continuer à pied !
Cette dernière option n&#x27;est pas envisagée quand on idéalise, mais s&#x27;avère bien plus pratique.&lt;&#x2F;p&gt;
&lt;p&gt;Dans son genre, Proudhon n&#x27;était pas un idéaliste mais un pragmatique. Il donnait dans la proposition concrète. Une proposition qui tendait vers un idéal mais qui était faite aussi de compromis afin de la rendre pratique. Quitte à se contredire ! Ce qui lui a été reproché parfois.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Parmi l&#x27;énumération nombreuse des droits de l&#x27;homme que la sagesse du XIXe siècle recommence si souvent et si complaisamment, deux assez importants ont été oubliés, qui sont le droit de se contredire et le droit de s&#x27;en aller.
— Charles Baudelaire&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Dans la conception du système découplé il n&#x27;est point nécessaire de dogmatiser ni de réaliser l&#x27;idéal dans son intégralité.
C&#x27;est une direction à suivre plus qu&#x27;un objectif à atteindre. Une force qui s&#x27;opère comme un contre-pouvoir aux processus qui sont employés par réflexe pavlovien.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;l-ordre-sans-le-pouvoir&quot;&gt;L&#x27;ordre sans le pouvoir&lt;&#x2F;h2&gt;
&lt;p&gt;Le pouvoir s&#x27;exerce sur des sujets, d&#x27;autres personnes ou entités. Quand il s&#x27;exerce sur soi, j&#x27;imagine qu&#x27;il doit s&#x27;appeler l&#x27;indépendance.&lt;&#x2F;p&gt;
&lt;p&gt;Dans notre système découplé l&#x27;abolition du pouvoir autoritaire peut se traduire par un passage du mode de coopération de &lt;a href=&quot;https:&#x2F;&#x2F;specify.io&#x2F;concepts&#x2F;microservices&quot;&gt;l&#x27;orchestration à la chorégraphie&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;(Image manquante : orchestration_vs_choregraphy.png — perdue avec la fermeture du blog Synbioz — Building Microservices by Sam Newman, O&#x27;Reilly)&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Il n&#x27;y a plus de relation impérative entre les composants du système, mais une relation réactive, comme le montrent &lt;a href=&quot;https:&#x2F;&#x2F;eprints.qut.edu.au&#x2F;622&#x2F;1&#x2F;SOD_%28revised%29.pdf&quot;&gt;les études plus formelles&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;La notion de contrôle de flux assurée par une cascade d&#x27;orchestrateurs disparaît au profit de l&#x27;apparition de plusieurs acteurs qui agissent de concert quand on leur donne la réplique.&lt;&#x2F;p&gt;
&lt;p&gt;Si le résultat de loin ressemble à un brouhaha, localement il est loin de brouiller l&#x27;écoute.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;la-decentralisation&quot;&gt;La décentralisation&lt;&#x2F;h2&gt;
&lt;p&gt;En passant du mode orchestration au mode chorégraphie, en plus de nous affranchir de la notion de contrôle nous décentralisons la décision pour la rendre locale.&lt;&#x2F;p&gt;
&lt;p&gt;Chaque service est en charge d&#x27;opérer sur son strict périmètre de responsabilité, ses propres données.
Aucun autre service ne peut faire d&#x27;ingérence dans ces dernières, ni produire d&#x27;effet de bord non maîtrisé.
Il ne peut y avoir d&#x27;effets de bords sur d&#x27;autres services.&lt;&#x2F;p&gt;
&lt;p&gt;Autrement dit les décisions sont prises localement et ont un impact direct local uniquement.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;La commune est par son essence… un être souverain. En cette qualité, la commune a le droit de se gouverner elle-même, de s&#x27;administrer, de s&#x27;imposer des taxes, de disposer de ses propriétés et de ses revenus, de créer pour sa jeunesse des écoles, d&#x27;y nommer des professeurs, de faire sa police, d&#x27;avoir sa gendarmerie, et sa garde civique ; de nommer ses juges ; d&#x27;avoir ses journaux, ses réunions, ses sociétés particulières, ses entrepôts, sa mercuriale, sa banque, etc. La commune prend des arrêtés, rend des ordonnances : qui empêche qu&#x27;elle n&#x27;aille jusqu&#x27;à se donner des lois ? Elle a son Église, son culte, son clergé, librement élus ; elle discute publiquement, en conseil municipal, dans ses journaux ou ses cercles, tout ce qui touche à ses intérêts ou excite son opinion… Il n&#x27;y a point de milieu : la commune sera souveraine ou succursale, tout ou rien
— P.J. Proudhon&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;le-federalisme&quot;&gt;Le fédéralisme&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Toutes mes idées économiques… peuvent se résumer en ces trois mots : Fédération agricole industrielle ; toutes mes vues politiques se réduisent à une formule semblable : Fédération politique ou décentralisation… Toutes mes espérances actuelles et futures sont exprimées par ce troisième terme, corollaire des deux autres : Fédération progressive
— P.J. Proudhon&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Pour que les systèmes puissent coopérer, ils doivent adhérer à des principes communs.
L&#x27;adhésion d&#x27;un service à un système est une initiative qui est gérée dans son périmètre de décision.&lt;&#x2F;p&gt;
&lt;p&gt;En souscrivant à un bus d&#x27;événements commun, les services forment une fédération.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;(Image manquante : La_Bretagne_Fédérale - novembre 1931.jpg — perdue avec la fermeture du blog Synbioz)&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Le contrat de fédération, dont l&#x27;essence est de réserver toujours plus aux citoyens qu&#x27;à l&#x27;État, aux autorités municipales et provinciales plus qu&#x27;à l&#x27;autorité centrale, pouvait seul nous mettre sur le chemin de la vérité.
— P.J. Proudhon&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Les échanges au sein de cette fédération sont régulés par un contrat. C&#x27;est la clé de voûte du système et de la fédération.&lt;&#x2F;p&gt;
&lt;p&gt;La définition des APIs et des formats de messages échangés de manière formelle et concrète permet aux composants du système de s&#x27;intégrer sans heurts.
Empaqueté et distribué, notre contrat peut être utilisé par les tests d&#x27;intégration des services qui y souscrivent pour assurer la cohésion de l&#x27;ensemble.&lt;&#x2F;p&gt;
&lt;p&gt;Voila l&#x27;aspect fédéraliste du système. Son rôle est d&#x27;assurer la composition d&#x27;entités autonomes afin de pouvoir achever collectivement des projets plus grands.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;la-propriete&quot;&gt;La propriété&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;em&gt;(Image manquante : data-ownership_thumb_650.png — perdue avec la fermeture du blog Synbioz)&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Chaque service est seul propriétaire et exploitant de ses données internes.
Il peut ainsi garantir sa cohérence interne et son autonomie.&lt;&#x2F;p&gt;
&lt;p&gt;Dans un système composé de &lt;a href=&quot;http:&#x2F;&#x2F;scs-architecture.org&#x2F;&quot;&gt;Self-Contained Systems&lt;&#x2F;a&gt;, dont l&#x27;intégration repose sur un flux d&#x27;événements chaque système maintient sa propre version d&#x27;une donnée plus globale.&lt;&#x2F;p&gt;
&lt;p&gt;Le flux d&#x27;événements est partagé entre les différents services, chacun constitue son jeu de données local à partir de ce flux.&lt;&#x2F;p&gt;
&lt;p&gt;La propriété dans ce cas est non privative.&lt;&#x2F;p&gt;
&lt;p&gt;Le fruit du travail d&#x27;un service, ce sont quelques effets de bords sur la donnée locale, mais surtout un nouvel événement. Événement qui sera exploité par quantité d&#x27;autres services pour en produire de nouveaux.&lt;&#x2F;p&gt;
&lt;p&gt;Échanger le fruit du travail tout en mutualisant les ressources, ça laisserait rêveur plus d&#x27;un communard !&lt;&#x2F;p&gt;
&lt;h2 id=&quot;une-majorite-de-minorites&quot;&gt;Une majorité de minorités&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Un État de 26 millions d&#x27;âmes, comme serait l&#x27;Italie, est un État dans lequel toutes les fiertés provinciales et municipales sont confisquées au profit d&#x27;une puissance supérieure, qui les gouvernent. Là, toute localité doit se taire, l&#x27;esprit de clocher, faire silence : hors le jour des élections, dans lequel le citoyen manifeste sa souveraineté par un nom propre écrit sur un bulletin, la collectivité est absorbée dans le pouvoir central… La fusion, en un mot, c&#x27;est-à-dire l&#x27;anéantissement des nationalités particulières, où vivent et se distinguent les citoyens, en une nationalité abstraite où l&#x27;on ne respire ni ne se connaît plus : voilà l&#x27;unité… Et qui profite de ce régime d&#x27;unité ? Le peuple ? Non, les classes supérieures.
— P.J. Proudhon&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Puisque notre système est composé d&#x27;unités indépendantes dont les choix d&#x27;implémentation se font localement.
Il est possible de faire cohabiter un grand nombre de services faisant tous appel à des technologies différentes.
Permettant à chaque service d&#x27;exploiter les technologies qui répondent le mieux à son périmètre de responsabilités.
La seule contrainte étant de respecter le contrat.&lt;&#x2F;p&gt;
&lt;p&gt;Un système qui valorise la diversité plutôt que de cacher les différences derrière un ordre dicté à un niveau supérieur.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;liberte-egalite-fraternite-ou-choucroute&quot;&gt;Liberté, Égalité, Fraternité ou choucroute ?&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Au témoignage de l&#x27;histoire, la fraternité, but suprême des révolutions, ne s&#x27;impose pas : elle a pour condition la liberté d&#x27;abord, l&#x27;égalité après. Comme si la justice nous disait à tous : Hommes, soyez libres ; citoyens, devenez égaux, et puis, frères, embrassez-vous.
— P.J. Proudhon&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Ce qui est vrai pour le système est vrai pour l&#x27;équipe qui le développe.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Organizations which design systems […] are constrained to produce designs which are copies of the communication structures of these organizations.
— M. Conway&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Le système fonctionne plutôt bien dans des équipes autogérées.&lt;&#x2F;p&gt;
&lt;p&gt;Puisque chaque membre est autonome dans son domaine, Il est indispensable que chacun ait une bonne connaissance de l&#x27;objectif global et final.
Pour que chacun ait cette maitrise et cette envie positive, ils doivent être impliqués dès le début du projet.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;(Image manquante : liberte-egalite-solidarite-20693_thumb_650.png — perdue avec la fermeture du blog Synbioz)&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;L&#x27;attachement au respect du contrat (API, conventions, &lt;em&gt;guidelines&lt;&#x2F;em&gt;…), est un autre pilier qui doit faire l&#x27;objet d&#x27;une grande attention. C&#x27;est ce qui garantit que le travail fourni par chacun peut s&#x27;intégrer correctement avec celui du voisin.&lt;&#x2F;p&gt;
&lt;p&gt;Finalement le ciment du tout doit être la fraternité bienveillante.
Se serrer les coudes, assister, écouter, concéder, concilier et réconcilier.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Le système de l&#x27;humanité ne sera connu qu&#x27;à la fin de l&#x27;humanité.
— P.J. Proudhon&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
</content>
        
    </entry>
</feed>
