Write it down once
Planning before diffing, and building a fence that teaches you the rule while it's stopping you.
The question I get about Claude Code is almost always some version of “okay but what do you type.” Which is fair. The interface is a text box, so of course that’s where you look. But the typing is genuinely the least interesting part of my setup, and has been for a while.
The method, if it deserves that word, is one sentence: every time I caught myself explaining the same preference twice, I stopped explaining it and put it in a file the model reads on its own. Everything below is just where I put things.
Plan mode
Start here, because it’s free and it’s the one thing I’d push on anybody.
Shift+Tab twice and the model can read but not write. It goes off, looks around, and comes back with a plan instead of a diff.
The value isn’t that the plan is better than what it would have done anyway. Sometimes it is, often it’s identical. It’s that a plan is cheap to argue with and a diff isn’t. When I read a plan and the third bullet says it’s adding an abstraction I don’t want, I type “no, put it in the existing helper” and that costs me nine seconds. When I meet that same decision as a 200-line diff across four files, I either accept it or make it A Whole Thing, and now I’m negotiating with sunk cost on both sides of the conversation.
So: plan mode for anything I can’t hold in my head, which is most things, and straight to it for “rename this variable.”
The part I didn’t expect is that it’s also a read on whether I know what I’m asking for. If the plan comes back and I can’t tell whether it’s right, that isn’t a model problem. That’s me not having decided yet, and finding that out before any code exists is kind of a gift.
A plan is cheap to argue with and a diff isn't.
/commit runs /simplify first
This is my favorite one, and it’s about four sentences of configuration.
I have a commit skill, because I got tired of relitigating message conventions (imperative subject, body explains why and not what, no British spellings sneaking in). Standard stuff. The interesting bit is what I bolted onto the front of it: before it stages anything or writes a word, it judges whether the diff is substantial enough to warrant a cleanup pass, and if it is, it runs /simplify and those edits land in the same commit.
Use judgment, this is a gate, not a reflex:
- Run simplify when the diff adds or reworks real logic: new functions
or classes, non-trivial control flow, a multi-file change, or more
than a couple dozen substantive lines.
- Skip it for trivial or mechanical changes: typos, config bumps,
lockfiles, pure renames, formatting-only diffs, one-liners.
- When it's borderline, lean toward running it.
Two things make this work, and I think the second one is the actual insight.
The first is what simplify catches. A model’s first draft tends to be correct and slightly too much: an extra layer of indirection, a helper that duplicates one three files over, a function sitting a level lower than it needs to. None of those are bugs. That’s exactly why they survive review, mine included, and it’s how a codebase gradually turns into something nobody volunteers to open.
The second is when. I could run simplify whenever I like. I never would. The moment before committing is the only moment I reliably still have the diff loaded in my head and haven’t already started thinking about the next thing. Attention is the scarce resource, not compute, so the cleanup has to be welded to a thing I’m already doing.
And the gate matters as much as the pass. An early version ran it on everything and it was insufferable. You bump a version number and something spends 40 seconds contemplating your package.json.
A hook that won’t let me cheat
Here’s the problem with skills: a skill is a document sitting on a disk. The failure mode isn’t that it’s wrong, it’s that in a hurry somebody (the model, or me) just writes a plain git commit and none of it happens.
So there’s a PreToolUse hook. Every git commit gets inspected and blocked unless the command carries a marker.
# Skill-emitted commit, carries the marker.
if printf '%s' "$command" | grep -qE 'CLAUDE_SKILL=commit([^A-Za-z0-9_-]|$)'; then
exit 0
fi
The prefix does nothing. It sets an env var for the git process and never touches the commit itself. It’s a token, and the only thing it proves is that the skill got loaded.
The nice part, which I would love to claim I designed on purpose: the deny message never says what the marker is. So the only route past the fence is to go read the skill, at which point you’ve read the conventions anyway. The fence teaches you the rule while it’s stopping you. Amends and fixups pass straight through, because the history-rewriting skills need them and they aren’t new commits.
There’s a sibling hook for gh pr create, and one that checks git identity, because I have a work email and a personal one and my projects are emphatically not sorted by directory (work repos live under ~/Herd right next to personal ones). Deciding by path would be wrong maybe a tenth of the time, which is exactly often enough to bite and rare enough that I’d stop checking.
Permissions themselves I leave on auto, and the deny list is two entries, both docker prune. Make of that what you will.
Design somewhere else, then hand over the artifact
The site you’re reading this on was designed in Claude Design and built by Claude Code, and I tried hard to keep them out of each other’s business.
What Claude Design gives you isn’t a picture of a website, it’s a design system: tokens, plus a set of components with real style objects behind them. So the handoff to Claude Code isn’t “here’s a vibe, please match it.” It’s “here is a file, transcribe it.” The token blocks at the top of my main.css are a verbatim copy of the design system’s token files, and each component’s CSS is a transcription of that component’s style object, one BEM block each. Both rules are written into the project’s CLAUDE.md, along with the one that actually enforces them: read the component, never work from memory.
Why bother, when you could just ask for a nice landing page? Because you’ll get one. And then the next page will be nice in a slightly different way, and by page four you have four color scales and a spacing system that’s really a rumor. Doing the design once, upstream, means each decision gets made a single time and then persists.
It also changes the shape of the failures, which I care about more than I expected to. When the build is wrong now, it’s wrong in a checkable way: this padding doesn’t match that token. That’s a bug with a correct answer, instead of a taste argument I’m having with a computer at 11 at night.
The CLAUDE.md is mostly scar tissue
Everyone’s first CLAUDE.md is preferences, and mine is too. American English. BEM. Alphabetize CSS declarations. A whole little ordering for HTML attributes, with an exception for when the class value is a wall of Tailwind and belongs at the end instead. Useful, boring, saves me re-typing myself.
The part that actually earns its place is the gotchas. Every entry is something that already cost me an hour, written down so it costs zero hours forever. My Jekyll site’s file has a note that assign inside an include leaks into the caller and survives into the next iteration of the enclosing loop, which is why every project card was quietly inheriting the previous project’s tag color. I would not have found that twice. I barely found it once.
Same logic covers the voice stuff. I keep one skill that holds my work register and another that holds this one, because “no em-dashes, no ‘the bottom line,’ keep the hedging” is not a thing I want to type into a chat box every time I sit down. (Yes, I’m aware of what that implies about this post. The opinions are mine, the American spellings are not.)
The peons
Not a tip, just true. Every hook event in my setup also plays a Warcraft III peon line. Session start, tool failure, permission request, subagent spawn. “Work work.” “Something need doing?” “Me not that kind of orc.”
It’s completely unserious and it’s staying. An audio channel for “the thing you started 90 seconds ago now wants something from you” is legitimately useful once you’ve context-switched into Slack, and the fact that it’s delivered by a small green man is orthogonal to that. There’s also an exercise-logging mode attached to it that I decline to justify here.
The transferable bit
None of this is clever. It’s just noticing you’ve repeated yourself and then spending ten minutes. The setup looks deliberate written out like this, but it accumulated over months, one annoyance at a time, and about a third of it exists because I was annoyed at 6 pm on a Friday.
Take the specifics with a grain of salt, obviously. This is sanded to fit my hands, my projects, and my particular neuroses about commit messages. The transferable bit is smaller and duller than the config: when you find yourself typing the same instruction a second time, that’s the signal.
Stop typing it. Go put it in a file.