I used to type claude and raw dog it. It worked, but I was leaving most of the tool on the table. Since it’s the future for the next year at least, might as well learn it end to end. Anthropic has a cool guide on how to use claude - these are my notes while learning.


the agentic loop

When you type a prompt, Claude Code doesn’t just generate text and hand it back. It enters a loop.

Read files -> Think/Plan -> Write code -> Run it -> Check output -> loop back or finish.

I asked it to add a dark mode toggle to this blog. It read my CSS, read my layout template, planned the approach, wrote the JS, ran bundle exec jekyll serve, saw a missing import, fixed it, ran again, verified. Seven tool calls. I typed one sentence.

Claude Code is a while loop with a language model as the condition, not an autocomplete.

The core tools it has access to:

  • Bash - run any shell command
  • Read - read files
  • Write/Edit - create or modify files
  • Grep/Glob - search code by content or filename pattern
  • Agent - spawn sub-agents for parallel work

That’s it. Everything Claude does is some combination of these. When it “understands your codebase”, it’s just doing a lot of reads and greps. When it “fixes a bug”, it’s editing a file and running the test suite. No magic.

The quality of the output depends on the quality of the loop, not the quality of the prompt. A vague prompt with good CLAUDE.md and clear project structure will outperform a perfect prompt in a messy repo.


CLAUDE.md is your steering wheel

Single biggest upgrade you can do: writing a proper CLAUDE.md.

CLAUDE.md is a markdown file that gets loaded into every conversation as part of the system prompt. It persists across sessions. Every rule you put here saves you from repeating yourself forever.

There’s a hierarchy:

  1. ~/.claude/CLAUDE.md - global, applies to everything
  2. project/CLAUDE.md - project-level, overrides global
  3. project/src/CLAUDE.md - subdirectory-level, most specific wins

What to include:

# Build & Run
- `bundle exec jekyll serve` to preview the blog
- `uv run pytest` for tests
- `ruff check --fix` before committing

# Conventions
- Always use uv, never pip
- Python 3.12+, type hints on public functions
- No emojis in code or output
- No em dashes - use hyphens or colons

# File Structure
- _posts/ for blog posts
- _manim/ for animation scripts
- assets/images/ for static assets

# Rules
- Read files before modifying them
- Run tests after code changes
- Never commit .env files

What NOT to include: your entire codebase architecture, vague instructions like “write clean code”, documentation dumps. Keep it actionable. If Claude can’t use it to make a concrete decision, it doesn’t belong.

The /init command generates a starter CLAUDE.md by scanning your repo. It’s decent as a starting point, but you’ll want to customize it heavily.

CLAUDE.md is a system prompt that persists across every conversation, not a README. Treat it like config.

I have a global rule that says “always use uv, never use pip”. I haven’t had to say that in a conversation since. Same for “no emojis” and “no em dashes”. These tiny constraints compound.

Boris Cherny (who created Claude Code) shared how he uses it - someone even made howborisusesclaudecode.com. His key insight: the team shares a single CLAUDE.md checked into git, and everyone contributes to it. Any time Claude makes a mistake, they add a note so it doesn’t repeat it. Over time it becomes institutional knowledge. He runs 10-15 concurrent sessions and uses Opus for everything - says it requires less steering than Sonnet, so it ends up faster despite being larger.

/model opusplan uses Opus for planning and Sonnet for execution. Saves tokens; works well.


the context window is a budget

200k tokens sounds like a lot. It’s not.

Every file read, every tool output, every error message, every git diff eats context. A medium codebase tour can burn 50k tokens before you even start the real work. When context fills up, Claude compresses old messages - your early instructions get fuzzy.

5.0% of 200k context used on file reads alone
  • /compact when the conversation gets long. It summarizes earlier context and frees up space.
  • Break big tasks into focused sessions. One session per feature, not a 3-hour marathon. I used to do marathon sessions. Way worse results.
  • Write specs in files. claude "read SPEC.md and implement it" beats 500 words of instructions pasted into chat. The spec stays in the file system, not the context window.
  • --resume to continue where you left off. Picks up the previous conversation so you don’t re-explain everything. You can rename sessions too.
  • Plan mode for architecture, fresh sessions for implementation. Use /plan to think through an approach, then start a new session to actually build it.

The mental model: every token in the context window is a dollar. File reads are expensive. Verbose error outputs are expensive. Repeating yourself because Claude forgot your earlier instruction is expensive. Budget accordingly.


remote control from your phone

This one changed how I work. /remote-control (or claude --remote-control) gives you a URL and QR code. Scan it with your phone, and you can manage the session from the Claude mobile app or claude.ai/code. Great for coffee and cigarette breaks.

Your local machine keeps doing all the work - filesystem, tools, MCP servers, everything stays local. The phone is a remote viewer. I’ve edited training hyperparameters while walking outside. Some might say I need to get a life.

