Skip to content

Introducing SDD to Brownfield Projects

A tutorial for gradually introducing SDD to existing projects.

Overview

A brownfield project is one with existing code. Through reverse extraction, you can extract specs from existing code and gradually introduce SDD.

Note: This tutorial is based on an interactive workflow using slash commands in Claude Code.

Scenario

Let's introduce SDD to an existing Express API project.

Current Structure

my-api/
├── src/
│   ├── controllers/
│   │   ├── authController.js
│   │   ├── userController.js
│   │   └── orderController.js
│   ├── services/
│   │   ├── authService.js
│   │   ├── userService.js
│   │   └── orderService.js
│   ├── models/
│   │   ├── User.js
│   │   └── Order.js
│   └── utils/
│       └── validation.js
└── package.json

Step 1: SDD Initialization

First, run the sdd init command in your terminal:

bash
cd my-api
sdd init
SDD project has been initialized!

Created items:
  .sdd/specs/
  .sdd/changes/
  .sdd/archive/
  .sdd/templates/
  .claude/commands/    <- Slash commands
  .claude/skills/      <- Development skills
  .sdd/constitution.md
  .sdd/AGENTS.md

Existing code remains unchanged.
Next step: Run /sdd.start in Claude Code.

Start in Claude Code

Now open the project in Claude Code and run /sdd.start:

/sdd.start

Claude analyzes the existing project:

Project Status

SDD Initialized
Constitution: Template (modification needed)
Specs: 0

Existing codebase detected:
- Files: 12
- Language: JavaScript

Tip: Run /sdd.reverse to extract specs from existing code.

Step 2: Codebase Analysis

/sdd.reverse scan

Claude analyzes the codebase:

Scanning codebase...

Analysis results:

Files: 12
Language: JavaScript (100%)
Complexity: B (Good)

Inferred domains:
  auth (high confidence) - 2 files
     authController.js, authService.js
  user (high confidence) - 2 files
     userController.js, userService.js
  order (high confidence) - 2 files
     orderController.js, orderService.js
  core (medium confidence) - 2 files
     validation.js, models/

Spec candidates: 12
  High confidence (>80%): 8
  Medium confidence (50-80%): 3
  Low confidence (<50%): 1

Metadata saved: .sdd/.reverse-meta.json
Auto-generated domains: 4 (core, auth, user, order)

Tip: Next step: /sdd.reverse extract

Step 3: Domain Creation

Design domains based on analysis results:

/sdd.domain
User: Create domain structure based on scan results

Claude: Domain Structure Proposal

Creating the following domains based on analysis results:

1. core
   Description: Common utilities
   Path: src/utils/
   Dependencies: none

2. auth
   Description: Authentication/authorization
   Path: src/controllers/auth*, src/services/auth*
   Dependencies: core

3. user
   Description: User management
   Path: src/controllers/user*, src/services/user*
   Dependencies: core, auth

4. order
   Description: Order management
   Path: src/controllers/order*, src/services/order*
   Dependencies: core, auth

Create these? [Y/n]

> Y

Domains created

Created files:
  .sdd/domains.yml              <- Domain configuration (YAML format)
  .sdd/domains/core/domain.md   <- Per-domain documentation
  .sdd/domains/auth/domain.md
  .sdd/domains/user/domain.md
  .sdd/domains/order/domain.md

Step 4: Reverse Extraction

Extract Specs

/sdd.reverse extract

Claude extracts specs from code:

Extracting specs...

## auth domain

### login (confidence: 95%)
Code location: authController.js:15-45, authService.js:10-35
Extracted scenarios:
  - Successful login with valid credentials
  - Failed login with wrong password
  - Non-existent user
Contract:
  Input: { email: string, password: string }
  Output: { token: string, user: User }

### register (confidence: 92%)
Code location: authController.js:47-80
Extracted scenarios:
  - Successful new user registration
  - Email duplicate error
  - Password format error

### logout (confidence: 88%)
...

## user domain

### get-user (confidence: 90%)
...

## order domain

### create-order (confidence: 88%)
...

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Total extracted specs: 11
Drafts saved: .sdd/drafts/

Start review? [Y/n]

Step 5: Review Specs

/sdd.reverse review

Review extracted specs interactively with Claude:

