A subagent is a helper that Claude Code starts with its own instructions, its own tools and an empty memory of your conversation. The usual first use is a reviewer. This part writes a read-only reviewer for a .NET solution, plants three defects on a branch, and compares five reviews with and without it. Then it looks at the same idea at a larger scale: the five agents that wrote this site's last batch of posts, and what a second reader found after all five said they were done.
- Save dotnet-reviewer.md.txt as
.claude/agents/dotnet-reviewer.mdand replace the six checks with what your code base cares about - Allow read-only git for both shells with settings.json
- On a feature branch, ask:
Use the dotnet-reviewer agent to review the changes on this branch against main.You should see anAgenttool call with"subagent_type": "dotnet-reviewer" - Read the findings: each one has a file and line, why it matters, and a fix
- Run
git status: a review changes nothing - To make the whole session the reviewer, start it with
claude --agent dotnet-reviewer; its tool list is then onlyRead, Grep, Glob, Bash, PowerShell
What a subagent is
A project subagent is one Markdown file in .claude/agents/, committed with
the code. The frontmatter names it, says when to use it and which tools it gets; the body
becomes its system prompt. According to the
subagents documentation, it starts
with a fresh context: it does not see your conversation or the files already read. The
main session writes it a task message, the subagent works, and one report comes back. Its
requests count toward the same usage limits as everything else you do.
The reviewer
---
name: dotnet-reviewer
description: Read-only reviewer for .NET changes in this solution. Use when asked to review a branch, a diff or recent changes. Reports findings with file and line; never edits.
tools: Read, Grep, Glob, Bash, PowerShell
model: inherit
---
No Edit and no Write in the tool list, so it cannot change a file
through those tools. The body then says what this code base cares about, as six checks. Two
of them:
- Time comes from the injected `TimeProvider` (`timeProvider.GetUtcNow()`).
`DateTime.Now`, `DateTime.UtcNow`, `DateTimeOffset.Now` and `DateTimeOffset.UtcNow` must
not appear under src/. The tests use `FakeTimeProvider`, so code that reads the system
clock cannot be tested.
- EF Core: no query inside a loop over the results of another query (N+1). Load related
rows in one query: a join, `Include`, a projection, or one query with `Contains` and
grouping in memory.
The rest covers cancellation tokens, sync over async, tests that assert nothing, and errors returned as Problem results, followed by a report format: file and line, what is wrong, why it matters here, a suggested fix, and permission to answer "No findings."
The test
A branch adds GET /doctors/schedule with tests. It builds with no warnings and
all 19 tests pass. Three defects were planted in it, the kind tests do not catch: a
DateTime.UtcNow in the service, one appointments query per doctor inside a
loop, and a test that calls the code and asserts nothing. That empty test is the one that
would have caught the first defect. One correct refactoring was added as a control.
| How the review ran | Defects found | N+1 rated as | Time | Cost |
|---|---|---|---|---|
| The agent, named in the prompt | 3 of 3 | Medium | 56 s | $0.48 |
| The agent, named in the prompt (repeat) | 3 of 3 | a finding to fix | 61 s | $0.32 |
| The agent, chosen by Claude from a plain "review" prompt | 3 of 3 | High | 69 s | $0.39 |
| No agent file: Claude used its built-in review skill | 3 of 3 | a finding | 29 s | $0.23 |
| No agent file: the main session reviewed alone | 3 of 3 | "Worth improving" | 20 s | $0.14 |
Every review found all three defects and left the correct refactoring alone. On a small diff with textbook defects, the custom reviewer did not find more than a plain "review this branch". It cost about twice as much and took two to three times as long. That is the result, and it is one small branch on one model.
What the agent did change
The findings read like a colleague who knows the house rules. From the first run:
### 1. High: the "today" default reads the system clock
`src/AppointmentDesk.Api/Features/Appointments/AppointmentService.cs:36`
- **Why it matters:** the code base bans `DateTime.UtcNow` under `src/`. With `FakeTimeProvider`, "today" in tests is always the real date and never the faked one, so this path cannot be tested.
Every agent finding came with a file and line, a reason and a fix, in all three runs. The main session reviewing alone argued the same point from the code and gave the empty test no line number. The clearest difference was severity: the agent treated the N+1 loop as a defect each time, because its instructions say so, while the main session alone filed it under "Worth improving". If your team has rules like that, the file is where they become consistent.
Three things the transcripts show
First, the main session writes the subagent's task message, and that message is not your prompt. In one run it added "missing AsNoTracking" to the list of things to look for, and the reviewer duly reported tracked entities as a finding. What the subagent looks at is partly decided by a message you never wrote.
Second, the agent's own instructions held against that message. Twice the main session
asked the reviewer to run the build and the tests. It declined: "I did not run
dotnet build or dotnet test: my instructions limit me to
read-only git commands, so nothing here comes from a test run."
Third, a reviewer with a fresh context does not simply agree. In one run the main session tried to fix the clock call itself, had the edit denied, and still told the reviewer the change was made. The reviewer looked: "I couldn't review the change because it isn't in the working tree." It can also be wrong. One report claimed the compiler "should also warn" about an unused variable; a full rebuild of that commit gives zero warnings. In one run the main session checked the top finding itself and said which findings it had not checked; in another it passed the report along with no check of its own.
Two at once
Asked to use two subagents at the same time, one listing every endpoint and one listing every test, the session started both from a single message. They overlapped for 33.4 seconds and finished 48.9 seconds after the first started; their own durations add up to 82.3 seconds. The subagents used 54% of that session's tokens, and it was the most expensive run of the part at $0.59. Parallel work buys time. It does not save tokens.
The same thing at scale: this site's last batch
On 2 October 2026, twenty of this site's posts about error messages were written by five subagents working in parallel, one per technology, each reproducing every error on a real machine before writing about it. One coordinating session wrote a common brief and one prompt per agent. The five used 1,113,540 tokens and 501 tool calls. The slowest took 27 minutes; the five together took 104 minutes of agent time. All five reported that every check passed.
The review afterwards still changed things. A command in one post had been shortened from what was actually run, which only showed when every code block was compared with the captured output. One answer claimed a message existed in more versions of a tool than the agent had looked at; reading the source at seven versions found it at three. Four posts used the batch's internal container names without saying so. An agent's "all checks pass" means its own checker passed.
What made the batch safe was the brief more than the prompts: each agent had its own folder, a list of things it must never touch, and an instruction to report everything it created or changed outside that folder. Each one did, including the agent that broke a port rule on purpose and said why.
When not to use one
The documentation's own advice matches these numbers: keep work in the main conversation when it needs back and forth, when phases share context, or when the change is quick, because a subagent starts from nothing and has to read its way in. Here the reviewer took more than forty seconds each time and about twice the tokens for the same three findings. A subagent earns its place when the work is self-contained, when its output would crowd your conversation, when you want it restricted to certain tools, or when you want a reader who has not seen the reasoning behind a change.
One limit of this reviewer deserves a plain statement. It is read-only because it has no edit tools and because the session's permissions do not allow shell commands that write. Bash and PowerShell can write files in principle, and no run tested that boundary. Part 2 is where that line is drawn.
Frequently asked
- Where do Claude Code subagents go in a project?
- In .claude/agents/ at the repository root, one Markdown file per subagent, committed with the code. The frontmatter needs a name and a description; tools and model are optional. The body of the file is the subagent's system prompt.
- Does a custom reviewer subagent find more bugs than asking Claude Code to review?
- Not in this test. Five reviews of one small branch with three planted defects all found all three, with or without the agent file. The agent's reviews were more consistent in format and severity and cost about twice as much.
- Do subagents save tokens in Claude Code?
- No. Each subagent sends its own requests, which count toward the same usage. Two parallel subagents here finished in 48.9 seconds instead of 82.3, and used 54% of the session's tokens. They save time and keep long output out of the main conversation.
Next: Part 6, a read-only MCP server in C#, so Claude Code can see your database schema without being able to change a row.