ZeNorm

Planning mode vs. just prompting

When making an agent plan before it writes code pays for itself, when it is pure ceremony, and what separates a useful plan from a restated prompt.

Alex Earll8 min read

Every agent harness now has some version of a plan step — plan mode, an architect pass, a "think before you edit" flag. The advice around them has collapsed into "always plan first," which is wrong often enough to be worth arguing with.

Planning is not free. It costs tokens, it costs a round trip, and most expensively it costs your attention at a moment when you were ready to move. The question is when that price buys something.

When planning is ceremony

Skip the plan when the change is confined to one file and there is one obvious way to do it.

Adding a nullable column and its migration. Fixing an off-by-one in a paginator. Adding a missing await. Renaming a variable. Adding a case to a switch that already has six. In all of these, the plan will say what you already know, and you will approve it without thinking, and the only outcome is that you read a paragraph and spent a minute.

There's a subtler cost. A plan step trains a reflex to approve. If nine out of ten plans are obvious restatements, you develop the habit of skimming and hitting accept — and then the tenth plan, the one that actually contains a bad architectural call, sails through on the same reflex. Requiring plans for trivial work degrades your review of plans for non-trivial work. This is the ordinary dynamic of any alert that mostly fires on nothing.

The honest test: before reading the plan, can you predict what it will say? If yes, don't ask for it.

When skipping the plan is expensive

The cost of no-plan isn't wasted tokens. It's that you discover the wrong approach after the code exists, and code-that-exists distorts your judgment.

Say the task is adding real-time presence to a document view. There are at least three reasonable approaches: piggyback on the existing sync layer, add a separate websocket channel, or poll a lightweight endpoint. They have genuinely different trade-offs around ops complexity, latency, and how they behave with many idle tabs.

Prompt an agent without a plan and it will pick one — usually the one most represented in its training data, or the one most similar to nearby code — and produce 400 lines implementing it. Now you're reviewing an approach and an implementation at the same time, and the implementation is the part demanding attention. The approach arrives disguised as a fait accompli.

Then sunk cost does its work. Four hundred lines exist. They mostly work. The bug you found is in the reconnect path. The path of least resistance is to fix the reconnect path — not to say "actually the whole channel should not exist, this belongs in the sync layer." You will patch. Everyone patches. The wrong architecture survives because throwing it away feels like waste, even though the waste already happened and the only question is whether you add to it.

This is the real argument for planning: it moves the approach decision to a moment when reversing it is free. A rejected plan costs one paragraph. A rejected implementation costs a day and an argument with yourself.

Three conditions each independently justify a plan:

  1. More than one reasonable approach exists. Not "more than one possible" — more than one that a competent engineer might defend.
  2. The change spans several modules. Multi-module changes have interface decisions, and interfaces are the expensive thing to get wrong. Related: how dependency ordering forces agents to invent interfaces they'll later have to change.
  3. You don't know the codebase area well. Then the plan is partly a research artifact. Its value is telling you what's there, and you should read it as a map rather than a proposal.

What a good plan artifact contains

Most plans are bad. They are bad in a specific, recognizable way: they restate the prompt as bullet points.

A useless plan:

Plan:
1. Add presence tracking to the document view
2. Update the backend to handle presence events
3. Add UI to show who is viewing
4. Add tests

This contains no information. Every line is derivable from the task title. There is nothing here you could disagree with, which means there is nothing here to review. If a plan can't be wrong, it can't be useful.

A plan worth reading has four things.

The approach, stated as a choice. Not "implement presence" but "presence rides on the existing Zero sync layer as a presence table with a TTL, rather than a separate channel."

The rejected alternatives, with reasons. This is the part almost everyone omits and the part that carries the most information. "Considered a dedicated websocket channel — rejected because it doubles the connection count per tab and we'd need a second auth path. Considered polling — rejected because sub-second presence updates at our tab counts would be more requests than the sync socket costs."

Rejected alternatives are what let you disagree productively. If the agent rejected polling because "it's inefficient" and you know your presence requirements are actually 10-second granularity, you've caught a wrong premise in one sentence. Without the alternatives you'd have caught it in code review, after the websocket existed.