There’s also /remote-env for web-based remote environments - lets you pick your default cloud environment and even “teleport” web sessions back to your terminal to continue locally. The combination of --resume and remote control means you never lose a session.


skills, hooks, and MCPs

Three extension points:

skills

Skills are on-demand instructions activated via slash commands. Save a markdown file in .claude/skills/ and it becomes a command - /review-pr, /deploy, whatever you need. They only load when triggered, so they don’t eat context sitting in your system prompt. Thariq (who works on Claude Code) says they’ve become the primary way teams standardize workflows. More on the full SKILL.md format below.

hooks

Hooks are shell commands that fire on events - before or after tool calls.

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "ruff check --fix \"$CLAUDE_FILE_PATH\" 2>/dev/null || true"
          }
        ]
      }
    ]
  }
}

That goes in .claude/settings.json. Now every time Claude writes or edits a Python file, ruff check runs automatically. Catches lint errors before they compound. I also have one that runs prettier on any JS/TS file write.

Hooks let you enforce quality gates without spending context tokens on “remember to lint” instructions.

MCPs

Model Context Protocol servers give Claude new tools - web search, database access, external APIs. They’re tool servers that speak a standard protocol.

I wrote about MCPs in detail here. The short version: I use exa for web search and context7 for up-to-date library docs. When Claude needs to look something up, it calls the MCP instead of hallucinating an answer.

MCPs are the most powerful extension point, but also the most setup. Skills and hooks cover 80% of what you need.


skills.md - making claude an expert

I used to paste the same PR review checklist into every conversation. Five lines, every time. That’s tokens wasted on repetition - the exact problem CLAUDE.md solves, except I didn’t want this checklist loading into every conversation. I wanted it on demand.

A skill is a directory with a SKILL.md file. YAML frontmatter for config, markdown for instructions. You can drop supporting files next to it - checklists, templates, examples - and Claude loads them only when referenced.

---
name: deploy
description: Deploy the application to production
disable-model-invocation: true
allowed-tools: Read, Bash, Grep
argument-hint: [environment]
context: fork
---

Deploy to $ARGUMENTS.

1. Run the full test suite
2. Build the production bundle
3. Run the deploy script for the target environment
4. Verify the health check endpoint
5. Post to #deployments channel

If any step fails, stop and report. Do not retry automatically.

disable-model-invocation: true means only I can trigger /deploy - Claude can’t decide to ship on its own. allowed-tools restricts what the skill can access. context: fork runs it in an isolated subagent so it doesn’t eat your main context. $ARGUMENTS gets replaced with whatever you type after the command.

Skills can also be invisible to you by design.

Setting You invoke Claude invokes
default yes yes
disable-model-invocation: true yes no
user-invocable: false no yes

Set user-invocable: false and the skill becomes invisible to you. Claude loads it automatically when your request matches the description.

---
name: api-patterns
description: REST API conventions for this codebase. Use when writing or reviewing API endpoints.
user-invocable: false
---

When writing API endpoints in this codebase:
- Use snake_case for all JSON fields
- Return { "data": ..., "error": null } on success
- Return { "data": null, "error": { "code": "...", "message": "..." } } on failure
- Always validate request body with pydantic models
- Rate limit: 100 req/min per API key

You never type /api-patterns. You say “add a new endpoint for user profiles” and Claude loads it because the description matched. It’s a CLAUDE.md that only activates in context. I have a few of these now - API patterns, error handling conventions, test structure, database naming. Claude just knows about them when it needs to.

One more trick. Skills can run shell commands before Claude sees them:

---
name: pr-summary
description: Summarize the current pull request
context: fork
---

## context
- diff: !`gh pr diff`
- comments: !`gh pr view --comments`
- changed files: !`gh pr diff --name-only`

Summarize this PR. Focus on what changed, why, and what reviewers should look at.

The ! commands execute first. Claude gets the actual diff, not the shell commands. I use this for anything that needs live data - PR summaries, deployment status, test results.

Skills live at three levels (highest priority wins): .claude/skills/ in the project, ~/.claude/skills/ globally, and in plugins. I keep team skills in the repo and personal ones in ~/.claude/skills/.

Skills are conditional system prompts with tool restrictions, argument handling, and isolation - not prompts. Treat them like functions.


agents.md - specialized workers

I had Claude review a PR and fix the bugs it found, all in one session. The review was worse because of it - it rushed to solving instead of analyzing. So I made a reviewer agent that can read code but can’t edit it. The reviews got sharper immediately.

An agent is a markdown file in .claude/agents/. Its own system prompt, its own tool access, optionally a different model.

---
name: code-reviewer
description: Reviews code for quality, security, and maintainability. Use after writing or modifying code.
tools: Read, Grep, Glob, Bash
model: sonnet
---

You are a senior code reviewer.

When invoked:
1. Run git diff to see recent changes
2. Focus on modified files
3. Review immediately

