Découvrez comment l’état d’esprit d’ingénierie des langages de Robert Griesemer et les contraintes réelles ont influencé le design du compilateur Go, des builds plus rapides et la productivité des développeurs.

Vous ne pensez peut‑être pas aux compilateurs sauf quand quelque chose casse — mais les choix derrière le compilateur et l’outillage d’un langage façonnent discrètement toute votre journée de travail. Le temps d’attente pour les builds, la confiance dans les refactorings, la facilité de relecture de code et la sérénité au moment de livrer découlent tous des décisions d’ingénierie du langage.
Quand un build prend des secondes au lieu de minutes, vous exécutez plus souvent des tests. Quand les messages d’erreur sont précis et cohérents, vous corrigez plus vite. Quand les outils s’accordent sur le formatage et la structure des paquets, les équipes passent moins de temps à débattre du style et plus de temps à résoudre des problèmes produit. Ce ne sont pas des « nice-to-haves » ; cela se traduit par moins d’interruptions, des livraisons moins risquées et un chemin plus fluide de l’idée à la production.
Robert Griesemer est l’un des ingénieurs du langage derrière Go. Ici, un ingénieur du langage ne se résume pas à « la personne qui écrit les règles de syntaxe », mais à celle qui conçoit le système autour du langage : sur quoi le compilateur est optimisé, quels compromis sont acceptables, et quels choix par défaut rendent les équipes productives.
Cet article n’est pas une biographie ni un plongeon profond dans la théorie des compilateurs. Il utilise plutôt Go comme étude de cas pratique montrant comment des contraintes — comme la vitesse de build, la croissance d’une base de code et la maintenabilité — poussent un langage vers certaines décisions.
Nous examinerons les contraintes pratiques et les compromis qui ont influencé la sensation et les performances de Go, et comment ils se traduisent en résultats de productivité quotidiens. Cela inclut pourquoi la simplicité est traitée comme une stratégie d’ingénierie, comment une compilation rapide modifie les workflows, et pourquoi les conventions d’outillage comptent plus qu’on ne le croit au premier abord.
En cours de route, nous reviendrons sans cesse à une question simple : « Qu’est‑ce que ce choix de conception change pour un développeur un mardi normal ? » Cette perspective rend l’ingénierie des langages pertinente — même si vous ne touchez jamais au code du compilateur.
« Ingénierie des langages » est le travail pratique qui transforme un langage de programmation d’une idée en quelque chose que des équipes peuvent utiliser au quotidien : écrire du code, le compiler, le tester, le déboguer, le déployer et le maintenir pendant des années.
On parle facilement des langages comme d’un ensemble de fonctionnalités (« génériques », « exceptions », « pattern matching »). L’ingénierie des langages prend du recul et se demande : comment ces fonctionnalités se comportent‑elles quand des milliers de fichiers, des dizaines de développeurs et des délais serrés sont en jeu ?
Un langage a deux grandes facettes :
Deux langages peuvent sembler similaires sur le papier, mais donner des sensations complètement différentes en pratique parce que leur outillage et leur modèle de compilation entraînent des temps de build, des messages d’erreur, un support éditeur et des comportements runtime différents.
Les contraintes sont les limites du monde réel qui façonnent les décisions de conception :
Imaginez ajouter une fonctionnalité qui oblige le compilateur à faire une analyse globale lourde sur l’ensemble du code (par exemple, une inférence de types très avancée). Cela peut rendre le code plus propre — moins d’annotations, moins de types explicites — mais aussi rendre la compilation plus lente, les messages d’erreur plus difficiles à interpréter et les builds incrémentaux moins prévisibles.
L’ingénierie des langages consiste à décider si ce compromis améliore la productivité globale — pas seulement si la fonctionnalité est élégante.
Go n’a pas été conçu pour gagner tous les débats sur les langages. Il a été conçu pour privilégier quelques objectifs qui comptent lorsque le logiciel est développé par des équipes, livré souvent et maintenu sur le long terme.
Beaucoup de l’« ambiance » de Go pousse vers un code qu’un coéquipier peut comprendre du premier coup. La lisibilité n’est pas qu’esthétique : elle affecte la vitesse à laquelle quelqu’un peut relire une modification, évaluer un risque ou faire une amélioration sûre.
C’est pourquoi Go préfère souvent des constructions simples et un petit ensemble de fonctionnalités centrales. Quand le langage encourage des patterns familiers, les bases de code deviennent plus faciles à parcourir, à discuter en revue de code et moins dépendantes d’« experts locaux » connaissant des astuces.
Go est conçu pour soutenir des cycles compiler‑et‑exécuter rapides. Cela se traduit par un objectif pratique de productivité : plus vite vous pouvez tester une idée, moins vous perdez de temps à changer de contexte, hésiter ou attendre l’outillage.
Sur une équipe, les boucles de feedback courtes se cumulent. Elles aident les nouveaux à apprendre par l’expérimentation et permettent aux ingénieurs expérimentés de faire de petites améliorations fréquentes plutôt que des méga‑PR risquées.
L’approche de Go pour produire des artefacts simples à déployer correspond à la réalité des services backend à longue durée de vie : mises à jour, rollbacks et réponse aux incidents. Quand le déploiement est prévisible, l’exploitation devient moins fragile — et les équipes peuvent se concentrer sur le comportement plutôt que sur des puzzles d’emballage.
Ces objectifs influencent autant les omissions que les inclusions. Go choisit souvent de ne pas ajouter des fonctionnalités qui augmenteraient l’expressivité mais aussi la charge cognitive, compliqueraient l’outillage ou rendraient le code moins standardisable dans une organisation en croissance. Le résultat est un langage optimisé pour un débit d’équipe soutenu, pas pour une flexibilité maximale dans tous les cas extrêmes.
La simplicité dans Go n’est pas une préférence esthétique — c’est un outil de coordination. Robert Griesemer et l’équipe Go ont traité la conception du langage comme quelque chose qui serait utilisé par des milliers de développeurs, sous pression temporelle, à travers de nombreuses bases de code. Quand le langage propose moins d’options « également valables », les équipes dépensent moins d’énergie à négocier le style et plus à livrer.
La majorité des frictions de productivité dans les grands projets ne vient pas de la vitesse pure d’écriture ; elle vient des frictions entre personnes. Un langage cohérent réduit le nombre de décisions à prendre par ligne de code. Avec moins de façons d’exprimer une idée, les développeurs peuvent anticiper ce qu’ils vont lire, même dans des dépôts inconnus.
Cette prévisibilité importe au quotidien :
Un grand ensemble de fonctionnalités augmente la surface que les relecteurs doivent comprendre et appliquer. Go restreint volontairement le « comment » : il y a des idiomes, mais moins de paradigmes concurrents. Cela réduit le va‑et‑vient en revue du type « utilise cette abstraction » ou « nous préférons ce tour de méta‑programmation ».
Quand le langage limite les possibilités, les standards d’une équipe sont plus faciles à appliquer de manière cohérente — surtout entre plusieurs services et sur du code longévif.
Les contraintes peuvent sembler limitantes immédiatement, mais elles améliorent souvent les résultats à grande échelle. Si tout le monde a accès au même petit ensemble de constructions, vous obtenez du code plus uniforme, moins de dialectes locaux et moins de dépendance à « la personne qui comprend ce style ».
En Go, vous verrez souvent des patterns simples répétés :
if err != nil { return err })Comparez cela à des styles très personnalisés dans d’autres langages où une équipe abusera des macros, une autre d’une hiérarchie complexe, et une troisième d’une surcharge d’opérateurs. Chacun peut être « puissant », mais cela augmente le coût cognitif pour passer d’un projet à l’autre — et transforme la revue de code en club de débats.
La vitesse des builds n’est pas un indicateur de vanité — elle façonne directement votre façon de travailler.
Quand une modification compile en secondes, vous restez concentré sur le problème. Vous essayez une idée, vous voyez le résultat et vous ajustez. Cette boucle serrée maintient l’attention sur le code plutôt que sur le changement de contexte. Le même effet se multiplie en CI : des builds plus rapides signifient des vérifications de PR plus courtes, moins d’attente et moins de temps perdu à savoir si un changement est sûr.
Les builds rapides encouragent des commits petits et fréquents. Les petits changements sont plus faciles à relire, à tester et moins risqués à déployer. Ils rendent aussi plus probable que les équipes refactorent proactivement au lieu de repousser les améliorations « à plus tard ».
À un niveau élevé, les langages et toolchains peuvent favoriser cela en :
Rien de tout cela n’exige de connaître la théorie des compilateurs ; il s’agit de respecter le temps des développeurs.
Les builds lents forcent les équipes à regrouper les changements : moins de commits, des PR plus volumineuses et des branches qui vivent plus longtemps. Cela cause plus de conflits de merge, plus de travail « fix forward » et un apprentissage plus lent — car vous découvrez ce qui a cassé bien après avoir introduit le changement.
Mesurez‑le. Suivez le temps de build local et le temps de build CI au fil du temps, comme vous suivriez la latence d’une fonctionnalité orientée utilisateur. Mettez des chiffres sur le tableau de bord de l’équipe, fixez des budgets et enquêtez sur les régressions. Si la vitesse de build fait partie de votre définition du « done », la productivité s’améliore sans effort héroïque.
Un lien pratique : si vous construisez des outils internes ou des prototypes de service, des plateformes comme Koder.ai bénéficient du même principe — boucles de feedback courtes. En générant des frontends React, des backends Go et des services PostgreSQL via chat (avec mode planification et snapshots/rollback), elles aident à garder l’itération serrée tout en produisant du code source exportable que vous pouvez posséder et maintenir.
Un compilateur est essentiellement un traducteur : il prend le code que vous écrivez et le transforme en quelque chose que la machine peut exécuter. Cette traduction ne se fait pas en une étape : c’est une petite chaîne de traitement, et chaque étape a un coût et un bénéfice différents.
1) Parsing
D’abord, le compilateur lit votre texte et vérifie que c’est du code grammaticalement valide. Il construit une structure interne (pensez à un « plan ») pour que les étapes suivantes puissent raisonner dessus.
2) Vérification des types
Ensuite, il vérifie que les pièces s’emboîtent : que vous ne mélangez pas des valeurs incompatibles, que vous n’appelez pas une fonction avec de mauvais paramètres, ou que vous n’utilisez pas de noms inexistants. Dans les langages à typage statique, cette étape peut faire beaucoup de travail — et plus le système de types est sophistiqué, plus il y a à analyser.
3) Optimisation
Puis, le compilateur peut essayer de rendre le programme plus rapide ou plus petit. C’est là qu’il passe du temps à explorer des façons alternatives d’exécuter la même logique : réarranger des calculs, supprimer des travaux redondants ou améliorer l’utilisation mémoire.
4) Génération de code (codegen)
Enfin, il émet du code machine (ou une forme de bas niveau) que votre CPU pourra exécuter.
Pour de nombreux langages, l’optimisation et la vérification de types complexes dominent le temps de compilation parce qu’elles exigent une analyse approfondie à travers fonctions et fichiers. Le parsing est typiquement peu coûteux en comparaison. C’est pourquoi les concepteurs posent souvent la question : « Combien d’analyse vaut‑il la peine de faire avant d’exécuter le programme ? »
Certains écosystèmes acceptent des compilations plus lentes en échange d’une performance runtime maximale ou de fonctionnalités puissantes au moment de la compilation. Go, influencé par l’ingénierie pratique des langages, penche vers des builds rapides et prévisibles — même si cela implique d’être sélectif sur les analyses coûteuses au moment de la compilation.
Considérez un diagramme de pipeline simple :
Code source → Analyse → Vérification des types → Optimisation → Génération de code → Exécutable
Le typage statique paraît être une « chose du compilateur », mais vous le ressentez surtout dans l’outillage quotidien. Quand les types sont explicites et vérifiés de manière cohérente, votre éditeur peut faire plus que colorer des mots clés : il peut comprendre à quoi se réfère un nom, quels méthodes existent, et où un changement cassera.
Avec des types statiques, l’autocomplétion propose les bons champs et méthodes sans deviner. « Aller à la définition » et « trouver les références » deviennent fiables parce que les identifiants ne sont pas de simples correspondances textuelles ; ce sont des symboles que le compilateur comprend. Cette même information alimente des refactorings sûrs : renommer une méthode, déplacer un type dans un autre paquet, ou scinder un fichier ne dépend plus de recherches fragiles.
La majeure partie du temps d’équipe n’est pas dépensée à écrire du code neuf : elle sert à modifier du code existant sans le casser. Le typage statique vous aide à faire évoluer une API en confiance :
C’est là que les choix de conception de Go s’alignent sur des contraintes pratiques : il est plus facile de livrer des améliorations régulières quand vos outils peuvent répondre de manière fiable à « qu’est‑ce que cela affecte ? »
Les types peuvent sembler cérémonieux — surtout en phase de prototypage. Mais ils évitent un autre type de travail : déboguer des erreurs surprises à l’exécution, courir après des conversions implicites, ou découvrir trop tard qu’un refactor a changé silencieusement le comportement. La rigidité est pénible sur le moment, mais elle paye souvent pendant la maintenance.
Imaginez un petit système où le paquet billing appelle payments.Processor. Vous décidez que Charge(userID, amount) doit aussi accepter une currency.
Dans un contexte dynamiquement typé, vous pourriez manquer un chemin d’appel jusqu’à ce qu’il échoue en production. En Go, après avoir mis à jour l’interface et l’implémentation, le compilateur signale chaque appel obsolète dans billing, checkout et les tests. Votre éditeur peut sauter d’erreur en erreur et appliquer des corrections cohérentes. Le résultat est un refactor mécanique, relisible et beaucoup moins risqué.
L’histoire des performances de Go ne se limite pas au compilateur : elle concerne aussi la manière dont votre code est structuré. La structure des paquets et les imports influencent directement le temps de compilation et la compréhension au quotidien. Chaque import élargit ce que le compilateur doit charger, vérifier et potentiellement recompiler. Pour les humains, chaque import augmente aussi la « surface mentale » nécessaire pour comprendre de quoi dépend un paquet.
Un paquet avec un graphe d’imports large et emmêlé a tendance à compiler plus lentement et à être plus difficile à lire. Quand les dépendances sont peu profondes et intentionnelles, les builds restent rapides et il est plus facile de répondre à des questions simples comme : « D’où vient ce type ? » ou « Qu’est‑ce que je peux changer sans casser la moitié du dépôt ?»
Les bases de code Go saines grandissent généralement en ajoutant des paquets petits et cohésifs — pas en grossissant quelques paquets et en les connectant davantage. Des frontières claires réduisent les cycles (A importe B importe A), qui sont pénibles pour la compilation et la conception. Si vous remarquez des paquets qui doivent s’importer mutuellement pour « faire le travail », c’est souvent le signe que des responsabilités sont mélangées.
Un piège courant est le dépôt « utils » (ou « common ») en décharge. Il commence comme un paquet pratique, puis devient un aimant à dépendances : tout le monde l’importe, donc toute modification déclenche des rebuilds massifs et rend les refactors risqués.
L’un des gains discrets de productivité de Go n’est pas une astuce syntaxique — c’est l’habitude que le langage soit livré avec un petit ensemble d’outils standards, et que les équipes les utilisent effectivement. C’est l’ingénierie des langages appliquée au workflow : réduire l’optionnalité là où elle crée des frictions, et faire du « chemin normal » la voie rapide.
Go encourage une base cohérente via des outils traités comme partie de l’expérience et non comme un écosystème optionnel :
gofmt (et go fmt) rend le style de code largement non négociable.go test standardise la découverte et l’exécution des tests.go doc et les commentaires de doc poussent à des API découvrables.go build et go run établissent des points d’entrée prévisibles.L’idée n’est pas que ces outils soient parfaits pour chaque cas particulier, mais qu’ils minimisent le nombre de décisions qu’une équipe doit renégocier en permanence.
Quand chaque projet invente sa propre toolchain (formatter, test runner, générateur de docs, wrapper de build), les nouveaux contributeurs passent leurs premiers jours à apprendre les « règles spéciales » du projet. Les défauts de Go réduisent cette variation projet à projet. Un développeur peut passer d’un dépôt à l’autre et retrouver les mêmes commandes, conventions de fichiers et attentes.
Cela paye aussi en automatisation : la CI est plus simple à configurer et à comprendre plus tard. Si vous voulez un guide pratique, voyez /blog/go-tooling-basics et, pour les considérations sur les boucles de feedback des builds, /blog/ci-build-speed.
Une idée similaire s’applique quand vous standardisez la création d’applications dans une équipe. Par exemple, Koder.ai impose un « happy path » cohérent pour générer et faire évoluer des applications (React pour le web, Go + PostgreSQL pour le backend, Flutter pour le mobile), ce qui réduit la dérive des toolchains par équipe et accélère l’intégration et la revue de code.
Mettez‑vous d’accord sur ceci dès le départ : le formatage et le linting sont des défauts, pas un sujet de débat.
Concrètement : exécutez gofmt automatiquement (sauvegarde de l’éditeur ou hook pre‑commit) et définissez une configuration de linter unique pour toute l’équipe. Le gain n’est pas esthétique : ce sont moins de diffs bruyants, moins de commentaires stylistiques en revue et plus d’attention pour le comportement, la correction et la conception.
La conception d’un langage ne porte pas que sur une théorie élégante. Dans les organisations réelles, elle est modelée par des contraintes difficiles à négocier : dates de livraison, taille d’équipe, réalité du recrutement et l’infrastructure déjà en place.
La plupart des équipes vivent avec une combinaison de :
Le design de Go reflète un « budget de complexité » clair. Chaque fonctionnalité du langage a un coût : complexité du compilateur, temps de build plus longs, plus de façons d’écrire la même chose et plus de cas particuliers pour les outils. Si une fonctionnalité rend le langage plus difficile à apprendre ou les builds moins prévisibles, elle entre en compétition avec l’objectif d’un débit d’équipe rapide et soutenu.
Cette approche guidée par les contraintes peut être une victoire : moins d’angles « astucieux », des bases de code plus cohérentes et un outillage qui fonctionne de la même façon à travers les projets.
Les contraintes signifient aussi dire « non » plus souvent que certains développeurs n’en ont l’habitude. Certains utilisateurs ressentent une friction lorsqu’ils veulent des mécanismes d’abstraction riches, des fonctionnalités de type plus expressives ou des patterns très personnalisés. L’avantage est que le chemin commun reste clair ; l’inconvénient est que certains domaines peuvent paraître contraints ou verbeux.
Choisissez Go si votre priorité est la maintenabilité à l’échelle d’une équipe, des builds rapides, un déploiement simple et une intégration rapide.
Considérez un autre outil quand votre problème exige fortement du modélisme avancé au niveau des types, de la méta‑programmation intégrée au langage ou des domaines où des abstractions expressives offrent un levier répétable important. Les contraintes ne sont « bonnes » que si elles correspondent au travail que vous devez accomplir.
Les choix d’ingénierie de Go n’affectent pas seulement la compilation — ils influencent comment les équipes exploitent le logiciel. Quand un langage pousse les développeurs vers certains patterns (retours d’erreur explicites, flux de contrôle simples, outillage cohérent), il standardise discrètement la façon dont les incidents sont investigués et corrigés.
Les retours d’erreur explicites de Go encouragent une habitude : traiter les échecs comme faisant partie du flux normal du programme. Plutôt que de « croire que ça ne va pas échouer », le code tend à exprimer « si cette étape échoue, signalez‑le clairement et tôt ». Ce comportement mène à des pratiques concrètes de débogage :
Il s’agit moins d’une fonctionnalité unique que de prévisibilité : lorsque la plupart du code suit la même structure, votre cerveau (et votre rotation d’astreinte) cesse de payer le prix des surprises.
Lors d’un incident, la question n’est rarement « qu’est‑ce qui est cassé ? » — c’est « où cela a‑t‑il commencé et pourquoi ? ». Des patterns prévisibles raccourcissent le temps de recherche :
Conventions de logging : choisissez un petit ensemble de champs stables (service, request_id, user_id/tenant, operation, duration_ms, error). Logguez aux frontières (requête entrante, appel dépendance sortante) avec les mêmes noms de champs.
Wrappage d’erreurs : wrappez avec l’action + contexte clé, pas des descriptions vagues. Visez « ce que vous faisiez » plus des identifiants :
return fmt.Errorf("fetch invoice %s for tenant %s: %w", invoiceID, tenantID, err)
Structure des tests : tests table‑driven pour les cas limites, et un test « golden path » qui vérifie la forme des logs/erreurs (pas seulement les valeurs de retour).
/checkout.operation=charge_card avec des pics de duration_ms.charge_card: call payment_gateway: context deadline exceeded.operation et incluent la région de la gateway.Thème : quand la base de code parle un langage cohérent et prévisible, la réponse à incident devient une procédure, pas une chasse au trésor.
L’histoire de Go est utile même si vous ne taperez jamais une ligne de Go : c’est un rappel que les décisions de langage et d’outillage sont avant tout des décisions de workflow.
Les contraintes ne sont pas des « limitations » à contourner ; ce sont des entrées de conception qui maintiennent la cohérence d’un système. Go assume des contraintes favorisant la lisibilité, des builds prévisibles et un outillage direct.
Les choix du compilateur importent parce qu’ils façonnent le comportement quotidien. Si les builds sont rapides et les erreurs claires, les développeurs exécutent plus souvent les builds, refactorent plus tôt et gardent les changements petits. Si les builds sont lents ou que les graphes de dépendances sont emmêlés, les équipes commencent à regrouper les changements et à éviter les nettoyages — la productivité diminue sans que personne ne l’ait décidé explicitement.
Enfin, beaucoup de gains de productivité viennent de défauts ennuyeux : un formatteur cohérent, une commande de build standard et des règles de dépendances qui préservent la compréhensibilité de la base de code en grandissant.
Si vous voulez approfondir les douleurs les plus communes, poursuivez avec /blog/go-build-times et /blog/go-refactoring.
Si votre goulot d’étranglement est le temps entre « idée » et un service fonctionnel, demandez‑vous si votre workflow soutient l’itération rapide de bout en bout — pas seulement la compilation rapide. C’est une des raisons pour lesquelles des équipes adoptent des plateformes comme Koder.ai : vous pouvez passer d’un besoin décrit en chat à une appli en fonctionnement (déploiement/hébergement, domaines personnalisés et export du code source) puis continuer à itérer via snapshots et rollback lorsque les exigences changent.
Toute conception optimise quelque chose et paie ailleurs. Des builds plus rapides peuvent signifier moins de fonctionnalités de langage ; des règles de dépendance strictes peuvent réduire la flexibilité. L’objectif n’est pas de copier Go — c’est de choisir des contraintes et des outils qui facilitent le travail quotidien de votre équipe, puis d’accepter les coûts de façon délibérée.
L’ingénierie des langages consiste à transformer une idée de langage en un système utilisable et fiable : compilateur, runtime, bibliothèque standard et outils par défaut que vous utilisez pour construire, tester, formater, déboguer et déployer.
Dans le quotidien, cela se manifeste par la vitesse des builds, la qualité des messages d’erreur, les fonctionnalités de l’éditeur (rename/go-to-definition) et la prévisibilité des déploiements.
Même si vous ne touchez jamais au compilateur, vous subissez ses effets :
Il est cité pour illustrer comment les ingénieurs du langage priorisent des contraintes (échelle d’équipe, vitesse des builds, maintenabilité) plutôt que la maximalisation des fonctionnalités.
Il s’agit moins d’une biographie que d’un prisme pour comprendre comment la conception de Go reflète une approche d’ingénierie orientée productivité : rendre le chemin commun rapide, cohérent et débogable.
Parce que le temps de compilation modifie les comportements :
go test et rebuilds plus souvent.Les builds lents provoquent l’effet inverse : regroupement des changements, PR plus volumineuses, branches qui vivent plus longtemps et plus de conflits.
Un compilateur réalise principalement :
Le temps de compilation augmente généralement avec et . Go mise sur des builds , quitte à limiter certaines analyses coûteuses au moment de la compilation.
Dans Go, la simplicité joue le rôle d’outil de coordination :
L’objectif n’est pas le minimalisme pour lui-même, mais la réduction du coût cognitif et social qui freine les équipes à grande échelle.
Les types statiques fournissent aux outils une information sémantique fiable, ce qui permet :
Le gain pratique est des refactorings mécaniques et relisables plutôt que du search-and-replace fragile ou des surprises à l’exécution.
Les imports impactent machines et humains :
Bonnes pratiques :
Les choix d’outillage par défaut réduisent le nombre de décisions à prendre en continu :
gofmt rend le formatage non négociable.go test standardise la découverte et l’exécution des tests.go build/go run offrent des points d’entrée prévisibles.Les équipes passent moins de temps à argumenter sur la chaîne d’outils et plus de temps à relire le comportement et la correction. Pour creuser, voir /blog/go-tooling-basics et /blog/ci-build-speed.
Considérez le feedback des builds comme une métrique produit :
Ces actions réduisent les frictions et améliorent l’itération.