how to write Claude Code skills
Table of content
by Ray Svitla
a Claude Code skill is a text file that changes how Claude behaves. that’s it. no API, no SDK, no build step. you write instructions in markdown, put them in the right place, and Claude reads them.
the simplicity is the feature. it’s also why 80% of published skills are mediocre — people think “just write some instructions” means “dump your stream of consciousness into a file.” it doesn’t.
here’s how to write skills that actually work.
the anatomy of a skill
a skill lives in a directory with at least one file: SKILL.md. optional extras include example files, MCP server configs, and custom slash commands.
my-skill/
├── SKILL.md # required: the instructions
├── examples/ # optional: example usage
├── .mcp.json # optional: MCP server configs
└── commands/ # optional: slash commands
└── do-thing.md
SKILL.md format
the SKILL.md file has two parts: frontmatter (metadata) and body (instructions).
---
name: "code-reviewer"
description: "rigorous code review with security focus"
version: "1.0.0"
author: "your-name"
tags: ["code-review", "security", "quality"]
---
# code review skill
when asked to review code, follow this process:
## step 1: security scan
- check for SQL injection, XSS, CSRF vulnerabilities
- flag any hardcoded secrets or API keys
- verify input validation on all user-facing endpoints
## step 2: logic review
- trace the data flow from input to output
- identify edge cases the code doesn't handle
- check error handling completeness
## step 3: style and maintainability
- flag functions longer than 50 lines
- check naming consistency
- identify duplicated logic
## output format
present findings as:
- CRITICAL: security issues or data loss risks
- WARNING: bugs or missing edge cases
- NOTE: style or maintainability suggestions
writing good instructions
be specific, not aspirational
bad:
write high-quality, maintainable, well-tested code
good:
- every function must have a JSDoc comment with @param and @return types
- extract functions longer than 30 lines into smaller units
- add error handling for every async operation: catch, log, re-throw with context
the first is a wish. the second is a checklist Claude can actually follow.
provide the alternative, not just the prohibition
bad:
never use any for TypeScript types
good:
never use `any` for TypeScript types. use `unknown` for truly unknown types,
then narrow with type guards. if you're typing a third-party library response,
create an interface in types/ that matches the actual shape.
Claude needs to know what to do instead. negative-only constraints create dead ends.
use examples
the most effective skill instructions include concrete before/after examples:
## naming conventions
// bad
const d = new Date();
const arr = users.filter(u => u.active);
// good
const currentDate = new Date();
const activeUsers = users.filter(user => user.isActive);
Claude learns patterns better from examples than from rules.
scope your instructions
don’t write a skill that tries to cover everything. a skill that’s great at code review and mediocre at testing and vaguely does deployment is worse than three separate skills that each do one thing well.
building a skill from scratch
let’s build a real skill: a documentation generator.
1. create the structure
mkdir doc-generator
cd doc-generator
2. write the SKILL.md
---
name: "doc-generator"
description: "generates comprehensive documentation from code"
version: "1.0.0"
tags: ["documentation", "readme", "api-docs"]
---
# documentation generator
when asked to generate documentation, follow this process:
## analysis phase
1. read the project's package.json / pyproject.toml / Cargo.toml for metadata
2. identify the main entry points and public APIs
3. scan for existing documentation and note gaps
## README generation
structure:
- project name and one-line description
- installation (copy from package manager config)
- quick start (simplest possible working example)
- API reference (public functions/classes with types)
- configuration (env vars, config files)
- contributing (if CONTRIBUTING.md doesn't exist)
## rules
- code examples must be runnable without modification
- never document internal/private functions in the README
- use the project's existing code style in examples
- if a function has JSDoc/docstring, use it as the source of truth
- link to source files using relative paths
## API documentation format
for each public function:
\```
### functionName(param1: Type, param2: Type): ReturnType
description from docstring or inferred from implementation
**parameters:**
- `param1` — what it does
- `param2` — what it does
**returns:** what and when
**example:**
\```typescript
const result = functionName('value', 42);
\```
**throws:** list of error conditions
\```
3. add a slash command
create commands/generate-docs.md:
generate comprehensive documentation for this project.
start with the README, then document all public APIs.
follow the documentation generator skill instructions.
4. test it
# install locally
claude skills add ./doc-generator
# test on a real project
cd ~/some-project
claude
> /generate-docs
5. iterate
the first version will be wrong about something. that’s fine. run it on 3-4 different projects, note where it fails, and add specific instructions to handle those cases.
publishing your skill
push the directory to GitHub and anyone can install it:
cd doc-generator
git init
git add -A
git commit -m "initial skill"
gh repo create doc-generator --public --push
users install with:
claude skills add your-username/doc-generator
common mistakes
writing a novel. your SKILL.md shouldn’t be 5000 words. every word burns context tokens on every session. be concise.
conflicting instructions. “always write comprehensive tests” and “keep sessions fast and token-efficient” will fight each other. choose priorities.
no examples. abstract rules produce abstract results. show Claude what good output looks like.
assuming context. your skill might be installed alongside 10 others. don’t assume it’s the only instruction file Claude is reading.
no testing. install your skill and use it for a week before publishing. the gap between “instructions that make sense to me” and “instructions that make Claude do what I want” is always larger than expected.
the deeper skill
writing good Claude Code skills teaches you something that has nothing to do with Claude Code: how to write unambiguous instructions for any executor — human or machine.
the same principles apply to writing SOPs for a team, documentation for an API, or onboarding guides for new hires. be specific. provide alternatives, not just prohibitions. show examples. scope narrowly. test against reality.
if Claude Code disappeared tomorrow, the ability to write clear, actionable, testable instructions would still be one of the most valuable skills in any organization. the skill system is just a convenient forcing function for developing it.
further reading
→ plugins system architecture — how skills load and compose → best Claude Code plugins — see what good skills look like → CLAUDE.md guide — the same principles apply to project instructions → context engineering — why instruction quality matters
Ray Svitla stay evolving