Skip to content

Spec Driven Development

Mo Abualruz edited this page Dec 3, 2025 · 1 revision

Spec-Driven Development Guide

Status: ✅ Complete

Last Updated: December 3, 2025


Overview

Spec-Driven Development is a systematic approach to building software where you define what you want to build (requirements), how you'll build it (design), and the tasks to execute (implementation plan) before writing code. This guide explains how to use RiceCoder's spec system to develop features methodically and ensure correctness.

What Are Specs?

A spec is a formal specification document that defines a feature or system in three phases:

  1. Requirements - What you want to build (user stories and acceptance criteria)
  2. Design - How you'll build it (architecture, data models, algorithms)
  3. Tasks - Implementation plan (concrete coding tasks with dependencies)

Specs bridge the gap between ideas and implementation, ensuring alignment before you write code.

Why Use Specs?

Benefits of Spec-Driven Development:

  • Clarity: Define requirements before design; design before implementation
  • Alignment: Ensure stakeholders agree on what's being built
  • Traceability: Every task traces back to requirements; every requirement traces to discovery
  • Quality: Acceptance criteria define success; implementation is complete when all criteria are met
  • Efficiency: Catch misunderstandings early, before writing code
  • Documentation: Specs serve as living documentation of your system

When to Use Specs

Use specs for:

  • New features (anything more than a bug fix)
  • Architecture changes
  • Major refactoring
  • New projects
  • Feature enhancements

Don't use specs for:

  • Bug fixes (fix directly)
  • Small refactoring (refactor directly)
  • Performance optimizations (optimize directly)

Spec Structure

Each spec has three components, organized in .agent/specs/{feature-name}/:

.agent/specs/my-feature/
├── requirements.md          # What to build
├── design.md                # How to build it
└── tasks.md                 # Implementation tasks

1. Requirements Document

File: requirements.md

Purpose: Define what you want to build using user stories and acceptance criteria

Structure:

# Feature Name - Requirements

## Introduction
Brief description of the feature

## Glossary
Define domain-specific terms

## Requirements

### Requirement 1: [Feature Name]

**User Story**: As a [role], I want [feature], so that [benefit]

#### Acceptance Criteria

1. WHEN [condition] THEN [expected outcome]
2. WHEN [condition] THEN [expected outcome]
3. ...

Key Elements:

  • User Story: Describes who wants what and why
  • Acceptance Criteria: Specific, testable conditions that define success
  • Glossary: Defines domain terms to avoid ambiguity

Example:

### Requirement 1: Add Task to List

**User Story**: As a user, I want to add new tasks to my todo list, so that I can capture things I need to do.

#### Acceptance Criteria

1. WHEN a user types a task description and presses Enter THEN the system SHALL create a new task and add it to the list
2. WHEN a user attempts to add an empty task THEN the system SHALL prevent the addition and maintain the current state
3. WHEN a new task is added THEN the system SHALL persist the task to local storage immediately

2. Design Document

File: design.md

Purpose: Define how you'll build the feature to satisfy requirements

Structure:

# Feature Name - Design

## Overview
High-level description of the design

## Architecture
System architecture and component organization

## Components and Interfaces
Detailed component descriptions

## Data Models
Data structures and relationships

## Correctness Properties
Formal properties that must hold true

## Error Handling
How errors are handled

## Testing Strategy
Unit and property-based testing approach

Key Elements:

  • Architecture: How components fit together
  • Data Models: What data structures you'll use
  • Algorithms: How key logic works
  • Error Handling: What happens when things go wrong
  • Correctness Properties: Formal statements about what must be true

Example:

## Architecture

```text
┌─────────────────┐
│   UI Layer      │
└────────┬────────┘

┌────────▼────────┐
│ Application     │
│ Layer           │
└────────┬────────┘

┌────────▼────────┐
│ Domain Layer    │
└─────────────────┘

Data Models

Task

pub struct Task {
    pub id: String,
    pub description: String,
    pub completed: bool,
    pub created_at: DateTime,
}

Correctness Properties

Property 1: Adding a task grows the list

For any task list, adding a valid task should increase the list length by one.

Validates: Requirement 1.1


### 3. Tasks Document

**File**: `tasks.md`

**Purpose**: Break design into concrete implementation tasks

**Structure**:

```markdown
# Feature Name - Implementation Plan

