Skip to main content

Chapter 23: Repository Architecture for SDD


Learning Objectives

By the end of this chapter, you will be able to:

  • Describe the canonical SDD repository structure and the purpose of each directory
  • Explain why separating intent (specs/) from implementation (src/) matters
  • Organize specifications into global, feature, constraint, and template categories
  • Use the memory/ directory for constitution, ADRs, and context files
  • Apply feature branch workflow with specs/[branch-name]/ structure
  • Version control specifications alongside code effectively
  • Set up a complete SDD repository from scratch through a hands-on tutorial
  • Handle monorepo considerations: shared specs and package-level constraints
  • Migrate an existing project to SDD structure using a step-by-step guide

The SDD Repository Structure

A Spec-Driven Development repository is not a conventional codebase with specs bolted on. It is a specification-first structure where intent drives implementation. The layout reflects this hierarchy: specifications are the source of truth; code is derived from them.

The Canonical Layout

repo/
├── specs/ # Specifications — the source of truth
│ ├── global/ # Project-wide specs
│ ├── constraints/ # Constraint documents
│ ├── templates/ # Specification templates
│ └── features/ # Feature specifications (or specs/[branch-name]/)
├── agents/ # AGENTS.md, skills, subagents
├── .cursor/rules/ # Editor configuration
├── src/ # Generated/implementation code
├── tests/ # Test suites
└── memory/ # Constitution, ADRs, context files

Each directory has a distinct role. Understanding them is the first step to building a maintainable SDD project.


Separating Intent from Implementation

The fundamental principle of SDD repository architecture is separation of intent from implementation.

Intent (specs/)

Intent lives in specs/. It answers WHAT and WHY:

  • What does the system do? (user stories, requirements, acceptance criteria)
  • Why does it do it? (problem statement, goals, non-goals)
  • What does it NOT do? (edge cases, constraints, boundaries)

Intent is stable. It changes when requirements change, not when implementation details change. A specification for "user registration" remains valid whether you implement it with PostgreSQL or MongoDB, REST or GraphQL.

Implementation (src/)

Implementation lives in src/. It answers HOW:

  • How is the feature built? (code, data structures, algorithms)
  • How does it integrate? (APIs, dependencies, deployment)

Implementation is derived from intent. When you refactor code, the specification does not change. When you change the specification, the code must change to match.

Why Separation Matters

Without separation:

  • Requirements drift into code comments
  • "Why did we do it this way?" becomes unanswerable
  • Refactoring risks breaking undocumented assumptions
  • AI and humans lack a single source of truth

With separation:

  • Specifications are the contract. Code is the fulfillment.
  • Traceability is explicit: every requirement maps to acceptance criteria, every acceptance criterion maps to tests, every test maps to code.
  • AI can generate correct code without guessing—it reads the spec.
  • Onboarding and handoffs are faster: read the spec first.

The specs/ Directory Organization

The specs/ directory is the heart of the repository. It contains multiple subdirectories, each serving a purpose.

Global Specs (specs/global/)

Project-wide specifications that apply across all features:

FilePurpose
project-overview.mdHigh-level vision, goals, scope
user-personas.mdTarget users and their needs
glossary.mdDomain terms and definitions
non-functional-requirements.mdPerformance, security, compliance (global)

Example: specs/global/project-overview.md

# Project Overview: TaskFlow

## Vision
TaskFlow is a productivity app for teams who need lightweight task management
without the overhead of enterprise tools.

## Goals
- Deliver core task CRUD in under 2 seconds
- Support 10,000 concurrent users on a single instance
- Mobile-first design (60% of users on mobile)

## Non-Goals (Out of Scope)
- Gantt charts and resource planning
- Custom workflows beyond status transitions
- Third-party integrations (Phase 1)

Constraint Documents (specs/constraints/)

Constraints that constrain all implementation:

FilePurpose
security.mdAuthentication, authorization, data protection
performance.mdLatency, throughput, resource limits
api-conventions.mdREST/GraphQL patterns, error formats
data-integrity.mdValidation rules, constraints, invariants

