All posts
Fullstack

A day of handing work to an agent

路 6 min read

Claude Code writes a large share of the code I ship, so this is the plain shape of a working day with it: what I describe, what comes back, and the loop at the end of each piece.

I write code with an agent every day. Claude Code, in a terminal beside the editor, on the Akera repositories I work in and on my own products. It isn't an experiment I'm running quietly on the side, so there's no reason to be vague about it.

I've written elsewhere about the rules file I put in a repository before any code, and about the review at the end, which is the one thing I never hand over. This post is what happens in between: the shape of an ordinary day.

A feature, described end to end

The largest unit I hand over is a whole feature described all the way through. Not "add an endpoint", but what it accepts, what the screen does with it, what the tests should prove, and what it must not do.

Roudhati, my kindergarten management platform, started with exactly that and nothing else. The brief I wrote before any code is still in the repository, and its first section isn't a feature list:

Before writing any feature code:

1. Read this entire brief and restate the scope in your own words, flagging anything ambiguous or contradictory.
2. Propose the folder structure and the full Prisma schema, and **wait for my approval**.
3. Generate a `CLAUDE.md` at repo root capturing the conventions in section 8, plus a `docs/DECISIONS.md` for architectural choices with rationale.

Then build **phase by phase** (section 9). At the end of each phase: run typecheck, lint, and tests; show me a summary of what changed; stop and wait. Do not start the next phase unprompted.

Ask before installing any dependency not listed in section 2.

Three of those instructions exist to slow things down. Restating the scope catches the places where my own brief contradicts itself, and I would much rather find those before a schema is built on top of them. Waiting for approval on the schema matters because everything downstream of it is cheap to change and the schema is not. The rule about dependencies is there because adding one is a decision with a maintenance cost, and it never looks like a decision inside a diff.

Phase by phase, then stop

The history shows how that played out:

42b0764 feat(auth): add registration, staff management and password changes
5acc039 test(e2e): add the two Playwright flows and fix the payment sheet preview
0277d63 feat(expenses,notes): add expenses, notes, the dashboard and print QA (Phases 5-6)
6417701 feat(attendance): add daily marking, monthly register and per-child stats (Phase 4)
18eaef9 feat(fees): add the fee ledger, payment recording, grid and receipt (Phase 3)
13a21b7 feat(children): add children, guardians, Arabic search and the enrollment form (Phase 2)
d205e7d feat(auth): add authentication, tenancy and the RTL app shell (Phase 1)
70e749f fix(db): stop migrate dev from dropping the Arabic search indexes
845ba3e chore: scaffold Roudhati monorepo with schema, migrations and seed (Phase 0)

Nine commits, and each one is a stopping point rather than a day's work. The fix sitting between phase zero and phase one is the useful detail: a phase landed, I read it, something was wrong with how migrations were being written, and it was fixed before the next phase started instead of three phases later. Phases that stop are what make that possible. A run that kept going would have built two more features on top of the problem, and the diff I had to read would have had the fix tangled through it.

Correcting what comes back

A feature that comes back usually has the right shape and some wrong details, and the wrong details are the work. The ones worth naming in advance are the ones that don't break anything. Roudhati's conventions call out three of that kind: a derived value computed in a React component instead of sent ready-made by the API, a permission checked with an inline role === 'OWNER' rather than through the role matrix, a ledger period built with new Date() instead of toPeriod. Each is a reasonable-looking line that typechecks, passes lint, and is wrong.

So the correction pass is mostly the same handful of questions, asked in the same order, about code that already works.

The mechanical half

The other large share of the day is work where the answer is already settled and only the typing is left. Scaffolding a module in the pattern the twelve beside it use. CRUD endpoints. A rename across forty files. Test fixtures. Migrations. Translation files.

In a shared repository that kind of task has a second requirement beyond being correct, which is not disturbing anything else. Manarway's committed rules for agents, written by a teammate, open on exactly that:

## 1. General Change Discipline

- Read nearby existing code before adding new code.
- Prefer existing module, service, repository, query, and UI patterns over inventing new structures.
- Keep generated files generated; do not hand-edit route trees or generated i18n types unless no generator exists.
- Do not fix unrelated old code while implementing a feature.
- If old code has issues, document them separately unless the current feature directly depends on fixing them.
- Preserve current behavior unless the user explicitly asks for behavior changes.

Every line there is about restraint rather than capability, and the two I lean on most are the ones about generated files and unrelated code. An NX monorepo with five frontend apps has route trees and translation types that a generator owns, and a hand-edit to one of those survives until the next generator run and then vanishes. The instruction not to tidy up neighbouring code while implementing something is what keeps a translation-file change readable as a translation-file change.

Reading code I didn't write

The third kind of task isn't a change at all, it's a question. Where does this live. What does this service actually do. Why does this break. Reproduce this from the bug report.

Munitron and Manarway each carry thirty-four files in docs/ and around ten shared packages, and most of both predates me. Asking the agent to find and explain a module is faster than reading it cold, and the answer arrives as a claim about the code rather than a change to it, which means I can check it in the place it came from. That asymmetry is why I'll hand over investigation freely and still read every line of a diff. A wrong explanation costs me a minute. A wrong change costs whatever it costs.

Writing that isn't code

Commit messages, pull request descriptions, documentation. Roudhati's rules ask for conventional commits, one concern each, and the subjects in that log are what they produce: a scope, then a description of what the diff does. Reading a diff and describing it accurately is a thing an agent is genuinely good at, and it's also work I'd otherwise do badly at the end of a long task.

The habit in this group I'd recommend hardest is handing over my own diffs before anyone else sees them. Not the agent's work, mine. An hour into the same file I stop seeing the obvious, and a second reader that never gets bored is worth having at that point.

The loop at the end of a piece

The scripted part

Every phase ends the same way, and the commands are in the rules file so neither of us has to remember them:

pnpm db:up          # Postgres in Docker (host port 5434)
pnpm db:migrate     # apply migrations
pnpm db:seed        # one realistic kindergarten, 25 children, full ledger
pnpm dev            # api + web + package watchers
pnpm typecheck      # all workspaces
pnpm test           # all workspaces

Typecheck, lint, tests, and then the part no script does: open the thing and use it. In Roudhati that means a phone-sized viewport in Arabic, right to left, because that's what the person who'd be using it has in her hand. A fee grid can pass every test and still be unusable with a thumb.

Then the diff

Only then the diff, which is where the day actually ends and which I've given its own post. The short version is that nothing merges unread, whoever wrote it, and that the three checks above are evidence rather than a verdict.

What I'd change

That brief sits at the root of Roudhati as roudhati-claude-code-prompt.md, and nothing has updated it since phase zero. The rules file it told the agent to generate has since learned things the brief doesn't know, including a whole section on migration drift that went in with the commit fixing it. So the repository now holds two descriptions of the same system, one of which is stale, and the stale one has the more inviting filename. I'd fold the parts still true into CLAUDE.md and move the rest into docs/ next to DECISIONS.md, where a historical document belongs.

The other thing I'd change is a single line. Manarway's rules for agents are committed, a hundred-odd lines, and genuinely good, and nothing loads them automatically: the repository has no CLAUDE.md at its root, so those rules only apply when someone thinks to mention the file. Qrder solves the same problem with one line of indirection, a CLAUDE.md containing @AGENTS.md and nothing else. In Munitron that file is gitignored, so there it would be a personal note to myself. In Manarway it isn't, so it's a one-line commit that would put the team's own rules in effect by default for everyone, which is presumably what whoever wrote them wanted.

Was this any good?

NextNothing merges unread