Building AI agents is getting easier. Getting them to work together and deliver reliable software is a different story.
I've been spending quite a bit of time thinking about how AI agents can work together to build software.
At first, the idea seems pretty straightforward. Instead of asking one AI model to handle everything, we can split the work between specialized agents. One plans the tasks, another writes the frontend, another handles the backend, and another tests the result.
Everyone has a role. Work can happen in parallel. In theory, we should be able to build software faster.
But there's a catch.
Getting several agents to generate code is relatively easy. Getting them to produce software that actually works together is much harder.
Imagine asking a frontend agent and a backend agent to build a task management application.
The frontend agent creates a beautiful task list and expects the API to return an assignedUser field. Meanwhile, the backend agent implements the endpoint using a field called assignee.
Both agents finish their tasks. Both report success. But the application doesn't work.
This is a small example, but it illustrates a much bigger problem. Each agent can do a reasonable job independently while the overall system still fails.
And these problems don't disappear just because we use a better model or write a more detailed prompt.
The real challenge is coordinating the work.
Start with the workflow, not the agents
When designing a multi-agent system, it's tempting to start by defining different roles:
- A planner that breaks down requirements.
- A frontend agent that writes React code.
- A backend agent that builds APIs.
- A tester that checks the implementation.
- A reviewer that looks for bugs.
This is a reasonable starting point, but I think there's a more important question to answer first:
How does work move from one stage to the next?
Before deciding how many agents we need, we should understand what needs to happen for a feature to go from an idea to working software.
For example, a simple development workflow could look like this:
Requirement
|
v
Planning
|
v
Define API Contracts
|
+----------+
| |
v v
Frontend Backend
| |
+-----+----+
|
v
Testing
|
v
Review
|
v
Delivery
Notice that the frontend and backend can work in parallel, but only after the API contract is defined.
That small detail makes a big difference.
Without a shared contract, each agent has to make assumptions. The more assumptions they make independently, the more work we may need to redo later.
This is why I prefer to think about the workflow first and the agents second.
Let the task dependencies decide what runs in parallel
Let's say we're building a task management application.
We have five tasks:
const tasks = [
{
id: "requirements",
dependencies: [],
},
{
id: "api-contract",
dependencies: ["requirements"],
},
{
id: "frontend",
dependencies: ["api-contract"],
},
{
id: "backend",
dependencies: ["api-contract"],
},
{
id: "integration-tests",
dependencies: ["frontend", "backend"],
},
];
The frontend and backend don't need to wait for each other. They only need to wait for the API contract.
Once that contract is ready, both tasks can start.
A simple scheduler can determine which tasks are ready to run:
function getReadyTasks(tasks, completed) {
return tasks.filter((task) => {
if (completed.has(task.id)) {
return false;
}
return task.dependencies.every((dependency) =>
completed.has(dependency),
);
});
}
This is a simplified example, but the idea is straightforward: a task becomes eligible to run when its dependencies are complete.
In a real implementation, we'd also track running and failed tasks, handle retries, and prevent multiple workers from claiming the same task.
The important part is that we don't need to tell agents when to start by manually coordinating every step. We can let the dependency graph determine that.
Parallelism should come from the structure of the work, not from simply having multiple agents available.
An agent saying "done" doesn't mean the task is done
This is another problem worth thinking about.
Imagine a backend agent returns the following result:
{
"status": "completed",
"summary": "Implemented the task assignment API",
"tests": "passed"
}
Looks good, right?
But what if the agent never actually ran the tests?
What if it ran the wrong command? Or what if the tests failed, but the agent misunderstood the output?
We shouldn't rely entirely on an agent's description of its own work.
Instead, the system should run the required checks and collect the results itself.
For example:
async function verifyProject(runner, workspace) {
const checks = [
["npm", ["run", "typecheck"]],
["npm", ["test", "--", "--runInBand"]],
["npm", ["run", "build"]],
];
const results = [];
for (const [command, args] of checks) {
const result = await runner.run(
command,
args,
workspace,
);
results.push(result);
if (result.exitCode !== 0) {
return {
passed: false,
results,
};
}
}
return {
passed: true,
results,
};
}
Here, runner represents a controlled command-execution service, and the example assumes the project has the corresponding npm scripts.
The distinction is simple: the agent writes the code, but the verification system checks whether it works.
This doesn't mean tests can prove that software is completely correct. They can't. But they give us independent evidence instead of relying on a model's confidence.
What happens when something fails?
Let's say the frontend expects this response:
{
"items": [],
"nextCursor": null
}
But the backend returns:
{
"data": [],
"total": 0
}
The integration test fails.
We could simply send the error back to the frontend agent and ask it to fix the problem. But that might be the wrong solution. Maybe the backend is the part that doesn't follow the agreed contract.
Before asking an agent to make another change, the workflow should identify what went wrong.
A useful failure report might look like this:
interface FailureReport {
taskId: string;
category:
| "implementation_defect"
| "contract_mismatch"
| "test_defect"
| "environment_failure";
summary: string;
evidence: string[];
}
This gives the orchestrator something more useful than a generic error message.
It can decide whether to retry the implementation, revisit the contract, fix the test, or ask a developer for help.
I'd also put a limit on automatic retries. If the same problem persists after a couple of attempts, the system should stop and explain what happened rather than keep generating code indefinitely.
Sometimes the best thing an agent can do is report that it is blocked.
The part I find most interesting
For me, the interesting part of multi-agent development isn't simply making agents more autonomous.
It's building a system where each piece of work has a clear purpose, where agents share the same assumptions, and where we can understand why a task succeeded or failed.
That requires a few things working together:
- A planner that creates useful tasks rather than vague instructions.
- A scheduler that respects dependencies.
- Shared contracts that keep implementations consistent.
- A controlled environment for making changes.
- Automated checks that produce real execution results.
- A review process that catches problems before delivery.
None of these ideas is particularly new in software engineering. We already use dependency graphs, CI pipelines, API contracts, isolated environments, and code reviews.
What's changing is that AI agents are now participating in these processes, and we need to design the surrounding system accordingly.
I wouldn't start by trying to make every agent fully autonomous. I'd start with a small workflow, make it observable, and see where it breaks.
Then I'd improve the parts that actually cause problems.
Final thoughts
Multi-agent systems can be useful, especially when a task has clear boundaries and several independent pieces of work.
But adding more agents doesn't automatically make software development faster or better.
If the agents don't share contracts, if their changes aren't integrated properly, or if nobody verifies the results, we may simply end up generating mistakes faster.
I think the more useful question is not how many agents we can run at once.
It's this:
How do we turn the work of several AI agents into a software change we can actually trust?
That's the problem I'd focus on when building an AI engineering workflow.
Top comments (0)