feature-spec について
Feature Specは、機能のアイデアを実装可能な仕様書にまとめるプロダクトマネージャー向けスキルです。機能要件、技術詳細、例外ケース、API契約、データモデル、UXフローを統合し、デザインと開発の連携を支援します。
主な機能
- 01機能要件と技術要件の記述
- 02API契約とデータモデルの仕様化
- 03例外ケースとエラー処理の記述
- 04UXフローとワイヤーフレームの定義
- 05プロダクト・デザイン・開発の連携
活用シーン
- 01新機能の仕様策定
- 02APIとデータ要件の文書化
- 03複雑な操作やワークフローの詳細化
- 04開発チームへの引き継ぎ資料作成
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ w95/ awesome-claude-corporate-skills --skill feature-specインストール後、Claude Code で/feature-specを入力して読み込まれたか確認します。
詳細
機能仕様書
概要
Feature Spec スキルを使うと、プロダクトマネージャーは設計とエンジニアリングをつなぐ、詳細で実装可能な機能仕様書を作成できます。機能要件、技術仕様、UX フローを単一の信頼できる情報源にまとめます。
このスキルを使用する場面
- 機能の技術要件を定義するとき
- API コントラクトとデータモデルを指定するとき
- エッジケースとエラー処理を文書化するとき
- UX フローとワイヤーフレームを作成するとき
- 設計とエンジニアリングの連携を確保するとき
- エンジニアリングチームに完全な仕様を提供するとき
- 複雑なインタラクションとワークフローを文書化するとき
機能仕様書テンプレート
エグゼクティブサマリー
機能名: [明確で説明的な名前] 機能 ID: [一意の識別子] オーナー(PM): [名前] オーナー(エンジニアリング): [名前] ステータス: [ドラフト / レビュー中 / 承認済み / 開発中] 最終更新日: [日付]
一文での説明: [ユーザーの視点から、この機能が何をするのか]
ビジネス上の背景: [この機能を開発する理由、顧客の課題、ビジネス目標]
主要指標: [成功をどのように測定するか]
機能要件
主なユースケース
ユースケース 1:[名前]
- アクター:[誰が]
- 前提条件:[処理前のシステム状態]
- 基本フロー:
- [ステップ 1]
- [ステップ 2]
- [ステップ 3]
- 代替フロー:
- [条件]の場合、[手順]を実行
- 事後条件:[処理後のシステム状態]
- エラー処理:[発生する可能性のある問題]
詳細な機能仕様
要件 1:リアルタイム更新
説明:チームメンバーがタスクを更新すると、そのプロジェクトを表示している他のすべてのチームメンバーに、2 秒以内に更新が表示されます。
受け入れ基準:
- 更新が 2 秒以内に、接続中のすべてのクライアントに配信される
- 更新には、タスク ID、新しいステータス、以前のステータス、タイムスタンプ、変更したユーザーが含まれる
- 配信には WebSocket を使用する(ポーリングは使用しない)
- 異なるデバイスやブラウザー間でも動作する
- オフラインのユーザーは、再接続時に更新を受け取る
技術的な注意事項:
- 永続的な接続管理が必要
- ネットワーク障害を適切に処理する
- エコーを防ぐため、送信者を除く全員に配信する
要件 2:アクティビティフィードの表示
説明:ユーザーは、プロジェクトに対するすべての変更を、新しい順に並べたタイムラインで確認できます。
受け入れ基準:
- フィードにすべてのアクティビティ(タスクの更新、コメント、割り当て、ステータス変更)が表示される
- タイムスタンプで並べ替え、新しいものを先頭に表示する
- 各項目に、実行者、アクション、対象オブジェクト、タイムスタンプ、コンテキストが表示される
- 最初の 20 件を読み込み、それ以降はページネーションで表示する
- ユーザーはアクティビティの種類、担当者、タスク、日付範囲で絞り込める
- キーワードでアクティビティを検索できる
技術的な注意事項:
- 大量のデータ読み込みを防ぐため、ページネーションを実装する
- 複数のサブイベントから複合アクティビティを作成する(同じ 1 分間に発生した更新をまとめる)
- パフォーマンス向上のため、非正規化フィールドを含むアクティビティテーブルに保存する
要件 3:通知の配信
説明:ユーザーは、自分に関係するアクティビティが発生したときに通知を受け取ります。
受け入れ基準:
- イベント発生から 5 秒以内にユーザーへ通知が届く
- 通知には、実行者、アクション、対象オブジェクト、コンテキストへのリンクが含まれる
- ユーザーは通知の対象となるアクティビティの種類を設定できる
- ユーザーは通知方法として、アプリ内、メール、Slack、モバイルプッシュを選択できる
- ユーザーはおやすみ時間を設定でき、その時間帯には通知を受け取らない
- 通知をまとめて通知疲れを防ぐ(メールは 1 時間に最大 1 通)
技術的な注意事項:
- 通知チャネルによって遅延が異なる(メールはアプリ内通知より遅い)
- 設定に基づいてユーザーの環境設定を尊重する
- 通知のバッチ処理を実装する
エッジケースとエラー処理
エラーシナリオ
シナリオ 1:ステータス更新中のネットワーク切断
- ユーザーがモバイルでタスクのステータスを更新します
- 確認前にネットワークが切断されます
- 期待される動作:
- UI に「保留中」の状態が表示されます
- 接続が復旧したら自動で再試行します
- 3 回の試行後も失敗した場合はエラーを表示します
- 手動で再試行できます
- 技術的なアプローチ:
- 更新内容をローカルにキューイングします(IndexedDB)
- 接続が復旧したら同期します
- 切断中にステータスが変更された場合は競合を処理します
シナリオ 2:更新イベントの重複
- 同じ更新が 2 回受信されます(ネットワークの再試行)
- 期待される動作:
- クライアントとサーバーの両方で重複を排除します
- アクティビティフィードに重複したエントリーを表示しません
- 更新を冪等にします(2 回適用しても安全です)
- 技術的なアプローチ:
- 更新にイベント ID を含めます
- 処理前にサーバーで重複を確認します
- すでに処理済みの場合も 200 OK を返します
シナリオ 3:同じタスクへの同時更新
- 2 人のユーザーが同じタスクを同時に更新します
- 期待される動作:
- 両方の更新を適用します(最終書き込み優先にはしません)
- データ損失がありません
- フィードに両方の更新が順番に表示されます
- 競合や破損が発生しません
- 技術的なアプローチ:
- オペレーショナル・トランスフォーメーションまたは競合のないレプリカを使用します
- フィードの順序付けにはタイムスタンプを使用します
- 負荷テストで検証します
シナリオ 4:閲覧中にユーザーの権限が変更される
- プロジェクトを閲覧中のユーザーが権限を失います(チームから削除されます)
- 期待される動作:
- アクティビティフィードが非表示になるか、エラーメッセージが表示されます
- ユーザーはアクセス可能なページにリダイレクトされます
- 機密データが漏えいしません
- 技術的なアプローチ:
- データを返す前にサーバーで権限を確認します
- クライアントで 403 Forbidden を適切に処理します
- わかりやすいエラーメッセージを表示します
シナリオ 5:非常に大きなアクティビティフィード(1000 件以上)
- 1 年分のアクティビティ履歴があるプロジェクト
- 期待される動作:
- フィードを引き続きすばやく読み込めます
- ページネーションがスムーズに動作します
- 検索パフォーマンスが許容範囲内です
- 技術的なアプローチ:
- タイムスタンプとフィルターにインデックスを作成します
- データベースクエリを最適化します
- クライアントで仮想スクロールを使用します
データモデルの仕様
データベーススキーマ
テーブル:activities
CREATE TABLE activities (
id UUID PRIMARY KEY,
project_id UUID NOT NULL REFERENCES projects(id),
actor_id UUID NOT NULL REFERENCES users(id),
action_type VARCHAR(50) NOT NULL,
-- task_status_changed, task_comment_added, task_assigned, etc.
target_type VARCHAR(50) NOT NULL,
-- task, comment, assignment, project
target_id UUID NOT NULL,
-- ID of the object being changed
old_value JSONB,
-- Previous state (for updates): { "status": "todo" }
new_value JSONB,
-- New state: { "status": "done" }
metadata JSONB,
-- Extra context: { "duration_hours": 8, "effort_score": 5 }
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
CONSTRAINT check_values CHECK (
(old_value IS NOT NULL OR new_value IS NOT NULL)
)
);
CREATE INDEX idx_activities_project_created
ON activities(project_id, created_at DESC);
CREATE INDEX idx_activities_actor_created
ON activities(actor_id, created_at DESC);
テーブル:notification_preferences
CREATE TABLE notification_preferences (
id UUID PRIMARY KEY,
user_id UUID NOT NULL UNIQUE REFERENCES users(id),
-- Activity type toggles
task_status_changed BOOLEAN DEFAULT true,
task_comment_added BOOLEAN DEFAULT true,
task_assigned_to_me BOOLEAN DEFAULT true,
-- Delivery method (can choose multiple)
email_enabled BOOLEAN DEFAULT true,
in_app_enabled BOOLEAN DEFAULT true,
slack_enabled BOOLEAN DEFAULT false,
mobile_push_enabled BOOLEAN DEFAULT true,
-- Do not disturb
dnd_enabled BOOLEAN DEFAULT false,
dnd_start TIME, -- 22:00
dnd_end TIME, -- 08:00
-- Batching
email_batch_frequency VARCHAR(20) DEFAULT 'hourly',
-- realtime, daily, weekly, never
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW()
);
テーブル:activity_subscriptions
CREATE TABLE activity_subscriptions (
id UUID PRIMARY KEY,
user_id UUID NOT NULL REFERENCES users(id),
resource_type VARCHAR(50) NOT NULL, -- task, project
resource_id UUID NOT NULL,
subscription_type VARCHAR(50) NOT NULL,
-- all_activities, mentions_only, assigned_only
created_at TIMESTAMP NOT NULL DEFAULT NOW()
);
CREATE UNIQUE INDEX idx_user_resource_subscription
ON activity_subscriptions(user_id, resource_type, resource_id);
データ辞書
フィールド:action_type
- 型:VARCHAR(50)
- 有効な値:
- task_status_changed:タスクが別のステータスに移動しました
- task_comment_added:タスクにコメントが追加されました
- task_assigned:タスクがユーザーに割り当てられました
- task_unassigned:ユーザーへのタスクの割り当てが解除されました
- task_description_updated:説明フィールドが変更されました
- task_due_date_changed:期限が変更されました
- task_priority_changed:優先度が変更されました
- project_created:プロジェクトが作成されました
- project_deleted:プロジェクトが削除されました
- team_member_added:チームメンバーがプロジェクトに追加されました
- team_member_removed:チームメンバーがプロジェクトから削除されました
フィールド:old_value および new_value
- 型:JSONB
- 構造は action_type によって異なります
- 例:
// For task_status_changed { "old": { "status": "todo" }, "new": { "status": "in_progress" } } // For task_comment_added { "new": { "comment_id": "abc123", "text": "This is a comment", "commenter": "Sarah" } } // For task_assigned { "new": { "assignee_id": "user123", "assignee_name": "John" } }
API 仕様
エンドポイント:アクティビティフィードの取得
リクエスト:
GET /api/v1/projects/{projectId}/activities
Query parameters:
- limit: number (default: 20, max: 100)
- offset: number (default: 0)
- action_type: string (optional, filter)
- actor_id: string (optional, filter)
- start_date: ISO8601 (optional, filter)
- end_date: ISO8601 (optional, filter)
- search: string (optional, keyword search)
Headers:
- Authorization: Bearer {token}
レスポンス(200 OK):
{
"data": [
{
"id": "activity-123",
"project_id": "proj-456",
"actor": {
"id": "user-789",
"name": "Sarah Chen",
"avatar_url": "https://..."
},
"action_type": "task_status_changed",
"action_label": "changed status",
"target": {
"type": "task",
"id": "task-999",
"name": "Build user auth",
"url": "/tasks/task-999"
},
"old_value": { "status": "todo" },
"new_value": { "status": "in_progress" },
"created_at": "2024-02-20T14:30:00Z",
"metadata": {
"duration_in_status": "2 days"
}
}
],
"pagination": {
"total": 250,
"offset": 0,
"limit": 20,
"has_more": true
}
}
レスポンス(401 Unauthorized):
{
"error": "Unauthorized",
"message": "Authentication required"
}
レスポンス(403 Forbidden):
{
"error": "Forbidden",
"message": "You don't have access to this project"
}
エンドポイント:タスクのステータス更新(アクティビティをトリガー)
リクエスト:
PATCH /api/v1/tasks/{taskId}
Body:
{
"status": "in_progress"
}
Headers:
- Authorization: Bearer {token}
- Content-Type: application/json
レスポンス(200 OK):
{
"id": "task-999",
"name": "Build user auth",
"status": "in_progress",
"updated_at": "2024-02-20T14:30:00Z",
"activity_id": "activity-123"
}
アクティビティが自動的に作成されます:
- action_type:task_status_changed
- old_value:{ "status": "todo" }
- new_value:{ "status": "in_progress" }
エンドポイント:通知設定の更新
リクエスト:
PUT /api/v1/notifications/preferences
Body:
{
"email_enabled": true,
"in_app_enabled": true,
"slack_enabled": false,
"mobile_push_enabled": true,
"email_batch_frequency": "hourly",
"dnd_enabled": true,
"dnd_start": "22:00",
"dnd_end": "08:00"
}
レスポンス(200 OK):
{
"id": "pref-123",
"user_id": "user-789",
"email_enabled": true,
"in_app_enabled": true,
"slack_enabled": false,
"mobile_push_enabled": true,
"updated_at": "2024-02-20T14:30:00Z"
}
WebSocket:リアルタイムのアクティビティ配信
接続:
WS /api/v1/ws?token={token}&project_id={projectId}
プロジェクトのアクティビティを購読:
{
"type": "subscribe",
"project_id": "proj-456"
}
アクティビティを受信(ブロードキャスト):
{
"type": "activity_created",
"data": {
"id": "activity-123",
"project_id": "proj-456",
"action_type": "task_status_changed",
"actor": { "id": "user-789", "name": "Sarah Chen" },
"target": { "type": "task", "id": "task-999", "name": "Build user auth" },
"new_value": { "status": "in_progress" },
"created_at": "2024-02-20T14:30:00Z"
}
}
ユーザー体験のフロー
フロー 1:アクティビティフィードの閲覧
画面 1:アクティビティフィードの一覧
┌─────────────────────────────────────┐
│ Activity Feed │
├─────────────────────────────────────┤
│ [Filter ▼] [Search _______________] │
├─────────────────────────────────────┤
│ Sarah Chen changed status │
│ "Build auth" → in progress │
│ 2 hours ago │
│ │
│ John Lee added comment │
│ "Build auth" - "Great progress" │
│ 3 hours ago │
│ │
│ Project created "Mobile App" │
│ by Jane Doe │
│ Yesterday │
│ │
│ [Load more] │
└─────────────────────────────────────┘
操作:
- アクティビティをクリック → タスク/オブジェクトに移動します
- フィルターをクリック → オプション(タイプ、日付、担当者)を表示します
- 検索欄に入力 → キーワードで絞り込みます
- 一番下までスクロール → 項目をさらに読み込みます
- リアルタイム:新しいアクティビティが上部に表示されます
フロー 2:タスクのステータスを更新する(アクティビティのトリガー)
現在の状態: 「todo」ステータスのタスク
ユーザーの操作: タスクをクリックし、ステータスのドロップダウンを「in_progress」に変更する
システムの動作:
- タスク更新の API 呼び出し(PATCH /api/v1/tasks/{id})
- サーバーがタスクのステータスを更新する
- サーバーがアクティビティレコードを自動的に作成する
- サーバーが WebSocket 経由でアクティビティをブロードキャストする
- クライアントがブロードキャストを受信する
- 接続中のすべてのユーザーのフィードにアクティビティがリアルタイムで表示される
- UI に楽観的な更新が表示される(ステータスが即座に変更される)
フロー 3:通知を設定する
画面:通知設定モーダル
┌──────────────────────────────────────┐
│ Notification Preferences │
├──────────────────────────────────────┤
│ Activity Types │
│ ☑ Task status changed │
│ ☑ Task assigned to me │
│ ☑ New comments │
│ ☐ Task description updated │
│ │
│ Delivery Methods │
│ ☑ Email │
│ ☑ In-app │
│ ☐ Slack │
│ ☑ Mobile push │
│ │
│ Email Settings │
│ Batch frequency: [Hourly ▼] │
│ │
│ Do Not Disturb │
│ ☑ Enabled │
│ From: [22:00] To: [08:00] │
│ │
│ [Save] [Cancel] │
└──────────────────────────────────────┘
パフォーマンス要件
応答時間の目標
| 操作 | 目標 | 許容範囲 | 許容不可 |
|---|---|---|---|
| アクティビティフィードの取得 | 500ms | 1s | >2s |
| リアルタイムブロードキャスト | 2s | 3s | >5s |
| タスクステータスの更新 | 1s | 2s | >3s |
| アクティビティの検索 | 1s | 2s | >3s |
| 通知設定の読み込み | 500ms | 1s | >2s |
スケーラビリティ要件
- サーバーあたり10,000件の同時 WebSocket 接続をサポートする
- 1分あたり1,000件のアクティビティ作成を処理する
- 100,000件以上の項目を含むアクティビティフィードを2秒未満で読み込む
- データベースクエリは p95 で500ms未満に応答する
データ量の見積もり
- アクティビティテーブル:約1億行(1年分のデータ)
- 通知設定:100万行(1ユーザーあたり1行)
- 1日あたりのアクティビティ作成数:50K~500K(使用状況による)
受け入れ基準
- すべての機能要件が実装されている
- すべての受け入れ基準が満たされ、テストされている
- すべてのエッジケースが適切に処理されている
- すべてのエラーシナリオがテストされている
- パフォーマンス目標(p95 レイテンシ)が達成されている
- 負荷テストでスケーラビリティが検証されている
- セキュリティレビューが完了している
- データの整合性が検証されている
- ドキュメントが完成している
- QA テストが承認されている
- プロダクトマネージャーの承認を得ている
- リリース準備が整っている
成果物
- 機能仕様書 - 完全な仕様書(20~30ページ)
- データモデル図 - スキーマの視覚化
- API ドキュメント - OpenAPI/Swagger 定義
- UX フロー図 - 各機能のユーザージャーニー
- ワイヤーフレーム - 各画面の UI モックアップ
- エラーハンドリングガイド - エッジケースと応答
- パフォーマンスベースライン - 負荷テストの結果
- 実装チェックリスト - エンジニアへの引き継ぎ資料