How I work with AI
I use Claude Code every working day. Most of what makes it useful is not in the model — it is in what you give it before it starts, and in refusing to let it make the decisions that are yours to make.
I design the work. The agent does the typing. Everything below exists to stop that arrangement from quietly reversing.
The work in question
You are looking at it. Open this page on a phone: the banner at the top carries the portrait, the name and the role, and it shrinks as you scroll past it. The navigation is a floating pill above the bottom edge. The theme control is a round button over the top-right corner.
That afternoon produced two new components in my design system — a tab bar and an icon button, each with stories, unit tests and an axe run — and five decisions I had to make, and wrote down. By hand it would have been two days.
The speed is not the interesting part. The interesting part is that the architecture is the one I would have reached alone, because I chose it: one question at a time, with the alternatives written out and the losing options recorded next to the winners.
I start by talking, not typing
Claude Code takes voice input, and I use it for the opening message of almost every piece of work. This is not about words per minute. It is that a typed instruction and a spoken one are different in kind.
When you type, you economize. You write add a bottom nav on mobile and stop, because the rest feels like effort. When you talk, you keep going — and what comes out in those extra thirty seconds is exactly the part that matters: the reason, the constraint, the thing you do not want, the place it has to fit. Specification is the expensive half of software, and speech is the cheapest way to produce it.
Here is what I actually sent to start the work above:
Let's make a modification of the site's behavior on small
screens: make the banner with my profile name and picture
sticky at the top, and move the About / Work / CV /
Accessibility tabs to a floating navbar at the bottom.
Move the theme switch to a floating round icon button at
the top right corner.
Remember to componentize all visual modules in the scorpius
design system.
Let me take all technical, design and architecture decisions
by choosing from a list of alternatives. Explain to me in
great technical detail what each alternative is about.Three things are doing the work there. A description of the end state rather than of the steps. A constraint about where the code belongs — the design system, not the page. And an instruction about who decides, which is the one I care about most.
Before anything: what does the repository say about itself?
The quality of what an agent produces in a repository is bounded by how well the repository explains itself. Which commands to run, and for what. Where the tests live. Which version is pinned on purpose and must not be helpfully upgraded.
That goes in CLAUDE.md, a file at the root that is loaded into context at the start of every session. Mine are short and almost entirely made of traps — the things a competent engineer would get wrong in their first week, where the mistake stays invisible until late. From one of my own projects:
## Every shell command needs the Node PATH prefix
The shell defaults to Node 22.12.0, which pnpm 11.9 rejects.
Prefix node/pnpm/npx commands with the pinned runtime:
export PATH="$HOME/.nvm/versions/node/v22.23.1/bin:$PATH"
## Pinned-back versions — do NOT "upgrade to latest"
ESLint stays 9.x — eslint-plugin-react crashes on ESLint 10.
pnpm resolving a slightly older version than absolute-latest
is the 7-day cooldown working, not a bug to "fix".The second block is the pattern worth copying. Left unsaid, a capable agent will "helpfully" bump that dependency, because upgrading looks like an improvement from the inside. Naming the constraint and the reason is what prevents it, and the reason is what stops the rule being re-litigated every session.
Writing these files turns out to be worth doing whether or not a model ever reads them. Most of it is the exercise of writing down what a team has only ever transmitted by tapping someone on the shoulder.
A CLAUDE.md is what is true; a skill is how to do a job
These are the two mechanisms people most often confuse, and the distinction is simple once you see it as a context budget.
CLAUDE.md is state. It is loaded every time, so it describes what is permanently true about this repository: commands, conventions, constraints, traps. Everything you put in it is paid for on every single request, which is why it should be short.
A skill is a procedure. It sits in a directory as aSKILL.md and is loaded only when it becomes relevant — how to add a dependency, how to cut a release, how to bring up a six-service stack. It can be long, because most sessions never pay for it. Its frontmatter is what decides when it loads:
---
name: antares-stack
description: Orientation for the OIDC learning stack — the
authorization server, three resource servers, the SPA and
the MCP server. Load this when working in any of those
repositories, or when the user mentions token exchange,
delegation, client credentials, or connecting an agent to
their inventory.
---That description is not documentation, it is a trigger. It is written for the moment of retrieval — it names the repositories and the vocabulary a request would use, so the skill loads when it is needed and stays out of the way when it is not. A description that says "documentation about the stack" never loads at the right time.
The rule I use: if I would say it in every conversation about this repository, it belongs in CLAUDE.md. If I would only say it when a particular job comes up, it is a skill.
Show the pattern, do not describe it
Describing a convention costs a paragraph and transmits about half of it. Pointing at a file that already embodies it costs a sentence and transmits all of it, including the parts you would not have thought to mention — the comment style, the order of properties, how the tests are named, where the edge cases went.
So I almost never re-explain a house style. I say: this new component follows the shape of the toggle group — same file layout, same token naming, same test structure and coverage, and the same kind of documentation alongside it. The tab bar on this page was built that way, and it came out consistent with twenty packages it had never seen, because it had seen one of them properly.
The same trick works for prose, for commit messages and for tests. A good example is the highest-density instruction available to you.
Ask for a plan, not a patch
Claude Code has a plan mode: it investigates, proposes an approach, and writes nothing until you approve it. I use it for anything that will touch more than one file.
The reason is economic. A plan is three paragraphs and costs nothing to reject. A patch that has already touched forty files across two repositories is expensive to reject, and — this is the real hazard — the effort already spent makes you want to accept it. Reviewing the approach before the diff exists keeps that pressure out of the decision.
It also surfaces disagreements while they are still cheap. Twice in that afternoon the proposed plan was not what I wanted, and both times the correction was one sentence rather than a revert.
Make it ask me
This is the instruction that changes the most, and it is one line:
Let me take all technical, design and architecture decisions
by choosing from a list of alternatives. Explain to me in
great technical detail what each alternative is about.Without it, an agent resolves ambiguity silently. It has to — it cannot proceed otherwise — so it picks something reasonable and moves on, and you discover the choice later as a fact rather than as a question. Most of those choices are fine. The ones that are not are architecture.
With it, the ambiguity comes back to me as a decision with its trade-offs written out. That afternoon it produced four rounds of questions and I answered sixteen of them: whether the banner should be fixed or sticky and shrinking, whether the theme control was one component in two layouts or two components sharing one module, whether the tab bar's orientation should be a prop or a custom property.
I took the sticky-and-shrinking option against the recommendation, and I split the theme control in two against my own first instinct. Both are written down, with the alternatives that lost and why they lost. Keeping that record is the part I would hold on to if I had to throw everything else away: it is the difference between a codebase whose shape someone chose and a codebase whose shape merely happened.
Say where the truth lives
A model will answer a version-specific question from memory, in a confident tone, with the shape of the answer from two years ago. That is the failure mode to engineer against, and it is mostly solved by being explicit about sourcing.
What I ask for, in roughly this order of importance:
- Check, do not recall. Name the official documentation, the specification or the release notes, and say that the answer must come from there rather than from memory.
- Primary sources only. The RFC over a blog post about the RFC. The framework's own migration guide over a tutorial. A library's changelog over an answer on a forum.
- Pin the version. "Astro 7", not "Astro" — the majority of wrong answers about a framework are right answers about an earlier major.
- Separate what was verified from what was inferred. I ask for it in those words. An agent that has to label the difference stops blurring it.
- Prefer the repository to the internet. The most reliable source for how this codebase works is this codebase. Ask it to read before it answers.
Ask it to write the prompt
When a task is large or I cannot yet see its shape, I do not write the instruction — I ask for one. Before we start, write the brief you would want to receive for this: what you would need to know, what you would need to look at first, and what you would want decided up front.
What comes back is a list of open questions, and it is consistently better than my own, because it is generated from what the work actually requires rather than from what I happened to think of. I answer it, and that answered list becomes the real instruction.
Nothing ships because it looked right
Every claim on this site is checked by something that can fail the build. pnpm contrast reads the color pairs out of the stylesheet and holds them to their WCAG minimums.pnpm audit:html checks the generated markup for landmark and heading structure, alternative text, unique ids, the skip link and link text that says more than its destination. The design system runs axe over every component story in a real browser.
This matters more with an agent, not less. An agent writes plausible code very quickly, and plausible code is precisely what a human reviewer is worst at catching. Automated gates do not get tired on the fortieth file, and they do not accept a change because it was a lot of work.
What I will not delegate
Architecture. Which trade-off to take. Whether an interface is usable by someone who is not me. What ships.
An agent is fast, tireless, and entirely willing to take a decision that was yours to take — not because it is overstepping, but because someone has to decide and you did not. That is the whole discipline: stay the engineer who designed the thing, and keep the questions coming back to you.
Everything on this site — the token architecture in the design system, the cascade layers, the build gates, the two-presentations-one-behavior theme control — is the product of that arrangement. I drew the blueprint and I made every call in it. The work just went faster.