Example: specs/constraints/security.md

# Security Constraints

## Authentication
- All API endpoints require JWT (except /health, /login)
- Tokens: RS256, 15 min access, 7 day refresh
- No passwords in logs or error messages

## Authorization
- RBAC: admin, member, viewer
- Resource-level: user can only access own resources unless admin

## Data

- PII: encrypt at rest (AES-256)
- Passwords: bcrypt cost 12, no plaintext storage

Templates (specs/templates/)

Reusable specification templates for new features:

FilePurpose
feature-spec-template.mdTemplate for feature specifications
plan-template.mdTemplate for implementation plans
api-contract-template.yamlOpenAPI/contract template

Example: specs/templates/feature-spec-template.md

# Feature: {{FEATURE_NAME}}

## Problem
[What problem does this solve?]

## User Stories
- As a [role], I want [goal] so that [benefit]

## Requirements
- FR-001: [Requirement]
- FR-002: [Requirement]

## Acceptance Criteria
- AC-001: [Testable criterion]
- AC-002: [Testable criterion]

## Edge Cases
- [List edge cases]

## Non-Goals
- [What we explicitly NOT do]

## Completeness Checklist
- [ ] All [NEEDS CLARIFICATION] resolved
- [ ] Every requirement has acceptance criteria

Feature Specs (specs/features/ or specs/[branch-name]/)

Feature-specific specifications. Two naming conventions:

Option A: specs/features/

specs/features/
├── 001-user-registration/
│ ├── spec.md
│ ├── plan.md
│ ├── data-model.md
│ ├── contracts/
│ └── tasks.md
├── 002-login/
│ └── ...
└── 003-password-reset/
└── ...

Option B: specs/[branch-name]/ (feature branch workflow)

specs/
├── 001-user-registration/
│ ├── spec.md
│ ├── plan.md
│ └── ...
├── 002-login/
│ └── ...
└── 004-real-time-chat/
└── ...

The branch-name convention aligns with Git: git checkout -b 004-real-time-chat creates a branch; specs/004-real-time-chat/ holds that feature's specs. When merging, specs merge with code.


The memory/ Directory

The memory/ directory stores project identity and institutional knowledge. It is the "long-term memory" of the project.

constitution.md

The project constitution defines principles that AI and humans must follow. It is always loaded when relevant.

Example: memory/constitution.md

# Project Constitution: TaskFlow

## Principles

1. **Simplicity First**: Prefer the simplest solution. No premature abstraction.
2. **Test-Driven**: Every feature has tests. Tests before implementation.
3. **Fail Fast**: Validate inputs at boundaries. Fail early with clear errors.
4. **No Secrets in Code**: No API keys, passwords, or tokens in source.
5. **Convention Over Configuration**: Use project conventions unless there's a reason not to.

## Architecture

- Layered: API → Service → Repository → Entity
- Database: PostgreSQL, migrations via Flyway
- API: REST, JSON, OpenAPI 3.0

## Constraints

- No external API calls in repositories (use services)
- No business logic in controllers (use services)
- All errors must be logged with context

Architectural Decision Records (ADRs)

ADRs capture why decisions were made. They live in memory/adr/.

Example: memory/adr/0001-use-postgresql.md

# ADR 0001: Use PostgreSQL for Primary Storage

## Status
Accepted

## Context
We need a relational database for task management. Options: PostgreSQL, MySQL, SQLite.

## Decision
Use PostgreSQL.

## Rationale
- JSONB support for flexible schema evolution
- Strong ACID guarantees
- Excellent ecosystem (pg, Prisma, etc.)
- Team familiarity

## Consequences
- Requires PostgreSQL in deployment
- Migrations via Flyway

Context Files

Context files are checkpoint documents, cross-session summaries, or other artifacts that help restore context:

  • memory/checkpoints/ — Session checkpoints
  • memory/context/ — Domain summaries, integration notes
  • memory/phr/ — Prompt History Records (effective prompts)

Feature Branch Workflow: specs/[branch-name]/

The feature branch workflow aligns specifications with Git branches.

