要件を実装に落とし込もう
ストーリー
「新機能の要件が来た」田中先輩がチケットを見せた。
タスク管理アプリに「タグ機能」を追加してほしい。 ユーザーがタスクにタグを付けて、タグで絞り込めるようにする。「これだけ? もう少し詳しい仕様はないんですか?」
「ないよ。実務では曖昧な要件から始まることがほとんどだ。 要件を分析して、実装可能なタスクに分解するのもエンジニアの仕事だ」
ユーザーストーリー
要件をユーザー視点で記述する形式です。
フォーマット
As a [ユーザー],
I want to [したいこと],
So that [目的/理由].
例: タグ機能
ユーザーストーリー 1:
ユーザーとして、タスクにタグを追加したい。
理由: タスクを分類して管理しやすくするため。
ユーザーストーリー 2:
ユーザーとして、タグでタスクを絞り込みたい。
理由: 特定のカテゴリのタスクだけを表示したいため。
ユーザーストーリー 3:
ユーザーとして、タグを編集・削除したい。
理由: 間違えたタグを修正したいため。
受け入れ条件(Acceptance Criteria)
各ユーザーストーリーに対して「完了の定義」を明確にします。
markdown
## ユーザーストーリー 1: タスクにタグを追加する
### 受け入れ条件
- [ ] タスク作成時にタグを0個以上指定できる
- [ ] 既存タスクにタグを追加できる
- [ ] タグは文字列で、1〜20文字まで
- [ ] 1タスクに最大10個のタグを付けられる
- [ ] 同じタグは重複して追加されない
### エッジケース
- 空文字列のタグは追加できない
- スペースのみのタグは追加できない
- 21文字 以上のタグはエラーになる
- 11個目のタグはエラーになるタスク分解
ユーザーストーリーを実装可能な小さなタスクに分解します。
markdown
## タスク分解: タグ機能
### データ層
1. Tag の型定義(interface)を作成する
2. Task interface に tags プロパティを追加する
3. タグのバリデーション関数を作成する
### ロジック層
4. addTag(taskId, tag) 関数を実装する
5. removeTag(taskId, tag) 関数を実装する
6. filterByTag(tag) 関数を実装する
### テスト
7. タグ追加のテストを書く
8. タグ削除のテストを書く
9. タグ絞り込みのテストを書く
10. エッジケースのテストを書くタスクの粒度
| 良いタスク | 悪いタスク |
|---|---|
| 「Tag interfaceを定義する」 | 「タグ機能を実装する」(大きすぎ) |
| 「addTag関数を実装する」 | 「色々なメソッドを追加する」(曖昧) |
| 「空文字タグのテストを書く」 | 「テストを書く」(範囲が不明確) |
目安: 1タスクは30分〜2時間で完了する大きさに分解する。
実践: 要件からコードへ
Step 1: 型定義
typescript
// 要件: 「タグは文字列で、1〜20文字まで」
type Tag = string;
interface Task {
id: number;
title: string;
completed: boolean;
tags: Tag[]; // 0個以上のタグ
}
// 定数
const MAX_TAG_LENGTH = 20;
const MAX_TAGS_PER_TASK = 10;Step 2: バリデーション
typescript
// 要件: 「空文字列、スペースのみ、21文字以上はエラー」
function validateTag(tag: string): string | null {
const trimmed = tag.trim();
if (trimmed.length === 0) {
return "タグは空にできません";
}
if (trimmed.length > MAX_TAG_LENGTH) {
return `タグは${MAX_TAG_LENGTH}文字以内にしてください`;
}
return null; // エラーなし
}Step 3: 機能実装
typescript
// 要件: 「タスクにタグを追加できる」「同じタグは重複しない」「最大10個」
function addTag(task: Task, tag: string): Task {
const error = validateTag(tag);
if (error) {
throw new Error(error);
}
const trimmedTag = tag.trim();
if (task.tags.includes(trimmedTag)) {
return task; // 重複は無視
}
if (task.tags.length >= MAX_TAGS_PER_TASK) {
throw new Error(`タグは最大${MAX_TAGS_PER_TASK}個までです`);
}
return {
...task,
tags: [...task.tags, trimmedTag],
};
}エッジケースの考え方
| カテゴリ | 確認項目 |
|---|---|
| 空入力 | null, undefined, 空文字列, 空配列 |
| 境界値 | 最小値、最大値、ちょうど境界の値 |
| 重複 | 同じデータが2回入力された場合 |
| 大量データ | 要素 数が非常に多い場合 |
| 権限 | 操作権限がないユーザーの場合 |
まとめ
| ポイント | 内容 |
|---|---|
| ユーザーストーリー | ユーザー視点で要件を記述 |
| 受け入れ条件 | 完了の定義を明確にする |
| タスク分解 | 30分〜2時間の単位に分割 |
| エッジケース | 境界値、空入力、重複を考慮 |
| 型→バリデーション→実装 | この順序で進める |
チェックリスト
- ユーザーストーリーを書ける
- 受け入れ条件を定義できる
- 要件を実装可能なタスクに分解できる
- エッジケースを洗い出せる
次のステップへ
要件分析の方法を学びました。
次のセクションでは、**テスト駆動開発(TDD)**を学びます。 テストを先に書いてから実装する手法で、バグの少ないコードを作りましょう。
推定読了時間: 30分