Guía · Cambios entre repos

Un cambio, muchos repos, ningún main roto

La mayoría de las funcionalidades reales cruzan fronteras de servicios. Un solo agente ve una porción; lo difícil es planificar el cambio completo y mergear las porciones en un orden donde cada repo siga siendo desplegable en todo momento.

Por Equipo de Aerunit · Última actualización

El problema

Por qué el trabajo entre repos se rompe

Imagina una funcionalidad que toca una API, su librería cliente y la web app. Entrégale cada repo a su propio agente con la misma descripción vaga y cada uno hará algo razonable. El agente de la API renombra un campo; el agente del cliente mantiene el nombre antiguo; el agente web inventa un tercero. Cada agente tiene razón en su propio repo y se equivocan juntos.

Dos cosas fallan: el contrato entre los repos se desalinea, y el orden de merge importa. Si mergeas al consumidor antes que al productor, main queda roto hasta que aterrice el otro PR.

Planifica una vez

Planifica una vez, a nivel de arquitectura

Escribe una story padre que describa todo el cambio —la misión, el nuevo contrato, el orden— y luego un sub-issue por repositorio, cada uno con sus propios criterios de aceptación. Linear soporta exactamente esto con issues padre y sub-issues. [4] El issue padre contiene la arquitectura; cada sub-issue es lo bastante pequeño para una ejecución de agente. Cómo escribir cada uno se cubre en nuestra guía de stories para agentes de codificación.

Mantente desplegable

Expand, migrate, contract

La forma más segura de cambiar una interfaz de la que dependen varios repos es una antigua. Martin Fowler lo llama parallel change:

"Parallel change, también conocido como expand and contract, es un patrón para implementar cambios incompatibles hacia atrás en una interfaz de forma segura, dividiendo el cambio en tres fases distintas: expand, migrate y contract." [1]

Expand

El productor acepta y devuelve tanto la forma antigua como la nueva.

Migrate

Cada consumidor migra a la forma nueva, un repo a la vez.

Contract

Una vez que todos los consumidores han migrado, se elimina la forma antigua.

Cada paso es desplegable por sí solo. El libro de Google sobre cambios a gran escala hace el mismo punto a mayor escala: "el cambio atómico más grande posible disminuye, contraintuitivamente" a medida que crece una base de código, así que los cambios grandes se dividen en piezas independientes. [2]

Hazlo explícito

Codifica el orden como dependencias

No guardes el orden en tu cabeza. En Linear, marca el sub-issue del consumidor como bloqueado por el sub-issue del productor —los issues bloqueados muestran una bandera naranja bajo "Blocked by" en la barra lateral del issue. [5] La porción del consumidor no empieza hasta que la del productor se haya mergeado.

Ejemplo: renombrar customer_name → display_name
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 significa mergeado —así que asegúrate de que mergear el productor también lo despliega o publica (para un SDK, una versión publicada) antes de que la porción del consumidor dependa de ella. Si tus despliegues no ocurren al mergear, añade un paso de release a la story del productor.

Leído de arriba abajo, eso es a la vez el plan y el orden de merge —y cada línea deja main desplegable. Es la misma idea que mergear trabajo paralelo una rama a la vez, algo que cubrimos en nuestra guía de agentes de codificación en paralelo.

Atrápalo en CI

Fija el contrato

Un contrato escrito solo en una story puede seguir desalineándose. Fíjalo en el código, para que un desajuste falle en CI: un cliente tipado compartido generado desde una única fuente, una especificación OpenAPI que ambas partes validan, o tests de contrato dirigidos por el consumidor. Pact describe la idea: un contrato está entre un consumidor —"un cliente que quiere recibir ciertos datos"— y un proveedor —"una API en un servidor que provee los datos que el cliente necesita." [3] Los tests del consumidor registran lo que espera; el CI del proveedor comprueba que sigue entregándolo.

Una elección

¿Un agente en todos los repos, o uno por repo?

Los Cloud Agents de Cursor pueden ejecutarse en entornos multi-repo, donde un agente cambia varios repositorios en una sola ejecución y abre un pull request en cada uno. [6] ¿Cuándo es la herramienta adecuada?

Un agente en todos los repos

Un cambio pequeño y muy acoplado que es seguro entregar junto —las mismas personas son dueñas de cada repo y puedes desplegarlos a la vez.

Un agente por repo

Un cambio mayor, dueños distintos, o algo que necesita despliegues escalonados. Porciones separadas con orden blocked-by mantienen cada paso revisable y desplegable.

Dónde entramos nosotros

Dónde encaja Aerunit

Esta guía pide dos cosas: un plan para todo el cambio, y un orden explícito que mantenga main desplegable. Aerunit hace ambas. Su generador de stories lee cada repositorio que toca el cambio y propone un issue padre más un sub-issue por repo afectado, cada uno con alcance, criterios de aceptación y no-objetivos.

Aerunit sigue las relaciones blocks / blocked-by de Linear, así que cuando el PR de la porción del productor se mergea, los sub-issues que bloqueaba se encolan automáticamente para Cursor Cloud Agents —la siguiente porción empieza sobre código ya mergeado, sin que tengas que despacharla a mano.

Un plan

Un issue padre más un sub-issue por repo, escrito a partir de tu código real.

Orden explícito

Las relaciones blocked-by hacen visible el orden de merge en Linear.

Una revisión por repo

Cada porción es su propio PR, revisado contra su propio sub-issue.

Respuestas rápidas

Preguntas frecuentes

¿Deberíamos pasar a un monorepo para los agentes?

No solo por los agentes. Un monorepo permite que un cambio aterrice de forma atómica, pero incluso Google, con uno de los monorepos más grandes, evita cambios atómicos masivos a esa escala y los divide en piezas más pequeñas e independientes. Los hábitos de esta guía —planificar una vez, expand-migrate-contract, orden explícito— funcionan en cualquiera de los dos esquemas. [2]

¿Cómo evito que los agentes de dos repos inventen nombres de campo distintos?

Decide el contrato antes de que arranque cualquier agente, escríbelo en la story padre y fíjalo en el código: un cliente tipado compartido, una especificación OpenAPI o tests de contrato dirigidos por el consumidor. Así, un desajuste falla en CI y no en producción. [3]

¿Qué pasa si una porción downstream falla después de que la upstream se haya mergeado?

Para eso sirve expand-migrate-contract: el cambio upstream acepta tanto la forma antigua como la nueva, así que nada se rompe mientras la porción downstream se arregla o se reintenta. Solo elimina la forma antigua cuando todos los consumidores hayan migrado. [1]

Aerunit

Mantén ocupados a los agentes. Tú pones el criterio.

Aerunit está en acceso anticipado solo por invitación. Paga por lo que usas: sin suscripciones que olvidar, y los equipos comparten un fondo de créditos sin coste adicional.

Solicitar acceso anticipado