The hardest part of using AI in a large software project is not getting a model to write code. The harder problem is giving it the right context, limiting its responsibilities, and verifying that its output is actually correct.
As a project grows, a single general-purpose agent becomes difficult to trust. It may understand one part of the system while making incorrect assumptions about another. It may modify the wrong layer, miss a contract between services, or report that a change is complete simply because the code compiles.
My approach is to treat agents as specialized members of an engineering team.
Models and agents are different things
A model provides the reasoning and generation capability. An agent provides the working environment around that model.
An agent defines:
- what the agent is responsible for;
- which repositories or files it should inspect;
- which tools it can use;
- whether it can modify code;
- which rules it must follow;
- what it must not do;
- how it should validate its work;
- and what format it should use for the final result.
This distinction is important because an agent’s quality does not come only from the model. A strong model with a vague scope can still produce unreliable work. A well-designed agent with clear boundaries can make the same model much more useful.
I choose models based on risk
I do not choose a model simply because it is the newest or most powerful one. I choose it based on the complexity and cost of failure.
For simple tasks such as repository exploration, searching for files, summarizing code, or performing small transformations, I prefer a fast model.
For implementation work, test writing, and focused refactoring, I use a balanced coding model. It should be capable enough to understand the local architecture without adding unnecessary complexity.
For high-risk tasks, I use a stronger reasoning model. These tasks include:
- cross-service contract analysis;
- database query correctness;
- security and tenant isolation;
- debugging complex data flows;
- release readiness;
- and deep pull request reviews.
For visual work, such as comparing an interface with a design reference or reviewing responsive behavior, a model with strong visual context is more useful than a model optimized only for code generation.
The important principle is that model selection happens at the task level. The agent defines the responsibility; the model provides the appropriate level of reasoning for that responsibility.
I design agents around boundaries
When I create an agent, I start with boundaries rather than capabilities.
I ask a few questions:
- Which part of the system does this agent own?
- Is it allowed to modify files?
- What kind of decisions can it make?
- Which project rules does it need to know?
- What risks should it actively check?
- What actions are explicitly forbidden?
- What evidence proves that the work is complete?
For example, an implementation agent may be allowed to modify a specific feature, but it should not redesign the entire architecture. A review agent should usually be read-only. A test agent should focus on regression coverage instead of changing production behavior to make tests pass.
This makes the agent’s responsibility easier to understand and its output easier to evaluate.
The main types of agents I use
I generally organize agents into several categories.
Implementation agents
These agents build features or modules. Their instructions usually describe the expected file structure, architectural patterns, validation rules, and test requirements.
They are useful when a project has established conventions that should be followed consistently.
Investigation agents
Investigation agents trace a problem through the system. They do not immediately change code. Instead, they inspect the actual path from input to storage to output and identify where the behavior diverges.
This is especially useful for bugs involving multiple services, queues, databases, or APIs.
Testing agents
Testing agents focus on unit tests, regression tests, request validation, query builders, transformations, and edge cases.
I keep their scope narrow because test agents should not redesign the production code simply to make the test easier to write.
Review agents
Review agents are read-only and look for correctness issues, security risks, contract mismatches, performance problems, or architectural violations.
A general code reviewer is useful, but specialized reviewers are often more effective. A security reviewer thinks differently from a database reviewer. A UI reviewer looks for different problems than an API reviewer.
Release and validation agents
These agents determine whether a change has enough evidence to move forward. They separate focused tests, full-suite results, browser checks, integration checks, and unresolved risks.
This prevents a successful type-check from being treated as proof that the complete feature works in production.
I prefer specialized agents over one giant agent
A single agent with instructions such as “understand the whole project and make the change” sounds convenient, but it creates several problems.
The agent has too much context, too many possible responsibilities, and too much freedom to make architectural decisions. It also becomes difficult to understand why it made a particular choice.
Instead, I prefer agents with focused responsibilities:
- one agent for feature implementation;
- one for contract verification;
- one for security;
- one for testing;
- and one for final review.
This does not mean creating an agent for every file. The boundary should be based on ownership, risk, and responsibility—not on file count.
My typical workflow
A feature usually moves through a sequence like this:
User request
↓
System and repository investigation
↓
Contract and scope analysis
↓
Implementation agent
↓
Focused tests
↓
Specialized reviews
↓
Browser or integration verification
↓
Release decision
The first agent does not necessarily write code. It may identify which repositories are affected and which contracts need to remain compatible.
The implementation agent then works within that defined scope. After that, focused tests verify the changed behavior. Specialized review agents examine the result from different perspectives.
Finally, I compare automated evidence with runtime evidence. A passing test is useful, but it is not always proof that the entire user journey works.
Read-only by default
One of the most useful rules in my setup is that review and investigation agents are read-only by default.
They can inspect files, search the codebase, run safe checks, and explain their findings. They cannot modify production code.
This creates a separation between:
- discovering what is wrong;
- deciding how to fix it;
- implementing the fix;
- and verifying the result.
That separation makes mistakes easier to detect. It also prevents an investigation agent from silently changing the system while trying to understand it.
What I do not delegate blindly
There are several things I never accept without verification:
- A model’s assumption that mock data represents real behavior.
- A passing build as proof of runtime correctness.
- A successful HTTP response as proof that data reached the database.
- A full test-suite result without separating existing failures from new failures.
- A visual change without browser verification.
- A security claim without tracing the actual authorization path.
- A database result without checking tenant and source isolation.
AI-generated work is only as trustworthy as the evidence behind it.
The principle behind my setup
My goal is not to build the largest possible collection of agents. My goal is to create a system where every agent has a clear reason to exist.
A good agent should answer five questions clearly:
- What does it own?
- What can it change?
- What must it protect?
- What must it never do?
- How do we know its work is correct?
When these questions are answered, AI agents become more than code-generation tools. They become structured engineering collaborators that can investigate, implement, review, and validate work without losing sight of the system’s boundaries.
The best agent is not the one that does everything.
It is the one that knows exactly what it is responsible for—and what it is not.