Spec Review: auth/login (1/11)

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Name: login
Description: User login handling
Confidence: 95%
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Extracted scenarios:

1. Normal login
   GIVEN: Valid email and password
   WHEN: POST /auth/login request
   THEN: Return JWT token and user info

2. Password mismatch
   GIVEN: Valid email, wrong password
   WHEN: POST /auth/login request
   THEN: 401 error (INVALID_PASSWORD)

3. User not found
   GIVEN: Non-existent email
   WHEN: POST /auth/login request
   THEN: 404 error (USER_NOT_FOUND)

Extracted contract:
  Input: { email: string, password: string }
  Output: { token: string, user: User }

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
AI Suggestions:
  - Add scenario: "Login attempt with locked account"
  - Contract improvement: Consider adding refreshToken

Needs clarification:
  - Is there a max login attempt limit?
  - What is the session expiration time?
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

[a] Approve  [e] Edit  [s] Skip  [r] Reject

Edit Example

> e

Entering edit mode

User: Add account lockout scenario. Lock for 30 minutes after 5 failures.

Claude: Scenario added

4. Account lockout
   GIVEN: Account with 5+ failed login attempts
   WHEN: POST /auth/login request
   THEN: 423 error (ACCOUNT_LOCKED, remaining time: 30 min)

Add this? [Y/n]

> Y

User: Also add refreshToken to output

Claude: Contract modified

Output:
  {
    token: string,
    refreshToken: string,  // Added
    user: User
  }

Apply this change? [Y/n]

> Y

Modification complete. Press [a] to approve.

> a

auth/login approved

Next spec: auth/register (2/11)

Bulk Approval

User: Bulk approve all remaining with 85%+ confidence

Claude: Bulk approval targets

85%+ confidence:
  - auth/register (92%)
  - auth/logout (88%)
  - user/get-user (90%)
  - user/update-user (85%)
  - order/create-order (88%)
  - order/get-order (85%)

Approve 6 specs? [Y/n]

> Y

6 specs approved

Remaining specs: 4 (manual review needed)
  - user/delete-user (82%)
  - order/list-orders (80%)
  - order/update-order (75%)
  - order/cancel-order (72%)

Step 6: Finalize Specs

/sdd.reverse finalize
Spec Finalization

Approved specs: 8
Manual review needed: 4

Finalize approved specs? [Y/n]

> Y

Specs finalized

Saved specs:
  .sdd/specs/login/spec.md (domain: auth)
  .sdd/specs/register/spec.md (domain: auth)
  .sdd/specs/logout/spec.md (domain: auth)
  .sdd/specs/get-user/spec.md (domain: user)
  .sdd/specs/update-user/spec.md (domain: user)
  .sdd/specs/create-order/spec.md (domain: order)
  .sdd/specs/get-order/spec.md (domain: order)
  .sdd/specs/validation/spec.md (domain: core)

domains.yml updated
Draft files cleaned up

Remaining drafts: .sdd/drafts/ (4)

Step 7: Validation

/sdd.validate
Validation complete

Specs: 8
Domains: 4
Dependencies: Valid

Warnings: 2
  - login: Output type User not defined in core
  - create-order: Possible missing dependency on payment

Errors: 0

Resolve Warnings

User: Add User type spec and resolve the warning

Claude: Spec added

Creating user-model spec (domain: core):

  ---
  id: user-model
  name: User Model
  status: approved
  domain: core
  ---

  # User Model

  interface User {
    id: string;
    email: string;
    name: string;
    createdAt: Date;
    updatedAt: Date;
  }

Adding user-model to login's dependencies

Warning resolved

Step 8: Check Project Status

/sdd.status
Project Status: my-api

Specs: 9
  Implemented (legacy): 9 (100%)
  Tests needed: 4
  Spec enhancement needed: 2

Domains:
  core: 2/2 complete 100%
  auth: 3/3 complete 100%
  user: 2/3 complete  67%
  order: 2/4 complete  50%

Drafts (unfinalized): 4
  -> Continue review with /sdd.reverse review

Recommended next actions:
  1. Review remaining drafts: /sdd.reverse review
  2. Add tests: "Write tests for auth/login"
  3. New features: /sdd.spec

Step 9: Gradual Improvement

