The single habit that's changed my output with AI more than any prompt trick is boring: I make the model write the plan before it writes the code. Not a vague "here is my approach" paragraph buried above a diff it's already applied, but a real artifact I read and correct before a single line is generated. Approving direction on a plan costs a paragraph of my attention. Discovering the wrong direction inside 400 lines of generated code costs the afternoon.
That gap is the whole argument. Everything below is about why the gap exists and how to build a workflow that always spends the cheap paragraph instead of the expensive afternoon.
Why models jump straight to code
Ask a coding model to "add rate limiting to the email sync" and it will, by default, start editing files. This isn't a flaw in any single model; it's what the training rewards. The model has seen an enormous number of examples where the answer to a coding request is code. So it produces code, immediately, confidently, and it produces a lot of it.
The problem is that a code request almost always contains an unstated assumption, and the model has to guess at it. Rate limit per what, per API key or per recipient domain? Enforced where, at the queue or at the send call? Backed by what, an in-memory counter that resets on restart or a persisted one? The model picks an answer to each of these silently and then builds the entire implementation on top of its picks. If it guessed wrong on the first one, everything downstream is wrong in a way that looks completely plausible, because the code is internally consistent. It's consistent with the wrong assumption.
This is the part that makes premature code so expensive. A wrong assumption doesn't announce itself. It compounds. The variable names, the function boundaries, the tests it writes, the edge cases it handles, all of them are shaped around the mistaken premise. By the time you read far enough to notice the model persisted a counter you wanted in memory, you're not correcting one decision. You're unwinding every decision that was built on it. Reviewing wrong code is slower than reviewing right code, because you have to first reverse-engineer which wrong thing it assumed.
What a good plan actually contains
A plan is worth reviewing only if it exposes the decisions the model would otherwise make silently. A plan that just restates the task back to me is theater. The ones I actually use have five parts:
- The files it will touch. A concrete list, with a one-line note on what changes in each. This alone catches a startling number of misunderstandings, because the wrong mental model shows up as the wrong file. If it's about to edit the queue consumer and I expected the change at the producer, we're done in ten seconds.
- The approach. The mechanism in plain terms: where the rate limit lives, what data structure backs it, how the counter is keyed and when it resets. This is where the silent assumptions get dragged into the light.
- The tradeoffs. What it's optimizing for and what it's giving up. If it chose an in-memory counter for simplicity at the cost of correctness across restarts, I want that stated so I can say yes or no on purpose.
- The edge cases. What happens at the boundary: the first request, a burst, a restart mid-window, a clock that jumps. The list tells me whether the model actually understands the problem or is pattern-matching on the phrase "rate limiting."
- What it will not do. The explicit non-goals. "This will not add distributed coordination across instances" is a sentence that saves an argument later, and forcing the model to write it surfaces scope I never asked for before it builds it.
That last one earns its keep more than people expect. Models are eager. Ask for rate limiting and you can get retries, circuit breakers, and a metrics dashboard thrown in. The non-goals section is where you catch the gold-plating while it's still a bullet point.
The review step is where the money is
Here's the actual sequence, and the order is the point. The model writes the plan. I read it. I catch the misunderstanding. I correct it. Only then does anything get implemented.
When I read a plan and see "persist the counter to the database," and I wanted it in memory because this is a single-instance deployment and I don't want the write load, I fix it with one sentence: "in-memory is fine, single instance, do not persist." That correction cost me a sentence and cost the model nothing to incorporate, because it hasn't written anything yet. The plan is clay. The code is concrete.
Compare the other timeline. No plan. The model persists the counter, writes a repository class for it, adds a migration, writes tests against the database, and hands me 300 lines. Now the same one-sentence realization means throwing out the repository, the migration, and the tests, and I have to be careful I got all of it, because deleting code always leaves a stray reference somewhere. The misunderstanding was identical. The price wasn't.
The lesson I keep relearning across ten products in production is that the cost of a wrong assumption scales with how much you have built on top of it before you notice. The plan review is deliberately positioning the discovery as early as it can possibly happen, when nothing is built on top of anything yet. This is the same instinct behind having a second model check work after the fact, which I wrote about in have AI review AI; planning first is the front-end version of the same idea. Catch it before, catch it after, just don't ship it in the middle unexamined.
Hand the approved plan to a faster model
Once the plan is right, the interesting decision it took, the judgment, is already spent. It lives in the approved plan. What's left is translation: turning an agreed spec into an agreed implementation. That's not the part that needs the most capable model.
So I split the work by model. Planning is reasoning under ambiguity, which is exactly where a high-capability model earns its cost; I plan with Opus, or with Fable for the genuinely hard designs. Implementing a spec that already has the files, the approach, and the edge cases nailed down is a job a fast model does well and cheaply, so I hand the approved plan to Sonnet to build. The plan is the contract between the two. The expensive model decides what to build, the fast model builds it, and because the contract is explicit, the fast model has far less room to go wrong. I go into the mechanics of this split in match the model to the task; plan-first is what makes the handoff clean, because a good plan is precisely the artifact the cheaper model needs to not need the expensive one.
None of this works if the plan is written against a hallucinated version of your codebase. The model has to be planning against the real files, the real conventions, the real constraints, which is why the context you feed it up front matters as much as the planning discipline itself. If the model can't see how your code actually works, it will write a confident, detailed, useless plan. That's the subject of give your AI the context it needs, and it's the precondition for everything here.
Plan mode, and the record it leaves
Some tools now formalize this with a "plan mode": the model is prevented from editing anything until you approve its plan. It can read, search, and reason, but the write tools are off until you say go. I use it deliberately, because it removes the temptation to let a promising-looking start run ahead of my review. The tool enforces the discipline I would otherwise have to enforce by willpower, and willpower loses to an eager model at 11pm.
There's a second payoff that I didn't anticipate when I started working this way. The approved plan is a written record of intent. It says what we decided to do, what we chose not to do, and why. Weeks later, when I'm staring at the in-memory counter wondering whether the missing persistence was an oversight or a decision, the plan answers it: decision, single instance, deliberate. My whole practice runs on written instruction that outlives any one session, from the reference libraries down to the agent definitions, and a plan is that same idea applied to a single task. Code tells you what was built. The plan tells you what was meant, and the difference between those two is where most of the confusing bugs live.
The habit is small and the phrasing is almost too simple: make it plan, read the plan, fix the plan, then let it build. But it moves the moment of discovery from the most expensive point in the process to the cheapest, and over a few hundred tasks that relocation is most of the difference between AI that speeds you up and AI that generates confident work you have to unwind.