How It Works

  1. Create branch: git checkout -b 004-real-time-chat

  2. Create spec directory: specs/004-real-time-chat/ (or specs/features/004-real-time-chat/)

  3. Run Spec Kit: /speckit.specifyspec.md

  4. Run /speckit.planplan.md, data-model.md, contracts/

  5. Run /speckit.taskstasks.md

  6. Implement: Execute tasks, commit code and specs together

  7. Merge: Merge branch; specs merge with code

Benefits

  • Traceability: Specs and code live in the same branch
  • Review: PRs include both spec changes and implementation
  • Isolation: No spec pollution from other features
  • Rollback: Revert branch = revert spec + code

Directory Layout per Feature

specs/004-real-time-chat/
├── spec.md
├── plan.md
├── data-model.md
├── contracts/
│ ├── chat-api.yaml
│ └── websocket-events.yaml
├── research.md
├── quickstart.md
└── tasks.md

Version Controlling Specifications

Specifications should be version controlled alongside code.

What to Commit

  • All spec files: spec.md, plan.md, data-model.md, contracts/, tasks.md
  • Templates: specs/templates/
  • Global specs: specs/global/
  • Constraints: specs/constraints/
  • Memory: memory/constitution.md, memory/adr/

What to Ignore (Optional)

  • memory/checkpoints/ — Session-specific; may be ephemeral
  • research.md drafts — If they contain sensitive or temporary notes

Commit Message Conventions

feat(004): add spec for real-time chat
feat(004): add implementation plan
feat(004): T-001 ChatRoom entity
fix(004): correct message schema in contract

Branching Strategy

  • main: Production-ready code + specs
  • feature branches: specs/[branch-name]/ + implementation
  • Spec changes: Commit with code; avoid spec-only branches unless doing design review

Tutorial: Set Up a Complete SDD Repository from Scratch

This tutorial walks you through creating an SDD repository from an empty directory.

Prerequisites

  • Git installed
  • A code editor (Cursor or VS Code)
  • Basic familiarity with spec-driven workflow

Step 1: Initialize Project

mkdir taskflow-app
cd taskflow-app
git init

Step 2: Create Directory Structure

mkdir -p specs/global specs/constraints specs/templates specs/features
mkdir -p agents
mkdir -p .cursor/rules
mkdir -p src tests
mkdir -p memory/adr memory/checkpoints memory/context

Step 3: Create Global Specs

Create specs/global/project-overview.md:

# Project Overview: TaskFlow

## Vision
TaskFlow is a lightweight task management app for small teams.

## Goals
- Simple task CRUD
- Real-time updates
- Mobile-first UI

## Non-Goals
- Enterprise SSO
- Gantt charts

Create specs/global/glossary.md:

# Glossary

- **Task**: A unit of work with title, description, status, assignee
- **Board**: A collection of tasks (e.g., "Sprint 1")
- **Status**: todo | in-progress | done

Step 4: Create Constraints

Create specs/constraints/security.md:

# Security Constraints

- JWT for all API auth
- No passwords in logs
- HTTPS only in production

Create specs/constraints/api-conventions.md:

# API Conventions

- REST, JSON
- Errors: { "error": { "code": "ERR_001", "message": "..." } }
- Pagination: ?limit=20&offset=0

Step 5: Create Feature Spec Template

Create specs/templates/feature-spec-template.md (use the template from earlier in this chapter).

Step 6: Create Constitution

Create memory/constitution.md:

# Constitution: TaskFlow

## Principles
1. Simplicity first
2. No secrets in code
3. Test-driven

## Stack
- Node.js + Express
- PostgreSQL
- REST API

Step 7: Create First Feature Spec

mkdir -p specs/001-task-crud

Create specs/001-task-crud/spec.md:

# Feature: Task CRUD

## Problem
Users need to create, read, update, and delete tasks.

## User Stories
- As a user, I want to create a task so I can track work
- As a user, I want to list my tasks so I can see what's pending

## Requirements
- FR-001: Create task with title, description, status
- FR-002: List tasks with pagination

