A vague brief gives an agent room to fill every gap with plausible improvisation. The result can compile, pass the tests that exist, and still solve the wrong problem. The failure starts before implementation.
The issue has never been coding speed. If speed was the key to great engineering, all interviews would test your typing speed. Engineering practices exist for a reason. And agentic AI has an interesting effect: it’s exposing what doesn’t work.
Weak tests, large iterations, slow feedback, and unclear specifications all give mistakes room to compound. Agents make that existing process problem visible because they can produce a large diff before a human notices the assumption underneath it.
Engineering Practices Matter
Small rant: up until recently, there was no realistic way to prove what were best practices. You could say SAFe was a steaming pile of turds, but people whose pay depended on it being important couldn’t or wouldn’t see it. DORA metrics appeared, but most of the industry lagged behind even knowing they existed.
AI agents have become the ultimate forcing function. If your processes are bad, the agent amplifies the badness at machine speed. If your processes are good (small iterations, clear specs, fast feedback, comprehensive testing), the agent amplifies that too.
I suspect the best results come from applying classic software engineering discipline to AI collaboration rather than inventing a separate category of “AI workflows”. This is the part of AI-assisted development I think teams most often underinvest in.
The Normal Coding Flow
So what does this look like in practice? Here’s the workflow that consistently produces good results for me.
It starts with brainstorming a detailed specification with the AI. Describe the idea and ask the LLM to iteratively ask you questions until you’ve fleshed out requirements and edge cases. By the end, compile this into a comprehensive spec.md. The catch, and the bit where skill matters, is that you can’t generate a spec for the full system. It won’t fit in the context. Scope management is everything: feed the LLM manageable tasks, ideally self-contained vertical slices from UI to database.
Once the spec exists, break the plan into small, logical, bite-sized tasks. You can iterate on this, editing and asking the AI to critique or refine it. Each task should be small enough for a subagent to handle within its context window.
Then implement each task, in order, committing results. Because the tasks are small, the implementation is straightforward. Small tasks are easier to review. The output is stochastic. You need to verify that the code is what you expect and the tests make sense. Small commits with good messages document the development process, which helps during code review, and as Simon Willison puts it, if you open a PR with hundreds of lines of unreviewed agent code, you’re delegating the actual work to your collaborators.
Finally, provide a way for the AI to check its own work. CI/CD, linters, type checkers, test suites, etc. These become your feedback loops. The agent writes code, the automated tools catch issues, the agent fixes them, with you overseeing the high-level direction.
The principle underlying all of this? Don’t make the AI operate on partial information. Do a “brain dump” of everything the model should know: high-level goals, invariants, examples of good solutions, warnings about approaches to avoid. If you’re using a niche library, paste in the docs. If you know naive solutions that are too slow, say so. I’ve found that the ten minutes spent on context engineering saves hours of rework downstream.
The Annotated Plan Workflow
The normal flow works. But is there a way to make it better?
Boris Tane, the author of the “SDLC Is Dead” essay, has a workflow that I believe is the most refined version of the research-plan-implement pattern.
Phase 1: Research. Deep-read the task directive. The agent examines the codebase thoroughly. You push it with explicit language like “deeply,” “in great detail,” “go through everything”. Findings go into a persistent research.md file. Not a verbal summary in chat. A real artefact you can review.
Phase 2: Planning. The agent writes plan.md as a real artefact. Not using the built-in plan mode but producing an actual file you can open in your editor. Plans include the approach, code snippets, file paths, and trade-offs considered.
Phase 3: The annotation cycle. This is where the work actually happens. You open the plan in your editor and add inline notes:
“Use drizzle:generate for migrations, not raw SQL.” “No, this should be PATCH, not PUT.” “Remove this section, we don’t need caching here.”
Then tell the agent: “I added notes, address them, don’t implement yet.”
One to six rounds of this. Each round, the plan gets more precise. The explicit “don’t implement yet” guard is essential: without it, the agent jumps to code the moment it thinks the plan is good enough. By the time implementation starts, the important decisions have been reviewed and recorded.
Phase 4: Implementation. Boring by design. The plan is so detailed that implementation is mechanical execution. This is the goal. All the interesting decisions happened in phases 2 and 3.
Is this overkill for every task? Of course. A quick bug fix or a small refactor doesn’t need six rounds of annotation. It’s a judgement call. But for anything that touches multiple files or introduces new patterns, the annotation cycle pays for itself. The trade-off is time spent planning vs time spent unwinding bad assumptions, and in my experience, bad assumptions always cost more.
The Night Shift Approach
What if you took the annotation cycle and removed yourself from the loop entirely?
That’s essentially what Jamon Holmgren does with his Night Shift workflow. He writes specs during the day with no AI help beyond quick lookups and a concise gap checklist, then hands them to agents overnight. The specs organise his own thinking and document the human intent and known constraints well enough for the agent to develop its own plan while he is away.
This is the annotated plan workflow taken to its logical conclusion. The specs are the plan. And the discipline of writing specs that stand on their own, without you there to clarify, turns out to be the hard part. Holmgren says it gets easier with practice, and he’s likely right, as writing specs and detecting gaps is becoming easier the more I use agents for coding.
I haven’t fully adopted the overnight loop, though. I’m not disciplined enough yet to produce the level of specs needed without human correction, but the spec-writing discipline has noticeably improved my own planning phase. Even if you never run agents overnight, writing specs as if you won’t be there forces a rigour that improves everything downstream.
Separate Planning From Execution
The critical principle running through all of this is a reviewed plan before implementation. Whether that boundary also needs a fresh session is a separate choice. Tane deliberately keeps research, annotation and implementation in one long session because he values the understanding accumulated there. I often save the plan to disk and start a fresh session when the research has filled the context with tool output and discarded alternatives.
That variation trades some accumulated conversation for a clean window. The durable plan makes the hand-off reviewable and gives you a recovery point if the agent crashes or wanders. You can also ask a fresh instance to review the specification for gaps before implementation, without pretending that clearing context is always the right move.
Review Before Proceeding
Always review the result before committing. Challenge the important decisions and ask why a particular approach was chosen. Plausible output is not proof of correctness.
Line-by-line manual review becomes harder as generated changes grow, but review itself is not optional. Shift effort towards the specification, contracts, tests, security checks, focused diff review, and runtime verification. A bad specification can generate the wrong code in subtle ways; a good specification still does not prove that the implementation is safe or correct.
The good news is that rewriting code with an LLM is cheap. If the approach is wrong, throw it away and try again with better constraints in your spec. The cost of regeneration is tiny compared to the cost of shipping something broken. This is a genuine mindset shift: code is now disposable. The spec and the tests are the valuable artefacts.
The pattern is deceptively simple: research thoroughly, plan carefully, annotate until it’s boring, then let the agent execute. The discipline isn’t in the tools. It’s in resisting the urge to skip straight to implementation, which, if we’re honest, is the urge that got us into trouble with software engineering long before AI entered the picture.