They also expose the agent's model of your system. An agent that rejects an option for a reason that isn't true of your codebase has told you its context is wrong before it has written anything on that wrong basis.

The files to touch, and the order. A concrete list. This is where you catch scope errors cheaply — if the plan lists a file you know is deprecated, or omits the one place presence would obviously need cleanup on disconnect, you've found a gap in fifteen seconds. The order matters too, because it reveals whether the agent understands the dependency structure or is just listing files.

What done looks like, checkable. Not "presence works" but "two browser sessions on the same document each show the other's avatar within 2s, and an avatar disappears within 30s of a tab closing." If the plan's done-condition isn't something you could test, the agent has no way to know when to stop, and it will stop when it runs out of obvious work instead.

A plan with these four elements is roughly a page. That's the right size. Multi-page plans for a day of work are their own smell — usually the sign of a task that should have been split.

Reviewing a plan is cheaper than reviewing a diff

This is the mechanical reason planning works, and it's underrated.

A plan and the diff it produces contain approximately the same decisions. The diff contains those decisions plus hundreds of lines of mechanical consequence — imports, error handling, type plumbing, the third argument of a function call. The decisions are in there, but they're distributed across the mechanical mass and you have to reconstruct them by reading.

Reading a plan is close to pure signal. Reading a diff is signal at maybe a tenth the density, spread across ten times the surface area. Same information, an order of magnitude more reading.

This is why plan review catches architectural problems that diff review misses, even by the same reviewer with the same care. In a diff, "this should have used the existing sync layer" requires holding the whole change in your head and comparing it against an alternative you have to construct yourself. In a plan, it's one sentence next to another sentence.

It does not follow that plan review replaces diff review. Plans say nothing about whether the code is correct, whether errors are handled, whether the concurrency is right. They cover a different failure class. Plan review catches "wrong thing." Diff review catches "thing done wrong." Both exist; the plan is just enormously cheaper per unit of the first kind.

The approved-without-reading failure

Here's the mode that makes planning actively negative rather than merely wasteful.

You ask for a plan. The agent produces one. You skim the headings, it looks structurally reasonable, you type "looks good, go ahead." Twenty minutes later there's an implementation of an approach you never actually evaluated.

Now you're worse off than if you'd never planned. Not the same — worse. Because you have a memory of having approved it. When the design turns out wrong, your prior is "we considered this and chose it," which is exactly backwards from the truth, and it makes you slower to reconsider. The plan manufactured confidence without doing the work confidence is supposed to rest on.

The unread plan also gives the agent something to point at. Later, when the implementation drifts, "this follows the approved plan" is a real defense, and it's not wrong — you did approve it. The plan becomes a laundering mechanism for a decision nobody made.

Concretely, guarding against this:

If you're not going to read it, don't ask for it. A plan you'll rubber-stamp is worse than no plan. Skip straight to the code and review that.

Make yourself respond to the alternatives section specifically. Not "approved" — something like "agree on rejecting the separate channel, but the polling rejection assumes sub-second updates and we only need 10s; does that change anything?" If you can't produce a sentence like that, you didn't read it.

Reject at least occasionally. If your approval rate is 100%, you're not reviewing. Some plans should come back with "wrong approach, do X instead." When that never happens across dozens of plans, the step has become theater.

Treat structure as a warning, not a signal. Agents produce well-formatted plans regardless of quality. Confident headings, numbered steps, and a Testing section are not evidence of a good plan. The alternatives section — the part that's hard to fake because it requires actual knowledge of your system — is where to look.

The rule I actually use

One file, one obvious approach: prompt directly.

Several modules, or more than one defensible approach, or unfamiliar territory: plan first, and read it properly — specifically the rejected alternatives, which is where the agent's model of your system either matches reality or doesn't.

Everything in between: ask yourself whether you can predict the plan. If you can, skip it. If you can't, that uncertainty is the reason to write one down.

And when a plan turns out to be the durable artifact — when the same decisions and their rejected alternatives need to survive past the session that produced them, into the next agent's context — it stops being a plan and starts being a spec. That's the point where drift between spec and code becomes the thing you're managing, and it's a different problem with different tooling.

Keep reading