LESSON 30分

要件を実装に落とし込もう

ストーリー

「新機能の要件が来た」田中先輩がチケットを見せた。

タスク管理アプリに「タグ機能」を追加してほしい。
ユーザーがタスクにタグを付けて、タグで絞り込めるようにする。

「これだけ? もう少し詳しい仕様はないんですか?」

「ないよ。実務では曖昧な要件から始まることがほとんどだ。 要件を分析して、実装可能なタスクに分解するのもエンジニアの仕事だ」


ユーザーストーリー

要件をユーザー視点で記述する形式です。

フォーマット

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分