Add Tests

Add tests to existing code:

User: login has no tests. Write tests based on the spec

Claude: Generating tests: login (domain: auth)

Generating tests based on spec scenarios:

src/auth/login.test.ts:

  import { describe, it, expect, beforeEach, vi } from 'vitest';
  import { login } from './authService';
  import { User } from '../models/User';

  describe('login', () => {
    beforeEach(() => {
      vi.clearAllMocks();
    });

    it('successfully logs in with valid credentials', async () => {
      const result = await login({
        email: 'user@test.com',
        password: 'valid-password'
      });

      expect(result.token).toBeDefined();
      expect(result.refreshToken).toBeDefined();
      expect(result.user.email).toBe('user@test.com');
    });

    it('returns 401 with wrong password', async () => {
      await expect(login({
        email: 'user@test.com',
        password: 'wrong-password'
      })).rejects.toThrow('INVALID_PASSWORD');
    });

    it('returns 404 for non-existent user', async () => {
      await expect(login({
        email: 'unknown@test.com',
        password: 'any-password'
      })).rejects.toThrow('USER_NOT_FOUND');
    });

    it('locks account after 5 failures', async () => {
      // Simulate 5 failures
      for (let i = 0; i < 5; i++) {
        await expect(login({
          email: 'user@test.com',
          password: 'wrong'
        })).rejects.toThrow();
      }

      await expect(login({
        email: 'user@test.com',
        password: 'correct'
      })).rejects.toThrow('ACCOUNT_LOCKED');
    });
  });

Create test file? [Y/n]

Add New Features

Develop new features using SDD from the start:

/sdd.spec Password reset
Writing new spec: password-reset (domain: auth)

Tell me about the functionality:
- Send reset link via email
- Set new password when clicking link
- What is the link validity period?

User: Send link via email, valid for 1 hour. New password must be 8+ characters.

Claude: Spec Draft

## password-reset (domain: auth)

### Scenarios

**Reset request**
- GIVEN: Registered email
- WHEN: POST /auth/password-reset/request
- THEN: Reset link sent via email

**Password change**
- GIVEN: Valid reset token, new password (8+ chars)
- WHEN: POST /auth/password-reset/confirm
- THEN: Password changed

**Expired token**
- GIVEN: Token older than 1 hour
- WHEN: POST /auth/password-reset/confirm
- THEN: 400 error (TOKEN_EXPIRED)

Save this spec? [Y/n]

Step 10: Ongoing Management

Check Code-Spec Synchronization

/sdd.sync
Spec-Code Sync Check

Synchronized: 7
  - login (domain: auth)
  - register (domain: auth)
  - logout (domain: auth)
  ...

Mismatch: 2
  - update-user (domain: user)
    Spec: email field modifiable
    Code: email is readonly
    -> Spec or code modification needed

  - create-order (domain: order)
    Spec: quantity required
    Code: quantity defaults to 1
    -> Spec update recommended

Fix these? [Y/n]

Domain Graph

/sdd.domain graph
mermaid
graph TB
    subgraph core[core domain]
        validation[validation ]
        user-model[user-model ]
    end

    subgraph auth[auth domain]
        login[login ]
        register[register ]
        logout[logout ]
        password-reset[password-reset ]
    end

    subgraph user[user domain]
        get-user[get-user ]
        update-user[update-user ]
        delete-user[delete-user ]
    end

    subgraph order[order domain]
        create-order[create-order ]
        get-order[get-order ]
        list-orders[list-orders ]
        update-order[update-order ]
    end

    core --> auth
    core --> user
    core --> order
    auth --> user
    auth --> order

Next Steps

  1. Add tests to verify specs
  2. Develop new features using SDD
  3. Gradually refactor legacy code

Summary

  1. Initialize project with sdd init (CLI)
  2. Check project status with /sdd.start (Claude Code)
  3. Analyze codebase with /sdd.reverse scan
  4. Create domain structure with /sdd.domain
  5. Extract specs with /sdd.reverse extract
  6. Interactive review with /sdd.reverse review
  7. Finalize with /sdd.reverse finalize
  8. Validate with /sdd.validate
  9. Manage code-spec sync with /sdd.sync
  10. Gradually add tests and improvements

MIT License