Devinでも仕様駆動開発!Playbookで再現する方法
仕様駆動開発(Spec-Driven Development)は、実装に入る前に仕様を明確に定義し、設計・計画を段階的に進めていく手法です。これまではCursorの .mdc を使ったワークフローが有名でしたが、Devinでも同じ考え方を再現できると感じました。本記事では、DevinのPlaybookを活用して仕様駆動開発を実現する方法を紹介します。
仕様駆動はキーは、Devinでは playbook が親和性が高いと思い実現できるか検証してみました。
また、チームメンバーによっては違うツール(IDE/CLI/自律型)も想定しつつ共有できることを前提に考えて以前の記事で紹介した Cursorで実践するスペック駆動開発:Kiro流フローを完全再現 で作成した rules で、仕様駆動開発を実現させました。
この方法であれば 自律型 / IDE / CLIといった環境が別でも利用できるようになります。
リポジトリに定義したファイルが共通ルールの鍵
Devinや他のAIツールを使うとき、必ずぶつかる課題があります。
それは「どうやってルールやナレッジをチーム全体で共有するか」という点です。
Devinの場合、Knowledgeに情報をため込めば事前知識として参照してくれますが、他のツールではKnowledgeを直接読み込む仕組みがないことも多いです。
つまり、ツールごとに知識を管理してしまうと、環境を変えたときにルールが引き継げなくなってしまいます。
そこで解決策となるのが、リポジトリに共通ディレクトリを用意し、Markdownファイルとして仕様やルールを定義することです。
こうしておけば、どのAIツールを利用しても、リポジトリ内にある仕様ファイルを参照して要件定義・設計・実装計画を進めることができます。
実際のやり方(手順)
フェーズを進めるときのポイント
フェーズごとの実行を進めるときは、タスクを必ず作業単位で分割することが大切です。
タスクを小さく区切ることで、AIへの指示が明確になり、期待した結果に近づけやすくなります。
逆にタスクが大きすぎると、AIの出力が粗くなり、仕様の意図を正しく反映できなかったり、本来のタスクから乖離してしまうことがあります。
そのため、子課題レベルまで分割して扱うことが、仕様駆動開発をAIと進めるうえでのポイントになります。
手順
Devinで仕様駆動開発を進める手順は以下の通りです。
-
Specファイルの準備
リポジトリに.cursor/rules/spec.mdcを用意します。
Cursorで作った仕様ファイルをそのまま使えます。 -
Playbookの設定
DevinのPlaybookにSpecファイルを読み込ませます。
これでDevinが仕様を理解し、段階的にタスクを進められるようになります。 -
フェーズに沿った実行
Devinは5段階のフローで作業します。- 要件定義フェーズ:作るものを明確化、機能・非機能要件の整理
- 設計フェーズ:実装方法や内部処理の設計
- 実装計画フェーズ:タスク分解、優先順位、テスト計画
- Issue作成フェーズ:GitHubでIssue作成、Projectに紐づけ
-
実行フェーズ:ローカルで作業 → コミット → PR作成
各フェーズで作成した仕様Markdownは.cursor_workflow/specs/に保存されます。
-
Knowledge保存
フェーズでの仕様やり取りが完了したあとに、まとめた情報をdevin/Knowledgeに保存します。
こうしておくと、次回以降の開発や他のツールで作業するときにも再利用しやすくなります。- Knowledgeをmarkdownで残すことで、他のAIでの指示として使える
- Devinの設定でもKnowledgeとして保存しておくことで参照できる
Specファイルを用意する
Devin が読み込めるように Playbook に Cursorで作った .cursor/rules/spec.mdc を読み込ませるようにします。
前回の記事で用意したSpecファイルを使います。
Specファイル
---
alwaysApply: false
---
# タスク実行の5段階フロー
## 実行ルール
### github認証情報
- owner: [Githubのオーナ名] # Github MCPを使用するのに必要。定義しないと毎回確認するようになります。
### ファイル操作
- 新規タスク開始時: 既存ファイル、既存issueの**内容を全て削除して白紙から書き直す** # 既存ファイルが存在する状態で再実行する場合に必要
- ファイル編集前に必ず現在の内容を確認
### フェーズ管理
- 各段階開始時: 「前段階のmdファイルを読み込みました」と報告
- 各段階の最後に、期待通りの結果になっているか確認
- `.cursor_workflow/specs/{指定したディレクトリ名}/requirements.md`が存在しない場合は必ず要件定義フェーズから始める
### 実行方針
- 段階的に進める: 一度に全てを変更せず、小さな変更を積み重ねること
- 複数のタスクを同時並行で進めないこと
- エラーが発生した場合は、報告すること
- エラーを解決してから次へ進むこと
- ユーザからの指示に無い機能を勝手に追加しないこと
## 1. 要件定義フェーズ
- ユーザからの指示に対して **「何を作るのか」を明確に定める。**
- 目的の明確化、現状分析、機能要件の定義、非機能要件の定義、制約条件の定義を実行する
- `.cursor_workflow/specs/{指定したディレクトリ名}/requirements.md`に文書化
- **必須確認**: 「要件定義フェーズが完了しました。設計フェーズに進んでよろしいですか?」
### 2. 設計フェーズ
- **必ず`.cursor_workflow/specs/{指定したディレクトリ名}/requirements.md`を読み込んでから開始すること**
- 要件定義フェーズで決定したことをもとに **「どのようにつくるか」「内部でどのように処理するか」を設計する**
- プログラマーが実際にコーディングできるレベルまで詳細化すること
- `.cursor_workflow/specs/{指定したディレクトリ名}/design.md`に文書化
- **必須確認**: 「設計フェーズが完了しました。実装計画フェーズに進んでよろしいですか?」
### 3. 実装計画フェーズ
- **必ず`.cursor_workflow/specs/{指定したディレクトリ名}/design.md`を読み込んでから開始すること**
- アジャイル・スクラム開発をすることを前提とする
- タスクを実行可能なユーザーストーリー単位に分解、優先順位を設定する
- `.cursor_workflow/specs/{指定したディレクトリ名}/implementations.md`に文書化
- **必須確認**: 「実装計画フェーズが完了しました。issue作成フェーズに進んでよろしいですか?」
### 4. issue作成フェーズ
- **必ず`.cursor_workflow/specs/{指定したディレクトリ名}/implementations.md`を読み込んでから開始すること**
- Github Projectを現在のリポジトリに紐づける形で新規作成する
- Github Issuesに各タスクの内容でIssueを作成する。Issueは先程作成したProjectに紐づける
- **必須確認**: 「issue作成フェーズが完了しました。実行フェーズに進んでよろしいですか?」
### 5. 実行フェーズ
- **必ずopenになっているissueの一覧を確認してから開始**
- **必ず対応するissueの詳細を確認してから開始**
- **issueの対応開始時に必ずローカル環境で作業ブランチを新規作成してチェックアウトする**
- 実装完了後に適切な単位でコミットメッセージを作成する
- 実装完了後にpull requestを作成する
- **必須対応** pull requestまで作成したら報告すること
- 次のissueに進む場合も実行フェーズを最初から繰り返すこと
設定するPlaybook
Devinで指定した playbook は、以下のようなコンテキストを用意することにしました。
最初のセッションする指定することで仕様駆動開発を開始することができます。
# Devin Playbook: 仕様駆動開発
## 🎯 目的
依頼内容を仕様化してから実装に進む「仕様駆動開発」を実現する。
Cursor の `.mdc` ファイルを基準に、仕様を `.cursor_workflow` に分割して管理する。
---
## 📂 実行フロー
### 1. `.cursor/rules/spec.mdc` を参照
* 依頼内容を理解する前に、必ず仕様ルールを確認する。
* `.mdc` 内に記載された出力形式や必要ファイル名を把握。
---
### 2. 要件を仕様化(3フェーズに分割)
依頼内容を以下のフェーズで整理する。
1. **要件定義フェーズ**
* ゴール、機能要件、非機能要件、前提条件を定義。
2. **設計フェーズ**
* 画面設計、API設計、データ構造、エラーハンドリング方針など。
3. **実装計画フェーズ**
* 実装ステップ、タスク分割、優先度、テスト計画を定義。
### 3. 仕様を `.cursor_workflow` に出力
* ディレクトリ構成:
.cursor_workflow/specs/{依頼ごとのディレクトリ}/
└── {mdcに記載されているファイル名}.md
* フェーズごとにMarkdownファイルを作成し、仕様を落とし込む。
---
### 4. 確認フロー
* 作成した仕様ファイルを依頼者に提示。
* レビュー結果を反映して確定版を保存。
* 仕様が確定したら、実装依頼へ進む。
---
## ✅ 成果物
* `.cursor_workflow/specs/` 内に保存された仕様Markdown群
* 要件定義 → 設計 → 実装計画 のトレーサビリティ
---
## 🚦 ルール
* 依頼内容をそのまま実装しない。必ず仕様化して確認を取る。
* `.mdc` を常に優先参照。
* フェーズごとに独立してレビュー可能な状態にする。
注意点:Playbookはセッション途中では実行できない
実際に使ってみて分かったのは、Playbookで定義したマクロは最初のセッションでしか有効にならないという点です。
セッション途中で「もう一度このPlaybookを参照してほしい」と指示しても、単なるテキストとして扱われてしまい、中のルールやコンテンツまでは読んでくれません。
そのため、途中から仕様を再確認させたい場合は、再度Playbookの内容を読み込ませる必要があるのです。
また、Devinを含めAIツールにはセッションのメモリ上限があるため、会話が長くなると過去の情報が自然に失われていきます。
私の場合は、長いセッションにはせず新しいセッションとして作ること形で対応しました。
つまり、「AIは過去の情報を忘れるのが前提」 と考えて、必要な仕様やルールはその都度参照させる仕組みを用意しておくのが安全です。
実際のフロー
最初のセッション
playbookは以下のように設定しておき、最初のセッションで指定することで利用することができます。
今回の指示では、以前に作っていた家計簿アプリベースに仕様駆動開発を行い、グラフ部分のライブラリを変更する依頼をだしました。