## Acceptance Criteria
- AC-001: POST /tasks returns 201 with task
- AC-002: GET /tasks returns paginated list

Step 8: Create .cursor/rules

Create .cursor/rules/sdd.mdc:

---
description: SDD project rules
globs: ["**/*.ts", "**/*.js"]
---

- Follow specs in specs/ for all implementation
- Use constitution in memory/constitution.md
- Write tests for every feature

Step 9: Create AGENTS.md

Create agents/AGENTS.md:

# TaskFlow Agents

## Default Agent
- Follows specs/ and memory/constitution.md
- Implements tasks from tasks.md
- Uses .cursor/rules for coding standards

Step 10: Verify Structure

tree -L 3 -I node_modules

Expected output:

.
├── agents
│ └── AGENTS.md
├── memory
│ ├── adr
│ ├── checkpoints
│ ├── context
│ └── constitution.md
├── specs
│ ├── constraints
│ │ ├── api-conventions.md
│ │ └── security.md
│ ├── features
│ │ └── 001-task-crud
│ │ └── spec.md
│ ├── global
│ │ ├── glossary.md
│ │ └── project-overview.md
│ └── templates
│ └── feature-spec-template.md
├── src
├── tests
└── .cursor
└── rules
└── sdd.mdc

Step 11: Commit

git add .
git commit -m "chore: initialize SDD repository structure"

Monorepo Considerations

In a monorepo (multiple packages), SDD structure adapts.

Option A: Shared Specs at Root

monorepo/
├── specs/
│ ├── global/
│ ├── constraints/
│ └── features/
├── packages/
│ ├── api/
│ │ ├── src/
│ │ └── tests/
│ ├── web/
│ │ ├── src/
│ │ └── tests/
│ └── shared/
│ └── src/
└── memory/

Shared specs apply to all packages. Feature specs may reference which package(s) they affect.

Option B: Package-Level Specs

monorepo/
├── packages/
│ ├── api/
│ │ ├── specs/
│ │ ├── src/
│ │ └── tests/
│ └── web/
│ ├── specs/
│ ├── src/
│ └── tests/
└── specs/
└── global/

Shared specs at root; package-specific specs in each package. Use when packages have distinct domains.

Package-Level Constraints

Create packages/api/specs/constraints/ for API-specific constraints. Reference global constraints in package constraints:

# API Package Constraints

- Inherit: ../specs/global/security.md
- Inherit: ../specs/global/api-conventions.md
- Package-specific: Use Express; no NestJS

Migration Guide: Restructuring an Existing Project for SDD

Migrating an existing project to SDD structure is incremental.

Phase 1: Add Structure (No Code Changes)

  1. Create specs/, memory/, agents/ directories
  2. Create memory/constitution.md — extract principles from existing codebase
  3. Create specs/global/project-overview.md — document current vision
  4. Create specs/constraints/ — add security, API conventions from existing patterns
  5. Add .cursor/rules/ with basic rules

Commit: "chore: add SDD directory structure"

Phase 2: Document One Feature

  1. Choose a well-understood feature (e.g., user login)
  2. Create specs/features/001-login/spec.md — write spec from existing behavior
  3. Create specs/features/001-login/plan.md — document current architecture
  4. Add memory/adr/ for key decisions (e.g., "Why JWT?")

Commit: "docs: add spec for login feature"

Phase 3: New Features Use SDD

  1. For all new features, use specs-first workflow
  2. Create spec before implementation
  3. Run Spec Kit commands if available
  4. Keep existing features as-is until they need changes

Phase 4: Retrofit Existing Features (Optional)

  1. When touching an existing feature, add specs first
  2. Refactor to match spec
  3. Gradually bring all features under spec coverage

Migration Checklist

  • Directory structure created
  • Constitution written
  • At least one spec documented
  • .cursor/rules configured
  • AGENTS.md created (if using agents)
  • Team trained on new workflow

Anti-Patterns to Avoid

Anti-Pattern 1: Specs in Code Comments

Bad: Requirements scattered in JSDoc or README

Good: Requirements in specs/; code references spec IDs

