# Parallel coding agents: how to coordinate and review changes

> How I think about parallel coding agents, shared code, review queues, and the checks that tell us whether their changes work together.

Author: [Flavio Copes](https://flaviocopes.com/about/) | Published: 2026-10-18 | Topics: [AI](https://flaviocopes.com/tags/ai/) | Canonical: https://flaviocopes.com/parallel-agents/

Starting another coding agent is easy. Deciding whether its work belongs in the application takes more thought.

I run several agents while building software. An agent can investigate a bug while another works on a separate change. That can be useful. It also gives me several streams of decisions to follow, each made with an incomplete view of the others.

A terminal full of finished tasks looks productive. But I still need to know whether the application works after those tasks meet.

This is where I would spend most of my attention when improving a parallel agent workflow. Getting work started is already convenient. Keeping the combined result understandable needs more care.

## Separate files can still share a decision

Imagine a small application that lets people save articles.

One agent changes how saved articles appear on the homepage. Another adds an archive button. A third improves the search results.

They can work in different files. Git might combine every change without a conflict. Yet the result can still be wrong.

The homepage treats archived articles as hidden. Search includes them. The archive button removes a record instead of setting its archive status, because its agent interpreted the task differently.

Nothing about different filenames prevents that disagreement. The agents needed the same definition of an archived article before they started.

This distinction also matters with ordinary human teams. Agents make it especially easy to create more simultaneous work than one person can comfortably review.

Before splitting a feature, I would ask which decisions the pieces share. A database field, a public URL, or the meaning of a status can connect otherwise separate tasks. Those decisions need an owner.

## Delegate around stable boundaries

An agent can research a library while another inspects the current implementation. Neither needs to change the code. Separate documentation pages can often be drafted independently. A reviewer can examine a finished change without editing it.

Implementation needs a closer look. If two tasks both need a new permission model, I would settle the permission model first. Then the agents can build against the same interface. If I cannot describe that interface yet, splitting the implementation adds uncertainty while the most important decision remains open.

A useful assignment says what outcome we want, which files or area the agent owns, and which assumptions it must preserve. It should also say where to stop.

For the saved-article application, that might mean asking one agent to update the search page using the existing archive flag. Changing how archiving works would require a separate discussion.

This gives an agent room to solve its task without letting a local convenience become a project-wide redesign.

## Write an assignment another worker can check

For the search worker, I would make the assignment concrete enough to review later:

> Update ordinary article search so it excludes records with the existing archive flag. Keep archived articles available through the archive page. Restoring an article must preserve its identifier and URL. You own the search query and its existing tests. Leave the shared article model and archive controls to their assigned owner. If the existing query cannot express this behavior, report what needs to change before editing those areas.

That last sentence gives the worker a useful response when its task turns out to cross a boundary. It can identify the dependency instead of quietly taking ownership of it.

I would also give it an acceptance case. Save two articles matching the same search, archive one, and check that ordinary search returns only the other. Restore the archived article and check that both appear. The archive page should still let us find the archived article before restoration.

The worker should return any unresolved question alongside its changes. For example, does search inside the archive use the same query? If the assignment only covers ordinary search, that question can reveal a shared dependency without turning the task into a search redesign.

This takes a little more preparation than “fix search.” It also gives the integrator something specific to compare with the finished work. A worker can choose its implementation while everyone checks the same behavior.

## A dashboard helps me find the work

I like being able to see which agents need attention. I wrote about [managing coding agents with Herdr](https://flaviocopes.com/herdr/), where persistent terminal workspaces make that easier.

But a status indicator describes activity. Its meaning depends on the workflow behind it.

An agent can finish because it edited the requested files. It can finish because its tests passed. It can also finish after discovering a blocker and explaining it. Those results need different follow-up actions even if the terminal is now idle.

I would want a finished task to come with something concrete to inspect. Which files changed? What behavior changed? Which checks ran against those files? What remains uncertain?

A short answer to those questions helps me decide what to review next. A long conversation transcript makes me reconstruct the assignment before I can assess the result.

For the dashboard itself, I would prefer a small distinction between work ready for review and work that needs a decision. Otherwise I can spend the afternoon answering easy questions while finished changes pile up.

## Someone has to own the combined application

The person or agent integrating changes needs authority to reject them.

That sounds obvious, but it gets awkward once several workers report success. There is a temptation to preserve every contribution because it took time, or because accepting all of them makes the system look effective.

I would rather discard a change that misunderstood the task than spend another session making it fit.

The integration owner should compare the result with the original request. It also needs to inspect the code the change connects to. A diff can be reasonable on its own while relying on an assumption the application never made.

In the article example, reviewing the archive button means reading the model and the search behavior too. Looking only at the new button misses the disagreement.

For a solo developer, I think ownership should usually stay with the person building the product. An agent can prepare the review, identify collisions, and run checks. Product decisions still need a clear place to land.

## Queuing writes solves a specific problem

When several workers push changes toward the same branch, coordinating those updates becomes necessary.

Git ordinarily rejects a non-fast-forward push when the remote branch has moved ahead. The [Git push documentation](https://git-scm.com/docs/git-push) explains this protection. It prevents an ordinary push from casually replacing the branch history, but it does not decide whether the new code behaves correctly.

A queue can give each update a turn. That helps avoid several workers trying to move the same branch at once.

The queue still needs to check what happens after the preceding updates land. A worker's original tests describe an earlier state of the code. Another change may have altered a function it calls, a dependency it uses, or the configuration under which it runs.

[GitHub's merge queue](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/configuring-pull-request-merges/managing-a-merge-queue) addresses this by checking queued changes against the target branch and earlier queued changes. It is a useful example of the problem a queue must handle, even if your own workflow uses something smaller.

I would start with one integration owner and a short queue. Building a distributed coordination service makes sense only after a simpler arrangement causes a real problem.

## Checks belong to a particular state of the code

Suppose an agent tests its archive feature. Everything passes.

Then the search change lands and introduces a shared helper. The archive feature is adjusted to use that helper before it is accepted.

The old test result is now incomplete evidence. It describes the code before that adjustment.

This does not mean rerunning every expensive check after every character changes. It means being precise about what the checks covered. A changed shared helper needs checks of its callers. A conflict resolution that alters behavior needs another look at that behavior.

I would record the revision tested alongside the result whenever changes travel through a queue. In a shared checkout, I would also check for uncommitted changes that were present during the run. A commit identifier alone cannot describe extra files another worker was still editing.

This is less exciting than launching more workers, but it makes a green result interpretable.

Suppose that combined check fails because the new shared helper excludes archived articles everywhere. Ordinary search now works, but the archive page is empty.

I would stop integration there and identify the helper's callers. The next assignment is to preserve the distinction between ordinary search and archive search. One owner should make that change while the affected workers wait or review it.

I would avoid asking each worker to patch its own caller independently. They could add different exceptions and leave the next feature with an even less predictable helper. After the owner changes the shared behavior, rerun the search and archive cases together, including restoration. Then the waiting changes can continue against the corrected interface.

## A missing worker should leave recoverable work

Agents can stop halfway through a task. A session can lose its connection. A tool can return an error after part of an operation succeeded.

The next worker needs enough information to inspect the current state before continuing.

For local code, that starts with the actual files and Git status. For an external operation, it means checking the system where the action happened. A chat message saying “about to push” cannot tell us whether the push completed.

I would give each task a small durable record with its scope and current handoff point. Finished work should point to its files or commit. Unfinished work should say what remains, including any operation whose result is uncertain.

The replacement should verify those statements. If a remote branch already contains the commit, there is no reason to repeat the push. If the files are only partially changed, it can decide whether to finish them or discard the attempt.

This also reduces the pressure to keep every agent session alive indefinitely. Work can survive a session if its state is inspectable elsewhere.

## Review capacity limits useful concurrency

You can have spare compute and still have too much work in progress.

Suppose several agents finish changes while I am reviewing the first one. Each waiting change gets older. The application continues moving, assumptions drift, and the eventual review requires more reconstruction.

Adding another worker to that situation increases the pile.

I would watch how long finished changes wait for review. If that queue keeps growing, I would pause implementation and use the available agents for read-only work. They can investigate an uncertain dependency, check a documented assumption, or review something already finished.

That work can help me clear the queue. Another partially overlapping feature usually cannot.

There is no universal agent count I would recommend. A set of independent content pages behaves differently from a set of changes to authentication. The work determines how much concurrency is useful.

## Independent review needs a clear question

A second agent can be useful when it checks a specific risk.

“Review this” is a broad request. “Check whether archived articles remain reachable through search, and verify that restoring one preserves its original URL” gives the reviewer behavior it can investigate.

I would include the original requirements and let it inspect the implementation. The review should explain a defect through a concrete user action or failing case. Praise and a general confidence statement do little for me.

Using another agent also leaves shared blind spots. Two agents can read the same ambiguous requirement and make the same assumption. For important behavior, I still want a test with an expected result I can explain, or a flow I can use myself.

A reviewer helps me focus my attention. I would not treat its approval as evidence that every relevant question was asked.

## A small parallel session

For the article application, I would first write down what archiving means and which behavior must stay unchanged.

Then I would split the work that is independent. One worker can update the search filter. Another can prepare the archive control against the agreed interface. A third can inspect the existing tests for cases the feature will affect.

I would keep changes to the shared model under one owner. Each worker would return its files and the checks it ran. I would review and integrate one finished change at a time, then use the combined flow before calling the feature complete.

If the shared assumptions changed halfway through, I would stop the affected workers and update the assignment. Letting them finish against an obsolete plan creates more work for whoever integrates it.

The [Git course](https://flaviocopes.com/courses/git/) covers the underlying history and collaboration tools. Those tools help us inspect the result, but they cannot settle the product definition on our behalf.

For a tightly connected feature, I would happily use one implementation agent and one reviewer. If I cannot keep up with the changes already finished, the next useful task is to review one of them.
