何が最初に壊れたのか
最初の失敗は、AIエージェントが明示的なメモリ基盤なしにスプリント間でコンテキストを維持できると仮定したことでした。スプリント2でそれが露呈し、誰も記録していなかったため、同じ判断が何度も再検討されました。忘れられたアーキテクチャ上の決定によって生じた手戻りのせいで、スプリント2の速度を約30%失いました。
2つ目のつらい教訓は、TDDフェーズの規律はスプリント4までは「余計な手間」に感じられていたことです。しかし、VERIFYフェーズの見落としによって誤ったポジティブテストが通ってしまいました。そのバグは回帰テストが見つけるまで、3スプリント生き延びました。その後、RED-VERIFY-GREEN-REFACTOR-VALIDATEの一連の流れは「提案」ではなくなり、筋肉の記憶のようなものになりました。
10スプリントのアーク
ゼロから始めて、プログラムは10回の2週間スプリントにわたって成長しました:
- スプリント1-2: 基盤づくり。MCPサーバ、基本的なCRUD、スケジューラの中核。速度: 約15ポイント/スプリント。テスト数: 24〜52。
- スプリント3-4: コンテンツパイプライン。音声ナレーション、多ページ対応、キュー管理。速度は約18で安定。テスト: 78〜112。
- スプリント5-6: 知能レイヤ。分析、フィードバックループ、ボイススタイルエンジン。速度: 約23。テスト: 156〜198。
- スプリント7-8: スケールと堅牢化。負荷テスト、NFR(非機能要求)の検証、CIパイプライン。速度: 約25。テスト: 245〜301。
- スプリント9-10: 本番環境への強化。認証、ヘルスチェック、Docker最適化、UAT。速度: 約26。テスト: 362〜415。
サイクルタイムは、スプリント1で平均4.2時間だったものが、スプリント10までに2.0時間へと低下しました。最大の改善はスプリント4-5で起きました。ドキュメントを「後回し」に扱うのをやめ、テストを書く前にドキュメントをチケットへ紐付けるようにしたときです。
何を間違えたのか
メモリアーキテクチャは後回しだった。 まずMCPツールの表面(インターフェース)を設計し、その後にメモリを付け足しました。その結果、スプリント6-7では、パーソナ(人格)に関するメモリとプログラム全体の検索を統合するためのつらいリファクタが必要になりました。教訓: ツール表面を設計する前に、メモリモデルを設計すること。
セキュリティテストが遅すぎた。 スプリント8でIanのセキュリティレビューを入れたところ、スプリント3の時点で見つかっているべきギャップが見つかりました。入力バリデーション、レート制限、認証は、最初から設計されたのではなく、あとから「取り付け」ました。コスト: 手戻りとして約2本分のフルストーリー。
ドキュメントの陳腐化は見えなかった――スプリント5でliving_docs_manageによって陳腐化検知が導入されるまで。そこまでは、仕様が実装から静かにズレていきました。複数のストーリーが、古くなった仕様に基づいて作られました。
再利用できるパターン
パターン1: サービス境界のためのResultモナド
すべてのサービス関数は、スローではなく型付きのResultを返します:
type Result<T> = { ok: true; value: T } | { ok: false; error: string };
function parseSchedule(input: string): Result<Schedule> {
if (!input || input.trim() === '') {
return { ok: false, error: 'Schedule input cannot be empty' };
}
// ... パースロジック
return { ok: true, value: schedule };
}
これによりtry/catchの乱立がなくなり、エラーパスをテスト可能にしました。すべてのサービスがスプリント6までにこの方式を採用しました。
パターン2: ピュア関数テストのアーキテクチャ
複雑なセットアップは必要だが、実行時の依存が不要なテストでは、合成データを使った同一箇所(co-located)のピュア関数を用います:
// 関数をインラインで定義し、合成データでテストする
function calculateMetrics(sprints: SprintData[]): Metrics {
return { total: sprints.reduce((s, m) => s + m.count, 0) };
}
describe('calculateMetrics', () => {
it('should aggregate across sprints', () => {
const data = [{ count: 10 }, { count: 20 }];
expect(calculateMetrics(data).total).toBe(30);
});
});
このパターンにより、合計415本のテストが、合計2秒未満で実行されるようになりました。
パターン3: ドキュメント駆動のTDD
返却形式: {"translated": "翻訳されたHTML"}DD-TDDワークフロー――ドキュメントを更新し、チケットに紐づけ、失敗するテストを書き、実装し、リファクタし、検証する――は、コードファーストのスプリントと比べて、見積もり上の手戻りを40%削減しました。重要な洞察は、テストの前に仕様を書くことで、実装の前にインターフェースについて考えさせられることです。
1. spec/guide を意図した挙動で更新
2. living_docs_manage(action='bind', document_id, ticket_id)
3. 失敗するテストを書く(TDD_RED)
4. 最小限で通るように実装(TDD_GREEN)
5. リファクタ(TDD_REFACTOR)
6. すべてのテストを検証 + ドキュメントを更新(TDD_VALIDATE)
実際に出荷されたもの vs. 私たちの構想
元々のV3の構想では、YouTube連携、ポッドキャスト生成、AIニュースの集約、そして25名規模の代理店体制を想定していました。実際に出荷されたのは、102個のMCPツールを備えた、本番運用に耐えるLinkedInキャンペーン・スケジューラです。加えて、4つのLinkedInページ、音声ナレーション、アナリティクス、そしてヘルスモニタリングのダッシュボードも含まれています。
YouTubeとポッドキャスト生成はバックログに残っています。率直な評価としては、私たちは大きく絞り込み、主要なプラットフォームを堅実に作り上げて納品しました。残りの機能のためのインフラは存在しますが、肝心の機能自体はまだありません。
私たちが変えたいこと
- メモリアーキテクチャから始める。 知識グラフとメモリモデルは、立ち上げ(inception)の段階で設計し、スプリント6まで待たない。
- セキュリティはスプリント1から。 すべてのサービスに対して、最初の機能の前に入力バリデーションと認証チェックを入れる。
- より小さなストーリー。 初期スプリントの5〜8ポイントのストーリーは大きすぎました。3チケットずつの2〜3ポイントのストーリーにすることで、より予測可能なベロシティが得られました。
- ペルソナの引き継ぎプロトコルを明示する。 Api Endorがバックエンドのチケットを完了し、React Iveがフロントエンドを引き継ぐ際、引き継ぎのコンテキストが失われがちでした。構造化された引き継ぎコメントがこれを解決します。
プログラムは完了しました。プラットフォームは動作しています。教訓は文書化されています。次に何が起こるかは、この取り組みのパターンが、それを引き継ぐ次のチームと出会ったときに生き残るかどうかにかかっています。



