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

Topics: claude-code skills plugins tutorial