Two workflows: how I actually use Claude Code
People ask what it's actually like to use Claude Code day to day, beyond the demo-length answer. The honest answer is that I run it two quite different ways depending on the context — one for my homelab, one for professional work — and the shape of each says something about what makes the tool actually useful versus just impressive.
Homelab: a persistent session I check in on
My NixOS homelab is a small fleet — a handful of always-on hosts doing DNS, an internal certificate authority, reverse proxying, home automation, that kind of thing, all declared in one flake. The way I actually work on it: Claude Code runs persistently on a Mac mini that's always on and always reachable, and day to day I talk to it through the Claude mobile app's remote session feature, over Tailscale. It stays running with the context of what it's been doing, and I drop in and out of it from my phone. The app makes it easy to scroll back and actually read what happened — which matters more than it sounds, because a terminal is not a great reading surface on a five-inch screen.
I do keep a tmux session on the box too, but it's an escape hatch, not the primary interface: if the mobile app's remote connection drops or the session gets logged out, I can SSH in, attach to tmux, and pick the work back up rather than losing it. It's there for recovery, not because I want to be scrolling raw terminal output on a phone by choice.
The part that makes this actually useful rather than a novelty is that a device on my network can start misbehaving, a service can need a config tweak, a cert can need renewing — and I don't need to be at a laptop. The tailnet means every host in the fleet is reachable the same way regardless of where I physically am, and the persistent session means I'm not re-explaining context every time — it already knows what the fleet looks like, what changed recently, what's mid-flight.
There's one more piece that makes the fleet feel like it's actually learning, rather than starting fresh every time: I symlink Claude's skills directory into my nixos-config repo, the same flake that declares every host. Skills end up git-versioned and shared across every host and every session — so if Claude trips up on something once, on any machine, I can turn that into a skill, and from then on it's persisted everywhere. The fleet gets more consistent the same way the config itself does: by declaring something once and syncing it everywhere, rather than re-teaching the same lesson to a fresh session on a different box.
The practical effect is that homelab maintenance stopped being a "set aside an evening" activity and became something I do in five-minute increments between other things — which, for a system that's supposed to be low-maintenance in the first place, is the whole point.
Professional: small PRs, a living plan, and review-as-diff
Work is a different shape of problem — more people, more risk,
more need for the trail of why a change happened to
survive past the change itself. My day job is lead maintainer of
Kong Insomnia, and
a recent piece of work — migrating script execution from an
Electron hidden BrowserWindow to a QuickJS-WASM
sandbox — is a decent illustration of the workflow:
Start with a plan, not a PR. Before writing code, I draft the plan as a gist. I use a gist specifically rather than a repo doc or a wiki page: it's versioned, so I can see how the plan itself evolved; it's a single link I can drop into any context — a Slack thread, a PR description, a conversation with Claude — without anyone needing repo access; and it's cheap to make private enough (a secret gist) without the overhead of setting up real access control for something that's essentially a scratchpad. For the sandbox migration, that was "Scripting sandbox: hidden-window to QuickJS rollout plan (opt-in to opt-out)".
The plan itself has a fixed shape: context and motivation (why this is worth doing at all), scope (what's explicitly in and out), and then a stepwise PR plan — a sequence of lean, tangible, individually testable slices of work, not one big-bang rewrite. It's a working document, not a spec: as I actually start building and uncover unknowns it didn't anticipate, I use Claude to keep it updated in place, so the plan and the code never drift far apart from each other.
Every PR has to prove itself. Each pull request needs an observable outcome and an end-to-end test — not just "the diff looks right," but something that demonstrates the change does what it claims, in a way a reviewer (or future me) can verify without re-deriving the reasoning. PR #10382, the opt-in QuickJS sandbox itself, shipped with 195 unit tests and an end-to-end smoke test exercising the real Electron app, including the case that mattered most: a runaway script actually gets isolated rather than freezing the UI.
One or two PRs in flight at a time. I don't
queue up a stack of parallel work. When
insomnia.sendRequest() support inside the sandbox
turned out to cause WASM heap crashes, it got pulled out into its
own follow-up PR rather than bloating #10382 or blocking it —
keeps each PR small enough to actually review properly, and
keeps the plan's assumptions checked against reality before the
next chunk builds on top of them.
Review feedback arrives as a PR, not comments.
This is the part people are usually most surprised by. Instead of
a reviewer leaving a pile of inline comments and going back and
forth on wording, they open their own PR against my branch
containing exactly the change they want to see before they'll
merge. On #10382, that was
PR #10395
— a teammate found that the sandbox's environment/variable bridge
would let a script pass keys like __proto__ or
constructor and manipulate the object's prototype
chain, and rather than describe the fix in a comment, opened a PR
directly against my branch with the guard already written.
There's no ambiguity about what "please fix this" means — the
reviewer has already written the fix. I can accept it outright,
counter with my own version, or use it as the starting point for
a conversation, but it collapses a lot of the normal review
back-and-forth into something concrete and mergeable from the
first round.
What ties them together
Different contexts, same underlying instinct: keep the loop short, keep the state somewhere durable, and don't make either me or Claude re-derive context that already exists. The persistent session on the Mac mini means the homelab conversation doesn't restart from zero. Skills synced through the same repo mean a lesson learned on one host doesn't have to be relearned on the next. The gist plan persists and gets updated live so the professional conversation doesn't drift from what's actually true. In none of these cases is the win "AI writes the code" — it's removing the friction around staying oriented, so the actual thinking gets to happen at a smaller, faster grain.
If you're figuring out how to fold Claude Code or similar tools into a Nix-based workflow — homelab or production — I'm happy to talk it through.