BMAD: plan before writing code
If I just write code, I lose the thread by week three. If I over-plan, I lose it sooner. I had to learn to plan without drowning in planning.
If I just write code, I lose the thread by week three. If I over-plan, I lose it sooner. I had to learn to plan without drowning in planning.
The context
This repo isn’t a one-evening project: 9 epics, around forty stories. I’ve carried it solo — no team, no company roadmap, no external deadline. That freedom is also the easiest trap for a personal project: the next “interesting” thing displaces the current one every Friday night.
The only thing I knew that fought that was a lightweight planning discipline. BMAD is the one I adopted — a method with short phases and versioned artefacts kept next to the code.
The decision
BMAD is just nine phases in strict order, each with a versioned artefact:
analyze → plan → architect → stories → validate
→ sprint → dev → review → qa → retro
analyzeyields a brief (_bmad/briefs/*.md): what we want, why, what’s out.planyields the PRD (_bmad/planning/prd.md): concrete requirements, metrics, gates.architectyields architecture.md: layering, protocols, threading model.storiesslices the architecture into small epics and stories (_bmad/epics/,_bmad/stories/). One story per PR.validateis the PO sweep: no orphan stories, no requirement without a story.sprintgroups stories into a sprint.dev,review,qaare the three implementation phases for a story.retrocloses the sprint with what’s done differently in the next one.
Each phase has a slash command (/bmad-analyze,
/bmad-plan, …). The crucial part: artefacts live in
the repo (_bmad/). Not in Notion, not in Linear. If
the plan doesn’t live next to the code, it goes stale
without anyone noticing.
Branches: one per story
Three branch kinds and that’s it:
| Branch | Role |
|---|---|
main | Stable releases. Never pushed to directly. |
staging | Integration. BMAD artefacts land here. |
feature/story-XX-XX-... | One per implementation story. |
Merges are always --no-ff (GitHub: “Create a merge
commit”). No squash, no rebase. The Git graph keeps the
bubble per story — you can see when a story started,
when it landed, what it reverted. Squash erases that;
rebase too.
More graph noise? Yes. That’s the price of visual
traceability. Two months later, when you’re searching for
when transport.serial changed and why, the graph shows
it; the squash doesn’t.
Why
ruff.extend-excludeis anchored to specific stories. Every legacy path excluded inpyproject.tomlcarries a comment pointing to the story that retires it. When the story completes, the exclude goes with it. Without that coupling, legacy paths become permanent: nobody dares touch them because “maybe we still need them”. With the coupling, the debt has an expiry date the day it’s incurred.
The real cost is higher than I would have admitted before. Every feature carries a mini brief, two review passes (code + QA), and a retro at the sprint. If this were just code, the first days would go faster.
The reward: epic 09 (aligning the vocabulary to
Lumware) was planned, executed, and merged in hours.
The entire python2arduino → lumware rename, docs,
imports, tests. Not because the work was small, but
because the structure to do it already existed. Without
BMAD it would have been a week of bugs.
What comes next
We’ve covered the whole system. What’s left is why it carries the name it does and why the color palette is R/G/B/W and not something else. Next post: the spectrum identity.