Doc-driven development

ddd is a skill for AI coding agents. You ask for what you want, as you already do. The agent writes it down in plain-language markdown docs first, then writes the code to match. The docs become a readable record of how the app works and how it changed.

The idea

The code is not the point anymore

AI writes the code now, and most of us no longer read it. What still matters is what the app does, and that's best said in plain language.

So ddd makes the docs the source of truth. You keep asking the agent for what you want; it updates the docs first, then the code. You don't have to write or edit the docs, but you can read them to follow along, and so can anyone on your team.

Structure

A folder of linked docs

Everything lives in docs/. Each folder answers one question, so there's always one obvious place to look. Docs link to each other with [[wiki-links]], and frontmatter records what each doc depends on. Everything outside plan/ is built and live; everything inside it isn't built yet. Pick a file to read it.

  • docs/
    • components/
      • guest-list.md
    • data/
      • dinner.md
      • person.md
    • features/
      • create-dinner.md
    • integrations/
    • pages/
      • my-dinners.md
    • plan/
      • reminders.md
      • done/
        • guest-notes.md
      • ideas/
    • .tech/
docs/features/rsvp.md
---
type: feature
uses: [data/rsvp, data/dinner, integrations/email]
shown-on: [pages/dinner]
---
# Reply to an invite

A guest opens their invite and says
whether they're coming.

## Who
- Anyone with the invite link.
- Guests don't need an account. They give
  their name and email once.

## Rules
- Replies close when the dinner starts.
- Each invite is for one person.
- If the dinner is full, new "going"
  replies join a waitlist.

