Planning with GSD
Most thrash in a build doesn't come from hard code. It comes from code you wrote against a target nobody pinned down. You implement a feature, the requirement shifts under you, and you rewrite it twice. The fix is a planning loop that forces the decisions out of your head and onto disk before an agent touches the keyboard. The mental model: an autonomous coding agent is only as good as the spec you hand it. Vague spec, vague output. A precise, version-controlled blueprint turns Claude from a guesser into an executor.
Call the loop what it does: Understand → Plan → Write the Spec → Define "Done" → Build. The first four are cheap and the last one is where you'd otherwise bleed days, so the trade is lopsided in your favor.
Understand, then write two files
Understanding is interrogation, not prose. Pin down the problem, the actual user, what already exists and why it falls short, your constraints, and the scariest unknown. One or two sentences each.
Then you produce two artifacts, and the split matters: PRD.md is for humans — what, who, why, and what "done" means. spec/plan.md is for the builder — architecture, schema, route map, ordered steps, and risks. Keep them separate because they change for different reasons. A stakeholder edits the PRD; you edit the plan when the implementation order shifts.
The single highest-leverage section is the implementation order. Make every step a day or less, and front-load the skeleton so the agent can run something end-to-end early:
## Implementation Order
1. Next.js (App Router) + Tailwind skeleton, deploys to Vercel preview
2. Supabase project + `profiles` table, RLS enabled
3. Dashboard route with hard-coded fake data
4. Swap fake data for a Supabase server-side query
5. Build the create-record server action
Write the spec, then define "done"
The spec isn't real until it's committed. One git commit gives the plan history, lets it survive a bad refactor, and makes it something a teammate — or tomorrow's you — can read. Don't skip it.
Defining "done" is where most specs go soft. "Works well" isn't checkable; a passing test, a rendered screen, or a queryable number is. Bake the baseline straight into the PRD:
## Done When
- [ ] No horizontal scroll at 375px width
- [ ] Browser console clean across every screen (zero red errors)
- [ ] Primary flow completes unattended: sign up → create → edit → delete
- [ ] Dashboard aggregates match the underlying Supabase rows
- [ ] RLS verified: logged-in users see only their own rows
- [ ] `.env` is gitignored; no secrets in history
- [ ] One push to `main` produces a clean Vercel build
Note the Supabase-specific teeth: "auth works" becomes "RLS verified, users see only their own rows." That's a check you can actually run.
Pitfalls
- Scope creep in the PRD. If the core feature list grows past three to five items, you haven't finished deciding. Tell Claude to cut to three and shove the rest into Out of Scope.
- Security as a bolt-on. Auth model, secret handling, and input validation belong in Done When now. Retrofitting RLS or moving keys to
.envafter the build is far more expensive than five minutes up front. - A plan that drifts from reality. If you started building without one, don't fake a clean spec — have Claude read the current tree and write the PRD to match what exists plus where you're headed.
- Treating the plan as frozen. It's version-controlled precisely so you can amend it. When reality contradicts the spec, update the spec and commit — don't let code and plan silently diverge.
⌨️ Exercise
Pick a real tool you'll actually build. In its repo, write PRD.md and spec/plan.md by hand (or run /kickoff). Constrain the core feature list to exactly three. Break the implementation order into steps no longer than a day each, with step 1 being a deployable Next.js skeleton. Rewrite every "Done When" item until it's observable, and include at least one Supabase RLS check phrased as something you can query. Commit both files, then confirm with git log --oneline -5. Done means zero remaining [brackets] in either file.