- [ ] 1. Set up project structure
  - Create directory structure
  - Set up testing framework
  - _Requirements: 1.1_

- [ ] 2. Implement core logic
  - [ ] 2.1 Implement data model
  - [ ] 2.2 Implement business logic
  - [ ]* 2.3 Write unit tests

- [ ] 3. Integrate with UI
  - [ ] 3.1 Create UI components
  - [ ] 3.2 Wire up event handlers

Key Elements:

  • Hierarchical structure: Parent tasks with sub-tasks
  • Dependencies: Clear ordering of tasks
  • Requirements traceability: Each task references requirements
  • Optional tasks: Marked with * for testing and documentation

Spec Workflow

The complete workflow for spec-driven development:

Phase 1: Create Requirements

  1. Identify the feature: What do you want to build?
  2. Write user stories: Who wants it and why?
  3. Define acceptance criteria: What does success look like?
  4. Get approval: Ensure stakeholders agree

Time: 20-40% of spec time

Deliverable: requirements.md

Phase 2: Create Design

  1. Understand requirements: What must be built?
  2. Design architecture: How will components fit together?
  3. Define data models: What data structures will you use?
  4. Plan algorithms: How will key logic work?
  5. Get approval: Ensure design satisfies requirements

Time: 30% of spec time

Deliverable: design.md

Phase 3: Create Tasks

  1. Break down design: What concrete tasks are needed?
  2. Order tasks: What depends on what?
  3. Define exit criteria: How do you know each task is done?
  4. Get approval: Ensure tasks cover all requirements

Time: 15% of spec time

Deliverable: tasks.md

Phase 4: Execute Tasks

  1. Implement tasks: Write code according to design
  2. Validate each task: Ensure exit criteria are met
  3. Run acceptance tests: Verify requirements are satisfied
  4. Get approval: Feature is complete

Time: Varies

Deliverable: Working code

Creating a Spec

Step 1: Initialize a Spec

rice spec create my-feature

This creates:

.agent/specs/my-feature/
├── requirements.md
├── design.md
└── tasks.md

Step 2: Write Requirements

Edit .agent/specs/my-feature/requirements.md:

# My Feature - Requirements

## Introduction

This feature allows users to [do something useful].

## Glossary

- **User**: A person using the system
- **Feature**: A capability of the system

## Requirements

### Requirement 1: Core Functionality

**User Story**: As a user, I want to [do something], so that [I get benefit].

#### Acceptance Criteria

1. WHEN [condition] THEN [expected outcome]
2. WHEN [condition] THEN [expected outcome]

Step 3: Write Design

Edit .agent/specs/my-feature/design.md:

# My Feature - Design

## Overview

This feature is implemented using [architecture pattern].

## Architecture

[Describe how components fit together]

## Data Models

[Define data structures]

## Correctness Properties

### Property 1: [Property name]

*For any* [input], [property should hold].

**Validates: Requirement 1.1**

Step 4: Create Tasks

Edit .agent/specs/my-feature/tasks.md:

# My Feature - Implementation Plan

- [ ] 1. Set up project structure
  - Create directories
  - Set up testing
  - _Requirements: 1.1_

- [ ] 2. Implement core logic
  - [ ] 2.1 Implement data model
  - [ ] 2.2 Implement business logic
  - [ ]* 2.3 Write unit tests

Step 5: Generate Code

rice gen --spec my-feature

RiceCoder will:

  1. Read your specification
  2. Analyze your project
  3. Generate implementation code
  4. Show a diff for review
  5. Ask for approval

Step 6: Review and Apply

Apply changes? (y/n): y

Your code is generated and applied!

Best Practices for Spec Writing

1. Write Clear User Stories

Good:

As a developer, I want to generate code from specifications, so that I can implement features systematically.

Bad:

The system should generate code.

Why: Good user stories explain who, what, and why. This provides context for design decisions.