Anti-Pattern 2: No Separation

Bad: Implementation details in spec ("Use PostgreSQL with connection pool of 10")

Good: Spec says "persistent storage"; plan says "PostgreSQL"

Anti-Pattern 3: Orphan Specs

Bad: Specs that don't match code; no traceability

Good: Every spec has corresponding implementation; tasks link to both

Anti-Pattern 4: Flat specs/

Bad: All specs in one folder with no organization

Good: global/, constraints/, templates/, features/ (or branch-name/)

Anti-Pattern 5: Memory in Code

Bad: "Why we did this" in code comments

Good: ADRs in memory/adr/


Try With AI

Prompt 1: Structure Audit

"I have a project at [path]. Analyze the current structure and list what's missing for SDD: (1) specs directory organization, (2) memory directory, (3) separation of intent vs implementation. Propose a concrete migration plan."

Prompt 2: Constitution from Codebase

"Review my codebase at [path]. Extract the implicit principles, conventions, and constraints. Draft a memory/constitution.md that captures what the codebase already follows. I'll refine it."

Prompt 3: Spec from Existing Feature

"I have an existing feature [describe]. I need a spec that documents current behavior. Create specs/features/[name]/spec.md with problem, user stories, requirements, acceptance criteria. Base it on the actual code."

Prompt 4: Migration Checklist

"I'm migrating [project] to SDD. Create a step-by-step migration checklist tailored to my project structure. Include: directory creation, file templates, and order of operations. No code changes yet—structure only."


Practice Exercises

Exercise 1: Create SDD Structure from Scratch

Create a new directory. Set up the full SDD structure (specs, memory, agents, .cursor/rules). Add a minimal project-overview.md, constitution.md, and one feature spec (e.g., "user registration"). Verify the structure with a tree command. Document what you created.

Expected outcome: A complete SDD skeleton with at least one feature spec.

Exercise 2: Migrate an Existing Project

Take a small existing project (or a subset of your current project). Add Phase 1 and Phase 2 from the migration guide: add structure, add constitution, document one feature. Do not change any code. Write a brief report: what was easy, what was hard, what would you do differently?

Expected outcome: Migration report and updated project structure.

Exercise 3: Monorepo Layout

Design an SDD structure for a monorepo with three packages: api, web, shared. Decide: shared specs at root or package-level? What goes in global vs package constraints? Draw the directory tree and write a one-page rationale for your choices.

Expected outcome: Monorepo layout diagram and rationale document.


Key Takeaways

  1. Canonical SDD structure: specs/ (global, constraints, templates, features), agents/, .cursor/rules/, src/, tests/, memory/ (constitution, ADRs, context). Each directory has a distinct role.

  2. Separation of intent and implementation: specs/ holds WHAT and WHY; src/ holds HOW. Intent is stable; implementation is derived. This enables traceability and AI clarity.

  3. specs/ organization: global/ for project-wide specs, constraints/ for cross-cutting rules, templates/ for reuse, features/ or [branch-name]/ for feature specs.

  4. memory/ for identity: constitution.md, ADRs, and context files capture project principles and decisions. They are the long-term memory that guides AI and humans.

  5. Feature branch workflow: specs/[branch-name]/ aligns specs with Git branches. Specs and code merge together. PRs include both.

  6. Migration is incremental: Add structure first, document one feature, use SDD for new features, retrofit existing features when touched. No big-bang rewrite.


Chapter Quiz

  1. What are the six main directories in the canonical SDD repository structure, and what is the purpose of each?

  2. Why does separating intent (specs/) from implementation (src/) matter? Give two concrete benefits.

  3. What goes in specs/global/ vs specs/constraints/ vs specs/features/?

  4. What is the purpose of memory/constitution.md? What should it contain?

  5. How does the feature branch workflow (specs/[branch-name]/) align with Git? What are two benefits?

  6. What should you commit to version control? What might you optionally ignore?

  7. In a monorepo, what are two options for organizing specs? When would you use each?

  8. What are the four phases of the migration guide for restructuring an existing project?