The Blocker Is Me
TL;DR — "Plan first" is advice at the level of telling a seven-year-old to brush their teeth before bed. Correct, useless. The real problem is that a generated plan looks exactly like a decided one, and after two weeks away you can't tell them apart — including your own. So every planning artifact in my setup has to show its reasoning: a backlog position justifies itself against the item below it, a coding rule names the failure it prevents, and an issue's type decides the shape of the spec that follows it.
I'm a customer engineer at Google Cloud. I have two kids. Until recently I was also a graduate student. Building a personal project on top of that sounded like a joke.
Agentic coding tools made it possible. Not "more productive" — possible, in the sense that a thing which could not exist now exists. The throughput constraint that had blocked every side project I'd ever started simply went away.
And then a different constraint showed up, which I did not expect and did not enjoy discovering.
The blocker is me. Not my time — my memory. I come back to my own repository after two weeks and I cannot reconstruct what I decided or why. The code is there. The commits are there.
It's worth being precise about where that time actually goes, because the obvious answer is the wrong one. It isn't that the project is written in English rather than Japanese — that costs something, but it's marginal. The load is the catch-up read. Every session opens with going back through the repository to work out where I stopped: which decisions are settled, which are still open, what the previous me was in the middle of. That happens before any work does, and on a weekend where I have three hours, it can take most of them.
What the catch-up read can't recover is the reasoning. Without it I can't tell whether the plan I'm looking at was something I thought hard about or something that merely looked right when it was generated.
That distinction turns out to be the whole problem.
"Plan first" is not advice
Every article about working with AI agents arrives at the same recommendation: plan before you generate. Write a spec. Think it through.
This is true in the way that telling a seven-year-old to brush their teeth before bed is true. Nobody disagrees. Nobody is helped.
The reason it doesn't help is specific. Generation is now cheap, which means a plausible plan costs nothing to produce. I can ask for a PRD and get a well-organised document with sensible headings in twenty seconds. It will have a user story. It will have acceptance criteria. It will look exactly like a document produced by someone who thought carefully about the problem.
Reviewing it doesn't separate the two either, because a confident wrong requirement reads identically to a correct one. The document is the same shape both ways. That's why "review your specs" is also not advice.
So the question isn't whether to plan. It's how to tell, later, whether the planning actually happened.
Make the artifact show its reasoning
The answer I landed on: every planning artifact has to contain a field that can't be filled in convincingly without doing the thinking.
Not a field that records a conclusion. A field that records the comparison.
The clearest case is the product backlog. Scrum says it's ordered — deliberately not "prioritized," because ordering by priority alone is one technique among several, and a position has to absorb value, risk, cost, and dependency at once. Mine has one column that does the work:
| # | Item | Blocks | Why here |
|---|---|---|---|
| 1 | ... | #3 | ... |
| 2 | ... | — | why above #3 specifically |
Why here is not a description of the item. It's a justification of the item's position relative to the one below it. You cannot write that convincingly without having actually made the comparison, and a model asked to fill it in produces something visibly hollow. A priority label — P1, High — has the opposite property: it's cheap to generate and impossible to argue with.
The development contract works the same way. Every rule in it must name the failure it prevents. Not "prefer explicit error handling" but "a catch that logs and continues converts a failure into a silent wrong answer." A rule with no stated failure is taste, and taste loses every argument it's ever in — so the field forces the question why do we actually believe this at the moment the rule is written, when it's cheap, rather than during a code review eighteen months later, when it isn't.
The one that surprised me
The example I'd point at if I could only pick one is the issue template, which is the most boring technology in this entire setup.
My issues come in three types: feature, bug, chore. That's a classic taxonomy and it has been in every tracker for twenty years. What's new is that the type decides the shape of the spec that follows it.
A feature spec opens with a user story and numbered acceptance criteria. Its first task is a failing test for the new behaviour.
A bug spec doesn't have a user story, because there isn't one — there's a thing that is broken. It opens with reproduction, expected versus actual, and the root cause stated as a mechanism: not "a null check was missing" but why the value was null on a path nobody expected. Its first task is a regression test that fails for the right reason.
A chore spec is built around a single line: what must not change. Without a stated invariant, a refactor and a rewrite are the same diff, and no reviewer can tell them apart. Its first task is writing tests for behaviour that must be preserved but currently has none — so that "nothing broke" is an observation rather than an assertion.
Before I made this distinction, my tooling pushed every issue through the feature template. A bug came out the other side with a user story that didn't exist and acceptance criteria that were fiction. The spec looked complete. It tested the wrong thing.
The template is upstream leverage. Three markdown files decide the quality of everything downstream of them, which is a much better return than any amount of care applied later.
"Professionals don't vibe-code"
I've seen this said, and I think it's right — but only once you say what makes work professional, because as a bare claim it's an appeal to status, and those lose arguments the same way taste does.
It isn't the job title and it isn't the size of the team. It's whether anyone has to come back to the thing — including you, in a month, having forgotten all of it.
Vibe-coding is genuinely good at the case where nobody does. Describe what you want, take what comes back, read it once, throw it away. For a prototype whose only job is to tell me whether an idea is worth pursuing, every document in this post is waste, and I'd say so out loud.
What changes when the code has to be returned to isn't the difficulty of writing it — agents write it either way. What changes is that the reasoning becomes load-bearing, and vibe-coding produces none. The code runs, and nobody can say why it's shaped that way, whether the shape was chosen or just fell out, or what breaks if it changes. That isn't a debt you pay down by reading the diff more carefully later. The information was never recorded.
So: professional work is work that outlives its author's memory. Spec-driven development is what it costs to make that true — and the cost is smaller than the phrase makes it sound.
The minimum set
I have more artifacts than this — a north star, epics, a written development contract. They're useful and I'd write them again.
But they're not the minimum, and saying so matters, because "here is my thirteen-document process" is the fastest way to get a good idea dismissed as over-engineering.
The minimum mandatory set is six documents and three templates:
textPRD → design doc → backlog → Issue → spec → worklog ↑ issue templates: feature / bug / chore
The templates sit inside that chain rather than beside it, because the Issue step is where the type gets decided and the type is what shapes the spec. Drop them and the chain still runs — it just produces feature-shaped specs for bugs, which is the failure above.
Everything else is optional. And the middle of that chain isn't mine — it's GitHub's. Issues, branches, pull requests. These are excellent tools and they work from pair-programming scale upward; you do not need a team of thirty to benefit from an issue that closes when its PR merges.
The rules I hold to are small:
- One issue = one spec = one branch = one PR. The four share a slug, and the PR closes the issue.
- No issue, no spec. If there's no issue, the work was never chosen — it's unplanned work entering through a side door. My tooling refuses to write a spec without one rather than quietly creating one, because a plan stops meaning anything the first time it's bypassed silently.
- A backlog item is not an issue. One item routinely becomes several — the change, the test coverage it turns out to need, the migration it forces — and those are different types, so they get differently shaped specs.
What's tested, and what's next
The enforcement layer is tested: ten behaviours of the gate that blocks a turn when a finished branch has no review, checked deterministically with no model involved. Both plugin manifests, the rulebook hashes, and a guard against leaking private context all run in CI.
The skills themselves are in a different state, and the verb matters. I am testing them in the ordinary sense — running them, watching where they go wrong, changing them. What I can't say is "tested" in the sense I just used it about the gate. There's no end-to-end run to point at, no reviewer has reviewed a real diff yet, and the eval suite is written but has never been executed, because the tooling for it is in early access and I don't have it.
The way papers put it: this part is future work, and I mean that in the hopeful sense rather than the apologetic one. I'm still working out what the right shape is, and the next things to find out are specific. Whether a reviewer holding a rulebook catches what a reviewer without one misses. Whether "why here" stays honest once the backlog is long enough to be annoying. Whether the three spec shapes hold up against an issue that is genuinely ambiguous about its type. Each of those is answerable by running it, and that suite exists so the answers come back as numbers rather than impressions.
I'm stating that plainly because a post arguing that artifacts should show their reasoning would be a poor place to hide which parts are evidence, which are design, and which are still open.
Let the agents remember
Here's the part I didn't predict. The artifact that has helped most is the work log — an append-only file, one entry per session, with what happened, what I decided, and why.
I expected the specs to be the valuable output. For someone with contiguous time, they probably are. For someone who touches the project every other weekend, the thing that rescues the session is the record of what I was thinking last time.
Which reframes the whole setup, and is the only conclusion I'd actually defend: this isn't discipline, it's externalised memory.
The steering files, the specs, the work logs, the issue that records that a piece of work was chosen — none of it exists because process is virtuous. It exists because I cannot hold this project in my head, and neither can a fresh agent session. Written down in a form that shows its reasoning, both of us can pick it up cold. The catch-up read doesn't go away — but it becomes a read of what was decided and why, rather than an excavation of the diffs to infer it.
I don't have to remember everything. That's the point. Let the agents work.
The setup is a plugin for Claude Code and Google Antigravity: gate-oriented-sdd. It's a reference implementation, not a product — the version matrix in the README says what's actually been tested against what.