The Nine Step Workflow I Use to Ship AI Written Code

I keep getting asked the same question, both in DMs and in person: what does your coding workflow actually look like now?
It is a fair thing to ask, because the honest answer used to be embarrassing. For a while my workflow was to open a repo, describe what I wanted, accept whatever came back, and then spend the rest of the afternoon finding out what else it had quietly changed. That is what most people mean when they say vibe coding. It feels incredible for about forty minutes and then it starts costing you.
What I run today is not more sophisticated in the sense of using fancier tools. It is just a lot more disciplined. There are nine steps, and the uncomfortable part is that the agent only really owns one of them. The other eight are yours.
1. Put an AGENTS.md at the root of the repo
Build commands, the architecture rules that actually matter, and the gotchas that take a new engineer two weeks to learn by getting burned. Write the dangerous ones in caps, because that is the line you want the model to trip over on its way to doing something stupid.
Keep it short. This is the part people get wrong. Past a couple hundred rules the model just starts ignoring them, and you have no way of knowing which ones it dropped.1 A tight file of thirty rules that get followed beats a beautiful three hundred line document that gets skimmed.
2. Make it read before it writes
Every serious tool has a plan mode now, and you should be living in it. For anything non-trivial, spawn subagents to do the exploring. One maps the upload path, one finds the existing parser, one lists everywhere a value gets computed.
The reason this works is not that subagents are smarter. It is that each one gets a clean context window and hands back a summary, so your main session stays light. Most of the time an agent goes off the rails it is because the context got polluted, not because the spec was bad.2
3. Write a one page spec
Goal, acceptance criteria, non-goals.
Almost nobody writes non-goals, and it is the section that saves you. Agents are enthusiastic. Leave the boundary undefined and you will end up maintaining three bonus features you never asked for, each of which now has tests and a migration. If a competent engineer could read your spec and build the wrong thing, an agent definitely will.
4. Decide how you will evaluate it before you build it
If your feature calls an LLM, you cannot write a normal assertion against the output. So go collect real examples, label the correct answers by hand, and accept that this boring afternoon of labelling is your actual test suite.
Tag every case and report accuracy per tag. A single average across clean inputs and messy ones will tell you everything is fine right up until the day it is not. Prefer deterministic scorers wherever you can get away with them.
And never let the agent write to your expected outputs file. That is the one file it can quietly fix to make everything pass, and it will, because you asked it to make the tests green and that is technically a way to do it.3
5. One task, one session
Clear the context in between. Stale reasoning from task three quietly poisons task four, and you lose an hour working out why the agent keeps referencing a decision you already reversed.
The handoff lives in a task file with checkboxes, not in the session history. That file is what makes a cold start work. For genuinely independent work, use git worktrees, so each agent gets its own branch and its own folder.4 Only do this when the tasks touch different files, because two agents editing the same file gives you a merge you will hate.
6. Verify in layers
A reviewer agent that did not write the code, then CI, then a PR bot, then you actually reading the diff.
Ask the reviewer something narrow. "Give me the top five risk areas with file and line, and tell me what tests are missing" gets you something usable. "Review this for issues" gets you mush that reads like a checklist and catches nothing.
Even with all of that, the bots catch under half the bugs that matter, so you cannot stop there.5 The diff still needs a human reading it.
7. Ship it quietly first
Behind a flag, on real traffic, with nobody seeing it. Give it a week and compare it against whatever it is replacing.
When you do turn it on, keep the human correction step in the UI permanently, even after the accuracy gets good. Every correction a user makes is a free labelled example, delivered by someone who knows what the right answer was. You will not get better training data than that anywhere else.
8. Trace everything in production
Watch how many times a human has to step in and fix the output. That number is your real quality metric, and it is the only one that survives contact with actual users.
Log the prompt version and the model version on every single trace. Without those two fields you will see a regression and have absolutely no way to attribute it to anything. Pick one observability platform and commit to it. Langfuse, Braintrust and LangSmith all do the same job, so running two just splits your data in half and leaves you querying the wrong one.
9. Close the loop
Something breaks in production, you label it, and it becomes a permanent test case. Do this consistently and your eval set grows from fifteen cases to a couple of hundred over six months, entirely from real failures rather than from things you imagined might go wrong while sitting at your desk.
Same with AGENTS.md. Every time the agent does something dumb that a rule would have prevented, add the line. Every time a rule stops being true, delete it.
The Part Nobody Wants to Hear
Look at that list again and notice where the work actually is.
Writing the context file is yours. Writing the spec is yours. Building the eval set is yours. Managing sessions is yours. Reviewing the diff is yours. Rolling it out safely is yours. Instrumenting production is yours. Feeding failures back is yours.
The agent writes the code. That is one step out of nine, and it is the step that was never the hard part.
Vibe coding is drafting. Engineering is verifying. The tools got extraordinarily good at the first one, which is exactly why the second one is now the whole job.