Le goulot d'étranglement réel avec les agents IA, ce n'est pas la qualité du code produit. C'est le travail séquentiel imposé par un répertoire unique. L'agent travaille sur la feature A, vous attendez 90 minutes, puis vous démarrez la feature B. Sur des features indépendantes — bounded contexts distincts, modules sans couplage — c'est du throughput sacrifié sans raison technique.
Git worktrees règlent ce problème proprement. Chaque agent reçoit son propre répertoire de travail isolé, sa propre branche, son propre état de compilation. Ils travaillent en parallèle sans jamais se voir. Sur ma mission actuelle (plateforme .NET 9 chez Edenred, architecture BFF + orchestrateur), cette approche m'a permis de passer de 270 minutes séquentielles à 105 minutes sur trois features indépendantes.
Le problème avec les agents séquentiels
Quand une feature est bloquée par une décision métier en attente, que fait l'agent ? Il attend. Quand le pipeline PO → Archi → Red → Green tourne sur la feature A, les features B et C dorment dans le backlog. Si B et C sont dans des bounded contexts différents — disons, le module HRIS et le module de facturation — aucune contrainte technique ne justifie cette attente.
La solution naïve est de lancer un second terminal et de laisser deux agents travailler sur le même répertoire. C'est une catastrophe annoncée : les deux agents voient les mêmes fichiers non-commités, l'un écrase le travail de l'autre, les états de compilation divergent, et vous finissez avec un working tree dans un état que ni vous ni l'agent ne savez interpréter.
La solution correcte est de donner à chaque agent un répertoire exclusif avec un état de fichiers qui lui appartient.
Git worktrees — rappel pour senior
Un worktree supplémentaire est une seconde copie du working tree rattachée au même dépôt .git. Commandes de base :
# Créer un worktree pour la feature B dans un répertoire sibling
git worktree add ../monrepo-feature-b feat/mbois/LCT-42500
# Lister les worktrees actifs
git worktree list
# Supprimer un worktree après merge
git worktree remove ../monrepo-feature-b
La commande worktree add crée un répertoire ../monrepo-feature-b, y checkout la branche feat/mbois/LCT-42500, et enregistre ce mapping dans .git/worktrees/. Le même .git sert de backend aux deux répertoires — aucune duplication du dépôt complet.
L'insight clé pour la parallélisation d'agents : chaque worktree a son propre index et son propre working tree. L'agent A peut modifier OrderAggregate.cs, lancer dotnet build, faire échouer ses tests et recommencer — l'agent B dans son worktree voit la version du dernier commit, pas le chaos de l'agent A. L'isolation est totale tant que le code n'est pas commité.
Le protocole de session à 3 agents
Avant de lancer un nouvel agent, une vérification est non-négociable :
# Étape 0 — toujours, avant chaque nouvel agent
git worktree list
La sortie ressemble à :
/c/Users/mikae/source/monrepo abc1234 [develop]
/c/Users/mikae/source/monrepo-feat-b def5678 [feat/mbois/LCT-42500]
/c/Users/mikae/source/monrepo-feat-c ghi9012 [feat/mbois/LCT-42501]
Cette liste vous dit quelles branches sont occupées. Git refusera de checkout une branche déjà présente dans un autre worktree — mais vérifier avant évite l'erreur et force une décision consciente sur le nommage.
Voici le script de setup complet pour une session à 3 agents :
#!/usr/bin/env bash
# setup-parallel-session.sh — 3 features indépendantes en parallèle
BASE=$(git rev-parse --show-toplevel)
PARENT=$(dirname "$BASE")
REPO=$(basename "$BASE")
git fetch origin
# Feature B
git worktree add "$PARENT/$REPO-feat-b" -b feat/mbois/LCT-42500 origin/develop
cd "$PARENT/$REPO-feat-b" && git submodule update --init && cd "$BASE"
# Feature C
git worktree add "$PARENT/$REPO-feat-c" -b feat/mbois/LCT-42501 origin/develop
cd "$PARENT/$REPO-feat-c" && git submodule update --init && cd "$BASE"
echo "Worktrees prêts :"
git worktree list
Trois points dans ce script qui ne sont pas optionnels :
git fetch origind'abord — chaque branche part deorigin/developà jour, jamais d'undeveloplocal potentiellement en retard.-b feat/mbois/LCT-XXXXX— on crée une nouvelle branche, on ne checkout jamais une branche existante dans un second worktree.git submodule update --initimmédiatement — sans ça, les répertoires de submodules sont vides et les tests d'intégration explosent avec des messages d'erreur qui ne mentionnent pas les submodules.
L'isolation en pratique
Voici ce qui se passe concrètement. L'agent A dans monrepo-feat-b modifie src/Application/Orders/OrderCommandHandler.cs, ajoute deux nouvelles classes, lance dotnet build qui échoue sur une interface manquante, corrige, relance — tout ça en travail non-commité. L'agent B dans monrepo-feat-c voit ce fichier dans l'état du dernier commit sur origin/develop. Zéro interférence.
Chaque agent compile indépendamment, exécute ses tests indépendamment, et commite sur sa propre branche. Les artefacts de build (bin/, obj/) sont dans leurs répertoires respectifs. Même un dotnet clean dans un worktree n'affecte pas l'autre.
Si vous utilisez Claude Code avec un fichier .claude/settings.json, chaque worktree peut avoir son propre fichier de settings — utile pour restreindre les permissions d'un agent au périmètre de sa feature :
{
"permissions": {
"allow": [
"Bash(dotnet build*)",
"Bash(dotnet test*)",
"Bash(git add src/Application/Hris/*)",
"Bash(git commit*)"
],
"deny": [
"Bash(git push*)"
]
}
}
Avec ce settings.json dans monrepo-feat-b/.claude/, l'agent ne peut pas pousser sans votre validation et ne peut stager que les fichiers du périmètre HRIS. C'est une contrainte de guardrail, pas de confiance.
La stratégie de merge
Quand les deux agents ont terminé et que les PRs sont ouvertes, l'ordre de merge a son importance. La règle que j'applique : merger d'abord la feature qui touche le moins de fichiers partagés.
# Étape 1 — identifier les fichiers communs entre les deux branches
git diff --name-only origin/develop...feat/mbois/LCT-42500 > /tmp/files-b.txt
git diff --name-only origin/develop...feat/mbois/LCT-42501 > /tmp/files-c.txt
comm -12 <(sort /tmp/files-b.txt) <(sort /tmp/files-c.txt)
# Étape 2 — merger la plus petite (en termes de surface partagée)
# Sur la PR avec le moins de fichiers communs, merger en premier via l'UI ADO/GitHub
# Étape 3 — rebaser la seconde sur develop mis à jour... non.
# Utiliser merge, pas rebase.
git fetch origin
git checkout feat/mbois/LCT-42501
git merge origin/develop # absorbe le premier merge proprement
Sur les features vraiment indépendantes, la liste de fichiers communs est courte — souvent vide, parfois limitée aux fichiers d'enregistrement DI (ServiceCollectionExtensions.cs) ou aux tests de convention. Ces conflits sont mécaniques : deux agents ont ajouté des lignes dans le même bloc d'enregistrement. Ils se résolvent en 2 minutes.
Pourquoi pas rebase ? Parce que rebase réécrit l'historique de la feature. Le pipeline multi-agents produit des commits atomiques et documentés (red:, green:, refactor:) qui ont de la valeur pour la revue de code et pour comprendre les décisions prises. Un rebase les écrase ou les réordonne. Merge préserve l'histoire — et sur une codebase d'entreprise, l'histoire compte.
Ce qui nécessite une coordination
Les worktrees ne sont pas une solution universelle. Trois cas où la parallélisation est contre-indiquée :
Migrations de base de données. Deux agents qui créent chacun une migration EF Core vont produire deux fichiers avec des timestamps conflictuels et des états de snapshot incompatibles. Les migrations doivent être séquentielles.
Changements d'infrastructure partagée. Si la feature A refactorise une interface que la feature B utilise, B doit partir après que A soit mergée. Lancer les deux en parallèle oblige B à merger une version de A qui n'est pas encore sur develop — ou à dupliquer le changement d'interface, ce qui est pire.
Changements de contrat d'API cassants. Si A renomme un champ dans un DTO partagé, B qui compile contre le DTO d'origine va produire du code incompatible. Le test de compilation de B passera en local (son worktree a l'ancien DTO) mais échouera en CI après le merge de A. Ce type de dérive est difficile à détecter.
Le signal pratique : si le diff prévu de deux features se chevauche sur des fichiers d'infrastructure, de contrat ou de modèle partagé, travaillez séquentiellement. La parallélisation est un gain quand les bounded contexts sont réellement indépendants.
Le piège des submodules
Sur mon dépôt, le répertoire Sql/Db est un submodule git qui contient le schéma SQL Server utilisé par les tests d'intégration Reqnroll. Sans ce submodule initialisé, le container Docker SQL Server ne démarre pas — et les tests d'intégration échouent avec des erreurs de connexion qui ne mentionnent pas les submodules.
La première fois que j'ai créé un worktree sans lancer git submodule update --init, j'ai perdu 25 minutes à investiguer une erreur de port SQL avant de réaliser que le répertoire du submodule était vide.
La règle est simple et doit devenir un réflexe :
# Immédiatement après git worktree add — pas "après avoir testé", immédiatement
cd /chemin/vers/le/nouveau/worktree
git submodule update --init
Si votre repo n'a pas de submodules, vous pouvez ignorer cette section. Si vous ne savez pas si vous en avez, vérifiez la présence d'un fichier .gitmodules à la racine.
Les chiffres réels
Trois features indépendantes sur la même sprint : refactoring du module HRIS (nouveaux champs, mapping, tests), exposition des rôles FC dans le BFF, et génération d'un export Excel pour les factures mensuelles. Chacune représente environ 90 minutes de pipeline complet avec un seul agent.
- Séquentiel — 3 × 90 min = 270 min (4h30)
- 3 worktrees en parallèle — 90 min de pipeline + 15 min de merge coordination = 105 min (1h45)
- Rapport — 2.6× de throughput sur la même journée
Le chiffre de 15 minutes pour le merge coordination est réaliste quand les features sont vraiment indépendantes. Il monte à 45 minutes si vous avez mal évalué les dépendances et que vous découvrez au moment du merge que deux agents ont modifié le même fichier d'enregistrement DI de manière incompatible. C'est encore rentable, mais ça réduit le gain.
La leçon : le gain de throughput est réel, mais il dépend directement de la qualité du découpage. Des features mal isolées au niveau backlog produisent des conflits de merge que le gain de parallélisation ne compense pas toujours.
Pour démarrer
La prochaine fois que vous avez deux features indépendantes dans votre sprint, avant de lancer le premier agent, prenez 3 minutes pour vérifier la surface de fichiers partagée. Si le diff prévu est dans des répertoires distincts, créez deux worktrees et lancez les deux agents.
Vous n'avez pas besoin d'infrastructure, pas de LangChain, pas d'orchestrateur externe. Juste git worktree add, la discipline de ne jamais checkout une branche déjà occupée, et le réflexe git submodule update --init après chaque création.
"Le goulot d'étranglement n'est pas la vitesse de l'agent — c'est le répertoire unique qui force la séquentialité. Un worktree par agent, un agent par feature indépendante : c'est la seule transformation qui change le throughput de façon structurelle."
Vous voulez mettre en place ce protocole dans votre équipe .NET ?
Je peux auditer votre workflow, configurer la parallélisation agents + worktrees
pour votre contexte, ou intervenir en mission sur vos projets .NET 9.