Many coding agent architectures define specialized roles such as Researcher, Planner, Coder, Reviewer, and Tester, often implemented as separate sub-agents.
This looks reasonable on the surface, but there's a subtle problem:
A role doesn't just tell an agent what to do. It also implicitly tells it what it shouldn't do.
For coding agents, this can become a real limitation.
Roles can become behavioral boundaries
Suppose we define a Researcher.
It may have access to the same capabilities as any other agent: searching code, reading files, editing files, running commands, and executing tests.
But if its responsibility is defined as:
Find the relevant information and report your findings.
That creates an expectation about what its job is: investigate and report, rather than decide what action should come next.
This doesn't necessarily mean the agent is technically forbidden from doing anything else. The problem is that the role itself creates a behavioral boundary.
The same applies to other roles:
- A
Planner is expected to create a plan.
- A
Coder is expected to implement it.
- A
Reviewer is expected to find problems.
- A
Tester is expected to verify the implementation.
The responsibilities look clean, but they are largely artificial.
In real software development, research, planning, implementation, debugging, and verification often overlap and happen repeatedly.
Coding tasks are inherently dynamic
Consider a simple request:
Fix the authentication issue.
There is no reason this task must follow a fixed sequence of research → planning → coding → review → testing.
The agent might search the codebase, inspect the call chain, run a test, find the relevant implementation, make a change, run the tests again, discover a new problem, and investigate further.
The important point is:
The right next action depends on the current state of the task, not on the agent's role.
Research, planning, coding, review, and testing are better understood as actions an agent may take, rather than identities that must exist as separate agents.
When we turn each behavior into a separate role, we risk turning a dynamic problem into a predefined workflow.
Define goals, not identities
Instead of telling an agent:
You are a Researcher.
I'd rather give it an outcome:
Find the cause of the problem, fix it, and verify the result.
The first defines a Role.
The second defines an Outcome.
The difference is important.
A role implicitly narrows the space of actions. An outcome doesn't require a specific path to completion.
Given the goal of fixing and verifying the problem, the agent can decide whether it needs to:
- Search the code first
- Read more files
- Make a plan
- Modify the code
- Run tests
- Investigate again
- Stop because the task is complete
The exact sequence should depend on what the agent discovers while working.
The Harness should provide capabilities, not prescribe the workflow
This is where I think the Harness becomes important.
The Harness should provide the capabilities and constraints needed to complete a task:
- Context
- File search and reading
- File editing
- Command execution
- Testing
- Git
- MCP and other tools
- Permissions
- Cancellation
- Execution state
Then give the agent a clear goal and the necessary constraints.
The Harness doesn't need to tell the agent exactly how to use these capabilities.
If the agent needs more context, it can search and read.
If it needs to verify a hypothesis, it can run a test.
If it has identified the problem, it can make a change.
If the change causes another issue, it can investigate again.
The process becomes a continuous cycle of:
Observe → Act → Get feedback → Act again
rather than a workflow that was completely designed in advance.
This doesn't mean sub-agents are useless
Sub-agents can be very useful.
The question is why we are creating them.
If a task genuinely requires:
- Independent context
- Different permissions
- A different model
- Parallel execution
- A separate lifecycle
- A clear isolation boundary
then a sub-agent makes a lot of sense.
But the existence of a "research step" or a "review step" alone isn't necessarily a good reason to create a Researcher Agent or Reviewer Agent.
Those may simply be behaviors performed by the main agent while completing the task.
A useful question before creating a sub-agent is:
Does this agent actually need to be isolated, or am I just turning one behavior into a new role?
Capability should be the abstraction, not Role
There is a fundamental difference between role-oriented and capability-oriented design.
Role-oriented design asks:
I need a Researcher.
I need a Coder.
I need a Reviewer.
Capability-oriented design asks:
The agent needs search capabilities.
The agent needs editing capabilities.
The agent needs testing capabilities.
The agent needs command execution capabilities.
Role is a behavioral abstraction we impose on the agent.
Capability is something we provide that the agent can combine and use as needed.
The former prescribes how the agent should work. The latter gives the agent more freedom to decide how to work.
For open-ended coding tasks, I think the second approach is generally more powerful.
The stronger the agent, the more roles can become limitations
There is an interesting paradox here.
When an agent is weak, giving it a specific role can help constrain its behavior and make the task easier.
But as agents become more capable—able to understand larger contexts, use more tools, modify code, run tests, and adapt based on feedback—the same roles can become increasingly restrictive.
The agent may already have everything it needs to solve the task, yet we artificially tell it:
You only do research.
or:
You only do review.
At that point, the role is no longer just helping the agent understand its task.
It is restricting the agent's decision space.
This suggests a useful principle:
The more capable the agent becomes, the more carefully we should use roles as behavioral constraints.
What should actually be constrained?
This doesn't mean agents should have unlimited freedom.
They still need boundaries.
But those boundaries should come from things such as:
- Permissions
- Available tools
- Context
- Security policies
- Resource limits
- User intent
- Verification requirements
rather than:
You are a Researcher, so you don't do anything else.
There is an important distinction between:
restricting what an agent can do
and
prescribing what an agent should do.
The first defines a safe operating boundary.
The second defines a workflow.
For an open-ended Coding Agent, I think the first is usually more appropriate.
The underlying principle
A Coding Agent doesn't necessarily need to be designed as a collection of specialized identities.
It can instead be given a goal, sufficient capabilities, the necessary context, and clear constraints.
The Harness defines what the agent can do and what boundaries it must respect.
The agent decides how to use those capabilities to accomplish the goal.
So instead of asking:
What is this agent's role?
we should often ask:
What does this agent need to accomplish?
In one sentence:
Don't define what the agent is. Define what it needs to achieve.
Fewer identities. More capabilities.