## After
- Host gets [[integrations/email#new-reply]]

## Acceptance criteria
- Given a dinner with seats left, when a
  guest replies "going", then they appear
  on the guest list.
- Given a full dinner, when a guest
  replies "going", then they join the
  waitlist.

## Open questions
- Should guests see who said maybe?
FolderAnswers
app.mdWhat the app is, who uses it, the words it uses
roadmap.mdThe order plans get built in: phases, in priority order
data/What the app remembers
features/What the app does, its rules, and how to tell it works
pages/What people see, top to bottom
components/Pieces reused across pages
integrations/What it talks to: sign-in, email, payments, APIs
plan/Committed work that isn't built yet: new features, extensions, changes
plan/ideas/Loose ideas, not committed and not on the roadmap
plan/done/Plans that were built and merged, kept as a record
.tech/Technical decisions, for whoever reads the code

Workflow

Docs first, then a plan, then the code

Most changes start as a message to the agent. It finds the docs the change belongs in, updates them, explains what that means for the app, and asks before touching the code. Here the request matches something already on the roadmap, so the agent builds that plan instead of starting from scratch.

~/Sites/dinner-club — claude
> /ddd let guests bring a plus-one

⏺ Bash(python .claude/skills/ddd/scripts/docs_status.py status)
  ⎿  No changes since last apply (17 docs).

⏺ This is already planned: plan/plus-ones.md (phase 2).
  It extends features/rsvp.md, so building it means merging
  its rules into rsvp.md. Updating the docs first.

⏺ Update(docs/features/rsvp.md)
  ⎿  1 line changed, 2 added
     15  - Replies close when the dinner starts.
     16 - Each invite is for one person.
     16 - Guests who are going can bring one plus-one.
     17 - A plus-one takes a seat. If there's room for the
     18   guest but not the plus-one, both join the waitlist.
     19  - If the dinner is full, new "going"

⏺ Update(docs/data/rsvp.md)
  ⎿  1 line added
     13  | note     | optional message to the host |
     14 | plus-one | their guest's name, if any   |

⏺ Plan

  What changes for people using the app
  - Guests replying "going" can add a plus-one's name.
  - Plus-ones take a seat, so "seats left" counts them.
  - Plus-ones show on the guest list under who brought them.

  Also affected
  - pages/dinner.md and components/guest-list.md: I'll add
    the plus-one line to the guest list.

  Question
  - The plan leaves one open: can a host turn plus-ones off
    for a dinner? If that can wait, I'll build them as always
    allowed and keep the question on the roadmap.

  When it's done
  - plus-ones.md is merged into rsvp.md, then moves to
    plan/done/ as the record of the change.

  Code: 5 files, 2 tests added.

Apply these changes to the code?
❯ 1. Yes, apply
  2. No, keep the doc changes for later
  3. No, undo the doc changes

If you edit a doc yourself

This is optional, but sometimes it's the quickest way to fix a rule or reword a label. No server watches your files. At the start of every turn the agent runs a status check that compares docs/ against the last build. If you changed something, even in another editor and without mentioning it, the agent sees the diff, checks which other docs it affects, and proposes the same kind of plan.

The rules the agent follows

  1. Docs first. Every change that affects behavior lands in docs/ before it lands in code. The agent turns each request in chat into a doc edit.
  2. Plan, then apply. The code is never rebuilt silently. The plan leads with what changes for users, not which files change.
  3. Behavior, not implementation. Docs say what happens, in words you'd use in a meeting. Technical detail goes in .tech/.
  4. Acceptance criteria are the contract. Each feature ends with Given / When / Then lines, which become tests. They pin down what the prose means.
  5. Open questions stop the build. If a doc leaves something undecided, the agent asks instead of guessing.
  6. Contradictions are surfaced. If two docs disagree, the agent asks which one wins.

Reviewing a change

The same change, two ways to read it

Here is the plus-one change from the session above, as a teammate would review it. Both come from the same update. The code diff is still there for whoever wants it; the doc diff is the one anyone can read.

Doc diffdocs/features/rsvp.md · 3 files changed, +6 −1
## Rules  - Replies close when the dinner starts.- - Each invite is for one person.+ - Guests who are going can bring one plus-one.+ - A plus-one takes a seat. If there's room for the guest but not the plus-one, both join the waitlist.  - If the dinner is full, new "going" replies join a waitlist. ## Acceptance criteria+ - Given a guest going with a plus-one, then "seats left" drops by two.

Planning

Plan the whole app, build one feature at a time

The docs can describe what the app will do, not only what it does today. Describe everything you have in mind and the agent writes it up as plans in plan/, grouped into phases, without building anything. Then it works through them one at a time.

The folder shows the stage

StageWhere it lives
Ideaplan/ideas/, a few lines, not committed and not on the roadmap
Plannedplan/, with rules and acceptance criteria written, on the roadmap
In progressplan/, marked status: in-progress, one at a time
Builtfeatures/, data/, pages/, and the rest

So you can tell what's built and what's coming from the folder tree, without opening anything. The order lives in roadmap.md: each phase is a checklist of plans and plain tasks, top to bottom. Phase 1 is the smallest version someone would actually use. Open to see one. Ideas aren't on it; is the list of them, and promoting one means moving it up into plan/ and adding it to the roadmap.

Working through it

Each /ddd next takes the next unchecked item through the same docs → plan → code loop: mark it in progress, ask any open questions, build it and run its tests. Then its doc moves out of plan/ to where it now belongs, the roadmap item is checked off, and the agent reports and stops.

~/Sites/dinner-club — claude
> /ddd next

⏺ Bash(python .claude/skills/ddd/scripts/docs_status.py next)
  ⎿  Phase 1: Core: plan/reminders
       docs/plan/reminders.md, status: planned

⏺ Next up is reminders: guests who said going or maybe get
  an email the day before. Done means its two acceptance
  criteria pass. One open question first:

  - Should guests who said maybe get the reminder too?

> yes, both

⏺ Update(docs/plan/reminders.md)
  ⎿  status: planned → in-progress; open question resolved

  … plan approved, code and tests written …

⏺ Bash(npm test -- reminders)
  ⎿  2 passed

⏺ Bash(git mv docs/plan/reminders.md docs/features/)
⏺ Update(docs/features/reminders.md)
  ⎿  status line removed: it's built
⏺ Update(docs/roadmap.md)
  ⎿  - [x] [[features/reminders]]

⏺ Reminders are done. Phase 1 is 3 of 4: the sending
  domain is the last item. Say "next" to continue.

Changing a feature that's already built

A built feature's doc describes only what's live, so you can trust every rule in it. Work that will change it is a plan of its own in plan/, until it's built. There are two kinds, both in the file browser above:

When one is built, the agent writes it into the docs it targets, which produces the doc diff in the Review section above. The plan then moves to plan/done/ as the record of why the rule changed. If the RSVP doc changes before a plan is built, the status check flags the plan so it gets re-checked against the current rules.

A few rules keep the plan honest:

Change tracking

How the agent knows what changed

The skill includes a small Python script, standard library only. It keeps a hash and a copy of every doc as it was at the last build, in docs/.state.json and docs/.state/.

CommandWhat it does
statusDocs added, changed or removed since the last build; which other docs link to them; broken links. Edits inside plan/ are listed as plan only, and plans whose target changed are flagged to re-check
diff <doc>Line diff of one doc against its last built version
mark-appliedRecords the current docs as built, after the code and tests pass
restore <doc>Puts a doc back to its last built version
roadmapProgress per phase, plus anything out of sync: an item checked but still in plan/, a plan missing from the roadmap, a link to a doc that moved
nextThe item to work on: the one in progress, or the first unchecked one

Install

Add it to your agent

With the skills CLI, from your project folder (needs Node 22.20 or later):

npx skills add fabiogiolito/ddd

It asks which agents to install it for: Claude Code, Cursor, Codex and others. Add -g to install it for every project, or -a claude-code -y to skip the questions. npx skills update ddd gets the latest version.

Claude (web and desktop)

Download ddd.skill and add it under Customize > Skills.

By hand

Copy skills/ddd/ from the repo into .claude/skills/ in a project, or ~/.claude/skills/ for all of them. Other agents can read skills/ddd/SKILL.md directly; it's plain markdown.

Usage

/ddd on its own reports what changed in the docs. /ddd plan <idea> writes plans and phases without building. /ddd next builds the next roadmap item. /ddd roadmap shows progress. Anything else after /ddd is a request: start an app or make a change.