Converted from Claude Code boilerplate to opencode: - CLAUDE.md -> AGENTS.md (opencode instructions) - .claude/settings.json -> opencode.json (permissions schema) - .claude/agents/ -> .opencode/agent/ (subagents with mode: subagent) - .claude/commands/ -> .opencode/command/ (slash commands with ) - .claude/skills/ -> .opencode/skills/ (7 skills, removed allowed-tools) - DevContainer updated to install opencode - All .claude/ paths and Claude Code references updated
471 lines
12 KiB
Markdown
471 lines
12 KiB
Markdown
# Functional Design Document Creation Guide
|
||
|
||
This guide provides practical guidance for creating a functional design document based on a Product Requirements Document (PRD).
|
||
|
||
## Purpose of the Functional Design Document
|
||
|
||
The functional design document translates the "what to build" defined in the PRD into "how to realize it".
|
||
|
||
**Main Content**:
|
||
- System architecture diagram
|
||
- Data model
|
||
- Component design
|
||
- Algorithm design (if applicable)
|
||
- UI design
|
||
- Error handling
|
||
|
||
## Basic Creation Flow
|
||
|
||
### Step 1: Review the PRD
|
||
|
||
Before creating the functional design document, always review the PRD.
|
||
|
||
```
|
||
Example prompt for having opencode create a functional design document from the PRD:
|
||
|
||
Create a functional design document based on the content of the PRD.
|
||
In particular, focus on the priority P0 (MVP) features.
|
||
```
|
||
|
||
### Step 2: Create the System Architecture Diagram
|
||
|
||
#### Using Mermaid Notation
|
||
|
||
Write the system architecture diagram using Mermaid notation.
|
||
|
||
**Example of a basic 3-tier architecture**:
|
||
```mermaid
|
||
graph TB
|
||
User[User]
|
||
CLI[CLI Layer]
|
||
Service[Service Layer]
|
||
Data[Data Layer]
|
||
|
||
User --> CLI
|
||
CLI --> Service
|
||
Service --> Data
|
||
```
|
||
|
||
**A more detailed example**:
|
||
```mermaid
|
||
graph TB
|
||
User[User]
|
||
CLI[CLI Interface]
|
||
Commander[Commander.js]
|
||
TaskManager[TaskManager]
|
||
PriorityEstimator[PriorityEstimator]
|
||
FileStorage[FileStorage]
|
||
JSON[(tasks.json)]
|
||
|
||
User --> CLI
|
||
CLI --> Commander
|
||
Commander --> TaskManager
|
||
TaskManager --> PriorityEstimator
|
||
TaskManager --> FileStorage
|
||
FileStorage --> JSON
|
||
```
|
||
|
||
### Step 3: Define the Data Model
|
||
|
||
#### Define Clearly with TypeScript Type Definitions
|
||
|
||
Define the data model using TypeScript interfaces.
|
||
|
||
**Example of a basic Task type**:
|
||
```typescript
|
||
interface Task {
|
||
id: string; // UUID v4
|
||
title: string; // 1-200 characters
|
||
description?: string; // Optional, Markdown format
|
||
status: TaskStatus; // 'todo' | 'in_progress' | 'completed'
|
||
priority: TaskPriority; // 'high' | 'medium' | 'low'
|
||
estimatedPriority?: TaskPriority; // Automatically estimated priority
|
||
dueDate?: Date; // Due date
|
||
createdAt: Date; // Creation timestamp
|
||
updatedAt: Date; // Update timestamp
|
||
statusHistory?: StatusChange[]; // Status change history
|
||
}
|
||
|
||
type TaskStatus = 'todo' | 'in_progress' | 'completed';
|
||
type TaskPriority = 'high' | 'medium' | 'low';
|
||
|
||
interface StatusChange {
|
||
from: TaskStatus;
|
||
to: TaskStatus;
|
||
changedAt: Date;
|
||
}
|
||
```
|
||
|
||
**Key Points**:
|
||
- Add a comment explaining each field
|
||
- Clearly note constraints (character count, format, etc.)
|
||
- Add `?` to optional fields
|
||
- Improve readability with type aliases
|
||
|
||
#### Creating an ER Diagram
|
||
|
||
When there are multiple entities, show the relationships with an ER diagram.
|
||
|
||
```mermaid
|
||
erDiagram
|
||
TASK ||--o{ SUBTASK : has
|
||
TASK ||--o{ TAG : has
|
||
USER ||--o{ TASK : creates
|
||
|
||
TASK {
|
||
string id PK
|
||
string title
|
||
string status
|
||
datetime createdAt
|
||
}
|
||
SUBTASK {
|
||
string id PK
|
||
string taskId FK
|
||
string title
|
||
}
|
||
```
|
||
|
||
### Step 4: Component Design
|
||
|
||
Clarify the responsibilities of each layer.
|
||
|
||
#### CLI Layer
|
||
|
||
**Responsibility**: Accept user input, validation, display results
|
||
|
||
```typescript
|
||
// CommandLineInterface
|
||
class CLI {
|
||
// Accept user input
|
||
parseArguments(): Command;
|
||
|
||
// Display results
|
||
displayResult(result: Result): void;
|
||
|
||
// Display errors
|
||
displayError(error: Error): void;
|
||
}
|
||
```
|
||
|
||
#### Service Layer
|
||
|
||
**Responsibility**: Implement business logic
|
||
|
||
```typescript
|
||
// TaskManager
|
||
class TaskManager {
|
||
// Create a task
|
||
createTask(data: CreateTaskData): Task;
|
||
|
||
// Get the task list
|
||
listTasks(filter?: FilterOptions): Task[];
|
||
|
||
// Update a task
|
||
updateTask(id: string, data: UpdateTaskData): Task;
|
||
|
||
// Delete a task
|
||
deleteTask(id: string): void;
|
||
}
|
||
```
|
||
|
||
#### Data Layer
|
||
|
||
**Responsibility**: Data persistence and retrieval
|
||
|
||
```typescript
|
||
// FileStorage
|
||
class FileStorage {
|
||
// Save data
|
||
save(data: any): void;
|
||
|
||
// Load data
|
||
load(): any;
|
||
|
||
// Check whether the file exists
|
||
exists(): boolean;
|
||
}
|
||
```
|
||
|
||
### Step 5: Algorithm Design (if applicable)
|
||
|
||
Design complex logic (e.g., automatic priority estimation) in detail.
|
||
|
||
#### Example of an Automatic Priority Estimation Algorithm
|
||
|
||
**Purpose**: Automatically estimate priority from a task's due date, creation timestamp, and status
|
||
|
||
**Calculation Logic**:
|
||
|
||
##### Step 1: Deadline Score Calculation (0-100 points)
|
||
```
|
||
- Overdue: 100 points (highest)
|
||
- 0-3 days until due: 90 points
|
||
- 4-7 days until due: 70 points
|
||
- 8-14 days until due: 50 points
|
||
- 14 or more days until due: 30 points
|
||
- No due date set: 20 points
|
||
```
|
||
|
||
**Formula**:
|
||
```typescript
|
||
function calculateDeadlineScore(dueDate?: Date): number {
|
||
if (!dueDate) return 20;
|
||
|
||
const now = new Date();
|
||
const daysRemaining = Math.floor((dueDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));
|
||
|
||
if (daysRemaining < 0) return 100; // Overdue
|
||
if (daysRemaining <= 3) return 90;
|
||
if (daysRemaining <= 7) return 70;
|
||
if (daysRemaining <= 14) return 50;
|
||
return 30;
|
||
}
|
||
```
|
||
|
||
##### Step 2: Elapsed Time Score Calculation (0-100 points)
|
||
```
|
||
- 30 or more days since creation: 100 points (highest)
|
||
- 21-30 days since creation: 80 points
|
||
- 14-21 days since creation: 60 points
|
||
- 7-14 days since creation: 40 points
|
||
- Less than 7 days since creation: 20 points
|
||
```
|
||
|
||
**Formula**:
|
||
```typescript
|
||
function calculateAgeScore(createdAt: Date): number {
|
||
const now = new Date();
|
||
const daysOld = Math.floor((now.getTime() - createdAt.getTime()) / (1000 * 60 * 60 * 24));
|
||
|
||
if (daysOld >= 30) return 100;
|
||
if (daysOld >= 21) return 80;
|
||
if (daysOld >= 14) return 60;
|
||
if (daysOld >= 7) return 40;
|
||
return 20;
|
||
}
|
||
```
|
||
|
||
##### Step 3: Status Score Calculation (0-100 points)
|
||
```
|
||
- In progress (in_progress): 100 points (highest priority)
|
||
- Not started (todo): 50 points
|
||
- Completed (completed): 0 points
|
||
```
|
||
|
||
**Formula**:
|
||
```typescript
|
||
function calculateStatusScore(status: TaskStatus): number {
|
||
if (status === 'in_progress') return 100;
|
||
if (status === 'todo') return 50;
|
||
return 0; // completed
|
||
}
|
||
```
|
||
|
||
##### Step 4: Total Score Calculation
|
||
|
||
**Weighted Average**:
|
||
```
|
||
Total Score = (Deadline Score × 50%) + (Elapsed Time Score × 20%) + (Status Score × 30%)
|
||
```
|
||
|
||
**Formula**:
|
||
```typescript
|
||
function calculateTotalScore(task: Task): number {
|
||
const deadlineScore = calculateDeadlineScore(task.dueDate);
|
||
const ageScore = calculateAgeScore(task.createdAt);
|
||
const statusScore = calculateStatusScore(task.status);
|
||
|
||
return (deadlineScore * 0.5) + (ageScore * 0.2) + (statusScore * 0.3);
|
||
}
|
||
```
|
||
|
||
##### Step 5: Priority Classification
|
||
|
||
**Classification by Threshold**:
|
||
```
|
||
- 70 points or more: high (high priority)
|
||
- 40-70 points: medium (medium priority)
|
||
- Less than 40 points: low (low priority)
|
||
```
|
||
|
||
**Formula**:
|
||
```typescript
|
||
function estimatePriority(task: Task): TaskPriority {
|
||
const score = calculateTotalScore(task);
|
||
|
||
if (score >= 70) return 'high';
|
||
if (score >= 40) return 'medium';
|
||
return 'low';
|
||
}
|
||
```
|
||
|
||
**Complete Implementation Example**:
|
||
```typescript
|
||
class PriorityEstimator {
|
||
estimate(task: Task): TaskPriority {
|
||
const deadlineScore = this.calculateDeadlineScore(task.dueDate);
|
||
const ageScore = this.calculateAgeScore(task.createdAt);
|
||
const statusScore = this.calculateStatusScore(task.status);
|
||
|
||
const totalScore = (deadlineScore * 0.5) + (ageScore * 0.2) + (statusScore * 0.3);
|
||
|
||
if (totalScore >= 70) return 'high';
|
||
if (totalScore >= 40) return 'medium';
|
||
return 'low';
|
||
}
|
||
|
||
private calculateDeadlineScore(dueDate?: Date): number {
|
||
if (!dueDate) return 20;
|
||
|
||
const now = new Date();
|
||
const daysRemaining = Math.floor((dueDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));
|
||
|
||
if (daysRemaining < 0) return 100;
|
||
if (daysRemaining <= 3) return 90;
|
||
if (daysRemaining <= 7) return 70;
|
||
if (daysRemaining <= 14) return 50;
|
||
return 30;
|
||
}
|
||
|
||
private calculateAgeScore(createdAt: Date): number {
|
||
const now = new Date();
|
||
const daysOld = Math.floor((now.getTime() - createdAt.getTime()) / (1000 * 60 * 60 * 24));
|
||
|
||
if (daysOld >= 30) return 100;
|
||
if (daysOld >= 21) return 80;
|
||
if (daysOld >= 14) return 60;
|
||
if (daysOld >= 7) return 40;
|
||
return 20;
|
||
}
|
||
|
||
private calculateStatusScore(status: TaskStatus): number {
|
||
if (status === 'in_progress') return 100;
|
||
if (status === 'todo') return 50;
|
||
return 0;
|
||
}
|
||
}
|
||
```
|
||
|
||
### Step 6: Use Case Diagram
|
||
|
||
Express the main use cases with sequence diagrams.
|
||
|
||
**Task Addition Flow**:
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant User
|
||
participant CLI
|
||
participant TaskManager
|
||
participant PriorityEstimator
|
||
participant FileStorage
|
||
|
||
User->>CLI: devtask add "task"
|
||
CLI->>CLI: Validate input
|
||
CLI->>TaskManager: createTask(data)
|
||
TaskManager->>TaskManager: Create task object
|
||
TaskManager->>PriorityEstimator: estimate(task)
|
||
PriorityEstimator-->>TaskManager: Estimated priority
|
||
TaskManager->>FileStorage: save(task)
|
||
FileStorage-->>TaskManager: Success
|
||
TaskManager-->>CLI: Created task
|
||
CLI-->>User: "Task created (ID: xxx)"
|
||
```
|
||
|
||
### Step 7: UI Design (if applicable)
|
||
|
||
For CLI tools, define table display and color coding.
|
||
|
||
#### Table Display
|
||
|
||
```
|
||
┌──────────┬──────────────────┬────────────┬──────────┬───────────────┐
|
||
│ ID │ Title │ Status │ Priority │ Due Date │
|
||
├──────────┼──────────────────┼────────────┼──────────┼───────────────┤
|
||
│ 7a5c6ff0 │ Buy milk on the │ Not started│ High │ 2025-11-05 │
|
||
│ │ way home. │ │ │ (1 day left) │
|
||
└──────────┴──────────────────┴────────────┴──────────┴───────────────┘
|
||
```
|
||
|
||
#### Color Coding
|
||
|
||
**Status Color Coding**:
|
||
- Completed (completed): green
|
||
- In progress (in_progress): yellow
|
||
- Not started (todo): white
|
||
|
||
**Priority Color Coding**:
|
||
- High (high): red
|
||
- Medium (medium): yellow
|
||
- Low (low): blue
|
||
|
||
### Step 8: File Structure (if applicable)
|
||
|
||
Define the data storage format.
|
||
|
||
**Example: Data storage for a CLI tool**:
|
||
```
|
||
.devtask/
|
||
├── tasks.json # Task data
|
||
└── config.json # Configuration data
|
||
```
|
||
|
||
**Example of tasks.json**:
|
||
```json
|
||
{
|
||
"tasks": [
|
||
{
|
||
"id": "7a5c6ff0-5f55-474e-baf7-ea13624d73a4",
|
||
"title": "Buy milk on the way home",
|
||
"description": "",
|
||
"status": "todo",
|
||
"priority": "high",
|
||
"estimatedPriority": "medium",
|
||
"dueDate": "2025-11-05T00:00:00.000Z",
|
||
"createdAt": "2025-11-04T10:00:00.000Z",
|
||
"updatedAt": "2025-11-04T10:00:00.000Z"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
### Step 9: Error Handling
|
||
|
||
Define the types of errors and how to handle them.
|
||
|
||
| Error Category | Handling | Display to User |
|
||
|-----------|------|-----------------|
|
||
| Input validation error | Abort processing, display error message | "Please enter a title of 1-200 characters" |
|
||
| File read error | Continue with empty initial data | "Data file not found. Creating a new one" |
|
||
| Task not found | Abort processing, display error message | "Task not found (ID: xxx)" |
|
||
|
||
## Reviewing the Functional Design Document
|
||
|
||
### Review Perspectives
|
||
|
||
Request a review from opencode:
|
||
|
||
```
|
||
Please evaluate this functional design document. Check it from the following perspectives:
|
||
|
||
1. Does it satisfy the requirements of the PRD?
|
||
2. Is the data model concrete?
|
||
3. Are the responsibilities of the components clear?
|
||
4. Is the algorithm detailed to an implementable level?
|
||
5. Is error handling comprehensive?
|
||
```
|
||
|
||
### Implementing Improvements
|
||
|
||
Make improvements based on opencode's feedback.
|
||
|
||
## Summary
|
||
|
||
Keys to successfully creating a functional design document:
|
||
|
||
1. **Consistency with the PRD**: Accurately reflect the requirements defined in the PRD
|
||
2. **Leverage Mermaid notation**: Express things visually with diagrams
|
||
3. **TypeScript type definitions**: Make the data model clear
|
||
4. **Detailed algorithm design**: Be concrete about complex logic
|
||
5. **Layer separation**: Clarify the responsibilities of each component
|
||
6. **Implementable level**: A level of detail that lets developers implement without confusion
|