Building Arc
2025-01-14
I've been using Claude Code for a while now and kept finding myself doing the same dance: think through a feature, break it into tasks, write tests first, implement, get it reviewed, ship. Every time. So I figured I'd just encode the whole thing into a plugin.
The first version was terrible. I tried to make it "flexible" and "configurable" which meant it did nothing well. The turning point was deciding to be opinionated. TDD isn't optional, it's mandatory. Reviews happen early, not at the end. Questions over commands.
That last one took me a while to figure out. My first reviewers were bossy—"Remove this caching layer" or "Refactor this into smaller functions." Turns out that's annoying and often wrong. The person building usually knows something the reviewer doesn't. Now they ask questions instead: "Do we need this caching layer in v1?" Same information, completely different energy. You can say "yes, because X" and move on.
I also learned that review at the end is useless. By that point you've already built the thing and any feedback is expensive to act on. Now every stage gets a quick sanity check before moving on. Catches the dumb stuff early when it's cheap to fix.
The plugin architecture itself taught me things. Claude Code has this concept of "skills" (instructions that get injected based on context) and "commands" (things you invoke explicitly). I kept conflating them. A skill is knowledge—how to do TDD, how to debug systematically. A command is an action—start a feature, run a review. Once I separated those cleanly, everything got simpler.
Context management is the whole game. LLMs have limited context windows, and if you fill them with garbage you get garbage out. So I ended up building dedicated agents for noisy tasks like running e2e tests. They do their thing in isolation and just report back results. Keeps the main conversation clean.
The weirdest lesson was about AI-generated code. It has a look. Inter font, purple gradients, white backgrounds, rounded corners on everything. I started calling it "slop." So I built a command specifically to clean it up—remove unnecessary comments, defensive checks that can't trigger, type escapes that shouldn't exist. It's absurd that this is necessary but here we are.
Git worktrees turned out to be essential. Working directly on main is asking for trouble. Now every feature gets its own worktree, tests have to pass before anything gets merged, and there's a proper cleanup process at the end. Boring infrastructure stuff, but it's the difference between "it works on my machine" and actually shipping.
I'm still iterating on it. Every project I use it on reveals something new. But the core insight hasn't changed: encode your process, make it opinionated, and let the machine handle the ceremony so you can focus on the interesting parts.