仕様書作成
specファイルを参照を開始して、仕様書作成から進みました。

# 家計簿アプリ グラフライブラリ移行 - 要件定義
## 1. ゴール
### 主要目的
家計簿アプリのグラフ表示機能において、現在使用している **Recharts** ライブラリを **Highcharts** (https://www.highcharts.com/) に変更し、より高機能で柔軟なグラフ表示を実現する。
生成された情報が多いため、以下に全文を折りたたみました。
仕様書作成
家計簿アプリ グラフライブラリ移行 - 要件定義
1. ゴール
主要目的
家計簿アプリのグラフ表示機能において、現在使用している Recharts ライブラリを Highcharts (https://www.highcharts.com/) に変更し、より高機能で柔軟なグラフ表示を実現する。
期待される効果
- より豊富なグラフ表示オプションの提供
- パフォーマンスの向上
- カスタマイズ性の向上
- プロフェッショナルなグラフ表示の実現
2. 現状分析
現在のグラフ実装状況
- 使用ライブラリ: Recharts 3.1.2
-
実装ファイル:
app/src/app/summary/page.tsx -
グラフ種類:
- 月次推移ラインチャート (LineChart) - 収入・支出・収支の推移表示
- 月別収支比較バーチャート (BarChart) - 月別の収入・支出比較
- 支出カテゴリ円グラフ (PieChart) - カテゴリ別支出内訳
現在のデータ構造
-
trendsData.monthlyTrends: 月次推移データ -
categoryChartData: カテゴリ別収支データ -
pieChartData: 円グラフ用支出データ
3. 機能要件
3.1 必須機能要件
- FR-001: 既存の3種類のグラフ(ライン・バー・パイ)をHighchartsで再実装
- FR-002: 現在のデータ表示内容を完全に維持
- FR-003: 日本語表示対応(ツールチップ、ラベル等)
- FR-004: レスポンシブデザイン対応
- FR-005: 既存のカラーパレット維持
3.2 データ表示要件
- FR-006: 月次推移グラフ - 収入(緑)、支出(赤)、収支(青)の3ライン表示
- FR-007: 月別比較グラフ - 収入・支出の棒グラフ表示
- FR-008: カテゴリ円グラフ - 支出カテゴリ別の割合表示
- FR-009: 金額フォーマット - 日本円表示(¥記号、3桁区切り)
- FR-010: ツールチップ表示 - ホバー時の詳細情報表示
3.3 インタラクション要件
- FR-011: グラフのホバー効果
- FR-012: 凡例の表示・非表示切り替え
- FR-013: データポイントのクリック対応(将来拡張用)
4. 非機能要件
4.1 パフォーマンス要件
- NFR-001: グラフ描画時間は現在と同等以上の速度を維持
- NFR-002: メモリ使用量の最適化
- NFR-003: 大量データ(12ヶ月分)でも滑らかな表示
4.2 互換性要件
- NFR-004: React 19.1.0 との互換性
- NFR-005: Next.js 15.4.4 との互換性
- NFR-006: TypeScript 5.x との互換性
- NFR-007: 既存のTailwind CSSスタイルとの共存
4.3 保守性要件
- NFR-008: 既存のコンポーネント構造を可能な限り維持
- NFR-009: 型安全性の確保(TypeScript対応)
- NFR-010: コードの可読性・保守性の向上
4.4 ライセンス要件
- NFR-011: Highchartsのライセンス要件への対応確認
- NFR-012: 商用利用時のライセンス考慮
5. 制約条件
5.1 技術的制約
- CON-001: 既存のGraphQLクエリ構造は変更しない
- CON-002: データ取得ロジックは変更しない
- CON-003: 既存のUI/UXデザインを維持
- CON-004: 既存のレスポンシブ対応を維持
5.2 スケジュール制約
- CON-005: 段階的な移行を実施(一度に全てを変更しない)
- CON-006: 既存機能の動作確認を各段階で実施
5.3 品質制約
- CON-007: 既存のテスト(存在する場合)を通過
- CON-008: ビルドエラーを発生させない
- CON-009: 既存の機能に影響を与えない
6. 前提条件
6.1 環境前提
- Node.js 18.0.0以上の環境
- pnpmパッケージマネージャーの使用
- 開発環境でのローカル動作確認が可能
6.2 技術前提
- Highchartsライブラリの基本的な使用方法の理解
- React/Next.jsでのライブラリ統合経験
- TypeScriptでの型定義作成能力
6.3 データ前提
- 既存のGraphQLクエリが正常に動作している
- テスト用のサンプルデータが利用可能
- 月次データ、カテゴリデータが適切に取得できる
7. 成功基準
7.1 機能的成功基準
- 全てのグラフが正常に表示される
- データの表示内容が現在と同一である
- 日本語表示が適切に動作する
- レスポンシブデザインが維持される
7.2 技術的成功基準
- TypeScriptエラーが発生しない
- ビルドが正常に完了する
- パフォーマンスが現在と同等以上である
- メモリリークが発生しない
7.3 ユーザビリティ成功基準
- 既存ユーザーが違和感なく使用できる
- グラフの視認性が向上している
- インタラクションが直感的である
設計書作成
仕様書を確認して設計フェーズに進みました。
本来であれば、設計書の次は計画フェーズの確認を返すはずでしたが、PRを立てたので自分の意図の操作にはなりませんでした。
このあたりの指示内容が明確ではなかったので、改善が必要と感じました。

追加のコメントを渡して、実行計画フェーズに進むように指示しました。
実装計画フェーズで、implementations.md でタスクの洗い出しをお願いします。
# 家計簿アプリ グラフライブラリ移行 - 設計フェーズ
## 前段階のmdファイルを読み込みました
requirements.mdの内容を確認し、以下の設計フェーズに進みます。
## 1. 技術設計概要
### 1.1 ライブラリ選択
- **移行先**: Highcharts 11.x (最新安定版)
- **インストール方法**: npm経由でのパッケージ管理
- **React統合**: `highcharts-react-official` パッケージを使用
- **TypeScript対応**: `@types/highcharts` で型定義を提供
生成された情報が多いため、以下に全文を折りたたみました。
設計書作成
家計簿アプリ グラフライブラリ移行 - 設計フェーズ
前段階のmdファイルを読み込みました
requirements.mdの内容を確認し、以下の設計フェーズに進みます。
1. 技術設計概要
1.1 ライブラリ選択
- 移行先: Highcharts 11.x (最新安定版)
- インストール方法: npm経由でのパッケージ管理
-
React統合:
highcharts-react-officialパッケージを使用 -
TypeScript対応:
@types/highchartsで型定義を提供
1.2 アーキテクチャ方針
- 既存のコンポーネント構造を最大限維持
- グラフコンポーネントの段階的置き換え
- データ変換ロジックの最小化
- 既存のGraphQLクエリ構造は変更しない
2. パッケージ設計
2.1 依存関係の追加
{
"dependencies": {
"highcharts": "^11.4.0",
"highcharts-react-official": "^3.2.1"
},
"devDependencies": {
"@types/highcharts": "^7.0.0"
}
}
2.2 依存関係の削除
{
"dependencies": {
"recharts": "^3.1.2" // 削除対象
}
}
3. コンポーネント設計
3.1 新規コンポーネント構成
app/src/components/charts/
├── HighchartsWrapper.tsx // 共通ラッパーコンポーネント
├── MonthlyTrendsChart.tsx // 月次推移ラインチャート
├── MonthlyComparisonChart.tsx // 月別収支比較バーチャート
└── ExpenseCategoryChart.tsx // 支出カテゴリ円グラフ
3.2 共通ラッパーコンポーネント設計
// HighchartsWrapper.tsx
interface HighchartsWrapperProps {
options: Highcharts.Options;
containerProps?: React.HTMLAttributes<HTMLDivElement>;
callback?: (chart: Highcharts.Chart) => void;
}
export const HighchartsWrapper: React.FC<HighchartsWrapperProps> = ({
options,
containerProps,
callback
}) => {
// Highcharts React統合実装
// レスポンシブ対応
// 日本語ローカライゼーション設定
};
3.3 個別チャートコンポーネント設計
3.3.1 月次推移ラインチャート
// MonthlyTrendsChart.tsx
interface MonthlyTrendsData {
monthName: string;
income: number;
expense: number;
balance: number;
}
interface MonthlyTrendsChartProps {
data: MonthlyTrendsData[];
height?: number;
}
export const MonthlyTrendsChart: React.FC<MonthlyTrendsChartProps> = ({
data,
height = 320
}) => {
// Highcharts line chart設定
// 3系列(収入・支出・収支)の表示
// 日本円フォーマット
// カラー設定(緑・赤・青)
};
3.3.2 月別収支比較バーチャート
// MonthlyComparisonChart.tsx
interface MonthlyComparisonChartProps {
data: MonthlyTrendsData[];
height?: number;
}
export const MonthlyComparisonChart: React.FC<MonthlyComparisonChartProps> = ({
data,
height = 320
}) => {
// Highcharts column chart設定
// 収入・支出の2系列表示
// グループ化された棒グラフ
};
3.3.3 支出カテゴリ円グラフ
// ExpenseCategoryChart.tsx
interface ExpenseCategoryData {
name: string;
value: number;
}
interface ExpenseCategoryChartProps {
data: ExpenseCategoryData[];
height?: number;
}
export const ExpenseCategoryChart: React.FC<ExpenseCategoryChartProps> = ({
data,
height = 256
}) => {
// Highcharts pie chart設定
// パーセンテージ表示
// カスタムカラーパレット
};
4. データ変換設計
4.1 データ変換戦略
- 既存のGraphQLレスポンス構造を維持
- Recharts用データ構造からHighcharts用への変換関数を作成
- 型安全性を確保したデータ変換
4.2 データ変換ユーティリティ
// app/src/utils/chartDataTransformers.ts
export const transformMonthlyTrendsData = (
data: MonthlyTrendsData[]
): Highcharts.SeriesOptionsType[] => {
return [
{
name: '収入',
type: 'line',
data: data.map(item => [item.monthName, item.income]),
color: '#10B981'
},
{
name: '支出',
type: 'line',
data: data.map(item => [item.monthName, item.expense]),
color: '#EF4444'
},
{
name: '収支',
type: 'line',
data: data.map(item => [item.monthName, item.balance]),
color: '#3B82F6'
}
];
};
export const transformPieChartData = (
data: ExpenseCategoryData[]
): Highcharts.SeriesOptionsType[] => {
return [{
name: '支出',
type: 'pie',
data: data.map(item => ({
name: item.name,
y: item.value
}))
}];
};
5. スタイリング設計
5.1 Highchartsテーマ設定
// app/src/config/highchartsTheme.ts
export const highchartsTheme: Highcharts.Options = {
lang: {
thousandsSep: ',',
decimalPoint: '.',
// 日本語ローカライゼーション設定
},
chart: {
backgroundColor: 'transparent',
style: {
fontFamily: 'inherit'
}
},
colors: ['#0088FE', '#00C49F', '#FFBB28', '#FF8042', '#8884D8', '#82CA9D'],
tooltip: {
backgroundColor: 'rgba(255, 255, 255, 0.95)',
borderColor: '#ccc',
borderRadius: 8,
shadow: true
}
};
5.2 レスポンシブ設定
export const responsiveOptions: Highcharts.ResponsiveOptions = {
rules: [{
condition: {
maxWidth: 768
},
chartOptions: {
legend: {
layout: 'horizontal',
align: 'center',
verticalAlign: 'bottom'
}
}
}]
};
6. 移行計画設計
6.1 段階的移行戦略
- フェーズ1: 開発環境でのHighchartsセットアップ
- フェーズ2: 月次推移ラインチャートの移行
- フェーズ3: 月別収支比較バーチャートの移行
- フェーズ4: 支出カテゴリ円グラフの移行
- フェーズ5: Rechartsライブラリの削除
6.2 各フェーズの成果物
- フェーズ1: パッケージインストール、基本設定
-
フェーズ2:
MonthlyTrendsChartコンポーネント -
フェーズ3:
MonthlyComparisonChartコンポーネント -
フェーズ4:
ExpenseCategoryChartコンポーネント - フェーズ5: クリーンアップとテスト
7. エラーハンドリング設計
7.1 チャート描画エラー対応
interface ChartErrorBoundaryState {
hasError: boolean;
error?: Error;
}
export class ChartErrorBoundary extends React.Component<
React.PropsWithChildren<{}>,
ChartErrorBoundaryState
> {
// Highchartsチャート描画エラーのキャッチ
// フォールバック表示の実装
// エラーログの記録
}
7.2 データ検証
export const validateChartData = <T>(
data: T[],
requiredFields: (keyof T)[]
): boolean => {
// データの存在確認
// 必須フィールドの検証
// 数値データの妥当性確認
};
8. パフォーマンス設計
8.1 最適化戦略
- チャートの遅延読み込み(React.lazy)
- データの不要な再描画防止(React.memo)
- Highchartsインスタンスの適切な破棄
8.2 メモ化設計
export const MonthlyTrendsChart = React.memo<MonthlyTrendsChartProps>(
({ data, height }) => {
const chartOptions = useMemo(() => {
return generateChartOptions(data);
}, [data]);
// チャート描画実装
}
);
9. テスト設計
9.1 単体テスト対象
- データ変換関数の正確性
- チャートコンポーネントの描画
- エラーハンドリングの動作
9.2 統合テスト対象
- SummaryPageでのチャート表示
- データ更新時の再描画
- レスポンシブ動作の確認
10. 互換性確保設計
10.1 既存インターフェース維持
-
SummaryPageコンポーネントの変更を最小限に抑制 - 既存のpropsインターフェースを可能な限り維持
- GraphQLクエリ結果の構造は変更しない
10.2 段階的置き換え設計
// 移行期間中の条件分岐
const USE_HIGHCHARTS = process.env.NODE_ENV === 'development';
return (
<div>
{USE_HIGHCHARTS ? (
<MonthlyTrendsChart data={trendsData} />
) : (
<RechartsLineChart data={trendsData} />
)}
</div>
);
11. 設定管理設計
11.1 環境変数設定
# .env.local
NEXT_PUBLIC_CHARTS_LIBRARY=highcharts
NEXT_PUBLIC_HIGHCHARTS_LICENSE_KEY=your_license_key
11.2 設定ファイル構成
// app/src/config/charts.ts
export const chartConfig = {
library: process.env.NEXT_PUBLIC_CHARTS_LIBRARY || 'recharts',
highcharts: {
licenseKey: process.env.NEXT_PUBLIC_HIGHCHARTS_LICENSE_KEY,
theme: 'default'
}
};
12. ライセンス対応設計
12.1 ライセンス確認事項
- 非商用利用での無料使用可能性の確認
- 商用利用時のライセンス購入要否
- ライセンス表示要件の確認
12.2 ライセンス管理
// Highchartsライセンス設定
if (process.env.NEXT_PUBLIC_HIGHCHARTS_LICENSE_KEY) {
Highcharts.setOptions({
credits: {
enabled: false
}
});
}
13. 確認事項
13.1 設計レビューポイント
- コンポーネント設計の妥当性
- データ変換ロジックの効率性
- エラーハンドリングの網羅性
- パフォーマンス最適化の適切性
13.2 次フェーズへの準備
- 実装計画フェーズでの詳細タスク定義
- 開発環境でのプロトタイプ作成
- ライセンス要件の最終確認
実装計画フェーズ作成
実装計画フェーズの内容をまとめて、一旦sleepさせてPRに上がってる内容を精査しました。

# 家計簿アプリ グラフライブラリ移行 - 実装計画フェーズ
## 前段階のmdファイルを読み込みました
requirements.mdとdesign.mdの内容を確認し、以下の実装計画フェーズに進みます。
## 1. 実装ステップ概要
### 1.1 実装方針
- 段階的移行による安全な実装
- 既存機能への影響を最小限に抑制
- 各フェーズでの動作確認を徹底
- TypeScript型安全性の確保
生成された情報が多いため、以下に全文を折りたたみました。
実装計画フェーズ
家計簿アプリ グラフライブラリ移行 - 実装計画フェーズ
前段階のmdファイルを読み込みました
requirements.mdとdesign.mdの内容を確認し、以下の実装計画フェーズに進みます。
1. 実装ステップ概要
1.1 実装方針
- 段階的移行による安全な実装
- 既存機能への影響を最小限に抑制
- 各フェーズでの動作確認を徹底
- TypeScript型安全性の確保
1.2 実装順序
- セットアップフェーズ: 環境構築とライブラリ導入
- 基盤構築フェーズ: 共通コンポーネントとユーティリティ作成
- チャート移行フェーズ: 個別チャートコンポーネントの段階的移行
- 統合テストフェーズ: 全体動作確認とパフォーマンステスト
- クリーンアップフェーズ: 旧ライブラリ削除と最終調整
2. フェーズ1: セットアップフェーズ
2.1 パッケージ管理
タスク ID: IMPL-001
優先度: 高
所要時間: 30分
実装内容
# Highchartsライブラリのインストール
pnpm --filter app add highcharts@^11.4.0
pnpm --filter app add highcharts-react-official@^3.2.1
pnpm --filter app add -D @types/highcharts@^7.0.0
成果物
-
app/package.jsonの依存関係更新 -
pnpm-lock.yamlの更新
完了条件
- Highchartsライブラリが正常にインストールされる
- TypeScript型定義が利用可能になる
- ビルドエラーが発生しない
2.2 基本設定ファイル作成
タスク ID: IMPL-002
優先度: 高
所要時間: 45分
実装内容
// app/src/config/highchartsTheme.ts
import Highcharts from 'highcharts';
export const initializeHighcharts = () => {
Highcharts.setOptions({
lang: {
thousandsSep: ',',
decimalPoint: '.',
resetZoom: 'ズームリセット',
resetZoomTitle: 'ズームレベルを1:1にリセット'
},
chart: {
backgroundColor: 'transparent',
style: {
fontFamily: 'inherit'
}
},
colors: ['#10B981', '#EF4444', '#3B82F6', '#FFBB28', '#FF8042', '#8884D8'],
tooltip: {
backgroundColor: 'rgba(255, 255, 255, 0.95)',
borderColor: '#ccc',
borderRadius: 8,
shadow: true,
style: {
fontSize: '12px'
}
},
credits: {
enabled: false
}
});
};
成果物
app/src/config/highchartsTheme.tsapp/src/config/charts.ts
完了条件
- Highcharts基本設定が完了
- 日本語ローカライゼーション設定が動作
- テーマ設定が適用される
3. フェーズ2: 基盤構築フェーズ
3.1 共通ラッパーコンポーネント作成
タスク ID: IMPL-003
優先度: 高
所要時間: 1時間
実装内容
// app/src/components/charts/HighchartsWrapper.tsx
import React, { useEffect, useRef } from 'react';
import Highcharts from 'highcharts';
import HighchartsReact from 'highcharts-react-official';
interface HighchartsWrapperProps {
options: Highcharts.Options;
containerProps?: React.HTMLAttributes<HTMLDivElement>;
callback?: (chart: Highcharts.Chart) => void;
className?: string;
}
export const HighchartsWrapper: React.FC<HighchartsWrapperProps> = ({
options,
containerProps,
callback,
className = ''
}) => {
const chartRef = useRef<HighchartsReact.RefObject>(null);
useEffect(() => {
const handleResize = () => {
if (chartRef.current?.chart) {
chartRef.current.chart.reflow();
}
};
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return (
<div className={`w-full ${className}`} {...containerProps}>
<HighchartsReact
ref={chartRef}
highcharts={Highcharts}
options={options}
callback={callback}
/>
</div>
);
};
成果物
app/src/components/charts/HighchartsWrapper.tsxapp/src/components/charts/index.ts
完了条件
- ラッパーコンポーネントが正常に動作
- レスポンシブ対応が機能
- TypeScript型定義が正確
3.2 データ変換ユーティリティ作成
タスク ID: IMPL-004
優先度: 高
所要時間: 1時間30分
実装内容
// app/src/utils/chartDataTransformers.ts
import Highcharts from 'highcharts';
export interface MonthlyTrendsData {
monthName: string;
income: number;
expense: number;
balance: number;
}
export interface ExpenseCategoryData {
name: string;
value: number;
}
export const transformMonthlyTrendsData = (
data: MonthlyTrendsData[]
): Highcharts.SeriesOptionsType[] => {
return [
{
name: '収入',
type: 'line',
data: data.map(item => [item.monthName, item.income]),
color: '#10B981',
marker: {
symbol: 'circle',
radius: 4
}
},
{
name: '支出',
type: 'line',
data: data.map(item => [item.monthName, item.expense]),
color: '#EF4444',
marker: {
symbol: 'circle',
radius: 4
}
},
{
name: '収支',
type: 'line',
data: data.map(item => [item.monthName, item.balance]),
color: '#3B82F6',
marker: {
symbol: 'circle',
radius: 4
}
}
];
};
export const transformMonthlyComparisonData = (
data: MonthlyTrendsData[]
): Highcharts.SeriesOptionsType[] => {
return [
{
name: '収入',
type: 'column',
data: data.map(item => [item.monthName, item.income]),
color: '#10B981'
},
{
name: '支出',
type: 'column',
data: data.map(item => [item.monthName, item.expense]),
color: '#EF4444'
}
];
};
export const transformPieChartData = (
data: ExpenseCategoryData[]
): Highcharts.SeriesOptionsType[] => {
const colors = ['#0088FE', '#00C49F', '#FFBB28', '#FF8042', '#8884D8', '#82CA9D'];
return [{
name: '支出',
type: 'pie',
data: data.map((item, index) => ({
name: item.name,
y: item.value,
color: colors[index % colors.length]
}))
}];
};
export const formatCurrency = (value: number): string => {
return `¥${value.toLocaleString('ja-JP')}`;
};
成果物
app/src/utils/chartDataTransformers.tsapp/src/utils/chartHelpers.ts
完了条件
- データ変換関数が正常に動作
- 型安全性が確保されている
- 単体テストが通過する
3.3 エラーハンドリングコンポーネント作成
タスク ID: IMPL-005
優先度: 中
所要時間: 45分
実装内容
// app/src/components/charts/ChartErrorBoundary.tsx
import React, { Component, ErrorInfo, ReactNode } from 'react';
interface Props {
children: ReactNode;
fallback?: ReactNode;
}
interface State {
hasError: boolean;
error?: Error;
}
export class ChartErrorBoundary extends Component<Props, State> {
public state: State = {
hasError: false
};
public static getDerivedStateFromError(error: Error): State {
return { hasError: true, error };
}
public componentDidCatch(error: Error, errorInfo: ErrorInfo) {
console.error('Chart rendering error:', error, errorInfo);
}
public render() {
if (this.state.hasError) {
return this.props.fallback || (
<div className="flex items-center justify-center h-64 bg-gray-50 rounded-lg">
<div className="text-center">
<p className="text-gray-600">グラフの表示でエラーが発生しました</p>
<button
className="mt-2 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
onClick={() => this.setState({ hasError: false, error: undefined })}
>
再試行
</button>
</div>
</div>
);
}
return this.props.children;
}
}
成果物
app/src/components/charts/ChartErrorBoundary.tsx
完了条件
- エラーバウンダリが正常に動作
- フォールバック表示が適切
- エラーログが記録される
4. フェーズ3: チャート移行フェーズ
4.1 月次推移ラインチャート実装
タスク ID: IMPL-006
優先度: 高
所要時間: 2時間
実装内容
// app/src/components/charts/MonthlyTrendsChart.tsx
import React, { useMemo } from 'react';
import Highcharts from 'highcharts';
import { HighchartsWrapper } from './HighchartsWrapper';
import { ChartErrorBoundary } from './ChartErrorBoundary';
import { transformMonthlyTrendsData, formatCurrency, MonthlyTrendsData } from '@/utils/chartDataTransformers';
interface MonthlyTrendsChartProps {
data: MonthlyTrendsData[];
height?: number;
}
export const MonthlyTrendsChart: React.FC<MonthlyTrendsChartProps> = React.memo(({
data,
height = 320
}) => {
const chartOptions = useMemo((): Highcharts.Options => {
const series = transformMonthlyTrendsData(data);
return {
chart: {
type: 'line',
height
},
title: {
text: undefined
},
xAxis: {
type: 'category',
title: {
text: '月'
}
},
yAxis: {
title: {
text: '金額 (円)'
},
labels: {
formatter: function() {
return formatCurrency(this.value as number);
}
}
},
tooltip: {
formatter: function() {
return `<b>${this.series.name}</b><br/>${this.x}: ${formatCurrency(this.y as number)}`;
}
},
legend: {
enabled: true,
align: 'center',
verticalAlign: 'bottom'
},
series,
responsive: {
rules: [{
condition: {
maxWidth: 768
},
chartOptions: {
legend: {
layout: 'horizontal',
align: 'center',
verticalAlign: 'bottom'
}
}
}]
}
};
}, [data, height]);
return (
<ChartErrorBoundary>
<HighchartsWrapper options={chartOptions} />
</ChartErrorBoundary>
);
});
MonthlyTrendsChart.displayName = 'MonthlyTrendsChart';
成果物
app/src/components/charts/MonthlyTrendsChart.tsx
完了条件
- ラインチャートが正常に表示される
- データが正確に反映される
- レスポンシブ対応が機能する
- ツールチップが適切に表示される
4.2 月別収支比較バーチャート実装
タスク ID: IMPL-007
優先度: 高
所要時間: 1時間30分
実装内容
// app/src/components/charts/MonthlyComparisonChart.tsx
import React, { useMemo } from 'react';
import Highcharts from 'highcharts';
import { HighchartsWrapper } from './HighchartsWrapper';
import { ChartErrorBoundary } from './ChartErrorBoundary';
import { transformMonthlyComparisonData, formatCurrency, MonthlyTrendsData } from '@/utils/chartDataTransformers';
interface MonthlyComparisonChartProps {
data: MonthlyTrendsData[];
height?: number;
}
export const MonthlyComparisonChart: React.FC<MonthlyComparisonChartProps> = React.memo(({
data,
height = 320
}) => {
const chartOptions = useMemo((): Highcharts.Options => {
const series = transformMonthlyComparisonData(data);
return {
chart: {
type: 'column',
height
},
title: {
text: undefined
},
xAxis: {
type: 'category',
title: {
text: '月'
}
},
yAxis: {
title: {
text: '金額 (円)'
},
labels: {
formatter: function() {
return formatCurrency(this.value as number);
}
}
},
tooltip: {
formatter: function() {
return `<b>${this.series.name}</b><br/>${this.x}: ${formatCurrency(this.y as number)}`;
}
},
legend: {
enabled: true,
align: 'center',
verticalAlign: 'bottom'
},
plotOptions: {
column: {
grouping: true,
shadow: false,
borderWidth: 0
}
},
series,
responsive: {
rules: [{
condition: {
maxWidth: 768
},
chartOptions: {
legend: {
layout: 'horizontal',
align: 'center',
verticalAlign: 'bottom'
}
}
}]
}
};
}, [data, height]);
return (
<ChartErrorBoundary>
<HighchartsWrapper options={chartOptions} />
</ChartErrorBoundary>
);
});
MonthlyComparisonChart.displayName = 'MonthlyComparisonChart';
成果物
app/src/components/charts/MonthlyComparisonChart.tsx
完了条件
- バーチャートが正常に表示される
- グループ化された棒グラフが機能する
- カラー設定が適切に適用される
4.3 支出カテゴリ円グラフ実装
タスク ID: IMPL-008
優先度: 高
所要時間: 1時間30分
実装内容
// app/src/components/charts/ExpenseCategoryChart.tsx
import React, { useMemo } from 'react';
import Highcharts from 'highcharts';
import { HighchartsWrapper } from './HighchartsWrapper';
import { ChartErrorBoundary } from './ChartErrorBoundary';
import { transformPieChartData, formatCurrency, ExpenseCategoryData } from '@/utils/chartDataTransformers';
interface ExpenseCategoryChartProps {
data: ExpenseCategoryData[];
height?: number;
}
export const ExpenseCategoryChart: React.FC<ExpenseCategoryChartProps> = React.memo(({
data,
height = 256
}) => {
const chartOptions = useMemo((): Highcharts.Options => {
const series = transformPieChartData(data);
return {
chart: {
type: 'pie',
height
},
title: {
text: undefined
},
tooltip: {
formatter: function() {
const percentage = ((this.y as number) / data.reduce((sum, item) => sum + item.value, 0) * 100).toFixed(1);
return `<b>${this.key}</b><br/>${formatCurrency(this.y as number)} (${percentage}%)`;
}
},
plotOptions: {
pie: {
allowPointSelect: true,
cursor: 'pointer',
dataLabels: {
enabled: true,
format: '<b>{point.name}</b>: {point.percentage:.1f}%',
style: {
fontSize: '12px'
}
},
showInLegend: false
}
},
series,
responsive: {
rules: [{
condition: {
maxWidth: 768
},
chartOptions: {
plotOptions: {
pie: {
dataLabels: {
enabled: false
},
showInLegend: true
}
},
legend: {
enabled: true,
layout: 'horizontal',
align: 'center',
verticalAlign: 'bottom'
}
}
}]
}
};
}, [data, height]);
return (
<ChartErrorBoundary>
<HighchartsWrapper options={chartOptions} />
</ChartErrorBoundary>
);
});
ExpenseCategoryChart.displayName = 'ExpenseCategoryChart';
成果物
app/src/components/charts/ExpenseCategoryChart.tsx
完了条件
- 円グラフが正常に表示される
- パーセンテージ表示が正確
- インタラクティブ機能が動作する
5. フェーズ4: 統合実装フェーズ
5.1 SummaryPageコンポーネント更新
タスク ID: IMPL-009
優先度: 高
所要時間: 2時間
実装内容
// app/src/app/summary/page.tsx の更新部分
import { MonthlyTrendsChart } from '@/components/charts/MonthlyTrendsChart';
import { MonthlyComparisonChart } from '@/components/charts/MonthlyComparisonChart';
import { ExpenseCategoryChart } from '@/components/charts/ExpenseCategoryChart';
import { initializeHighcharts } from '@/config/highchartsTheme';
// コンポーネント内でHighcharts初期化
useEffect(() => {
initializeHighcharts();
}, []);
// Rechartsコンポーネントの置き換え
// 既存のLineChart, BarChart, PieChartを新しいコンポーネントに置き換え
実装手順
- Highcharts初期化処理の追加
- 既存のRechartsインポートをHighchartsコンポーネントに置き換え
- データ構造の互換性確認
- スタイリングの調整
成果物
- 更新された
app/src/app/summary/page.tsx
完了条件
- 全てのグラフがHighchartsで表示される
- 既存のデータ表示が維持される
- レイアウトが崩れない
- TypeScriptエラーが発生しない
5.2 アプリケーション初期化設定
タスク ID: IMPL-010
優先度: 中
所要時間: 30分
実装内容
// app/src/app/layout.tsx または app/src/app/page.tsx
import { initializeHighcharts } from '@/config/highchartsTheme';
// アプリケーション起動時にHighcharts初期化
useEffect(() => {
initializeHighcharts();
}, []);
成果物
- 更新されたレイアウトファイル
完了条件
- アプリケーション全体でHighcharts設定が適用される
- 初期化処理が正常に動作する
6. フェーズ5: テスト・検証フェーズ
6.1 単体テスト実装
タスク ID: IMPL-011
優先度: 中
所要時間: 3時間
実装内容
// app/src/components/charts/__tests__/MonthlyTrendsChart.test.tsx
import { render, screen } from '@testing-library/react';
import { MonthlyTrendsChart } from '../MonthlyTrendsChart';
const mockData = [
{ monthName: '2024年1月', income: 300000, expense: 200000, balance: 100000 },
{ monthName: '2024年2月', income: 320000, expense: 220000, balance: 100000 }
];
describe('MonthlyTrendsChart', () => {
it('should render chart with provided data', () => {
render(<MonthlyTrendsChart data={mockData} />);
// テスト実装
});
});
テスト対象
- データ変換関数の正確性
- チャートコンポーネントの描画
- エラーハンドリングの動作
- レスポンシブ機能
成果物
- 各コンポーネントの単体テストファイル
- データ変換ユーティリティのテスト
完了条件
- 全ての単体テストが通過する
- カバレッジが80%以上
- エラーケースのテストが含まれる
6.2 統合テスト実装
タスク ID: IMPL-012
優先度: 中
所要時間: 2時間
実装内容
- SummaryPageでのチャート表示テスト
- データ更新時の再描画テスト
- レスポンシブ動作の確認テスト
成果物
- 統合テストファイル
完了条件
- 統合テストが全て通過する
- 実際のデータでの動作確認完了
- パフォーマンステスト完了
6.3 ローカル動作確認
タスク ID: IMPL-013
優先度: 高
所要時間: 1時間
確認項目
- 開発サーバーが正常に起動する
- 全てのグラフが正常に表示される
- データの表示内容が正確である
- レスポンシブデザインが機能する
- ツールチップが適切に表示される
- エラーハンドリングが動作する
実行手順
# 開発サーバー起動
pnpm --filter app dev
# ブラウザでlocalhost:3000/summaryにアクセス
# 各グラフの動作確認
# 画面サイズ変更でのレスポンシブ確認
7. フェーズ6: クリーンアップフェーズ
7.1 Rechartsライブラリ削除
タスク ID: IMPL-014
優先度: 低
所要時間: 30分
実装内容
# Rechartsライブラリの削除
pnpm --filter app remove recharts
確認事項
- Rechartsの全ての参照が削除されている
- ビルドエラーが発生しない
- 不要なインポートが残っていない
7.2 コードクリーンアップ
タスク ID: IMPL-015
優先度: 低
所要時間: 45分
実装内容
- 不要なコメントの削除
- インポート文の整理
- 型定義の最適化
- ESLintエラーの修正
完了条件
- ESLintエラーが0件
- TypeScriptエラーが0件
- 不要なコードが削除されている
7.3 ドキュメント更新
タスク ID: IMPL-016
優先度: 低
所要時間: 30分
実装内容
- README.mdの更新(使用ライブラリ情報)
- コンポーネントのJSDocコメント追加
- 設定ファイルのコメント追加
成果物
- 更新されたドキュメント
8. 優先度とスケジュール
8.1 優先度マトリックス
| 優先度 | タスクID | タスク名 | 所要時間 |
|---|---|---|---|
| 高 | IMPL-001 | パッケージ管理 | 30分 |
| 高 | IMPL-002 | 基本設定ファイル作成 | 45分 |
| 高 | IMPL-003 | 共通ラッパーコンポーネント作成 | 1時間 |
| 高 | IMPL-004 | データ変換ユーティリティ作成 | 1時間30分 |
| 高 | IMPL-006 | 月次推移ラインチャート実装 | 2時間 |
| 高 | IMPL-007 | 月別収支比較バーチャート実装 | 1時間30分 |
| 高 | IMPL-008 | 支出カテゴリ円グラフ実装 | 1時間30分 |
| 高 | IMPL-009 | SummaryPageコンポーネント更新 | 2時間 |
| 高 | IMPL-013 | ローカル動作確認 | 1時間 |
8.2 実装スケジュール
総所要時間: 約15時間(テスト・ドキュメント含む)
推奨実装順序:
- Day 1: IMPL-001 → IMPL-002 → IMPL-003 (2時間15分)
- Day 2: IMPL-004 → IMPL-005 → IMPL-006 (3時間15分)
- Day 3: IMPL-007 → IMPL-008 (3時間)
- Day 4: IMPL-009 → IMPL-010 → IMPL-013 (3時間30分)
- Day 5: IMPL-011 → IMPL-012 (5時間)
- Day 6: IMPL-014 → IMPL-015 → IMPL-016 (1時間45分)
9. リスク管理
9.1 技術的リスク
| リスク | 影響度 | 対策 |
|---|---|---|
| Highchartsライセンス問題 | 高 | 事前にライセンス要件を確認 |
| パフォーマンス劣化 | 中 | ベンチマークテストの実施 |
| 既存データとの非互換性 | 中 | データ変換関数の十分なテスト |
| TypeScript型エラー | 低 | 段階的な型定義の実装 |
9.2 スケジュールリスク
| リスク | 対策 |
|---|---|
| 想定以上の実装時間 | 優先度の高いタスクから実装 |
| テストでの問題発見 | 各フェーズでの動作確認を徹底 |
| 統合時の問題 | 小さな単位での統合テスト |
10. 成功基準
10.1 機能的成功基準
- 全てのグラフ(ライン・バー・パイ)が正常に表示される
- データの表示内容が既存実装と同一である
- 日本語表示が適切に動作する
- レスポンシブデザインが維持される
- ツールチップが適切に表示される
10.2 技術的成功基準
- TypeScriptエラーが0件
- ESLintエラーが0件
- ビルドが正常に完了する
- 単体テストが全て通過する
- 統合テストが全て通過する
10.3 パフォーマンス成功基準
- グラフ描画時間が既存実装と同等以上
- メモリ使用量が適切な範囲内
- 大量データでも滑らかに表示される
10.4 ユーザビリティ成功基準
- 既存ユーザーが違和感なく使用できる
- グラフの視認性が向上している
- インタラクションが直感的である
11. 次ステップ
11.1 実装開始前の確認事項
- 要件定義・設計フェーズの承認完了
- 開発環境の準備完了
- Highchartsライセンス要件の確認完了
11.2 実装完了後の作業
- ユーザーテストの実施
- 本番環境への適用検討
- 運用・保守手順の策定
12. 実装計画フェーズ完了確認
必須確認: 「実装計画フェーズが完了しました。実装作業に進んでよろしいですか?」
12.1 作成された成果物
- 詳細な実装タスクリスト(16タスク)
- 優先度とスケジュール計画
- リスク管理計画
- 成功基準の定義
12.2 次フェーズへの準備
- 実装環境の確認
- 必要なツール・ライブラリの確認
- テスト戦略の確認
Knowledgeへ保存する
内容が問題なければ、いままでのセッションの流れをKnowledgeを作成する依頼をかけます。
ここで指示内容が明確ではなかったため、 mdcファイルを作成してcursor/rulesにmdcファイルとして保存してしまいました。
問題がある場合は、適宜修正を依頼をかけて直していきました。

まとめ
- Devinでも仕様駆動開発を再現できる
- Playbook + Specファイルでツール間を横断してルールを共有可能
- Plabyook はセッション途中では識別できないため、新しいセッションで行う
- Devinの仕様駆動で進める手順でも、意図しない挙動になることがあった
仕様駆動開発を行うことが可能にはなったものの、Devinが正しく手順通りにすすめることができなかったところはありました。
セッション途中で、指示を行ったりする必要はあるようです。
決まった動きでplaybookを実行させるのは、もう少し工夫が必要と感じました。
実務レベルの開発サイクルでは、精度を挙げていくためにKnowledgeを工夫していくことは必要そうです。
付録:今回生成された仕様・設計・実行計画
今回、Devinが仕様駆動開発で生成されたものを gistにアップしておきました。
Discussion