The AI pair-programming workflow that holds up: slice work into tasks with a mechanically checkable outcome, hand the agent one task plus a conventions file, review the diff like it came from a contractor you haven't worked with before, and take the keyboard back on the second hallucinated API. The productivity gain is real — on the right tasks I'd estimate 2–4× — but it comes from decomposition and verification discipline, not from prompt wizardry, and it goes negative the moment you start supervising a debugging spiral you could have avoided by writing the code yourself.
Decompose to verifiable tasks
The unit of work is a task a competent contractor could complete from the brief alone, sized at 15–90 minutes of human effort. Smaller and the overhead of briefing exceeds the work; larger and you get a 900-line diff you can't honestly review. The non-negotiable ingredient is a "done when" that a command can check:
Task: Add rate limiting to POST /api/import
Done when: `pytest tests/test_import.py -k rate_limit` passes,
and existing tests still pass
Constraints: use the existing RateLimiter in core/throttle.py; no new deps
Out of scope: the admin bypass — separate task
Writing "done when" forces the decomposition to be real. If you can't state a check, the task is actually a design question, and design questions go to a conversation, not an agent session. I keep a plans file with 5–10 such briefs and burn through them one per session — one task per session, one task per branch, because sessions that mix tasks produce diffs that mix concerns.
Context is a file, not a chat history
Everything you find yourself repeating belongs in a conventions file (CLAUDE.md, AGENTS.md, or your tool's equivalent) that gets loaded every session: build and test commands, the error-handling pattern, the naming rules, the three libraries you've standardised on, and — most valuable per line — the forbidden things ("never write raw SQL outside db/", "don't touch the generated files in api/types/"). Keep it 50–150 lines; past that, models skim it the way humans skim a 40-page onboarding doc. This file is a versioned, reviewed artifact, and maintaining it is the same discipline as treating prompts as engineering: when the agent gets something wrong twice, the fix is usually a line here, not a longer chat message.
Verify like you don't trust it, because you don't
Green output isn't done. My checking order, cheapest first: run the tests named in the brief, then run the app and exercise the change by hand (agents excel at making tests pass and are indifferent to whether the feature works), then read the full diff. Reading the diff, look for the four agent-specific failure patterns, which are different from human ones:
- Tests weakened to pass — assertions deleted, tolerances widened, a
skipadded. This is the classic. Diff the test files first. - Error handling that swallows —
except Exception: passand its cousins appear under pressure to make red things green. - Scope creep — a drive-by refactor of a neighbouring module that turns a 100-line review into a 400-line one. Revert it; re-brief if it was a good idea.
- Plausible-but-wrong API usage — code that reads correctly and calls a method that doesn't exist or ignores a required flag. Type checkers and compilation catch most of it, which is a real argument for typed codebases in agent-heavy teams.
A second model reviewing the diff catches a useful slice of this cheaply — with the caveats about what AI review reliably misses — but you remain the reviewer of record. Never let the model that wrote the code be the only thing that reviewed it.
The three signals to take the keyboard back
Supervised agents fail with momentum: each fix attempt is plausible, so you grant one more iteration, and forty minutes evaporates. I take over on any of three signals, mechanically:
- Second hallucinated API in one session — the model's picture of this codebase or library is wrong, and more turns repaint the same picture.
- Third edit to the same file for the same failing test — it's guessing. Loops of guess-run-guess don't converge; they wander.
- The error moves — each fix relocates the failure instead of removing it. That's a wrong mental model of the bug, and yours is now needed.
Taking over isn't abandoning the tool. Usually ten minutes of hands-on debugging finds the actual cause, and then I hand back a corrected brief ("the bug is X in Y; fix it this way"). The expensive habit is negotiating with a stuck agent out of sunk cost.
Know the terrain
Where agents earn the multiple: mechanical refactors across many files, test scaffolding, migrations with an example to imitate, glue code against well-documented APIs, and first drafts of anything formulaic. Where they burn it: novel algorithms, performance work without profiling data, subtle concurrency, and API design — anything where taste or a global view of the system is the actual work. I route the first list aggressively to the agent and keep the second list on my keyboard from the start, which matters more than any prompt improvement I've found. If you want to make the routing judgement empirical for your own codebase, log a month of tasks with outcomes — the same measurement habit that applies to any LLM system applies to the one sitting in your terminal.
What I'd do
Keep a brief backlog with command-checkable "done when" lines. One task, one session, one branch. A 100-line conventions file that grows a line every time the agent repeats a mistake. Verify in order: tests, hands-on run, full diff with the four patterns in mind. Take the keyboard back on the second hallucination, the third same-file retry, or a moving error — and hand back a better brief afterwards. Treat the agent as a fast contractor with no memory and no stake in the outcome: brilliant with a good brief and a vigilant reviewer, expensive with either one missing.