2. Make Acceptance Criteria Testable

Good:

WHEN a user adds a task with a non-empty description THEN the task SHALL appear in the list

Bad:

The system should handle tasks properly.

Why: Testable criteria define success objectively. You know when you're done.

3. Use WHEN/THEN Format

Good:

WHEN a user clicks the delete button THEN the task SHALL be removed from the list

Bad:

Users can delete tasks.

Why: WHEN/THEN format is precise and testable. It describes specific scenarios.

4. Define Correctness Properties

Good:

Property: Adding a task increases list length by one

*For any* task list and valid task, adding the task should result in list length increasing by exactly one.

Bad:

Tasks should be added correctly.

Why: Correctness properties are formal statements that can be tested with property-based testing.

5. Keep Requirements Focused

Good: One requirement per user story

Bad: Multiple unrelated requirements in one story

Why: Focused requirements are easier to design and implement.

6. Design Before Implementation

Good: Complete design before writing code

Bad: Start coding, then figure out design

Why: Design phase catches issues early, before you've written code.

7. Trace Everything

Good: Every task references requirements; every requirement references discovery

Bad: Tasks that don't connect to requirements

Why: Traceability ensures nothing is missed and scope is clear.

8. Get Approval at Each Phase

Good: Stakeholders approve requirements, then design, then tasks

Bad: Write everything, then ask for approval

Why: Early approval prevents rework and ensures alignment.

Real-World Example

Feature: Add Task to Todo List

Step 1: Requirements

# Add Task Feature - Requirements

## Introduction

Users need to add new tasks to their todo list to capture things they need to do.

## Glossary

- **Task**: An item on the todo list with a description
- **Todo List**: A collection of tasks

## Requirements

### Requirement 1: Add Task to List

**User Story**: As a user, I want to add new tasks to my todo list, so that I can capture things I need to do.

#### Acceptance Criteria

1. WHEN a user types a task description and presses Enter THEN the system SHALL create a new task and add it to the list
2. WHEN a user attempts to add an empty task THEN the system SHALL prevent the addition and maintain the current state
3. WHEN a new task is added THEN the system SHALL persist the task to local storage immediately

Step 2: Design

# Add Task Feature - Design

## Overview

Tasks are added through a text input field. When the user presses Enter, the task is created, added to the list, and persisted.

## Data Models

### Task

```rust
pub struct Task {
    pub id: String,
    pub description: String,
    pub completed: bool,
    pub created_at: DateTime,
}

Correctness Properties

Property 1: Adding a task grows the list

For any task list and valid task description, adding the task should increase the list length by one.

Validates: Requirement 1.1

Property 2: Empty tasks are rejected

For any empty or whitespace-only string, attempting to add it as a task should fail and leave the list unchanged.

Validates: Requirement 1.2

Property 3: Tasks are persisted

For any task added to the list, querying local storage should return the same task.

Validates: Requirement 1.3


#### Step 3: Tasks

```markdown
# Add Task Feature - Implementation Plan

- [ ] 1. Set up project structure
  - Create src/models/task.rs for Task struct
  - Create src/storage/mod.rs for persistence
  - Set up testing framework
  - _Requirements: 1.1, 1.2, 1.3_

- [ ] 2. Implement data model
  - [ ] 2.1 Define Task struct with id, description, completed, created_at
  - [ ] 2.2 Implement Task validation (non-empty description)
  - [ ]* 2.3 Write unit tests for Task model

- [ ] 3. Implement storage
  - [ ] 3.1 Implement local storage interface
  - [ ] 3.2 Implement save_task function
  - [ ]* 3.3 Write unit tests for storage

- [ ] 4. Implement UI
  - [ ] 4.1 Create input field component
  - [ ] 4.2 Wire up Enter key handler
  - [ ] 4.3 Call add_task on Enter
  - [ ]* 4.4 Write integration tests

- [ ] 5. Checkpoint - Ensure all tests pass
  - Ensure all tests pass, ask the user if questions arise.

Step 4: Generate and Implement

rice gen --spec add-task

Review the generated code, approve it, and start implementing tasks.

