
Retour d’expérience : comment je détricote aujourd’hui un framework multiagent lancé trop vite dans l’euphorie du vibe coding 2025, et pourquoi je crois que la bonne question n’est plus « est-ce que ça marche ? ».
Le moment où j’ai compris que je ne tenais plus le volant
Il y a quelques mois, j’ai lancé un framework multiagent/multiclient. Front, API, core, orchestrateur, agents, registry, tout avançait à une vitesse que je n’avais jamais connue en vingt-cinq ans de métier. Les agents généraient des routes, reformulaient des contrats, proposaient des abstractions. L’intégration continue (CI) restait verte. Les démos roulaient. J’étais, comme beaucoup en 2025, dans l’euphorie du vibe coding.
Puis un jour, j’ai voulu ajouter un paramètre de plus au lancement d’un run. Une modification banale. Et j’ai hésité.
Pas parce que le code était sale. Il était propre. Pas parce que les tests cassaient. Ils passaient. J’ai hésité parce que je n’étais plus sûr où poser ce paramètre. Dans le schéma de l’agent ? Dans le contrat API ? Dans le registry ? Dans un détournement du HIL (human-in-the-loop, les moments où le système s’interrompt pour demander quelque chose à l’utilisateur) ? Chaque option se défendait. Aucune ne s’imposait. Je me suis rendu compte que je ne savais plus vraiment quelles frontières mon système était censé respecter.
Ce n’était pas un bug. C’était une dette d’une autre nature.
Comprehension debt : l’autre dette, celle qui ne signale rien
Addy Osmani a récemment mis un mot dessus dans son article Comprehension Debt - the hidden cost of AI generated code : comprehension debt. La dette de compréhension. C’est l’écart qui se creuse entre la quantité de code que contient votre système et la quantité que n’importe quel humain comprend vraiment de ce qui s’y passe.
Pourquoi elle est plus redoutable que la dette technique
Elle ne ressemble pas à sa cousine technique. La dette technique, on la sent : builds lents, dépendances emmêlées, cette peur sourde chaque fois qu’on touche ce module. On sait à peu près où elle vit, on peut planifier son remboursement.
La comprehension debt, elle, ne prévient pas. Le code reste lisible, les tests restent verts, les revues se déroulent normalement. Simplement, personne ne sait plus pourquoi telle décision a été prise, ni ce qui se passera quand on va la modifier. La théorie du système s’évapore silencieusement pendant que son tonnage augmente.
L’asymétrie de vitesse, nouveau terrain de jeu
L’asymétrie est fondamentale : l’IA produit du code plus vite que je ne peux l’auditer. Historiquement, la revue humaine était un goulot d’étranglement, mais un goulot productif. Lire une pull request (PR) forçait à comprendre. Ce goulot a sauté, et les tests ne rattrapent pas cela : ils ne valident que ce qu’on a pensé à spécifier.
On parle beaucoup de la vitesse à laquelle l’IA permet de produire du code. Pas assez de la vitesse à laquelle elle peut produire du glissement architectural.
Là où les frontières sont devenues ambiguës
En revenant sur le framework, je n’ai pas vu un système qui avait “mal tourné”. J’ai vu un système dont certaines frontières étaient devenues moins nettes.
Ce n’était pas de la négligence. C’était de l’ambiguïté de conception : plusieurs choix raisonnables localement, mais qui rendaient de moins en moins évident le rôle exact de chaque couche.
1. La frontière agent/orchestrateur était devenue moins nette
J’avais deux primitives dans la tête au départ : un agent qui sait faire une chose bien précise (un appel LLM structuré, avec ses outils et son prompt), et un orchestrateur qui décrit un workflow (séquence d’étapes, branchements, reprises sur erreur, parallélisation). Au fil des générations assistées, certains agents avaient commencé à lire directement le contexte de l’orchestrateur. D’autres prenaient des décisions de routage. L’unité d’exécution atomique et le workflow s’étaient mis à se chevaucher. Chaque chevauchement faisait gagner une interface. Chaque chevauchement rendait le système moins testable et moins composable.
2. Les paramètres de lancement n’avaient plus de point d’entrée évident
Chaque orchestrateur attendait ses inputs par convention locale, un champ ici, une string là, un objet ailleurs. Le registry ne savait plus quoi exposer. Le front saisissait des formulaires faits à la main. L’API validait mollement. Rien n’était faux. Rien n’était solide non plus.
3. Le HIL couvrait un périmètre ambigu
Le HIL, qui devait servir aux interventions humaines pendant l’exécution, commençait à être appelé aussi pour capturer les inputs de démarrage. C’est typiquement le genre de réutilisation “pratique” que l’IA propose volontiers : plutôt que d’introduire un nouveau mécanisme, on étire un existant. Sauf que le HIL a été conçu pour un cas d’usage précis, et couvrir tous les inputs diluait sa sémantique.
Chacun de ces points a été produit proprement, avec des tests, sans débat en revue. Aucun ne relevait d’une erreur identifiable. Mais mis bout à bout, ils rejoignaient exactement ce qu’Osmani décrit : un système dont la trajectoire devient moins maîtrisée pendant que la CI reste verte.
L’ADR comme outil de souveraineté technique
C’est là que j’ai commencé à prendre les ADR (Architecture Decision Records) au sérieux. Pas comme exercice documentaire. Comme outil de souveraineté technique.
Parce qu’au fond, la bonne question n’est pas : « est-ce que le code marche ? ». La bonne question devient :
« Est-ce que ce changement modifie une décision structurante ? »
Et rien que le fait de la poser est déjà précieux.
Les cinq critères qui déclenchent un ADR
Un changement mérite probablement un ADR s’il :
- touche une frontière de système,
- modifie un invariant,
- introduit une dépendance durable,
- change un contrat d’interface,
- ou déplace une logique centrale.
Ce qu’un ADR force à expliciter
Un ADR force à expliciter cinq choses qu’on se raconte rarement à soi-même en codant : le contexte dans lequel la décision se pose, la décision retenue, les alternatives rejetées, les conséquences assumées, et surtout les invariants qu’on veut protéger.
Le code dit ce qui a été fait. Les tests disent ce qu’on vérifie. L’ADR dit pourquoi on a choisi cette direction plutôt qu’une autre, et ce qu’on s’interdit désormais de casser.
Dans un monde où l’IA génère vite, beaucoup, et parfois trop facilement, cette couche-là devient essentielle. Pas parce que la documentation serait soudain plus importante qu’avant. Parce que le rythme de production a rendu impossible de tenir la trajectoire uniquement par la relecture. On ne garde pas la maîtrise d’un système en relisant chaque ligne. On la garde en maintenant une compréhension partagée des décisions qui structurent sa trajectoire.
Le test de l’hésitation
J’ai découvert quelque chose d’assez contre-intuitif en adoptant cette pratique :
Le simple fait de se demander « est-ce que ce changement mérite un ADR ? » est déjà un indicateur puissant, indépendamment de la réponse.
Cette question introduit délibérément de la friction. Elle ralentit le geste. Quand je me pose la question et que je ne suis pas immédiatement sûr, l’hésitation elle-même signale qu’il y a probablement quelque chose de structurant qui bouge.
Trois formes d’hésitation que j’ai appris à ne plus ignorer :
🚩 La minimisation. Quand je me dis « c’est juste un petit ajustement » sur un composant qui est en réalité central. L’ajustement est peut-être trivial en lignes de code ; il ne l’est pas forcément en conséquences.
🚩 Le besoin de validation sociale. Quand je sens que j’aurais envie d’en parler à mon équipe avant de merger, même vaguement. L’intuition du besoin de validation est souvent l’intuition d’un invariant qui bouge.
🚩 L’alternative innommée. Quand je ne suis pas certain de pouvoir expliquer en deux phrases pourquoi je fais ce changement plutôt qu’une alternative crédible. Si l’alternative mérite d’être nommée, le choix entre les deux mérite d’être tracé.
La friction comme ressource
Dans un environnement où l’IA rend tout trop fluide, la friction est une ressource. C’est une thèse à rebours de l’époque. Depuis dix ans, l’industrie tout entière a érigé le zéro friction en vertu cardinale : déploiement continu, onboarding d’un clic, IDE qui complète avant qu’on ait formulé l’intention. On a confondu friction inutile (formulaires à rallonge, approbations arbitraires) et friction productive (les secondes où l’on se demande si l’on est en train de prendre la bonne décision). L’IA, en supprimant les deux d’un coup, a rendu visible ce que la seconde apportait.
Si vous n’avez pas de friction, vous n’êtes pas en train de concevoir, vous êtes en train de subir la génération.
La friction n’est pas un bug du processus. C’est le signal qu’une décision est en train de se prendre. La supprimer totalement, c’est déléguer la conception sans même savoir qu’on l’a déléguée.
Se poser la question, c’est déjà reprendre la main sur le rythme imposé par l’outil. C’est rétablir le goulot d’étranglement pédagogique que l’asymétrie de vitesse avait supprimé mais à un niveau plus utile : celui de la décision, pas de la ligne.
Anatomie d’un ADR qui protège la trajectoire
Je travaille sur un format léger, inspiré de Michael Nygard. Rien de lourd. Un fichier markdown de 80 à 200 lignes. Un statut, une date, un scope, et une structure qui force à répondre aux questions qu’on aurait évitées autrement.
Le scope est souvent le champ qui m’a le plus surpris. Lister les packages et les frontières impactées force à se demander où la décision s’applique réellement. Dans un système multiagent/multiclient comme le mien, c’est parfois la moitié du travail d’analyse.
Pour rendre tout ça concret, voici un ADR que j’ai écrit il y a quelques jours. C’est volontairement le plus fondateur de mon projet, et il est rétroactif. Il documente une frontière qui aurait dû être explicitée au tout début. Si je dois ne garder qu’un exemple de ce que j’appelle “détricoter”, c’est celui-là.
ADR-000 - Agent vs Orchestrateur
- Status:
accepted - Date:
2026-02-20 - Scope:
core
Contexte
Le projet utilise deux notions importantes : agent et orchestrateur.
Dans le code, la différence entre les deux existe déjà, mais elle n’était pas clairement expliquée. Cela crée des questions récurrentes :
- où mettre une nouvelle logique ?
- est-ce qu’un agent peut piloter un workflow ?
- qu’est-ce qui doit être exposé à l’API ?
- qui décide de l’ordre des étapes ?
Cet ADR fixe donc un vocabulaire commun pour éviter les ambiguïtés.
Décision
Agent
Un agent représente une capacité précise.
Son rôle est d’exécuter une tâche ciblée, par exemple :
- construire un prompt,
- appeler un LLM,
- appeler des tools,
- parser une réponse,
- valider un résultat structuré.
Un agent ne décide pas du déroulement global du workflow. Il sait faire une chose, mais il ne décide pas quand ni pourquoi cette chose doit être faite.
On peut résumer ainsi :
Un agent sait faire X.
Orchestrateur
Un orchestrateur représente un workflow complet.
Son rôle est d’organiser les étapes d’un run :
- choisir quel agent appeler,
- décider de l’ordre des étapes,
- gérer les conditions,
- relancer en cas d’erreur,
- demander une validation humaine,
- produire une sortie finale.
C’est aussi l’orchestrateur qui est exposé vers l’extérieur : API, registry, UI.
On peut résumer ainsi :
Un orchestrateur décide quand, dans quel ordre et dans quelles conditions on fait X, Y ou Z.
Règle de séparation
La règle est simple :
L’agent porte une capacité.
L’orchestrateur porte une intention.
Autrement dit :
Ce qui va dans un agent :
- logique LLM,
- transformation ou validation locale,
- exécution d’une tâche spécialisée.
Ce qui va dans un orchestrateur :
- enchaînement des étapes,
- branchements,
- conditions,
- gestion d’erreur,
- interaction humaine,
- parallélisation.
Alternatives considérées
-
N’avoir qu’un seul concept “agent” Rejeté, car cela mélange exécution et orchestration dans un même objet.
-
Faire du workflow sans concept d’agent Rejeté, car on perd la réutilisabilité et la clarté de responsabilité.
-
Laisser les agents s’appeler entre eux Rejeté, car le workflow devient implicite, donc plus difficile à comprendre et à observer et a coder.
Conséquences
- Tout appel LLM doit vivre dans un agent.
- Toute logique de workflow doit vivre dans un orchestrateur.
- Les agents restent réutilisables et testables isolément.
- Les orchestrateurs deviennent l’unité exécutable exposée au système.
- La séparation des responsabilités devient plus lisible dans le code.
Impact code
packages/core/src/orchestrator/types.tspackages/core/src/orchestrator/orchestrator-engine.tspackages/core/src/orchestrator/orchestrator.tsapps/client-intent/src/domain/**/agents/apps/client-intent/src/domain/**/orchestrators/apps/client-nbi/src/domain/**/agents/apps/client-nbi/src/domain/**/orchestrators/
Documentation à maintenir
- Les ADR suivants s’appuient sur cette distinction.
- Ce document sert de référence quand on hésite sur l’emplacement d’une logique.
Cet ADR ne contient presque rien qu’un humain attentif n’aurait pu déduire en lisant le code. Et pourtant, sans lui, chaque décision de placement de logique redevient négociable à chaque PR. Avec lui, la question « est-ce que ce morceau va dans l’agent ou dans l’orchestrateur ? » a une réponse stable, opposable, partageable. Les glissements futurs deviennent repérables parce qu’on peut maintenant dire : ce changement viole l’ADR-000.
C’est exactement ce que j’appelle protéger la trajectoire.
Le détricotage en pratique
Aujourd’hui, je travaille en deux mouvements parallèles.
1. Les ADR rétroactifs
Pour les décisions qui avaient été prises implicitement, par accumulation de petits commits raisonnables. L’ADR-000 en fait partie. Ce travail est un peu ingrat, on documente du passé, et il faut parfois reconnaître qu’une décision qu’on aurait voulue claire s’est en réalité prise par défaut. Mais il révèle immédiatement les endroits où la décision documentée et le code effectif ne coïncident plus. Chaque écart devient une tâche de refactor ciblée, au lieu d’un malaise diffus.
2. La règle du « ADR avant, pas après »
Pour tout nouveau changement structurant. Si je sens la moindre hésitation, je rédige l’ADR avant d’ouvrir mon éditeur. Cela prend trente minutes. Ça économise des semaines de dérive silencieuse. Et accessoirement, ça me donne un prompt beaucoup plus propre pour l’IA quand je lui confie ensuite l’implémentation : un ADR est un spec compact qui a déjà épuisé la part difficile de la réflexion.
Le rythme compte plus que la volumétrie. Je ne documente pas tout. Je documente ce qui structure. Mon dossier ADR compte aujourd’hui une quinzaine de fichiers sur un projet qui en comporterait des centaines si j’avais voulu “couvrir” chaque décision. Ce n’est pas une bibliothèque. C’est une ossature.
L’IA nous pousse vers des problèmes plus lourds
Il y a une dernière chose que je n’avais pas anticipée, et qui rend tout ce travail ADR encore plus critique qu’il n’y paraît.
L’IA ne m’a pas fait gagner du temps sur ce que je faisais avant. Elle m’a poussé à m’attaquer à des problèmes que je n’aurais pas pris en charge il y a trois ans. Un framework multiagent/multiclient avec orchestration, registry, HIL, checkpoints, contrats front/API partagés, ce n’est pas un projet que j’aurais lancé seul en 2022. La complexité aurait été hors de portée pour une seule personne, ou même une petite équipe.
Ce n’est pas spécifique à moi. C’est un mouvement général. Les projets qui sortent depuis dix-huit mois sont structurellement plus ambitieux. Le ticket d’entrée technique s’est déplacé. Ce qu’on considérait comme “un gros système” est devenu le niveau de base, et on s’attaque maintenant à des architectures qui relevaient hier de la R&D d’entreprise.
Et c’est là que le piège se referme. L’IA nous donne la capacité de produire du code pour ces systèmes plus complexes. Elle ne nous donne pas automatiquement la capacité de tenir leur conception. Les problèmes sont devenus plus fondamentaux, frontières entre sous-systèmes, contrats inter-services, invariants transverses, propagation d’état. Exactement la classe de problèmes qui ne se résout pas ligne par ligne, et que les tests seuls ne peuvent pas protéger.
Plus les systèmes qu’on s’autorise à construire deviennent ambitieux, plus la comprehension debt devient coûteuse à ignorer. Ce n’est pas un hasard si le réveil est arrivé maintenant : il n’y a pas eu de dégradation soudaine de nos pratiques, il y a eu une élévation silencieuse du niveau de complexité que nous nous autorisons à affronter.
Les ADR ne documentent pas le code. Ils protègent la trajectoire.
Si je devais résumer ce que cette année m’a appris en une phrase, ce serait celle-ci :
Dans un monde où la production de code devient trop facile, la souveraineté se déplace vers la décision explicite.
C’est un déplacement, pas une perte. L’IA ne nous retire pas la maîtrise technique. Elle rend obsolètes les endroits où on pensait l’exercer.
Ce n’était pas dans la relecture, on ne peut pas relire plus vite qu’elle ne produit. Ce n’était pas dans les tests, on ne peut pas tester ce qu’on n’a pas pensé à spécifier. Ce n’était pas non plus dans les specs exhaustives, une spec assez détaillée pour décrire un programme est le programme, juste écrit dans un langage non-exécutable. Tous ces artefacts restent nécessaires. Aucun ne suffit plus à tenir la trajectoire d’un système.
Ce qui reste, ce qui devient même la ressource rare, c’est la capacité à décider explicitement ce qui structure le système, à nommer les invariants qu’on veut protéger, à tracer les alternatives rejetées. Bref : à poser des décisions qui ne soient pas absorbées par le flux de génération.
Les ADR sont l’artefact le plus simple et le plus robuste que j’ai trouvé pour matérialiser ce déplacement. Ils coûtent peu. Ils survivent aux refactors. Ils résistent au turn-over. Ils imposent une friction juste au bon endroit : là où une décision structurante est en train de se prendre sans qu’on s’en rende compte. Dans un monde où le code devient quasi-gratuit à produire, c’est la couche où la souveraineté technique se reloge.
Les ADR ne documentent pas le code. Ils protègent la trajectoire.
AiBrain