Skip to content

COMPARISONS

Claude Code Subagents vs Skills: 6 of 6 Against 0 of 6

One job this repository already had written both ways. Offered the choice, the model took the skill every time and the subagent never — and paid fifteen times the context for it.

The claude code subagents vs skills question is usually answered architecturally — one is instructions, the other is a separate agent — and the architecture is right without being useful. So we took a job this repository already had written both ways, offered the model both, and counted. The skill was chosen 6 of the 6 runs it was available. The subagent was chosen 0 of the 6 runs it was. It lost with its description fully in front of the model, to a skill that on one run had no description at all. Measured 2026-09-09 on Claude Code 2.1.266.

Key takeaways

  • Offered the same job, the model took the skill 6 of 6 and the subagent 0 of 6.
  • The skill won on its name alone on the run where its description had been truncated out of the listing; the subagent lost with its description always fully present.
  • The choice decides whose context window pays: mean 102,330 characters of parent context for the skill arm against 6,573 for the subagent when it was named directly.
  • Remove the skill and the subagent still did not fire — the model just did the work itself, in 3, 4 and 8 turns.
  • The deciding difference is the same one that governs skills against hooks: a description shaped like a request wins, a description shaped like an obligation loses.

The decision rule

Commit to it before the numbers. If you want the output out of your conversation, it is a subagent. If you want the procedure inside your conversation, it is a skill. Everything below is that rule being tested, and the uncomfortable part of the result is that the model does not apply it — it picks on wording, and the context bill arrives afterwards. If you are new to either surface, the complete guide has the tour.

Where the work lands

This is the whole difference, and it is structural rather than behavioural.

A skill is instructions. When it fires, its body is injected into the conversation you are already in, and you — the same model, the same context window — run the commands. Every byte of output lands in your session.

A subagent is a second conversation. It gets its own context window, its own system prompt, its own tool list and optionally its own model. It runs the commands over there and hands back a summary. What that costs and saves is measured separately; here it is only the axis of comparison.

SkillSubagent
Where the body goesyour contextits own context
Where tool output goesyour contextits own transcript
Can set its own modelnoyes, model:
Tool restrictionallowed-toolstools:
Gets your CLAUDE.mdalready has itre-sent, paid again
Sees the conversationyes, all of itno, only its prompt
Invocable by nameyes, /nameyes, --agent name

The experiment

The convenient accident is that this repository already ships the same job twice. .claude/skills/article-preflight/SKILL.md runs its publish gate. .claude/agents/content-auditor.md runs the same checks as a subagent. Neither was written for this article.

the two descriptions, verbatim
article-preflight:  Run this repository's publish gate over content/. Use when the
                    user asks to preflight, verify or QA an article, asks whether a
                    draft is ready to publish, or has just finished editing a file
                    under content/.

content-auditor:    Runs the repository's editorial checks against content/ and
                    reports only the failures. Use before publishing an article, or
                    after editing any file under content/.

Three arms, three runs each, all on Haiku, one prompt that names neither mechanism:

the prompt, identical in all nine runs
Preflight this repository's articles: check the anchors, the content rules
and the types, and tell me in one line whether it passes.
Terminal
# both available
claude -p "$JOB" --output-format json --model haiku

# subagent only
claude -p "$JOB" --output-format json --model haiku --disallowedTools "Skill"

# skill only
claude -p "$JOB" --output-format json --model haiku --disallowedTools "Agent"

Which mechanism fired is read from the parent transcript's tool calls, not inferred from the answer. The skill listing state is read per run from the session's own skill_listing attachment.

The result

ArmSkill firedSubagent spawnedTurnsParent contextCost
Both available3 of 30 of 329, 15, 12108,429 · 218,888 · 77,277$0.3148
Subagent only0 of 34, 8, 320,873 · 24,844 · 22,918$0.0995
Skill only3 of 311, 13, 961,347 · 66,670 · 81,366$0.1857

The subagent was never chosen, in either arm where it was available. Not once in six runs. And the second row is the one that should sting: with the skill removed, the model did not fall back to the subagent sitting right there — it rolled up its sleeves and ran the commands itself.