Checklist:
- Clear and readable code
- Well-named functions and variables
- No duplicated logic
- Proper error handling
- No exposed secrets
- Input validation at boundaries
- Test coverage for new code

Organize feedback by priority:
- Critical (must fix before merge)
- Warning (should fix)
- Suggestion (nice to have)

Include specific code references with file:line format.

No Write. No Edit. The reviewer can’t “fix” things for you - it can only read and report. That constraint makes it better at the job. I run it on Sonnet because reviews don’t need Opus and it’s faster.

You can invoke agents by just describing what you need (“review my changes”), by @-mentioning (@"code-reviewer (agent)" check the auth module), or session-wide with claude --agent code-reviewer.

The most underrated part: agent memory. Set memory: project and the agent persists what it learns to .claude/agent-memory/. I have a researcher agent that’s been building up knowledge about this blog’s codebase for weeks:

---
name: researcher
description: Deep codebase exploration without making changes
tools: Read, Grep, Glob
memory: project
---

You are a codebase researcher. Analyze code and provide insights.
Never modify files. Focus on understanding architecture,
data flow, and dependencies.

As you discover patterns and conventions, save them to your
agent memory for future reference.

Read-only, gets smarter over time. Since the memory writes to .claude/agent-memory/, you can commit it to git. The accumulated knowledge carries across the whole team.

You can also inject skills directly into an agent’s system prompt:

---
name: api-developer
description: Implement API endpoints following team conventions
skills:
  - api-patterns
  - error-handling
---

Implement API endpoints. Follow the conventions from the preloaded skills exactly.

The agent starts with the full skill content loaded. No discovery step, no hoping it finds the right conventions on its own.

Built-in agents for reference:

Agent Model Tools Purpose
Explore Haiku Read-only Fast codebase search
Plan Inherits Read-only Architecture planning
general-purpose Inherits All Complex multi-step tasks

Subagents work best with a narrow job and restricted tools. A reviewer that can’t edit code gives better reviews than one that can - it has no choice but to actually read.


plugins - packaging it all up

After I’d built up skills and agents I liked, I wanted them in every repo without copying files around. That’s plugins.

A plugin is a directory with a manifest, bundling skills, agents, hooks, and settings into one unit:

my-plugin/
  .claude-plugin/
    plugin.json
  skills/
    review-pr/
      SKILL.md
  agents/
    security-reviewer.md
  settings.json
{
  "name": "team-standards",
  "description": "Code review and security standards for the team",
  "version": "1.0.0"
}

Test locally with claude --plugin-dir ./my-plugin. Skills get namespaced automatically - /team-standards:review-pr instead of /review-pr - so two plugins can both have a “review” skill without colliding.

Over copying files, you get: versioning with semver, namespacing to prevent collisions, and install instead of “copy these 12 files to the right directories”. One gotcha - plugin agents can’t use hooks, mcpServers, or permissionMode for security reasons. If you need those, copy the agent to .claude/agents/ locally.

Plugins are the distribution layer: skills are what you teach Claude, agents are what Claude becomes in a given context.


stop writing slop

Claude’s default writing voice is terrible. Hedging, filler, corporate buzzwords everywhere.

I noticed the pattern: every commit message started with “Refactor”. Every code comment said “This function handles”. Every response began with “Certainly!” or “I’d be happy to help!”. Same adverbs everywhere - “arguably”, “notably”, “essentially”.

The fix is in CLAUDE.md. Ban the patterns explicitly:

# Writing Rules
- Never say: certainly, I'd be happy to, great question, delve,
  utilize, leverage, comprehensive, robust, streamline, arguably,
  essentially, notably, facilitate
- No hedging: remove "I think", "perhaps", "it seems like"
- No filler: remove "In order to", "It's worth noting that",
  "As mentioned earlier"
- Be direct. State facts. Skip the preamble.
- Code comments: say WHY, not WHAT
- Commit messages: what changed and why, no "Refactor" prefix

Check out stop-slop - it’s a skill that evaluates prose quality and flags exactly this kind of thing.

The model writes what you let it write. No constraints means the default, and the default is slop.


how I actually use it

The actual workflow I use now for any non-trivial task:

  1. Write a spec in a file. Clear description of what needs to happen.
  2. Point Claude at it. claude "read PRD.md and improvise it" or describe the task directly if it’s simple.
  3. Use plan mode for anything touching 3+ files. /plan first, review the approach, course-correct early.
  4. Let it execute. Watch the tool calls. Let the agents handle reviews. Verify with tests.
  5. Iterate in focused sessions. /compact when context gets heavy. --resume to continue.

Surprising how good it is. This blog on how to use Claude was written by Claude.

Claude Code is the best programmer in the world, after Karpathy.


<
Previous Post
building pytorch from scratch
>
Next Post
from residual connections to attention residuals