# Claude Code Skills vs Subagents vs Hooks vs Workflows: Which to Use in 2026

> Source: <https://pub.towardsai.net/claude-code-skills-vs-subagents-vs-hooks-vs-workflows-which-to-use-in-2026-120db6ae5b3c?source=rss----98111c9905da---4>
> Published: 2026-09-21 21:31:01+00:00

***Key Takeaways
-*** A **skill** is text Claude reads in your window. A **subagent** is a second Claude with its own window. That one difference decides everything else.

- CLAUDE.md, rules, and skills are requests. A**hook** is a guarantee: it fires on a lifecycle event whether Claude agrees or not, and exit code 2 blocks the action.

- Slash commands are skills now.**Plugins** aren’t a primitive at all, they’re the box you ship the others in.

- A**workflow** moves the plan out of Claude’s head and into a script: up to 1,000 agents per run, 16 at a time, one result back.

- Every primitive encodes an assumption about what the model can’t do alone. Re-test that assumption after each model release, and delete what no longer earns its place.

Claude Code now has eight ways to change how it behaves, and most teams use one. In a study of 2,853 public repositories with agent configuration files, 90.6% had a context file like CLAUDE.md, 5.5% had a skill, and 4.6% had a subagent ([arXiv, February 2026](https://arxiv.org/abs/2602.14690)). Most teams wrote down their conventions and stopped there.

What follows is the decision table I wanted when I started: all eight primitives in one place, then a head-to-head for each pair people actually search for. Every example below is a file I actually run, not a hypothetical.

For months my CLAUDE.md had a line telling Claude never to touch .env. It held maybe nine times out of ten, which sounds fine until the tenth time rewrites a key I then have to rotate. I moved those four words into a PreToolUse hook that exits 2 on a path match, and the failure rate went to zero, because a hook doesn't read the instruction, it just refuses the write. The line in CLAUDE.md wasn't wrong. It was in the wrong primitive.

The official docs spread these across six comparison tabs and three pages. Here they are in one place. Read the last column first: it’s the one that tells you what you can actually rely on.

Slash commands aren’t a row because they’re skills you invoke by name. More on that below.

For the four rows that perform work, the question that separates them is **who holds the plan**. In a skill, Claude does, following your text, inside your window. In a subagent, Claude does, turn by turn, in a separate window. In an agent team, a lead session does, across peers. In a workflow, a script does, and Claude only sees the final answer. Hooks are the odd one out: nobody holds a plan. The event fires and your code runs.

Here’s what that looks like on one real job, adding a rate limiter to an API route in a Next.js SaaS. A rule says every route validates input. A skill, /new-api-route, carries the twelve-step procedure. A subagent, code-reviewer, reads the diff in its own window and returns three findings. A hook runs Prettier on every file Claude touched, without asking. Four primitives on one feature, and none of them overlap.

A skill is text Claude reads. A subagent is a second Claude.

A skill’s description sits in your context every session, capped at 1,536 characters in the docs’ skill listing, and its body loads when it’s used. Everything it does happens in your window, with your history and your tools. A subagent gets its own window, its own system prompt, its own tool list, and often its own model. It does the work and sends back a summary. That’s the whole difference, and it decides everything else: what each one costs, what it can see, and what it can break.

My code-reviewer is the cleanest example I have, because it's both at once. The subagent is the reviewer; the skills are its checklists.

```
docs/ai-context/agents/code-reviewer.md---name: code-reviewerdescription: Review code for architecture, performance, patterns, and quality.  Use proactively after implementing features or when reviewing changes.tools: Read, Glob, Grep, Bashmodel: opusskills:  - code-review  - security-reviewer---
You are a code review specialist. Review all files in the current change setfor architecture compliance, performance issues, pattern violations, and codequality. Produce a severity-based report.
```

When Claude hands a 40-file diff to this subagent, the 40 files load into *its* window. Yours gets six findings back. The two skills it preloads are the same ones you could run yourself with /code-review in the main conversation. Same checklist, different room.

So the rule is short. **Use a skill** when Claude should know something or follow a procedure: how we write API routes, the release checklist, the naming conventions. **Use a subagent** when the work produces output you won’t read again (searches, logs, review noise), or when it needs a different model or a narrower tool set than your session has. The documented defaults: 20 subagents running at once, nested up to three layers deep.

Two practitioners, same idea. Simon Willison keeps a standing instruction to “use your judgement to decide an appropriate lower power model and run that in a subagent” for coding tasks: judgment stays on the main loop, the typing gets delegated ([Willison, July 2026](https://simonwillison.net/2026/Jul/3/judgement/)). Addy Osmani’s version: subagents burn more tokens, “so spend them where a second opinion is worth paying for” ([Osmani, June 2026](https://addyo.substack.com/p/loop-engineering)). Both are buying the same thing: a clean window, and a reviewer who isn’t the author.

The bridge between the two is context: fork. Add it to a skill's frontmatter and the skill's text becomes the prompt for a subagent instead of loading into your window. One thing that trips people: a forked skill runs in the background by default, so set background: false to wait for its result. The anatomy of a SKILL.md itself, including the trick that injects a live git diff before Claude reads it, is in [the loop engineering guide](https://vibeready.sh/blog/loop-engineering-claude-code/?utm_source=medium&utm_medium=syndication&utm_campaign=claude-code-primitives#skill-state-guardrails).

Everything above is a request. Claude reads it and usually complies. A hook is not a request.

The official memory docs say it plainly: Claude treats CLAUDE.md and auto memory “as context, not enforced configuration. To block an action regardless of what Claude decides, use a PreToolUse hook instead.” Anthropic’s own guide to steering Claude Code draws the line in one sentence: “The model choosing to run a formatter is different from the formatter running automatically” ([Anthropic, June 2026](https://claude.com/blog/steering-claude-code-skills-hooks-rules-subagents-and-more)).

A hook is an entry in settings.json: an event, a matcher, and a command. The command gets the event as JSON on stdin and answers with an exit code. Exit 0 means no objection. Exit 2 means blocked, and whatever you wrote to stderr goes back to Claude as the reason. Here's the one I'd put in a SaaS repo first.

**Refuse edits to files that should never change in a session (PreToolUse)**

``` bash
#!/bin/bash# .claude/hooks/protect-files.shINPUT=$(cat)FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
for pattern in ".env" "prisma/migrations/" "terraform/"; do  if [[ "$FILE_PATH" == *"$pattern"* ]]; then    echo "Blocked: $FILE_PATH matches protected pattern '$pattern'" >&2    exit 2    # exit 2 = block it; stderr becomes Claude's feedback  fidoneexit 0        # exit 0 = no objection; the normal permission flow applies
{  "hooks": {    "PreToolUse": [      {        "matcher": "Edit|Write",        "hooks": [          {            "type": "command",            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh"          }        ]      }    ]  }}
```

Secrets, applied migrations, infrastructure. Ask Claude to “add a comment to .env” and the edit is blocked before it happens, with the script’s message handed back so Claude adjusts instead of retrying. The same shape on a PostToolUse event runs Prettier on every file Claude touches.

The gate doesn’t have to live inside the session. A git pre-commit hook or a CI check is a deterministic gate too; the difference is timing. A Claude Code hook stops a bad edit before it exists, a CI gate stops it before it merges, and most teams end up wanting both. Either way the rule is the same: conventions go in CLAUDE.md, guardrails go in hooks.

Hook output lands in context, so hooks and skills pair up naturally: the hook runs the linter, and a /fix-lint skill tells Claude how to resolve what it found.

If you learned Claude Code in 2025, you wrote commands as flat Markdown files in .claude/commands/. Those still work, but custom commands have been merged into skills. A file at .claude/commands/deploy.md and a skill at .claude/skills/deploy/SKILL.md both create /deploy and behave the same way.

What the skill form adds is a folder for supporting files and two frontmatter switches that answer the question “who is allowed to fire this?”

```
---name: deploydescription: Deploy the current branch to Cloud Run after the release checklist passes.disable-model-invocation: true     # only you can run /deploy; Claude never auto-invokes it---
```

disable-model-invocation: true is for anything with side effects: deploys, releases, database migrations. Claude can't reach for it on its own, and it costs zero context until you type it. The mirror switch, user-invocable: false, hides a skill from the / menu so only Claude can load it, right for reference material you'd never invoke by hand. The built-in commands you already use, like /loop and /goal, are the same mechanism shipped by Anthropic.

A plugin doesn’t do anything by itself. It’s a folder with a manifest at .claude-plugin/plugin.json and any mix of skills/, agents/, hooks/hooks.json, and an .mcp.json, plus newer slots for LSP servers, background monitors, and saved workflows. Install it and every part inside becomes available, with skills namespaced as /plugin-name:skill so two plugins can both ship a /review.

The trigger for building one is specific: a second repository needs the same setup. Until then, keep everything standalone in .claude/. When the day comes, claude plugin init my-kit scaffolds the manifest, and claude --plugin-dir ./my-kit loads it for testing without installing.

I don’t ship my own kit as a plugin, and the reason says something about where plugins stop. A plugin distributes to Claude Code. My kit has to work in Cursor and Windsurf too, so one make ai-setup target does the plugin's job across tools: it copies the 14 master rules into .claude/rules/, .cursor/rules/*.mdc, and .windsurf/rules/, each in that tool's frontmatter dialect, and symlinks CLAUDE.md to AGENTS.md so all of them read one core file. That's the same "same setup, every repo" promise, one layer up. Where the instruction files themselves should live is [its own decision](https://vibeready.sh/blog/agents-md-for-saas/?utm_source=medium&utm_medium=syndication&utm_campaign=claude-code-primitives#agents-md-vs-claude-md).

A subagent is one helper you send off. A workflow is a script that sends off many helpers and hands you one answer. Claude writes the script for the task you describe, you approve it, and it runs in the background while you keep working. Nothing in between lands in your window.

Workflows shipped on May 28, 2026, in v2.1.154, the same day as Opus 4.8. You start one by putting ultracode in your prompt (the trigger word was workflow until a June 1 rename) or by asking for a workflow in plain words ([Claude Code changelog](https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md)). Two numbers to keep in mind: a run can use up to 1,000 agents, but only 16 run at once. "Hundreds of parallel agents" means hundreds per run, not at the same time.

Here’s one on a problem every multi-tenant SaaS has: making sure no API route forgets to scope its queries by organization.

```
> ultracode: audit every route handler under src/app/api for Prisma queries that  skip organizationId scoping, and adversarially verify each finding
// .claude/workflows/audit-org-scoping.js  (saved from /workflows with s)export const meta = {  name: 'audit-org-scoping',  description: 'Find API routes that skip organizationId scoping, then verify each finding',}
js
const found = await agent('List every route.ts under src/app/api/.', {  schema: { type: 'object', required: ['files'],            properties: { files: { type: 'array', items: { type: 'string' } } } },})
js
const findings = await pipeline(found.files, file =>  agent(`Check ${file}: is every Prisma query scoped by organizationId? Report violations.`, { label: file }),)
js
const verified = await pipeline(findings.filter(Boolean), f =>  agent(`Adversarially verify this finding. Return null if it does not hold: ${JSON.stringify(f)}`, { label: 'verify' }),)
return verified.filter(Boolean)
```

Read it top to bottom. One agent lists the routes. One agent per route checks it. One agent per finding tries to knock it down. You get the findings that survived, and next quarter the same script runs again as /audit-org-scoping. That last step, a second agent checking the first, is what a plain subagent fan-out doesn't give you. It's the same idea as the separate judge in [my eval suite](https://vibeready.sh/blog/ai-evals-without-a-research-team/?utm_source=medium&utm_medium=syndication&utm_campaign=claude-code-primitives).

One limit to know before your first run: a workflow can’t stop to ask you anything. If you want to sign off between stages, run two workflows. And try it on one directory first, because a run can burn far more tokens than doing the task in conversation.

The biggest public example is Bun’s rewrite from Zig to Rust: about a million lines in 11 days, roughly 50 workflows, about $165,000 at API pricing, and 19 known regressions, with Bun disclosing that Anthropic had acquired it and that the work used a pre-release model ([Bun, July 2026](https://bun.com/blog/bun-in-rust)). Zig’s creator, Andrew Kelley, called the output “[a] million lines of unreviewed slop” ([The Register, July 2026](https://www.theregister.com/devops/2026/07/14/zig-creator-calls-buns-claude-rust-rewrite-unreviewed-slop/5270743)). Both facts hold. A workflow can write a million lines. Reviewing them is still your job.

The rule: a focused side task is a subagent. A job too big for a few of them, or one where you want the findings checked before you see them, or one you’ll run again, is a workflow.

A subagent works inside your session and reports back to it. An agent team is several full Claude Code sessions with a shared task list that message each other directly. Teams are experimental, off by default, and switched on with CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1. Each teammate costs a full context window, so the docs say start with three to five. Use one when the workers need to argue: five teammates trying to disprove each other's theory about a bug is a team job. A review from three angles that only needs to report back is still subagents.

Don’t pick from the feature list. Pick from the symptom. Each primitive has a moment where it becomes obvious, and here they are with the version I hit building a multi-tenant Next.js product.

The same table tells you when to *update* what you have. A repeated review comment is a rule edit, not another chat correction. A skill you keep tweaking by hand needs another revision. And a rule Claude follows fine without the rule is a rule to delete.

Most repos need three of the eight, arranged so each one carries a different kind of knowledge. Rules hold conventions, one topic per file, scoped to the paths they describe. Skills hold procedures, named after the verb. Subagents wrap a skill or two in a narrower tool list and their own window. A hook or two guards the few things that must never happen. That’s the whole setup, and it fits in four places.

```
.claude/├── rules/database.md               # "every query is scoped by organizationId"; loads with the data layer├── skills/new-api-route/SKILL.md   # the twelve-step procedure, run as /new-api-route├── agents/code-reviewer.md         # preloads code-review + security-reviewer; read-only tools; own window└── settings.json                   # the protect-files hook from above
```

The order you add them matters less than keeping each thing in its lane. A convention that ends up in a skill gets skipped whenever the skill isn’t invoked. A procedure that ends up in CLAUDE.md costs context on every request. A guardrail that lives anywhere but a hook is a suggestion. This is the arrangement I ship, and it works the same whether you write it yourself or start from a kit; rules and skills carry over to [Cursor and Windsurf](https://vibeready.sh/build-saas-with-ai/?utm_source=medium&utm_medium=syndication&utm_campaign=claude-code-primitives), subagents are Claude Code only.

Every rule, skill, and subagent you add is a patch for something the model couldn’t do reliably at the time. Models improve. A setup that only ever grows ends up carrying patches for problems that are gone: a rule that costs context on every request to prevent a mistake the model stopped making, or a review subagent that costs tokens to catch what the model now gets right the first time.

Anthropic’s own harness team hit this at scale. Their setup added a second agent to judge the first one’s work, because agents asked to grade themselves “tend to respond by confidently praising the work.” It helped, but the full arrangement cost over twenty times a solo run, $200 against $9, and only paid off on tasks the model couldn’t do alone. When Opus 4.6 arrived, they deleted an entire component the new model no longer needed ([Anthropic Engineering, March 2026](https://www.anthropic.com/engineering/harness-design-long-running-apps)).

The practical version is simple. After each model release, pick one rule or skill and turn it off for a week. If nothing regresses, delete it. Your [harness](https://vibeready.sh/blog/what-is-harness-engineering/?utm_source=medium&utm_medium=syndication&utm_campaign=claude-code-primitives#how-harness-engineering-works) should get smaller as the model gets better, and the only way to know whether yours does is to test it.

**Official docs for each primitive**

[Skills](https://code.claude.com/docs/en/skills) · [Subagents](https://code.claude.com/docs/en/sub-agents) · [Hooks reference](https://code.claude.com/docs/en/hooks) · [Plugins](https://code.claude.com/docs/en/plugins) · [Dynamic workflows](https://code.claude.com/docs/en/workflows) · [Agent teams](https://code.claude.com/docs/en/agent-teams) · [CLAUDE.md and rules](https://code.claude.com/docs/en/memory) · [Extend Claude Code (the official comparison)](https://code.claude.com/docs/en/features-overview)

[Claude Code Skills vs Subagents vs Hooks vs Workflows: Which to Use in 2026](https://pub.towardsai.net/claude-code-skills-vs-subagents-vs-hooks-vs-workflows-which-to-use-in-2026-120db6ae5b3c) was originally published in [Towards AI](https://pub.towardsai.net) on Medium, where people are continuing the conversation by highlighting and responding to this story.
