Pourquoi le travail multi-repos casse
Imaginez une fonctionnalité qui touche une API, sa librairie cliente et l'application web. Confiez chaque repo à son propre agent avec la même description vague, et chacun fait quelque chose de raisonnable. L'agent de l'API renomme un champ, l'agent client garde l'ancien nom, l'agent web en invente un troisième. Chaque agent a raison dans son propre repo et tort ensemble.
Deux choses tournent mal : le contrat entre les repos dérive, et l'ordre de merge compte. Merger le consommateur avant le producteur laisse main cassé jusqu'à ce que l'autre PR atterrisse.
Planifier une fois, au niveau de l'architecture
Écrivez une story parente qui décrit tout le changement — la mission, le nouveau contrat, l'ordre — puis une sub-issue par dépôt, chacune avec ses propres critères d'acceptation. Linear prend exactement cela en charge avec les issues parentes et les sub-issues. [4] L'issue parente contient l'architecture ; chaque sub-issue est assez petite pour une seule exécution d'agent. Comment rédiger chacune est couvert dans notre guide sur les stories pour agents de codage.
Expand, migrate, contract
La façon la plus sûre de changer une interface dont dépendent plusieurs repos est une ancienne. Martin Fowler appelle cela parallel change :
"Parallel change, aussi connu sous le nom d'expand and contract, est un patron pour implémenter des changements rétro-incompatibles d'une interface de manière sûre, en découpant le changement en trois phases distinctes : expand, migrate et contract." [1]
Expand
Le producteur accepte et renvoie à la fois l'ancienne et la nouvelle forme.
Migrate
Chaque consommateur migre vers la nouvelle forme, un repo à la fois.
Contract
Une fois que chaque consommateur a migré, l'ancienne forme est supprimée.
Chaque étape est déployable à elle seule. Le livre de Google sur les changements à grande échelle fait le même constat à plus grande échelle : "le plus grand changement atomique possible diminue, contre-intuitivement" à mesure qu'une base de code grandit, si bien que les gros changements sont découpés en morceaux indépendants. [2]
Coder l'ordre comme des dépendances
Ne gardez pas l'ordre dans votre tête. Dans Linear, marquez la sub-issue consommatrice comme bloquée par la sub-issue productrice — les issues bloquées affichent un drapeau orange sous "Blocked by" dans la barre latérale de l'issue. [5] La tranche consommatrice ne démarre pas tant que la tranche productrice n'a pas été mergée.
Parent: Show customers' chosen display name everywhere
├─ API-1 Expand: accept + return display_name alongside customer_name
├─ SDK-1 Use display_name in the typed client (blocked by API-1)
├─ WEB-1 Render display_name in account pages (blocked by SDK-1)
└─ API-2 Contract: remove customer_name (blocked by WEB-1)Blocked-by signifie mergé — assurez-vous donc que merger le producteur le déploie aussi ou le publie (pour un SDK, une version publiée) avant que la tranche consommatrice n'en dépende. Si vos déploiements ne se font pas au merge, ajoutez une étape de release à la story du producteur.
Lu de haut en bas, c'est à la fois le plan et l'ordre de merge — et chaque ligne laisse main déployable. C'est la même idée que merger du travail parallèle une branche à la fois, que nous couvrons dans notre guide sur les agents de codage en parallèle.
Fixer le contrat
Un contrat écrit seulement dans une story peut quand même dériver. Fixez-le dans le code, pour qu'une incohérence échoue en CI : un client typé partagé généré à partir d'une seule source, une spécification OpenAPI que les deux côtés valident, ou des tests de contrat dirigés par le consommateur. Pact décrit l'idée : un contrat se situe entre un consommateur — "un client qui veut recevoir certaines données" — et un fournisseur — "une API sur un serveur qui fournit les données dont le client a besoin." [3] Les tests du consommateur enregistrent ce qu'il attend ; la CI du fournisseur vérifie qu'il le livre toujours.
Un agent sur plusieurs repos, ou un par repo ?
Les Cloud Agents de Cursor peuvent fonctionner dans des environnements multi-repos, où un agent modifie plusieurs dépôts en une seule exécution et ouvre une pull request dans chacun. [6] Quand est-ce le bon outil ?
Un agent sur plusieurs repos
Un changement petit et étroitement couplé, sûr à livrer ensemble — les mêmes personnes possèdent chaque repo et vous pouvez les déployer en même temps.
Un agent par repo
Un changement plus important, des propriétaires différents, ou quelque chose qui nécessite des déploiements échelonnés. Des tranches séparées avec un ordre blocked-by gardent chaque étape révisable et déployable.
Où Aerunit intervient
Ce guide demande deux choses : un plan pour tout le changement, et un ordre explicite qui garde main déployable. Aerunit fait les deux. Son rédacteur de stories lit chaque dépôt touché par le changement et propose une issue parente plus une sub-issue par repo concerné, chacune avec son périmètre, ses critères d'acceptation et ses non-objectifs.
Aerunit suit les relations blocks / blocked-by de Linear, donc lorsque le PR de la tranche productrice est mergé, les sub-issues qu'elle bloquait sont automatiquement mises en file pour Cursor Cloud Agents — la tranche suivante démarre sur du code déjà mergé, sans que vous ayez à la répartir à la main.
Un plan
Une issue parente plus une sub-issue par repo, rédigée à partir de votre code réel.
Ordre explicite
Les relations blocked-by rendent l'ordre de merge visible dans Linear.
Une revue par repo
Chaque tranche est son propre PR, revue par rapport à sa propre sub-issue.
Questions fréquentes
Devrions-nous passer à un monorepo pour les agents ?
Pas seulement pour les agents. Un monorepo permet à un changement d'atterrir de façon atomique, mais même Google, avec l'un des plus grands monorepos, évite les changements atomiques massifs à cette échelle et les découpe en morceaux plus petits et indépendants. Les habitudes de ce guide — planifier une fois, expand-migrate-contract, ordre explicite — fonctionnent dans les deux configurations. [2]
Comment empêcher les agents de deux repos d'inventer des noms de champs différents ?
Décidez du contrat avant que l'un ou l'autre agent ne démarre, écrivez-le dans la story parente et fixez-le dans le code : un client typé partagé, une spécification OpenAPI, ou des tests de contrat dirigés par le consommateur. Ainsi, une incohérence échoue en CI plutôt qu'en production. [3]
Que se passe-t-il si une tranche en aval échoue après que celle en amont a été mergée ?
C'est tout l'intérêt d'expand-migrate-contract : le changement en amont accepte à la fois l'ancienne et la nouvelle forme, donc rien n'est cassé pendant que la tranche en aval est corrigée ou relancée. Ne retirez l'ancienne forme qu'une fois que chaque consommateur a migré. [1]