Contracts Parallelize Agents

James Phoenix
James Phoenix

If you’re waiting for Agent A to finish before starting Agent B, you’re wasting time. Define the contract between them and dispatch both now.

The default way to run agents on a multi-step codegen task is sequential. Agent A builds the data layer, then Agent B builds the thing that consumes it. B has to wait, because it doesn’t know the shape of A’s output. So the task stalls on the slowest producer.

There is a faster way. Write the contract first, then run both agents against it in parallel.


The Setup

Last night I was building a green-screen meme rendering pipeline. The work split cleanly into two:

  • Task A: Ingest pipeline. Scrape 2,150 Patreon green-screen memes, sample 100 backgrounds, 100 music tracks, and 47 sound effects from GCS, run ffprobe on each, compute quality scores, and emit a local manifest.
  • Task B: Agentic state machine. Duplicate the existing slideshow state machine for green-screen memes. Add tools to search local assets, choose a composite (green-screen + background + music + captions), and render via ffmpeg with a deterministic critic.

The naive plan: ship A, then ship B. A is a multi-hour scrape plus a non-trivial ingest script. B is a state machine rewrite plus new tools. Run them back-to-back and I burn a day.

Instead I wrote a contract and ran two Codex agents simultaneously.


The Contract Is the Decoupling Point

A contract is not just a TypeScript interface. It is the set of types, schemas, folder shapes, and provider interfaces that describe exactly what the producer emits and exactly what the consumer expects. For this task:

Leanpub Book

Read The Meta-Engineer

A practical book on building autonomous AI systems with Claude Code, context engineering, verification loops, and production harnesses.

Continuously updated
Claude Code + agentic systems
View Book
export type SeedAsset =
  | GreenScreenTemplateAsset
  | BackgroundAsset
  | MusicAsset
  | SoundEffectAsset;

export interface LocalAssetProvider {
  searchGreenScreens(q: AssetSearchQuery): Promise<GreenScreenTemplateAsset[]>;
  searchBackgrounds(q: AssetSearchQuery): Promise<BackgroundAsset[]>;
  searchMusic(q: AssetSearchQuery): Promise<MusicAsset[]>;
  searchSoundEffects(q: AssetSearchQuery): Promise<SoundEffectAsset[]>;
  getAssetById(id: string): Promise<SeedAsset | null>;
}

Plus a folder layout (artefacts/seed-assets/<kind>/<asset-id>/source.ext with metadata.json), a manifest schema with schemaVersion: 1, a render input schema, and a critic output schema.

That is the contract. Agent A produces things that satisfy it. Agent B consumes things that satisfy it. Neither agent needs the other to exist.


Two Agents, Parallel Runs

While Agent A was running its 2,150-post overnight Patreon scrape and later writing the ingest CLI behind pnpm assets:ingest-seed, Agent B was:

  • Duplicating state-machine.ts into green-screen-state-machine.ts
  • Building a LocalAssetProvider that reads the manifest
  • Writing search tools that delegate to the provider
  • Writing render_green_screen_asset with standardized ffmpeg args (scale, chromakey, overlay, drawtext)
  • Writing a render critic that parses ffprobe output and checks caption bounds, dimensions, and audio policy

Agent B did not wait for ingest to finish. It mocked the three real assets I named (the gorilla clip, Madonna Vogue, John Cena SFX) as a local manifest that already satisfied the contract. This worked because the interface hides where the assets came from. Local files, DB rows, or API responses behind the same interface are interchangeable.

Agent A never had to read a single line of Agent B’s code. It shipped a CLI that writes files into the agreed folder shape and a manifest validated against the agreed schema.

When Agent A finished, I pointed Agent B’s provider at the real manifest. Nothing else changed.


Why This Works

Contracts reduce coordination cost to near zero. Both agents work against a frozen reference, not against each other’s in-flight code. You get three properties for free:

  1. Independent progress. A crash or rewrite in one agent does not stall the other. Agent B’s state machine was rendering against mock assets for hours before Agent A’s real manifest existed.
  2. Cheap mock implementations. The first consumer of the contract is a fake. That fake becomes your integration-test fixture, because it already has the shape Agent A will eventually produce.
  3. Swap-in upgrades later. When ingest moves behind an Effect API route and a Postgres table with pgvector embeddings, the provider interface does not change. The state machine does not notice.

This is the same reason microservices with OpenAPI specs let product and platform teams ship in parallel. The contract is the seam. Agents exploit seams better than humans because they do not need any context from adjacent work. They only need the shape.


Where to Put the Contract

A contract is cheap to write and load-bearing once written. Three decisions matter:

  • Who owns it. Not the producer, not the consumer. The human dispatching both agents owns it. Otherwise each agent drifts toward a shape convenient for itself and the seam breaks.
  • Where it lives. In the repo both agents can see. Not in chat, not in a scratch file. The contract is a first-class module (src/core/green-screen/types.ts) both agents import from.
  • How strict it is. Strong enough that a producer cannot emit garbage a consumer will accept. I used Zod schemas plus TypeScript discriminated unions. The parser at ingest-time and the parser at state-machine-time is the same parser.

Over-specify early. Under-specifying feels cheap (“just typed JSON”) but bites later when one agent assumes a field exists and the other never emits it.


Limits

Contracts do not parallelize everything:

  • Design ambiguity. If the shape of the data is the real question, writing the contract is the real work. Both agents should block on that.
  • Leaky seams. If B routinely needs producer internals (raw ffmpeg output, not the probe summary), the contract is wrong. Widen it or redraw the seam.
  • Moving APIs. Breaking the contract mid-run requires re-dispatching both agents with the new version. Version contracts explicitly.

The question I ask before splitting work: can I describe every value crossing this seam without reading either side’s implementation? If yes, ship two agents. If no, write the contract until yes.


The Meta-Pattern

This is how I think about stacking agent runs now. Every multi-agent task has a dependency graph. Wherever there is an edge, there is a potential contract. The sum of contracts defines how wide my parallel lane is.

Three agents means three contracts at minimum, probably one per shared seam. Each contract is cheap. Each unlocks a parallel lane.

The human role shifts. I am not writing the code the agents write. I am writing the contracts the agents code against. That is the compound leverage: one hour of contract design buys ten hours of parallel agent compute.

Sequential is the slow path. Contract-first is how I stack runs.


See Also

Topics
Ai AgentsCodegen TasksContract DesignParallel Processing

Newsletter

Become a better AI engineer

Weekly deep dives on production AI systems, context engineering, and the patterns that compound. No fluff, no tutorials. Just what works.

Join 306K+ developers. No spam. Unsubscribe anytime.


More Insights

Cover Image for Mock the LLM, Keep the Tools Real

Mock the LLM, Keep the Tools Real

Agent systems have exactly one non-deterministic component: the model’s choice of tool call. Stub that. Let everything else run.

James Phoenix
James Phoenix
Cover Image for Hand-Roll the Core

Hand-Roll the Core

The further a piece of code sits from the core of your system, the more you can give to agents. At the core, hand-roll it or write the spec yourself. Fully outsource and you lose the ability to patch it.

James Phoenix
James Phoenix