Workflow Diagram

┌─────────────────────────────────────────────────────────────┐
│                    Spec-Driven Development                  │
└─────────────────────────────────────────────────────────────┘

1. Create Requirements
   ├─ Write user stories
   ├─ Define acceptance criteria
   └─ Get approval ✓

2. Create Design
   ├─ Design architecture
   ├─ Define data models
   ├─ Plan algorithms
   └─ Get approval ✓

3. Create Tasks
   ├─ Break down design
   ├─ Order tasks
   ├─ Define exit criteria
   └─ Get approval ✓

4. Execute Tasks
   ├─ Implement task 1
   ├─ Validate task 1
   ├─ Implement task 2
   ├─ Validate task 2
   └─ ... repeat until complete

5. Validate Against Requirements
   ├─ Run acceptance tests
   ├─ Verify all criteria met
   └─ Get approval ✓

6. Feature Complete!

Common Patterns

Pattern: Feature with Multiple Requirements

### Requirement 1: User Authentication
**User Story**: As a user, I want to log in, so that I can access my account.

### Requirement 2: User Profile
**User Story**: As a user, I want to view my profile, so that I can see my information.

### Requirement 3: User Settings
**User Story**: As a user, I want to change my settings, so that I can customize my experience.

Each requirement becomes a separate section in design and tasks.

Pattern: Feature with Optional Components

- [ ] 1. Core functionality (required)
  - [ ] 1.1 Implement core logic
  - [ ] 1.2 Implement basic UI

- [ ]* 2. Advanced features (optional)
  - [ ]* 2.1 Implement caching
  - [ ]* 2.2 Implement analytics

Optional tasks are marked with * and can be skipped for MVP.

Pattern: Feature with Dependencies

- [ ] 1. Set up database
  - [ ] 1.1 Create schema
  - [ ] 1.2 Create migrations

- [ ] 2. Implement API (depends on 1)
  - [ ] 2.1 Create endpoints
  - [ ] 2.2 Add validation

- [ ] 3. Implement UI (depends on 2)
  - [ ] 3.1 Create components
  - [ ] 3.2 Wire up API calls

Tasks are ordered so dependencies are satisfied.

Troubleshooting

"I don't know what to write in requirements"

Solution: Start with user stories. Ask:

  • Who is the user?
  • What do they want to do?
  • Why do they want to do it?

Then define acceptance criteria by asking:

  • What conditions must be true for this to work?
  • What should happen in each case?

"My design is too vague"

Solution: Add more detail:

  • Draw architecture diagrams
  • Define data structures explicitly
  • Describe algorithms step-by-step
  • List error cases and how they're handled

"I have too many tasks"

Solution: Group related tasks:

  • Create parent tasks for logical groupings
  • Keep individual tasks focused and small
  • Aim for tasks that take 1-4 hours

"Requirements keep changing"

Solution: Treat specs as living documents:

  • Update requirements when they change
  • Update design to match new requirements
  • Update tasks accordingly
  • Get approval for changes before implementing

"I'm not sure if my design is correct"

Solution: Validate against requirements:

  • Does the design satisfy all acceptance criteria?
  • Are all error cases handled?
  • Are data models sufficient?
  • Can you implement the design?

If you can't answer yes to all, refine the design.

Tips & Tricks

1. Use Templates

Create template specs for common patterns:

rice spec create --template feature my-feature

2. Collaborate on Specs

Share specs with team members:

# Export spec
rice spec export my-feature > my-feature.md

# Share and get feedback
# Then update based on feedback

3. Reference Existing Specs

Learn from existing specs in your project:

rice spec list
rice spec show existing-feature

4. Use Specs for Documentation

Specs serve as documentation:

  • Requirements explain what the system does
  • Design explains how it works
  • Tasks explain how to build it

5. Iterate Quickly

Don't try to get specs perfect on first try:

  1. Write rough requirements
  2. Get feedback
  3. Refine design
  4. Get feedback
  5. Create tasks
  6. Start implementing

Iteration is normal and expected.

See Also


Last updated: December 3, 2025

Clone this wiki locally