At first I used AI as overpowered autocomplete. Output went up, my reviews got lighter, and some of what I merged looked fine while quietly crossing boundaries it shouldn't have. The fix wasn't using less AI. It was being clear about who decides what.
Who owns what
| AI can | You still own |
|---|---|
| Draft an implementation | The architecture it has to fit |
| Suggest tests | What "correct" and "good enough" mean |
| Offer three ways to do it | Picking one, and the trade-off you accepted |
| Explain the code it wrote | Being able to explain it without the AI |
The test I use: if I can't explain why a change exists, I don't merge it.
The loop
- Constrain to Compare: ask for options
- Compare to Slice: pick one
- Slice to Verify: review it all
- Verify to Constrain: tighten
- 1Constrain. Write the behaviour, the non-goals, and the performance and security limits before asking for anything.
- 2Compare. Ask for options with trade-offs, not one-shot code.
- 3Slice. Generate pieces small enough that you read every line.
- 4Verify. Same bar every time: tests, types, review. What fails becomes a constraint for the next round.
What slips through when you skip it
The same four things, almost every time: abstractions nobody needed yet, logic leaking across domain boundaries, edge cases left unhandled, and tests that check the implementation instead of the behaviour. They all look reasonable in a diff, which is exactly why the loop exists.
The advantage was never typing faster. It's keeping your judgment while you move quickly.