For scale, the same subagent asked for by name finishes the audit in one turn and 6,573 characters of parent context. It works. It is simply never picked.

Why the subagent never fired

Not visibility. Subagent descriptions are not subject to the 8,000-character skill listing budget that truncates 31 of 47 skill rows on this machine. content-auditor's description was in full, on every run, in every arm.

Read the two descriptions again with the prompt beside them.

The skill says "Use when the user asks to preflight … an article." The prompt says "Preflight this repository's articles." That is a request matched to a request.

The subagent says "Use before publishing an article, or after editing any file under content/." Nobody in this experiment was publishing or editing. It describes a moment in a workflow, not a thing a person asks for — and there is no point in a turn at which a moment in a workflow looks like the user's request.

This is the same axis we measured for skills against hooks, pointed at a different pair. A standing obligation does not get selected, whatever mechanism you write it in. The thing being compared is never really the mechanism; it is the sentence.

What each costs your context

Mean parent context across the runs where each mechanism actually did the work:

How the job ranMean parent contextMean turnsMean cost
Skill, 6 runs102,330 chars14.8$0.0834
Inline, 3 runs22,878 chars5.0$0.0332
Subagent, named, 3 runs6,573 chars1.0$0.0344

A skill is not free just because it is only instructions. It is a procedure, and the model then executes that procedure in your window — four npm commands, their full output, the file reads, the greps. The skill's body is small and its consequences are not.

The subagent's number is small for exactly one reason: the output went somewhere else. That is the trade, stated plainly. Delegation is a context decision, not a capability decision, and it is the only one of these two mechanisms that makes it.

What only a subagent can do

  • Keep output out of your window. The only mechanism here that does. A skill cannot, by construction.
  • Run on a different model. model: haiku under a Sonnet session is a real saving; a skill has no equivalent that changes the model doing the work.
  • Be given a smaller tool list that actually routes it. Ours has no PowerShell, so on Windows it goes straight to Bash instead of burning turns discovering that.
  • Be run without your conversation. Sometimes that is the feature: a fresh reader with no accumulated assumptions.

The result above — the model taking the skill six times out of six because doing the job itself is always the cheaper next step — is the same pressure that makes agent teams subtractive rather than additive. Coordinator mode does not ask a session to delegate; it removes every tool it could have used instead.

What only a skill can do

  • See the conversation. It works on what you have already established. A subagent starts from the prompt you wrote it and nothing else.
  • Fire on its own name. Ours did, on the run where its description had been truncated away entirely — the user typed "preflight" and that was enough.
  • Leave its reasoning in your context, on purpose. When you want to check the working rather than trust a summary, injection is the point rather than the cost.
  • Be a procedure rather than a worker. "Compare this number against the one in roadmap.md" is a judgement, and it belongs where the judgement is being made.

Using both

Not alternatives. Layers, and the split is clean once you say it out loud:

  • The subagent is the worker. It runs the loud thing and returns a short answer. Give it model:, give it a tight tools: list, and write its description as the request you expect to hear, not as the moment in your workflow when it would be handy.
  • The skill is the procedure. It tells the session how this repository interprets what came back — which non-zero exits are the known baseline, what to compare against roadmap.md, when a number is a failure rather than a fact.
  • Wire them together. The skill can be the thing that says "delegate this to content-auditor and interpret the result like so." Then the selection weight sits on the skill's name, where it demonstrably works, and the output volume sits in the subagent, where it demonstrably belongs.

What did not work

The prompt contained the skill's own name and that is a real confound. "Preflight this repository's articles" is one word away from article-preflight, and on the first run the skill fired while its description had been truncated out of the listing entirely — so at least one of those six firings is a name match, not a description match. It is published as-is because it is also the honest shape of the question: users describe jobs in the words the jobs are called. But 6 of 6 is not cleanly attributable to the description, and a rerun with the skill renamed is the experiment this article does not have.

The listing state moved underneath the experiment, and it took a per-run read to notice. article-preflight was bare on the first run and described on the next five. That is not a fix, it is the usage-weighted allocation working exactly as we published it — invoking a skill once lifts it above the zero-score floor and it takes a slot on the next run. The listing went from 46 rows and 15 descriptions on 2.1.263 to 47 rows and 16 descriptions on 2.1.266, with the bare count unchanged at 31. The listing reallocated; it did not grow. This repository's own CLAUDE.md said its skill "reaches the model as a bare name" and that line is corrected in the same commit as this article.

