The AI Pair Programming Loop I Actually Use
Most bad output from a coding model is a briefing problem, not a model problem. This is the loop that fixes the briefing.
Workflow: A repeatable sequence you can adopt step by step.
Contents (7 sections)
The difference between people who get useful work out of a coding assistant and people who conclude it is a toy is rarely the tool. It is whether they have a loop. Without one, every interaction restarts from zero: a vague request, a plausible answer, a slow realisation that the answer is wrong, and a decision to just write it manually.
This is the loop. Five steps, in order, and the order matters.
Key takeaways
- 01Write the acceptance criteria before the prompt. If you cannot say what 'done' looks like, the model cannot either.
- 02Constraints matter more than instructions: what not to touch, what to reuse, what not to install.
- 03Generate in units you are willing to read in one sitting. Anything larger gets skimmed, and skimming is where bugs enter.
- 04Verify by running it, not by reading it. Reading catches style; running catches behaviour.
- 05Integrate yourself. The last mile — naming, placement, matching local idiom — is the part you should not delegate.
Step 1 — Brief#
Before writing a prompt, write down what finished looks like. Not the implementation, the observable outcome.
Goal
Users can export a filtered table view as CSV.
Done when
- Export button appears only when the table has at least one row
- Exported columns match the currently visible columns, in the same order
- Filenames include the active filter name and the date
- Values containing commas, quotes or newlines are correctly escaped
- No new dependencyThis takes three minutes and does more for the outcome than any prompt-engineering trick. It also has a useful side effect: about one time in five, writing the criteria reveals that the task is underspecified, and you go and resolve the ambiguity with a human instead of letting a model guess.
Step 2 — Constrain#
Instructions tell a model what to do. Constraints tell it what not to do, and constraints are where the real leverage is, because the failure mode of a capable model is not incompetence — it is enthusiasm.
The four constraints worth writing almost every time:
- Reuse
Name the existing helper, hook or module it must use. Left unsaid, a second implementation of something you already have is the single most common output.
- Scope
Name the files it may change. "Only touch
src/features/export/" prevents a two-file task becoming an eleven-file diff.- Dependencies
State explicitly whether new packages are allowed. Usually they are not.
- Idiom
Point at a file that already does something similar. One good example outperforms a paragraph of style rules.
Step 3 — Generate#
Ask for work in units you are actually willing to read closely. For me that is roughly one coherent change: a function and its test, a component, a route handler. Around 200 lines of diff, my reading quality drops sharply, and a diff I skim is worse than no diff at all, because it arrives with the confidence of finished work.
Two things worth doing here:
Ask for the plan first on anything non-trivial. A short plan is cheap to read and cheap to correct. Correcting a plan takes thirty seconds; correcting an implementation built on a wrong plan takes an hour.
Let it finish. Interrupting mid-generation to add a constraint you forgot usually produces a worse result than letting it complete and then asking for a revision, because the model loses the thread of what it was doing.
Step 4 — Verify#
Read for one thing only: does this do what the acceptance criteria said? Then stop reading and run it.
The order matters because reading is good at catching the wrong class of problem. Reading catches naming, structure and obvious logic errors. It is unreliable at catching the errors models actually make — plausible code that handles the happy path and quietly mishandles an edge case. For the CSV example above, the escaping rule is the one that reading will pass and a single test with a comma in a cell will fail.
it("escapes values containing commas and quotes", () => {
const csv = toCsv([{ note: 'contains, a comma and "quotes"' }], ["note"])
expect(csv.trim().split("\n")[1]).toBe('"contains, a comma and ""quotes"""')
})If there is one thing to take from this loop, it is that a single test on the ugliest input you can think of is worth more than a careful read of the whole diff.
Step 5 — Integrate#
Do this part yourself. Rename things to match the local vocabulary, move the file to where your project would have put it, delete the defensive branch that exists for a case that cannot happen, and remove the comments explaining what the code does.
This is not busywork. Generated code that is functionally correct but idiomatically foreign is the mechanism by which a codebase becomes incoherent — and incoherence is expensive in a way that is invisible for about three months and then obvious forever.
Where the loop is not worth it#
Being honest about this matters more than defending the workflow:
- Trivial changes. A one-line fix does not need a brief. Type it.
- Code you do not understand yet. Generating a change to a subsystem you cannot evaluate produces the illusion of progress. Read the subsystem first.
- Deeply novel logic. Genuinely new algorithms, unusual domain rules, anything where the correct behaviour lives in someone's head rather than in a convention. Models are strong on the well-trodden and weak exactly where your product is unusual, which is often the part that matters most.
The compressed version#
Brief it in writing. Tell it what not to touch. Ask for a plan on anything real. Read for intent, then run it against the ugliest input you can construct. Integrate by hand.
Nothing here is clever, and that is roughly the point. The wins come from doing the unglamorous step — writing down what "done" means — before the interesting step.
Related reading
Other Hamzify pieces on this topic, the same tools, or the next format worth reading.
How to Review AI-Generated Code Without Reading Every Line
A review workflow tuned to the specific mistakes coding models make: a triage order, the six failure patterns worth hunting for, and where to spend your attention.
A Context Checklist for Briefing Coding Agents
A reference checklist for what an AI coding agent needs to know before it starts: the seven inputs that change output quality, and the ones that only add noise.
Cursor Review: Two Weeks Inside a Real Codebase
A hands-on review of Cursor as a daily driver on an existing production codebase — where agent mode earns its keep, where it costs you time, and who should stay in their current editor.
From the same tool
More Hamzify coverage of Cursor
Reviews, comparisons, builds and workflows that mention Cursor, collected in one place. Open the Cursor coverage.