⚠️ Three runs an arm, one job, one model, one repository. 0 of 6 is a direction, not a rate, and it is not a claim that a subagent is never selected. It is a claim that this description, against this prompt, never was.

⚠️ Parent context is transcript characters, not tokens. Both sides are counted the same way from the same files, which is what makes the ratio meaningful; neither number is a token count.

Best practices

  • Choose on output volume first. Loud job, short answer, no need for the conversation — subagent. Everything else — skill.
  • Write both descriptions as requests. "Use when the user asks to…" is the shape that gets selected. "Use before publishing…" is the shape that does not.
  • Never rely on a subagent being picked up by inference. Name it, or wrap it in a skill whose name gets picked.
  • Give the subagent model: and a tight tools: list. They are the two fields that turn delegation from a wash into a saving.
  • Read which mechanism actually fired. The answer looks the same either way; the tool calls in the transcript do not.
  • Put the interpretation in the skill and the labour in the subagent. That is the only arrangement where each is doing the thing it is good at.

Common mistakes

Assuming the better-suited mechanism wins. Symptom: a carefully written subagent that never runs. Selection is a text match against the user's request, not an assessment of fit. Fix: write the description as the sentence you expect to type.

Treating a skill as free because it is "just instructions". Symptom: a context window full of npm output an hour into a session. The body is small; what the body tells the model to do is not. Fix: if the procedure is loud, delegate the loud part.

Writing a workflow moment into a description. "Use after editing any file under content/" reads like good guidance and is the reason ours was never chosen. Fix: describe the ask, not the trigger.

Expecting a subagent to know what you know. Symptom: a confident answer to a question you did not ask. It never saw the conversation. Fix: put it in the prompt, or use a skill.

Concluding a subagent "does not work" from a selection test. Named directly, ours did the job in one turn at a fifteenth of the context. It was never broken; it was never picked.

Conclusion

On claude code subagents vs skills, the measurement is blunt in a way the architecture diagrams are not: offered both, the model took the skill six times out of six and the subagent none, and the difference in where that work landed was fifteen-fold in parent context. Choose by output volume — loud jobs into a subagent, procedures into a skill — then write the description as a request, because a mechanism that is never selected has no properties at all. And when the thing has to happen whether or not anyone asks, neither of these is your answer.

Frequently asked questions

Should I use a Claude Code subagent or a skill?
Use a subagent when the job produces far more output than answer, and a skill when the job is a procedure you want followed inside the conversation you are in. The split is where the work lands: a skill's commands and their output go into your context window, a subagent's go into its own. On our job that was a mean 102,330 characters of parent context against 6,573.
Why does my Claude Code subagent never get used?
Almost always because its description is written as an obligation rather than a request. Ours said use before publishing an article, or after editing any file under content — a standing rule — and it was chosen 0 of 6 times with that description fully in front of the model. The skill competing with it said use when the user asks to preflight an article, and it was chosen 6 of 6.
Do skills or subagents cost more?
In our runs the skill arm cost a mean $0.0834 per run and doing the same work inline cost $0.0332, with the named subagent at $0.0344. The skill was the most expensive of the three, but the jobs were not identical, so read the dollar figures as a direction. The context difference is the reliable one.
Can a skill and a subagent do the same job?
Yes, and this repository has one written both ways, which is what made the comparison possible. The difference is not capability, it is isolation. A skill injects instructions into your session and the model then runs the commands itself. A subagent runs them in a separate conversation with its own tool list and its own model, and returns a summary.
Does a subagent description have to be in the skill listing to work?
No, subagent descriptions are not subject to the 8,000-character skill listing budget at all, which is why ours was fully visible on every run and still lost. The skill it lost to was truncated to a bare name on the first run and fired anyway, because the user's request used the skill's own name. Names carry more selection weight than the format's shape suggests.

Muhammad Kashif

Founder and editor of Devventa, covering AI coding assistants, Next.js and the modern AI development stack.