【詳細版】なぜ AI エージェントを使った開発はすぐカオスになるのか - コンテキスト汚染を防ぐ「保証駆動開発」という考え方
本記事は AI エージェント時代の開発における「思想レベル」の整理を試みたものです。
まだ発展途上の考え方であり、ツッコミ・議論・異なる視点からのフィードバックを歓迎します。
TL;DR(要約)
AI エージェント時代、開発の本質は「コードを書くこと」から「正しさを設計し続けること」へ変わる。
何が変わるのか?
- 実装コスト → 0: AI が瞬時にコードを生成。開発の主要コストは「仕様を確定する工数」へ移行
- 決定論 → 確率論: AI の出力は毎回わずかに異なる。確率的ゆらぎを前提とした設計が必要
- コード中心 → 仕様中心: 価値の源泉は「コード」ではなく「正しい仕様(SSoT)」と「判断の記録(DR)」
保証駆動開発(GDD)とは?
AI 時代の開発体系。以下の 3 つのレイヤーを統合した「三位一体モデル」:
- SSoT レイヤー(仕様レイヤー): 唯一の真実(Single Source of Truth)を構造化する入力境界。AI が一意に解釈できる詳細仕様
- テストレイヤー(検証レイヤー): AI の確率的出力ゆらぎを検出し、意図しないスコープ拡大を防ぐ出力境界(Contract Test など)
- 保証駆動レイヤー(Guarantee-Driven / GDD): 運用中も正しさを継続的に保証し、SSoT とテストを束ねて動的安定を実現する制御ドライバ
ここで「Driven(駆動)」という概念を持つのは保証レイヤーのみであり、SSoT とテストはその保証駆動を成立させるための境界レイヤーである。
重要な原則
- PoC は必ず廃棄: 仕様確定のための実験装置。コードは再生成させる(AI の生成コストは低い)
- Decision Record で判断を分離: 現在の原則(SSoT)と過去の経緯(DR)を分離し、AI の思考汚染を防ぐ
- ドキュメントに寿命を設定: 情報過多は AI を汚染する。全ドキュメントに廃棄条件を明示
見積もりの再定義
従来の見積もり(実装工数中心)は通用しない。AI 時代の工数構造は根本的に変化する:
何が変わるのか?
- 激減: 実装工数(従来の 60% → ほぼ 0%)
- 増加: 仕様確定工数(設計・探索・PoC・ステークホルダー合意)
- 増加: 事後ドキュメント管理工数(SSoT 更新・DR 記録)
- やや減少: 検証工数(AI の高速生成により試行回数増)
- 変わらず: 要件定義工数(人間の意思決定は必須)
4 つの開発パターン
| パターン | 特徴 | 推奨シーン |
|---|---|---|
| D: 従来型 | 人間が実装(ベースライン) | AI を使えない環境 |
| A: 仕様完全確定型 | 事前に仕様を完全確定、AI が一括生成 | 本番システム、複雑な機能 |
| B: ペアプロ型 | AI とペアプロ、実装中に仕様決定 | プロトタイプ、個人開発 |
| C: ハイブリッド型 | コア仕様だけ事前確定、細部は後で記録 | 実務推奨 |
迷ったら → パターン C(ハイブリッド型)
速度と保守性のバランスが最も良く、実務で汎用的に使える。
詳細な工数例と調整方法は第 7 章を参照。
未解決の課題
- DR の墓場化: Decision Record が蓄積し、検索困難・矛盾放置のリスク
- TypeScript vs YAML: SSoT の実装形式はプロジェクトに応じて選択(両方を認める)
結論
AI 時代の開発とは、「正しさを設計し続ける営み」である。
コード生成は AI に任せ、人間は「何を作るべきか」の仕様確定と、「それが正しいか」の保証設計に集中する。
これが保証駆動開発(GDD: Guarantee-Driven Development)の本質である。
目次
- 背景と問題意識:なぜ従来の開発は通用しなくなるのか
- PoC / MVP フェーズ:仕様を決めるための"実験"としての開発
- 仕様確定と SSoT:唯一の真実を構造化する
- 三位一体モデル:SSoT(仕様) × Test(検証) × Guarantee(保証駆動)
- Decision Record:AI と人間の判断を切り分ける
- ドキュメント寿命管理:情報過多が AI を汚染する
- AI 時代の見積もり再定義:実装工数から仕様確定工数へ
- 運用と保証:動的安定を支える継続的保証構造
- 組織と役割:エンジニア・デザイナー・PM の再定義
- 結論:AI 時代の開発とは「正しさを設計し続ける営み」
Appendix. AI 開発と量子モデル的思考
第 1 章 背景と問題意識:なぜ従来の開発は通用しなくなるのか
1.1 コード中心開発の限界
かつてアプリケーション開発は「コードを書くこと」そのものが価値だった。
プログラムを設計し、実装し、テストする一連の行為が人間の知的生産として成立していた。
しかし AI エージェントが登場した今、その前提は根本から崩れつつある。
AI が人間よりも速くコードを書き、仕様の変更にも自動的に対応し、テストまでも生成・実行する時代においては、
もはや「コードを書くこと」自体が本質的価値ではなくなる。
本質は、「どのような構造で正しさを維持し続けるか」へと移行した。
つまり、開発とはコード生成の行為ではなく、
生成されたシステムの安定と整合を保証する設計行為に変わっていく。
1.2 AI エージェント開発の本質:確率的出力の世界
AI エージェントによる生成は決定論的ではなく、**確率的(stochastic)**である。
同一の仕様入力を与えても、温度パラメータ・前文脈・モデル状態などによって出力は毎回わずかに異なる。
これは、もはや開発者が「固定的な正解」を手にする時代ではないことを意味する。
出力のばらつきを前提とし、確率的分布を制御して安定化させる設計が必要となる。
AI 開発とは、"確率的出力を制御する設計行為"である。
確率的出力がもたらす 2 つの課題
この確率的性質は、開発において 2 つの課題をもたらす。
課題 A: コード表現のゆらぎ
同じ仕様でも、実装の表現が毎回わずかに異なる。
// 実行1
const TOKEN_LIFETIME = 48 * 60 * 60;
// 実行2
const TOKEN_DURATION_SECONDS = 172800; // 48 hours
// 実行3
const TOKEN_EXPIRY = 48 * 3600;
影響:
- 変数名、コメント、改行位置が生成ごとに揺れる
- Git diff で毎回異なる行が表示される
- コードレビューで「前回と何が変わったか」が分かりにくい
課題 B: 変更範囲の予期せぬ拡大
局所的な変更指示が、意図しない範囲まで波及する。
// 指示: 「トークン期限を48時間に変更」
// 期待: 定数の値のみ変更(1行の diff)
// 実際: 認証ロジック全体が再構築される(数百行の diff)
影響:
- 1 行変えるつもりが、何十ファイルも変更される
- 依存関係が意図せず増える
- テストが予期せず壊れる
2 つの課題の関係
これらは独立した問題ではなく、課題 B は課題 A の派生的影響である。
AI の出力が確率的にゆらぐため、「どこまで変更すべきか」の判断も不安定になる。
本書で提案する保証駆動開発(GDD)では、出力(入出力契約)を厳密に制約することで、
両方の課題に対処する。詳細は第 3 章で説明する。
この性質により、仕様の確定という概念も静的なものではなくなった。
SSoT(Single Source of Truth)は固定的な真実ではなく、その時点における唯一の事実源となる。
仕様は変動する。確認によって一時的に確定し、再生成(再実装)によって再び不確定性を帯びる。
この確認と再生成の往復サイクルこそが、AI 時代の開発の本質である。
パラダイムシフト: 決定論から確率論へ
この変化は、単なる「不確実性の増加」ではない。
開発の本質的なパラダイムシフトである。
従来の開発:
- 「仕様 → 実装 → テスト」という決定論的な線形プロセス
- 一度正しく作れば、それは正しいまま
AI 時代の開発:
- AI 生成コードは、テストするまで「正しいかもしれない」状態
- 量子力学の波動関数のように、テスト(観測)によって初めて状態が確定する
- テストをやめれば、再び不確定状態に戻る
AI が生成したコードは、テストという「観測」によって初めて「正しさ」が確定する。
この「観測による確定」という概念は、なぜ継続的なテストと保証が必要なのかを説明する。
詳細な理論的背景については、Appendix「AI 開発と量子モデル的思考」を参照されたい。
1.3 閾値と誤差訂正という新しい設計概念
この確率的な世界では、「正解を一点で定義する」ことは事実上不可能である。
代わりに、ばらつきを吸収し、安定領域を保つための設計が必要になる。
その中心となる考え方が「閾値」と「誤差訂正」である。
| 項目 | 旧来の開発 | AI 時代の開発 |
|---|---|---|
| 成果の定義 | 正解 1 点(仕様=結果) | 分布の中の安定領域 |
| 設計思想 | 決定論的ロジック | 確率論的安定 |
| 品質保証 | テストパス/NG の二値 | 閾値・信頼区間による許容 |
| 破綻時の対応 | バグ修正 | エラー訂正・再生成 |
AI エージェントの出力は常にばらつきを伴う。
それを「エラー」とみなして修正するのではなく、
システムの中で許容し、訂正・安定化させる構造が必要となる。
具体的には以下のような仕組みが求められる:
- 閾値設計:性能指標は平均値ではなく P95・P99 の信頼区間で評価
- 確率制御:AI 生成の温度や出力長を調整し分布を安定化
- 誤差訂正構造:Rollback・Retry・Mini-PoC などでばらつきを再吸収
- 保証駆動(GDD):システム全体で"誤差訂正コード"として機能する運用層
AI 開発とは、ノイズを排除することではなく、ノイズを前提に安定構造を設計する営みである。
この思想は、エラーをゼロにする「静的な品質保証」から、
エラーを検知・訂正しながら持続する「動的な品質保証」への転換を意味する。
それが後に登場する**保証駆動(Guarantee-Driven Development)**の基礎となる。
1.4 ここから生まれる新しい前提
1️⃣ 開発は決定論ではなく確率論的プロセス
2️⃣ 「唯一の正解」ではなく「安定解の範囲」を定義する設計が必要
3️⃣ テスト・保証は"出力のばらつき"を許容する構造になる
この「確率的出力」と「誤差訂正構造」を前提にして、
次章以降では次のような開発モデルを扱う:
-
PoC / MVP フェーズ:確率的出力を利用した高速探索(Vibe Coding)
- 仕様を確定させるための実験的開発
- プロトタイプの量産と仕様の安定性検証
-
正式版開発・運用フェーズ:三位一体モデルによる動的安定
- SSoT レイヤー(仕様レイヤー):SSoT を基点に AI 生成を制御する入力境界
- テストレイヤー(検証レイヤー):自動テストで仕様適合性を検証する出力境界
- 保証駆動レイヤー(Guarantee-Driven / GDD):SSoT とテストを束ね、ゆらぎを収束させる制御ドライバ
この 3 つのレイヤーを保証駆動レイヤーが同時並行で循環させることで、
AI 開発のスピードと人間の精度を両立する。
それが後に登場する**「SSoT(仕様) × Test(検証) × Guarantee(保証駆動)」三位一体モデル**である。
1.5 要約
AI エージェント時代の開発とは、
確率的なばらつきを前提に、安定領域を設計し続ける行為である。
そのためには、SSoT(仕様レイヤー)・テストレイヤー・保証駆動レイヤーの三層構造を固定化せず、
常に保証駆動レイヤーを中心に循環させ続ける必要がある。
これが、新しい開発体系の出発点である。
第 2 章 PoC / MVP フェーズ:仕様を決めるための"実験"としての開発
AI エージェント時代の PoC は、「仕様を決めるための開発」であり、「製品を作るための開発」ではない。
2.1 PoC の再定義:仕様探索としての開発
PoC は仕様探索のための仮説検証の場である。
AI の確率的出力を最大限に利用し、プロトタイプを量産しながら「どの仕様が最も安定するか」を見極める。
- 目的:仕様仮説の検証(構造の安定性を探る)
- 手法:Vibe Coding(AI との対話的プロトタイピング)
- 方針:「PoC のコードは正式版に流用しない」
PoC は仕様の SSoT を確立する前段階であり、
仕様の曖昧さ・不確定性をあえて抱えながら AI を実験的に動かす。
2.2 Vibe Coding とその意義
Vibe Coding とは、AI エージェントとの対話を通じて高速にプロトタイプを生成する手法である。
従来の「設計 → 実装」という順序を逆転させ、「生成 → 観察 → 仕様抽出」というサイクルを回す。
| 従来の開発 | Vibe Coding |
|---|---|
| 仕様を先に決める | 生成しながら仕様発見 |
| 精密な設計文書 | 対話的プロンプト |
| 実装に時間 | 数分で複数案生成 |
| 変更コスト大 | 破棄前提・低コスト |
この手法により、「どの仕様が実装可能か」「どのアーキテクチャが安定するか」を、
実際にコードを動かしながら検証できる。
2.3 PoC で検証すべき 3 つの観点
| 観点 | 目的 | 確認項目 |
|---|---|---|
| 技術的実現性 | AI がその仕様で安定生成できるか | 生成の一貫性・エラー率 |
| アーキテクチャ | システム構造が拡張可能か | 責務分離・依存関係 |
| 仕様の明瞭性 | SSoT として記述可能か | 曖昧さの残存・境界条件の明確性 |
PoC の成果物は「動くコード」ではなく、**「検証された仕様仮説」**である。
2.4 PoC と正式版の明確な分離
PoC は仕様を確定させるためのものであり、正式版で流用しない。
なぜ作り直しが合理的なのか
従来の開発では、PoC コードを「もったいない」という理由で流用することが多かった。
しかし AI 時代では、再実装コストが劇的に低下したため、この判断は逆転する。
コスト比較:
| フェーズ | 人間による開発 | AI 支援開発 |
|---|---|---|
| PoC 実装 | 2 週間 | 2 週間(検証重視) |
| 仕様抽出 | 1 週間 | 1 週間(SSoT + DR 作成) |
| 正式版実装 | 2〜4 週間 | 数時間〜1 日 |
| リファクタの場合 | 1〜2 週間 | (技術負債が残る) |
AI 時代では、「PoC コードをリファクタ」よりも「SSoT から再生成」の方が:
- ✅ 速い(数時間 vs 1〜2 週間)
- ✅ クリーン(技術負債なし)
- ✅ テスト可能(仕様が明確)
PoC で得られた暗黙知の扱い
ただし、PoC で発見したドキュメント化しにくい知見は失われてはならない。
暗黙知の記録先:
- SSoT: 確定した仕様・決定事項
- DR(設計原則 DR): PoC で判明した実装上の制約・性能特性・エッジケース
記録すべき暗黙知の例:
# DR-078: 商品検索のキャッシュ実装 [設計原則 DR]
## 前提(PoC で判明)
- 検索 API のレスポンスが平均 800ms(遅い)
- ユーザーは 5 分以内に同じ検索を繰り返す(68%)
## 判断
- インメモリキャッシュを実装(採用)
- TTL: 5 分
- キャッシュなしでは UX 上の問題
## 実装上の制約
- キャッシュミス時の性能劣化を防ぐため、非同期で更新
- メモリ使用量が 100MB を超えたら LRU で削除
## 廃棄条件
- 検索 API 側でキャッシュ機能が提供された場合
- レスポンスタイムが 200ms 以下に改善された場合
この設計原則 DR があれば、AI は再生成時に:
- キャッシュ実装を含める
- 性能要件を満たす設計にする
- PoC の知見を反映できる
リスクの再定義
PoC コードを流用した場合のリスク:
- ❌ 仕様の不確定性が本番環境に持ち込まれる
- ❌ テストされていないコードパスが残存する
- ❌ AI 生成時の前提条件が不明瞭になる
- ❌ 技術的負債として堆積する
作り直した場合のリスク:
- ⚠️ PoC の暗黙知が失われる可能性
→ 対策: 設計原則 DR に記録 - ⚠️ AI 生成の品質ばらつき
→ 対策: SSoT + DR で制約を明示
AI 時代の原則: 再実装コストが低いなら、クリーンな実装を選ぶべき。
PoC 終了時には、得られた知見を SSoT と設計原則 DR に構造化し、
正式版ではゼロから AI に再生成させる。
これが AI 時代のリスクマネジメントである。
2.5 Mini-PoC による探索的開発
PoC と正式版の間には、もう 1 つの重要な開発形態がある。
それが**Mini-PoC(ミニ PoC)**である。
Mini-PoC の位置づけ
Mini-PoC は、正式版開発中に生じる小さな不確実性を解消するための、短期的な実験である。
課題:
SSoT を厳格に管理すると、小さな変更(UI の色変更・文言調整など)でも
SSoT 更新のコストが発生し、探索的な開発がしづらくなる。
解決:
Mini-PoC は、SSoT 更新なしで短期間(1 日以内)の実験を可能にする。
採用された場合のみ SSoT を更新し、却下された場合は知見のみ DR に記録する。
PoC と Mini-PoC の違い
| 観点 | PoC | Mini-PoC |
|---|---|---|
| 目的 | 仕様の確定 | 小さな不確実性の解消 |
| 期間 | 1〜2 週間 | 1 日以内 |
| スコープ | 機能全体・アーキテクチャ | 単一要素・局所的変更 |
| SSoT 更新 | 終了後に確定 | 採用時のみ |
| コード流用 | しない(再生成) | しない(再生成) |
Mini-PoC の原則
- ✅ 1 営業日以内に完了
- ✅ 単一機能・コンポーネント単位
- ✅ 結果(採用・却下)を簡易 DR に記録
- ❌ 本番環境にデプロイしない
- ❌ 複数の Mini-PoC を同時並行させない
Mini-PoC とは、SSoT の厳格性とアジャイルな柔軟性を両立する仕組みである。
詳細なワークフロー・ルール・記録方法については、
実運用を通じて体系化していく必要がある(今後の課題)。
2.6 要約
PoC フェーズは「仕様探索の実験場」であり、
AI の確率的生成を活用して高速に仮説検証を行う。
その成果は仕様の確定であり、コードの再利用ではない。
正式版開発中の小さな不確実性には、Mini-PoC で対応し、
SSoT 更新のコストと探索的開発の柔軟性を両立する。
この姿勢こそが、AI エージェント開発における最も重要なリスクマネジメントとなる。
第 3 章 仕様確定と SSoT:唯一の真実を構造化する
PoC の結果から得られた知見をもとに、
「何を正とするか」を定義したものが SSoT(Single Source of Truth)である。
3.1 SSoT の意義
SSoT は単なる仕様書ではなく、AI が参照し生成を制御するための唯一の事実源である。
ただし、それは固定的な真実ではなく「その時点における唯一の事実源」であり、
開発・テスト・運用すべての出発点となる。
3.2 SSoT の条件
- 曖昧性を排除(if/then 構造で明示)
- 人間と AI の双方が解釈可能(JSON, YAML, Schema など)
- 不変領域と可変領域を区別
- 廃棄条件(寿命)を明記
SSoT とは、正しさを記録するものではなく、正しさを更新するための基準点である。
SSoT を更新するたびに、AI が参照する文脈が再定義され、
開発全体が一斉に「再生成」される。
そのため、SSoT は常に生きた構造として管理される必要がある。
3.3 SSoT の具体的構造
注意: 以下で示す SSoT の構造(YAML 形式での管理、階層設計、具体的な記述例)は、
保証駆動開発の概念を説明するための一例です。
実際のプロジェクトでは、技術スタック・組織構造・開発フェーズに応じて、
最適な管理方法(JSON Schema、Protocol Buffers、独自 DSL など)を検討する必要があります。
SSoT は単一ファイルではなく、階層化された構造体として設計する。
3.3.1 SSoT の階層構造
ssot/
├── core/ # システム全体の不変原則
│ ├── architecture.yaml # アーキテクチャ原則
│ ├── security.yaml # セキュリティポリシー
│ └── data-model.yaml # コアデータモデル
│
├── design/ # デザイン・UX 原則
│ ├── design-principles.yaml
│ ├── component-rules.yaml
│ └── accessibility.yaml
│
├── features/ # 機能仕様
│ ├── authentication.yaml
│ ├── payment.yaml
│ └── notification.yaml
│
└── operations/ # 運用ルール
├── deployment.yaml
├── monitoring.yaml
└── incident-response.yaml
3.3.2 SSoT の記述例
core/architecture.yaml:
version: "2.1.0"
last_updated: "2024-11-10"
status: "active"
principles:
separation_of_concerns:
rule: "UI・ビジネスロジック・データ層を明確に分離"
rationale_ref: "DR-001"
violations: "error"
api_first:
rule: "すべての機能は API として提供"
rationale_ref: "DR-002"
exceptions:
- "管理画面の内部機能"
- reason_ref: "DR-015"
technology_stack:
frontend:
framework: "Next.js 14"
state_management: "Zustand"
locked_until: "2025-12-31" # 変更禁止期限
backend:
runtime: "Node.js 20 LTS"
framework: "Fastify"
locked_until: null # 必要に応じて変更可
design/design-principles.yaml:
version: "1.5.0"
last_updated: "2024-11-05"
last_verified: "2024-11-05" # 定期検証日
component_image_placement:
rule: "right"
scope: ["Card", "Profile", "ProductDetail"]
rationale_ref: "DR-045"
verification_schedule: "quarterly" # 3ヶ月ごと検証
exceptions:
- component: "HeroSection"
placement: "left"
rationale_ref: "DR-078"
button_hierarchy:
primary_action:
color: "brand-blue"
position: "right-most"
rationale_ref: "DR-052"
destructive_action:
color: "semantic-red"
confirmation_required: true
rationale_ref: "DR-061"
features/authentication.yaml:
version: "3.2.0"
last_updated: "2024-10-20"
session_management:
token_lifetime: "24h"
rationale_ref: "DR-023"
renewal_strategy: "sliding_window"
invalidation_triggers:
- "password_change"
- "security_alert"
- "manual_logout"
password_policy:
min_length: 12
require_complexity: true
rationale_ref: "DR-088"
exceptions:
- user_type: "demo_account"
min_length: 6
rationale_ref: "DR-091"
3.3.3 SSoT と DR の連携
各 SSoT エントリは rationale_ref で DR を参照する。
SSoT での記述:
token_lifetime: "24h"
rationale_ref: "DR-023"
対応する DR-023:
# DR-023: 認証トークン有効期限の設定
## 判断内容
有効期限を 24 時間とする
## 前提条件
- ユーザーは毎日ログインする想定
- セキュリティリスクを最小化したい
- UX を損なわない範囲での厳格化
## 検討した選択肢
1. 24 時間(採用) ← SSoT に反映済み
2. 7 日間(却下: セキュリティリスク)
3. 無期限(却下: コンプライアンス違反)
## 廃棄条件
- ユーザーからログアウト苦情が月 10 件以上
- SSO 導入により認証方式が変わる場合
- セキュリティ監査で要件変更が必要になった場合
3.4 SSoT の運用原則
原則 1: 階層による寿命管理
| 階層 | 変更頻度 | 例 |
|---|---|---|
| core/ | 低 | アーキテクチャ、セキュリティ |
| design/ | 中 | デザイン原則 |
| features/ | 高 | 機能仕様 |
| operations/ | 中 | 運用ルール |
原則 2: バージョン管理
SSoT の各ファイルにバージョンを付与し、変更履歴を追跡可能にする。
version: "3.2.0"
changelog:
- version: "3.2.0"
date: "2024-10-20"
changes: "トークン更新戦略をsliding_windowに変更"
rationale_ref: "DR-112"
原則 3: 定期検証のトリガー
last_verified: "2024-11-05"
verification_schedule: "quarterly"
next_verification: "2025-02-05" # 自動計算
定期検証時には:
- SSoT の内容が現在も妥当か確認
-
rationale_refで参照している DR の前提条件をチェック - 必要なら SSoT 更新 + 新 DR 作成
3.4 実装形式の選択:YAML vs TypeScript
SSoT をどの形式で管理するかは、プロジェクトの性質によって選択すべき重要な判断である。
3.4.1 形式の比較
| 観点 | YAML | TypeScript |
|---|---|---|
| 型安全性 | ❌ 実行時エラー | ✅ コンパイル時エラー |
| IDE サポート | △ 限定的 | ✅ 補完・定義ジャンプ・リファクタ |
| 参照の追跡 | ❌ 文字列参照のみ | ✅ Cmd+クリック で辿れる |
| フロントエンド統合 | △ パース処理が必要 | ✅ そのまま import 可能 |
| バックエンド統合 | ✅ 言語非依存 | △ TypeScript 環境が必要 |
| AI の生成精度 | ○ 構造的で生成しやすい | ✅ 型情報で生成精度向上 |
| 人間の認知負荷 | △ スキーマが不明瞭 | ✅ 型定義で構造が明確 |
| 非エンジニアの編集 | ✅ テキストエディタで可能 | △ TypeScript 知識が必要 |
| 多言語プロジェクト対応 | ✅ 容易 | △ 各言語で型変換が必要 |
3.4.2 TypeScript による SSoT 実装例
推奨: フロントエンド中心のプロジェクトでは TypeScript を採用
// ssot/features/document-generation/form-complete.ts
import type { DecisionRecordRef } from "@/ssot/types";
/**
* 書類生成フォームの完全な定義
*
* 構造:
* - domain: APIとのマッピング、データ型
* - ui: フォーム構造、コンポーネント
* - behavior: 動的な表示/無効化ルール
* - validation: バリデーションルール
*/
export const DOCUMENT_GENERATION_FORM = {
version: "1.0.0",
lastUpdated: "2024-11-13",
// ========== ドメイン層 ==========
domain: {
apiEndpoint: "/api/v1/documents",
fields: {
applicantName: {
type: "string" as const,
apiField: "applicantName",
required: true,
maxLength: 100,
},
applicantType: {
type: "enum" as const,
apiField: "applicantType",
values: ["individual", "corporate"] as const,
required: true,
},
corporateName: {
type: "string" as const,
apiField: "corporateName",
required: false,
maxLength: 200,
},
taxId: {
type: "string" as const,
apiField: "taxId",
required: false,
pattern: /^[0-9]{13}$/,
transform: (value: string) => value.replace(/-/g, ""),
},
},
},
// ========== UI 層 ==========
ui: {
sections: [
{
id: "applicant_info",
title: "申請者情報",
fields: [
{
fieldId: "applicantType" as const,
component: "radio_button" as const,
label: "申請者区分",
options: [
{ value: "individual" as const, label: "個人" },
{ value: "corporate" as const, label: "法人" },
],
default: "individual" as const,
},
{
fieldId: "applicantName" as const,
component: "text_field" as const,
label: "申請者名",
placeholder: "山田 太郎",
},
{
fieldId: "corporateName" as const,
component: "text_field" as const,
label: "法人名",
placeholder: "株式会社〇〇",
// 同一ファイル内で参照(型安全)
behaviorRef: "corporateName_visibility" as const,
},
{
fieldId: "taxId" as const,
component: "formatted_text_field" as const,
label: "法人番号",
formatPattern: "####-####-#####",
behaviorRef: "taxId_visibility" as const,
},
],
},
],
},
// ========== 動作ルール層 ==========
behavior: {
corporateName_visibility: {
condition: {
type: "field_equals" as const,
field: "applicantType" as const,
value: "corporate" as const,
},
actions: {
show: ["corporateName" as const],
enable: ["corporateName" as const],
},
rationale: "DR-156" as DecisionRecordRef,
},
taxId_visibility: {
condition: {
type: "all" as const,
conditions: [
{
type: "field_equals" as const,
field: "applicantType" as const,
value: "corporate" as const,
},
{
type: "field_not_empty" as const,
field: "corporateName" as const,
},
],
},
actions: {
show: ["taxId" as const],
enable: ["taxId" as const],
},
},
},
// ========== バリデーション層 ==========
validation: {
applicantName: [
{
type: "required" as const,
message: "申請者名は必須です",
},
{
type: "max_length" as const,
value: 100,
message: "100文字以内で入力してください",
},
],
corporateName: [
{
type: "conditional_required" as const,
condition: {
field: "applicantType" as const,
operator: "equals" as const,
value: "corporate" as const,
},
message: "法人の場合、法人名は必須です",
},
],
taxId: [
{
type: "conditional_required" as const,
condition: {
field: "applicantType" as const,
operator: "equals" as const,
value: "corporate" as const,
},
message: "法人の場合、法人番号は必須です",
},
{
type: "pattern" as const,
pattern: /^[0-9]{4}-[0-9]{4}-[0-9]{5}$/,
message: "法人番号の形式が正しくありません",
},
],
},
} as const;
// 型定義(自動推論)
export type FormConfig = typeof DOCUMENT_GENERATION_FORM;
export type FieldId = keyof FormConfig["domain"]["fields"];
export type BehaviorRuleId = keyof FormConfig["behavior"];
3.4.3 TypeScript 形式の利点
1. 参照の追跡が容易
behaviorRef: "corporateName_visibility" // Cmd+クリックで定義にジャンプ
// ↓
behavior: {
corporateName_visibility: { ... } // ← ここに移動
}
VSCode 上で Cmd+クリック により、ファイル間・ファイル内の参照を自由に辿れる。
これは YAML では実現できない、TypeScript の最大の利点である。
2. 型エラーによる typo 防止
behaviorRef: "corporate_name_visibility"; // ❌ 型エラー
behaviorRef: "corporateName_visibility"; // ✅ OK
コンパイル時に参照の整合性が保証される。
3. 単一ファイルで完結
複雑なフォーム定義でも、関心事ごとにセクション分離することで、
単一ファイル内で完結し、認知負荷を低減できる。
// 機能単位で1ファイル
ssot / features / document - generation / form - complete.ts;
// セクションコメントで視認性確保
// ========== ドメイン層 ==========
// ========== UI 層 ==========
// ========== 動作ルール層 ==========
// ========== バリデーション層 ==========
4. AI による生成精度向上
// AI へのプロンプト例
"DOCUMENT_GENERATION_FORM を読み込んで、
React Hook Form + Zod で実装してください。
ui.sections の構造に従ってコンポーネントを生成し、
behavior ルールで useEffect を実装してください。"
AI は型情報を活用して、より正確なコード生成が可能になる。
3.4.4 採用判断の指針
| プロジェクト特性 | 推奨形式 | 理由 |
|---|---|---|
| フロントエンド中心 | TypeScript | 型安全性・IDE サポート・統合容易性 |
| マイクロサービス(多言語) | YAML | 言語非依存・パース容易性 |
| 非エンジニアも編集 | YAML | テキストエディタで編集可能 |
| 複雑な条件ロジック | TypeScript | 関数による動的定義・型による検証 |
| CI/CD での自動検証が中心 | YAML | 構造的検証ツールが豊富 |
| VSCode でのワークフロー | TypeScript | 定義ジャンプ・リファクタリング対応 |
推奨: モダンなフロントエンド開発では、TypeScript 形式を採用し、
型安全性と開発体験を優先すべきである。
3.4.5 ハイブリッド構成の是非
「階層によって形式を使い分ける」という選択肢も考えられる:
❌ ハイブリッド構成(推奨しない)
ssot/
├── core/
│ ├── architecture.yaml # ← YAML(仕様のみ)
│ └── security.yaml # ← YAML(仕様のみ)
└── features/
└── form-complete.ts # ← TypeScript(実装で使用)
しかし、この構成は推奨しない。理由は以下の通り:
問題 1: 「仕様のみ」という境界は曖昧
// core/security の値は実装で直接参照されるケースが多い
export const SECURITY_POLICY = {
password: {
min_length: 12, // ← 「仕様」だが実装でも使用
},
} as const;
// 実装での参照例
function validatePassword(password: string) {
if (password.length < SECURITY_POLICY.password.min_length) {
throw new Error("パスワードが短すぎます");
}
}
core/ も「直接インポートが必要」になるケースは多く、
「仕様のみ」と「実装で使う」の境界は実際には非常に曖昧。
問題 2: 参照の一貫性が失われる
// ❌ ハイブリッド構成の場合
import { FORM_CONFIG } from "@/ssot/features/form-complete"; // TS
import archYaml from "@/ssot/core/architecture.yaml"; // YAML
// YAMLはパース処理が必要
const architecture = parseYaml(archYaml);
// 型安全性が失われる
const principle = architecture.layering.strict_separation; // any型
// Cmd+クリックで辿れない
// ✅ TypeScript統一の場合
import { FORM_CONFIG } from "@/ssot/features/form-complete";
import { ARCHITECTURE_PRINCIPLES } from "@/ssot/core/architecture";
// 型安全に参照
const principle = ARCHITECTURE_PRINCIPLES.layering.strict_separation; // boolean型
// Cmd+クリックで辿れる ✅
問題 3: 判断コストの継続的発生
開発者の思考:
「新しいSSoTを書くとき、YAMLで書くべき? TypeScriptで書くべき?」
「これは仕様のみ? 実装で使う? → じゃあYAML? TS?」
「後で実装から参照することになったら、YAMLからTSに変換?」
この判断コストは、開発が進むたびに発生し続ける。
問題 4: 非エンジニア編集は幻想
実際には非エンジニアが直接 SSoT を編集するケースは稀:
# YAML(非エンジニアが編集可能という想定)
design_principles:
component_image_placement: "right"
rationale_ref: "DR-045"
// TypeScript(実際にはこちらも十分編集可能)
export const DESIGN_PRINCIPLES = {
// 画像の配置は右側
component_image_placement: "right",
rationale_ref: "DR-045",
} as const;
視覚的な差はほとんどなく、むしろ VSCode の補完が効く TypeScript の方が編集しやすい。
結論: 全て TypeScript 統一を推奨
✅ 統一構成(推奨)
ssot/
├── core/
│ ├── architecture.ts # ← 全て TypeScript
│ ├── security.ts
│ └── data-model.ts
│
├── design/
│ ├── design-principles.ts
│ └── component-rules.ts
│
├── features/
│ └── document-generation/
│ └── form-complete.ts
│
└── operations/
├── deployment.ts
└── monitoring.ts
統一による利点:
| 側面 | 利点 |
|---|---|
| 認知負荷 | 「全部 TypeScript」というシンプルなルール |
| 参照の追跡 | 全て Cmd+クリック で辿れる |
| 型安全性 | システム全体で型エラー検出 |
| 実装コスト | パース処理不要、直接 import 可能 |
| 判断コスト | 「どちらで書くか」で迷わない |
| AI 生成精度 | 型情報により生成精度向上 |
| リファクタリング | VSCode の一括変更が全体に適用可能 |
原則: 無理して YAML を導入せず、TypeScript で統一することで、
開発体験・型安全性・認知負荷のすべてを最適化できる。
例外: 真に YAML が必要なケース
唯一、以下のような多言語プロジェクトでは YAML を検討してもよい:
ssot/
├── shared/ # 全言語で共有
│ ├── api-schema.yaml # OpenAPI仕様
│ └── domain-model.yaml # ドメインモデル
│
└── frontend/ # TypeScript専用
└── features/
└── form-complete.ts
ただしこの場合も、TypeScript 側では型定義として再エクスポートすることで、
TypeScript 部分の型安全性を確保すべきである:
// ビルド時にYAMLから型生成
import type { ApiSchema } from "@/generated/api-schema";
// TypeScript側は型安全に使用
export const useApiSchema = (): ApiSchema => { ... };
3.5 SSoT・DR・実装の三層構造
┌─────────────────────────────────────────┐
│ SSoT: 何が正しいか(規範) │
│ - token_lifetime: "24h" │
│ - rationale_ref: "DR-023" │
└──────────────┬──────────────────────────┘
│ 参照
↓
┌─────────────────────────────────────────┐
│ DR: なぜそうなったか(根拠) │
│ - ユーザー調査結果 │
│ - セキュリティ要件 │
│ - 廃棄条件 │
└──────────────┬──────────────────────────┘
│ 根拠
↓
┌─────────────────────────────────────────┐
│ 実装: SSoT を実現するコード │
│ - AI が SSoT から自動生成 │
│ - テストで SSoT 適合性を検証 │
└─────────────────────────────────────────┘
この三層構造により:
- AI: SSoT だけ見て実装を生成
- 人間: DR を見て判断の妥当性を検証
- 保証: テストで SSoT との乖離を検知
SSoT は「何」を定義し、DR は「なぜ」を説明し、実装は「どのように」を表現する。
3.6 出力制約と変更スコープの関係
SSoT で出力を厳密に定義することは、確率的出力を制御するための中核戦略である。
しかし、出力だけを制約しても不十分である。
なぜなら、AI が「どのように」その出力を実現するかは制御できないからだ。
ここで重要になるのが、変更スコープの制御である。
なぜ変更スコープの制御が必要か
シナリオ: トークン期限の変更
出力制約のみの場合(不十分):
# SSoT: 出力のみ定義
generateToken:
output:
type: "{ token: string, expires: Date }"
// AIの生成結果(出力は正しいが...)
function generateToken(userId: string): Token {
// AIが「より良い実装」として全体を再設計
const config = await fetchAuthConfig(); // 外部依存追加
const policy = new TokenPolicy(config); // 新しいクラス
const calculator = new ExpiryCalculator(); // 新しいクラス
const duration = policy.getLifetime(); // 複雑化
const expires = calculator.calculate(duration);
// キャッシュ層を追加
const cache = TokenCache.getInstance();
cache.set(userId, expires);
// ログ強化
logger.info(`Token generated for ${userId}`);
return { token: sign(userId), expires }; // 出力は正しい ✅
}
問題点:
- ✅ 出力形式は守られている
- ❌ 実装が 100 行に膨れた
- ❌ 外部依存が増えた
- ❌ 状態管理(キャッシュ)が追加された
- ❌ 変更の意図(期限変更)が埋もれた
→ 出力は正しいが、システム全体が不安定化
出力制約 + 変更スコープ制御の場合(適切):
# SSoT: 出力 + 変更スコープを定義
generateToken:
output:
type: "{ token: string, expires: Date }"
contract: "返り値の構造を変更しない"
scope:
modify:
- "トークン期限の計算ロジックのみ"
must_not_modify:
- "関数の依存関係"
- "エラーハンドリング"
- "ログ出力"
constraints:
- "新しいクラス・関数の追加禁止"
- "外部設定への依存禁止"
// AIの生成結果(スコープ制約により局所的変更)
function generateToken(userId: string): Token {
const expires = new Date(Date.now() + 48 * 60 * 60 * 1000); // ← ここだけ
return { token: sign(userId), expires };
}
効果:
- ✅ 出力形式が守られている
- ✅ 変更が 1 行に限定されている
- ✅ 既存の構造が保たれている
- ✅ 変更の意図が明確
→ 出力も構造も安定
因果関係: スコープ制御が出力制御を支える
【逆説的だが重要な関係】
出力を安定させるには
↓
生成プロセス全体を安定させる必要がある
↓
そのためには、変更範囲(スコープ)を限定する
↓
スコープが限定されると、AIの選択肢が減る
↓
選択肢が減ると、出力のゆらぎも減る
つまり:
出力制御のためには、変更スコープの制御が必要である。
スコープ制御の 3 つのレベル
| レベル | 制御対象 | SSoT での表現 |
|---|---|---|
| ファイル | 変更対象ファイルの限定 | scope.modify: ["auth.ts"] |
| 関数 | 変更対象関数の限定 | scope.modify: ["generateToken"] |
| 論理 | 変更対象ロジックの限定 | scope.modify: ["期限計算のみ"] |
最も効果的なのは、論理レベルでの制約である。
# 論理レベルのスコープ制御
generateToken:
change_intent: "トークン有効期限を24h→48hに延長"
scope:
modify_only: "期限計算式(Date.now() + X)のXの値"
preserve:
- "関数シグネチャ"
- "返り値の構造"
- "sign()関数の呼び出し方法"
- "エラーハンドリング"
実装例: SSoT でのスコープ定義
// ssot/features/authentication.ts
export const AUTHENTICATION_SSOT = {
generateToken: {
// 出力制約
output: {
type: "{ token: string, expires: Date }" as const,
contract: "返り値の構造を変更しない",
},
// スコープ制約
scope: {
changeIntent: "トークン有効期限を48時間に変更",
modifyOnly: ["トークン期限の計算式"],
preserve: [
"関数シグネチャ: (userId: string) => Token",
"sign()関数の使用方法",
"Date型での期限表現",
],
mustNotAdd: [
"新しい関数・クラス",
"外部設定ファイルへの依存",
"キャッシュ・状態管理",
],
},
// 検証
validation: {
outputContract: "generateToken.output.test.ts",
scopeContract: "変更差分がscope.modifyOnlyの範囲内か検証",
},
},
} as const;
スコープ制御の限界
ただし、スコープ制御にも限界がある:
// スコープ内の変更でも、品質は劣化しうる
// パターン1: 非効率な実装
const expires = new Date(
Date.now() + 48 * 60 * 60 * 1000 * Math.pow(10, 0) // 無駄な計算
);
// パターン2: 可読性の低下
const expires = new Date(Date.now() + 172800000); // マジックナンバー
// パターン3: 微妙なバグ
const expires = new Date(Date.now() + 48 * 60 * 60 * 1000); // タイムゾーン未考慮
これらは「スコープ内」かつ「出力は正しい」が、品質に問題がある。
→ だからこそ、後述の**保証駆動(GDD)**による継続的検証が必要となる。
まとめ
出力制約と変更スコープの関係:
出力制約だけ
→ AIは自由に実装を変更
→ 出力は正しいが、システムが不安定化
出力制約 + スコープ制約
→ AIの選択肢が限定される
→ 出力も構造も安定
出力制約 + スコープ制約 + 保証駆動
→ 品質劣化も検知
→ 時間的安定を実現
SSoT とは、「何を作るか」だけでなく、
「どのように作るか(変更範囲)」も定義する仕様である。
これが、AI 時代の Single Source of Truth の本質的な役割である。
3.7 SSoT における曖昧性の排除
AI 時代の SSoT は、人間の「読みやすさ」よりも「曖昧性の排除」を優先する。
原則: 冗長性を恐れない
従来のドキュメント観:
対象: 人間の読者
価値: 簡潔さ・わかりやすさ
手法: 要約・抽象化
AI 時代の SSoT 観:
対象: AIの解釈(人間は要約で読む)
価値: 網羅性・検証可能性
手法: 徹底的な詳細化
問題: 曖昧な記述の例
# ❌ 曖昧な記述
password_policy:
min_length: 12
require_complexity: true
この記述では、AI は以下を推測で実装する:
- 「complexity」とは何か?
- 大文字を含む?小文字を含む?数字を含む?記号を含む?
- それらの組み合わせ数は?
- どの記号を許可する?
→ AI ごとに異なる実装が生成される
解決策 1: 徹底的な詳細化
# ✅ 曖昧性を排除した記述
password_policy:
min_length: 12
complexity:
definition: "以下の文字種のうち、最低3種類を含むこと"
character_types:
uppercase:
required: false # 3種類のうちの1つ
definition: "A-Zの大文字を少なくとも1文字含む"
regex: "[A-Z]"
examples: ["A", "B", "Z"]
lowercase:
required: false # 3種類のうちの1つ
definition: "a-zの小文字を少なくとも1文字含む"
regex: "[a-z]"
examples: ["a", "b", "z"]
digit:
required: false # 3種類のうちの1つ
definition: "0-9の数字を少なくとも1文字含む"
regex: "[0-9]"
examples: ["0", "5", "9"]
special_char:
required: false # 3種類のうちの1つ
definition: "以下の記号を少なくとも1文字含む"
allowed_chars: "!@#$%^&*()_+-=[]{}|;:,.<>?"
regex: "[!@#$%^&*()_+\\-=\\[\\]{}|;:,.<>?]"
examples: ["!", "@", "#"]
minimum_types_required: 3
validation_algorithm: |
1. 各character_typeの正規表現でパスワードをチェック
2. マッチしたcharacter_typeの数をカウント
3. カウント >= minimum_types_required ならば合格
人間の反応:
- 「長い...」「冗長すぎる...」
しかし:
- ✅ AI は曖昧さなく実装できる
- ✅ テストケースも自動生成可能
- ✅ 人間は必要な時だけ AI に要約させれば良い
解決策 2: 具体例による補完
詳細化に加えて、検証可能な具体例を含める。
password_policy:
complexity:
# ... 上記の詳細定義 ...
# 具体例で補完
valid_examples:
- value: "Passw0rd!"
reason: "大文字・小文字・数字・記号の4種類"
- value: "MyP@ss123"
reason: "大文字・小文字・記号・数字の4種類"
- value: "Str0ng#Key"
reason: "大文字・小文字・数字・記号の4種類"
- value: "abc123!@#"
reason: "小文字・数字・記号の3種類"
invalid_examples:
- value: "password"
reason: "小文字のみ(1種類)"
- value: "PASSWORD"
reason: "大文字のみ(1種類)"
- value: "Pass123"
reason: "大文字・小文字・数字の3種類だが、記号がないため2種類扱い(誤りの例)"
note: "実際には3種類なので合格"
- value: "password123"
reason: "小文字・数字の2種類"
# AIへの指示
implementation_constraint: |
valid_examplesはすべて検証をパスし、
invalid_examplesはすべて拒否する実装にすること。
テストケースは、これらの例を含めること。
効果:
- ✅ AI は具体例を参照して実装の正しさを検証できる
- ✅ 人間も具体例から理解しやすい
- ✅ テストケースが自動的に定義される
解決策 3: Contract Testing との統合
SSoT から自動的に Contract Test を生成することで、曖昧性を排除する。
password_policy:
# ... 詳細定義 ...
contract_tests:
- name: "valid_examplesは全て通過"
test_type: "positive"
inputs: ["Passw0rd!", "MyP@ss123", "Str0ng#Key", "abc123!@#"]
expected: true
- name: "invalid_examplesは全て拒否"
test_type: "negative"
inputs: ["password", "PASSWORD", "password123"]
expected: false
- name: "境界値テスト: 最小文字数"
test_type: "boundary"
inputs:
- value: "Pass123!@#$" # 12文字(最小)
expected: true
- value: "Pass123!@#" # 11文字
expected: false
→ SSoT 更新時に、Contract Test が自動生成・実行される
人間はどう読むのか?
「こんなに詳細な SSoT を、人間がいちいち読むのか?」という疑問への答え:
読まない。AI に要約させる。
エンジニア「パスワードポリシーの要件を教えて」
↓
AI(SSoTを参照)「12文字以上で、大文字・小文字・数字・記号のうち3種類以上です」
↓
エンジニア「記号は何が使える?」
↓
AI(SSoTを参照)「!@#$%^&*()_+-=[]{}|;:,.<>? が使えます」
重要な点:
- 一次情報(SSoT)は完全に保持されている
- 要約は AI が動的に生成する
- 曖昧な部分があれば、再質問で詳細を確認できる
AI 時代の SSoT とは、AI が読むための一次情報源であり、
人間は必要に応じて AI に要約・抽出させて読む。
まとめ
SSoT における曖昧性排除の原則:
- 冗長性を恐れない: 人間の読みやすさより AI の解釈精度を優先
- 具体例を含める: 検証可能な正例・負例を明示
- Contract Test と統合: SSoT から自動的にテストを生成
- 人間は要約で読む: 一次情報は AI が管理、人間は AI に要約させる
これにより、「require_complexity: true」のような曖昧な記述から脱却し、
AI が確実に正しい実装を生成できる SSoT を構築できる。
第 4 章 三位一体モデル:SSoT(仕様)× Test(検証)× Guarantee(保証駆動)
4.1 背景:線形モデルの崩壊
従来の開発は「仕様を決める → 実装 → テストする」という線形モデルで成り立っていた。
しかし AI エージェント時代では、AI がコードを再生成し続けるため、
仕様変更が即時に全体へ波及し、工程の境界が崩壊する。
結果として、SSoT(仕様レイヤー)・テストレイヤー・保証駆動レイヤーは同時並行で動作する動的平衡モデルに再構築される。
ここで「Driven(駆動)」の役割を持つのは保証レイヤーのみであり、SSoT とテストはそのための入力境界・出力境界として機能する。
4.2 三位一体モデルの定義
| 軸 | 定義 | 目的 |
|---|---|---|
| 仕様レイヤー(SSoT / S) | SSoT を基点に AI の生成を制御する「入力境界」 | 正しさの定義 |
| テストレイヤー(Test / T) | 自動テストによって仕様適合性を検証する「出力境界」 | 正しさの確認 |
| 保証駆動レイヤー(GDD / G) | S / T の両境界を用いてゆらぎを収束させる制御ドライバ(Guarantee-Driven) | 正しさの持続 |
この 3 つを独立ではなく循環的に運用することで、
AI 開発のスピードと人間の精度を両立する。
4.3 実運用構造
PoC で SSoT(仕様レイヤー)を確立し、その SSoT を基点に AI がコードを生成する。
その結果をテストレイヤーで検証し、運用段階では保証駆動レイヤーによって継続的安定を保つ。
PoC → SSoT(仕様レイヤー)→ Test(検証レイヤー)→ Guarantee(保証駆動レイヤー)→(仕様更新があれば再循環)
各フェーズが直列ではなく、生成・検証・保証が同時進行する。
この構造により「AI が自ら修正を提案しても、常に正しさが保たれる」開発基盤が実現する。
4.4 テストレイヤーと保証駆動レイヤーの違い
| 観点 | テストレイヤー(従来の TDD が担ってきた領域) | 保証駆動レイヤー(GDD) |
|---|---|---|
| 主目的 | 実装が仕様通りか検証 | 時間経過・変更後も壊れない保証 |
| タイミング | 開発中・更新時 | 運用・継続監視 |
| 成果物 | テストケース | 回帰パイプライン・監視設定 |
| トリガー | コード修正 | デプロイ・障害発生 |
| ツール例 | Jest / Playwright / Pact | CI/CD / Datadog / Feature Flag |
| 比喩 | 出発前の安全点検 | 飛行中の安全制御 |
※従来の Test-Driven Development(TDD)は、このテストレイヤーの設計に主眼を置いた手法であり、本書ではそれを保証駆動レイヤーと分離して位置づけている。
TDD は瞬間の正しさを、GDD は時間的な正しさを保証する。
両者が連携して初めて「動的安定」が実現する。
4.5 保証駆動の実践構造
| 項目 | 内容 | 実践例 |
|---|---|---|
| 自動回帰テスト | 差分による破壊を検知 | GitHub Actions + Jest + Pact |
| モニタリング | 異常の早期検知 | Datadog / Grafana / Sentry |
| フェイルセーフ | 障害時の安全制御 | Cloud Run Rollback / Feature Flag |
| Mini-PoC | 局所的再実験 | Sandboxed branch + 自動比較 |
| SLA/SLO 保証 | 定量的品質維持 | 稼働率>99.5%, P95<400ms |
GDD は「運用中も常に検証が続く状態」を作り出す。
テストレイヤーが"検証の瞬間"を担い、保証駆動レイヤーが"時間の連続"を守る。
4.6 検証結果と有効性
10 ケースの検証を実施した結果、ほぼすべての領域で三位一体モデルが有効であることを確認。
| 検証項目 | 結果 | 備考 |
|---|---|---|
| 認証フロー変更 | 成功 | Feature flag 有効 |
| 丸め処理変更 | 成功 | Golden case 検証 |
| タイムゾーン変更 | 条件付き成功 | Fixed clock 適用 |
| 検索ロジック拡張 | 条件付き成功 | 真理表 + 契約テスト |
| 冪等性検証 | 成功 | Outbox + Retry |
| 権限モデル再設計 | 部分的成功 | OPA による補完 |
| UI 文言変更 | 成功 | Role-based test |
| API 互換性 | 成功 | Schema registry |
| CSV 入力ゆらぎ | 成功 | Fuzz test |
| デザインシステム更新 | 条件付き成功 | Visual regression |
→ すべてのフェーズで保証駆動が「誤差訂正層」として機能。
特に権限モデルは Policy-as-code による形式保証が有効。
4.7 失敗パターンと対策
| 失敗パターン | 原因 | 対策 |
|---|---|---|
| SSOT 肥大化 | 不変領域の未定義 | 境界属性を明示 |
| テスト乖離 | UI 依存過多 | 契約テストに置換 |
| 仕様変更の意図喪失 | Decision Record 欠如 | 判断理由と廃棄条件を明記 |
| 運用時再現不能 | モニタリング不足 | フェイルセーフ + 回帰保証 |
4.8 結論
- 「仕様 → テスト → 運用」という線形フェーズは崩壊した。
- 現実は「仕様・テスト・保証」が常時循環する動的モデルである。
- 三位一体モデルにより、SSoT・TDD・GDD の三層が補完しあう。
AI エージェント時代の開発とは、
正しさを一度作ることではなく、
正しさを継続的に設計し続けることそのものである。
4.9 既存手法との関係:GDD は再パッケージか?
三位一体モデルは、既存の開発手法と類似点が多い。
これは既存手法の再パッケージなのか、それとも新しい体系なのか?
GDD と既存手法の対応関係
一見すると、GDD は以下の既存手法の組み合わせに見える:
GDD = BDD + TDD + SRE + ADR + Infrastructure as Code
| GDD の要素 | 既存手法 |
|---|---|
| 仕様レイヤー(SSoT) | BDD, Infrastructure as Code |
| テストレイヤー(Test) | TDD, Contract Testing |
| 保証駆動レイヤー(Guarantee) | SRE, CI/CD, モニタリング |
| Decision Record | ADR (Architecture Decision Records) |
| PoC → 正式版の流れ | プロトタイピング手法 |
この指摘は正しい。GDD は既存手法の組み合わせであることは否定しない。
しかし、GDD の独自性は「AI エージェント開発への最適化」にある
従来の開発手法は「人間がコードを書く」前提で設計されていた。
GDD は「AI がコードを生成する」前提で、既存手法を再解釈・再構成している。
1. SSoT の役割の変化
従来(Infrastructure as Code, Design System):
- 人間が読んで理解するための「標準化された仕様書」
- ある程度の曖昧性は人間が文脈から補完できる
- 可読性が最優先
GDD の SSoT:
- AI が解釈してコードを生成する「入力仕様」
- 曖昧性を徹底的に排除(if/then で明示、正規表現で定義、真理表で網羅)
- 人間の可読性より、AI 解釈の一意性を優先
- AI のコンテキスト長を考慮した構造化
→ 目的が「人間のための標準化」から「AI のための制約仕様」へ変化
2. Decision Record の目的の変化
従来(ADR - Architecture Decision Records):
- 人間のための「アーキテクチャ判断の記録」
- 「なぜこの技術を選んだか」を後から振り返るため
- 記録が増えても人間が文脈を理解できる
GDD の DR:
- AI の思考汚染を防ぐための「判断の分離機構」
- 「現在の原則(SSoT)」と「過去の経緯(DR)」を明確に分離
- 廃棄条件を明示し、陳腐化した判断を AI に参照させない
- AI のコンテキスト長の限界を考慮した寿命管理
→ 目的が「人間のための記録」から「AI の参照制御」へ変化
3. PoC の位置づけの変化
従来(プロトタイピング):
- 技術検証のための「捨てコード」
- 可能なら本番に流用してコスト削減
- コードの再利用が前提
GDD の PoC:
- 仕様を確定するための「実験装置」
- コードは必ず廃棄(AI の再生成コストが低いため)
- 得られた知見を設計原則 DR として構造化
- AI に再生成させることで、PoC の技術的負債を持ち込まない
→ 目的が「コードの検証」から「仕様の確定」へ変化
4. テストレイヤーの焦点の変化
従来(TDD: Test-Driven Development):
- 人間が書いたコードの品質を保証
- リファクタリングの安全網
- バグの早期発見
GDD におけるテストレイヤー:
- AI が生成したコードの「確率的ゆらぎ」を検出する検証レイヤー
- 意図しないスコープ拡大を防ぐための境界テストを提供
- Contract Testing でのSSoT との自動整合性チェックを担う
- AI の生成結果が「仕様の範囲内」にあることを保証するための出力境界となる
→ 目的が「人間のミス検出」から「AI の確率的制御」へ変化
5. 保証駆動の対象の変化
従来(SRE / CI/CD):
- デプロイ後の「システムの信頼性」を保証
- 障害検知・自動復旧
- インフラの安定性
GDD の保証駆動:
- **生成プロセス全体の「確率的制御」**を保証
- SSoT → AI 生成 → テスト → 本番 の各段階での整合性
- 「意図した通りに生成されたか」の検証
- AI の再生成による予期せぬ変更の検出
→ 目的が「システムの安定性」から「生成プロセスの制御」へ変化
GDD の本質: AI エージェント開発への再解釈
GDD は既存手法の組み合わせである。
しかし、その各要素を「AI がコードを生成する」前提で再解釈し、
AI 特有の課題(確率的出力・スコープ拡大・コンテキスト長制約)に対応した体系である。
GDD が解決する AI 特有の課題:
| 課題 | 従来手法では対応困難 | GDD での対応 |
|---|---|---|
| コード表現のゆらぎ | △(人間が補正) | ✅ SSoT + 出力制約 |
| 変更範囲の予期せぬ拡大 | △(レビューで検出) | ✅ 境界テスト + Contract Test |
| 判断の文脈が AI に伝わらない | ×(暗黙知) | ✅ DR の構造化記録 |
| 過去の判断が AI を汚染 | ×(問題にならない) | ✅ DR の寿命管理・廃棄条件 |
| SSoT とコードの乖離 | △(人間が気づく) | ✅ 三層検証(8.2 節) |
結論: GDD は「AI 時代への適応」である
従来の開発手法は、人間中心の開発環境で生まれた。
GDD は、それらの手法をAI エージェント開発という新しい文脈で再構成したものである。
既存手法の組み合わせ = 再パッケージという批判は正しい。
しかし、その組み合わせ方・焦点の当て方・優先順位が、AI 開発に特化して最適化されている。
これは、アジャイルが「反復開発」という既存概念を体系化したのと同様に、
GDD は「AI 時代の開発」という新しい文脈で既存手法を統合したものである。
第 5 章 Decision Record:AI と人間の判断を切り分ける
5.1 なぜ Decision Record が必要か
AI 開発では、仕様の矛盾や曖昧さに直面した際、
「どちらが正しいか」ではなく「どちらを選ぶか」の判断が必要になる。
従来の開発との違い:
- 人間: 暗黙知として判断を内包できる
- AI: 過去の判断履歴がないと、同じ議論を繰り返す
Decision Record(DR)は、その選択の理由・経緯・破棄条件を残す仕組みである。
5.2 Decision Record の構造
基本構造
記録項目:
-
判断時点の前提条件
「なぜこの判断が必要だったか」の文脈 -
採択した選択肢と棄却理由
「何を選び、何を捨てたか」 -
廃棄条件(いつ・何が変われば無効か)
「どうなったらこの判断を見直すか」
記述例:
# DR-023: 認証トークンの有効期限を 24 時間に設定
## 前提
- ユーザーは毎日ログインする想定
- セキュリティリスクを最小化したい
## 判断
- 24 時間で自動ログアウト(採用)
- 7 日間のロングセッション(却下:セキュリティリスク)
- 無期限(却下:コンプライアンス違反)
## 廃棄条件
- ユーザーからログアウト苦情が月 10 件以上
- SSO 導入時
5.3 AI と DR の関係
AI は DR を参照して過去の人間判断を学習し、
再生成時のノイズを減らす。
DR がある場合:
- AI「過去に認証期限 24 時間が選ばれた理由は…」
- 一貫性のある提案が可能
DR がない場合:
- AI「認証期限は 7 日が良いでしょうか?」
- 毎回同じ議論をやり直す
5.4 DR の分類:判断の射程による設計
すべての DR が同じ寿命を持つわけではない。
判断の射程(影響範囲) によって、DR の性質と寿命は異なる。
| DR の種類 | 影響範囲 | 寿命の目安 | 廃棄条件 |
|---|---|---|---|
| 設計原則 DR | システム全体・類似機能 | 長期(1 ~ 3 年) | 前提条件の変化・方針転換 |
| 実装判断 DR | 特定機能・モジュール | 中期(6 ヶ月~ 1 年) | 機能改修・技術スタック変更 |
| 一時的判断 DR | 特定タスク・緊急対応 | 短期(1 ~ 3 ヶ月) | 該当タスク完了・恒久対応実施 |
例: 画像位置の判断
# DR-045: コンポーネント内の画像は右配置を標準とする [設計原則]
## 前提
- ユーザーインタビュー(2024-11-05): 78%が右配置を好む
- 視線動線の研究: 左 → 右の順で視認する傾向
## 判断
- 画像は右配置(採用)
- 左配置(却下:ユーザー要望と不一致)
## 影響範囲
- Card コンポーネント
- Profile コンポーネント
- ProductDetail コンポーネント
- 今後追加される類似コンポーネント
## 廃棄条件
- ユーザー調査で逆の傾向が確認された場合
- デザインシステム全体が大幅に刷新される場合
- アクセシビリティ要件により変更が必要になった場合
## 種別
設計原則 DR(長期保持)
この DR は「1 年経過」では廃棄されず、前提条件が有効な限り保持される。
PoC から得られた暗黙知の記録
設計原則 DR の中でも特に重要なのが、PoC フェーズで発見した暗黙知である。
PoC で得た実装上の知見は、SSoT には書ききれない場合が多い。
これらを設計原則 DR として記録することで、正式版の再生成時に AI が参照できる。
記録すべき暗黙知:
- パフォーマンス特性(「この API は遅いのでキャッシュ必須」)
- 実装順序の制約(「この順序でないとエラーになる」)
- 外部サービスの暗黙的仕様(「ドキュメント化されていない挙動」)
- エッジケースへの対応(「この入力パターンで問題が出る」)
- 本番運用での発見事項(「この設定値でないと安定しない」)
記述例:
# DR-078: 商品検索のキャッシュ実装 [設計原則 DR]
## 前提(PoC で判明した事実)
- 検索 API のレスポンスタイムが平均 800ms
- ユーザーは同じ検索を 5 分以内に繰り返す傾向(68%)
- キャッシュなしでは UX 上の問題(体感的に遅い)
## 判断
- インメモリキャッシュを実装(採用)
- TTL: 5 分
- キャッシュキー: クエリ文字列のハッシュ
**検討した代替案:**
- キャッシュなし(却下: UX 要件を満たさない)
- Redis 外部キャッシュ(却下: インフラコスト増、レイテンシ改善は限定的)
## 実装上の制約
- キャッシュミス時の性能劣化を防ぐため、非同期で更新
- メモリ使用量が 100MB を超えたら LRU で削除
- キャッシュ破棄は検索条件の変更時のみ(商品データ更新時は不要)
## 影響範囲
- 商品検索機能
- 今後追加される類似の検索機能
## 廃棄条件
- 検索 API 側でキャッシュ機能が提供された場合
- レスポンスタイムが 200ms 以下に改善された場合
- メモリ使用量が常時 50MB を超える状態が継続する場合
## SSoT 統合情報
- 統合先: features/search.yaml
- 統合日: 2024-11-10
- 統合内容: performance.cache_required = true
## 種別
設計原則 DR(長期保持)
設計原則 DR の役割:
この設計原則 DR があることで、AI は正式版の再生成時に:
- ✅ キャッシュ実装を含めるべきと判断できる
- ✅ 性能要件(800ms→ キャッシュ時は即時)を満たす設計にする
- ✅ PoC で試行錯誤した結果(TTL 5 分、メモリ制限 100MB)を反映できる
- ✅ なぜその設計なのか(検索 API が遅い)を理解できる
設計原則 DR とは、PoC の試行錯誤を無駄にしないための記憶装置である。
5.5 DR の寿命管理:汚染と忘却のバランス
だが DR を無限に保持すると、AI が"多世界解釈"のように矛盾を内包してしまう。
したがって、DR にも寿命管理が必要となる。
廃棄判断のフローチャート:
DR を確認
↓
├─ 設計原則 DR?
│ ├─ YES → 前提条件は有効?
│ │ ├─ YES → 保持(SSoT の設計思想セクションに統合検討)
│ │ └─ NO → 廃棄(変更理由を新 DR として記録)
│ │
│ └─ NO → 実装判断 DR or 一時的判断 DR?
│ ├─ 該当機能は存続?
│ │ ├─ YES → 保持(ただし6ヶ月ごとにレビュー)
│ │ └─ NO → 廃棄
│ │
│ └─ 該当タスクは完了?
│ └─ YES → 廃棄(アーカイブへ移動)
重要な原則:
設計原則 DR は、時間ではなく前提条件で寿命を判断する。
これにより:
- 長期的に有効な設計判断は保持される
- 一時的な実装判断は積極的に廃棄される
- AI の参照コンテキストは「原則」と「詳細」が分離される
5.6 設計原則は SSoT へ、判断経緯は DR へ
重要な原則:
設計原則 DR の「判断内容」は SSoT に統合し、「判断経緯」のみを DR に残す。
これは DR と SSoT の役割分担を明確にする設計である。
| 要素 | 配置先 | 理由 |
|---|---|---|
| 何が正しいか | SSoT | AI が判断する際の唯一の参照先 |
| なぜそうか | DR | 人間が検証・変更判断する際の根拠資料 |
SSoT への統合例
SSoT (design-principles.yaml):
design_principles:
component_image_placement:
rule: "right"
scope: ["Card", "Profile", "ProductDetail", "新規類似コンポーネント"]
reason_ref: "DR-045"
last_verified: "2024-11-05"
exceptions:
- component: "HeroSection"
placement: "left"
reason_ref: "DR-078"
DR-045 (decisions/DR-045-image-placement.md):
# DR-045: コンポーネント画像配置の原則決定
## 状態: Active (SSoT に統合済み)
## 判断時点の前提
- ユーザーインタビュー実施: 2024-11-05
- 回答者 78%が右配置を好む
- 視線動線研究との整合性確認済み
## 検討した選択肢
1. 右配置(採用) ← SSoT に反映
2. 左配置(却下: ユーザー要望と不一致)
3. 可変式(却下: 一貫性を損なう)
## 廃棄条件
- 新規ユーザー調査で逆の結果が出た場合
- デザインシステム全体が刷新される場合
## SSoT 統合情報
- 統合先: design-principles.yaml
- 統合日: 2024-11-10
この構造の利点
AI の動作フロー:
AI: 類似コンポーネントの画像位置を決定する必要がある
↓
1. SSoT を参照 → "component_image_placement: right"
↓
2. 即座に判断可能 ✅
↓
3. (必要なら) reason_ref で DR-045 を参照し根拠を理解
人間の検証フロー:
人間: この原則は今も妥当か?
↓
1. SSoT で last_verified を確認 → "2024-11-05"
↓
2. DR-045 を参照 → ユーザー調査が根拠
↓
3. 新規調査を実施 → 前提条件を再検証
↓
4. 変更必要なら SSoT を更新 + 新 DR 作成
SSoT と DR の境界原則
| 情報の性質 | 配置先 | 例 |
|---|---|---|
| 現在有効な規範 | SSoT | 画像は右配置 |
| 規範の適用範囲 | SSoT | Card/Profile/ProductDetail |
| 例外パターン | SSoT | HeroSection は左配置 |
| 判断に至った経緯 | DR | ユーザー調査の結果 |
| 棄却した選択肢とその理由 | DR | 左配置を却下した理由 |
| 廃棄条件 | DR | どうなったらこの原則を見直すか |
重要な分離ルール:
SSoT に「何」と「いつ」、DR に「なぜ」と「もし」を記録する。
これにより:
- AI の参照効率: SSoT だけで判断完結
- Single Source の維持: 「何が正しいか」は SSoT に一元化
- 判断の透明性: DR で経緯を追跡可能
- 前提条件の検証: DR で廃棄条件を確認可能
-
情報の鮮度管理: SSoT の
last_verifiedで定期検証をトリガー
DR の新しい位置づけ
Decision Record とは、AI の思考汚染を防ぐための"判断の墓標"ではなく、
SSoT という"現在の真実"を支える"判断の根拠アーカイブ"である。
AI に「過去の判断」を学習させるのではなく、
「現在の原則」だけを SSoT で参照させる——
そして人間が「なぜその原則なのか」を検証する際に DR を活用する——
この役割分担こそが、DR と SSoT の健全な関係である。
5.7 DR の墓場化問題:今後の課題
しかし、DR の運用には重大な課題が残されている。
それが**「DR の墓場化」**である。
墓場化のリスク
DR を記録しても、結局は読まれずに蓄積されていくだけの「判断の墓場」になる可能性がある:
- 検索困難: DR が膨大になり、どれが有効か分からなくなる
- 文脈の喪失: 判断の背景が時間とともに失われ、後から読んでも理解できない
- 矛盾の放置: 古い DR と新しい DR が矛盾したまま並存する
- メンテナンスコスト: DR の更新・廃棄が SSoT 更新より重くなる
AI 時代における特有の課題
AI エージェント開発では、さらに以下の問題が生じる:
- コンテキスト長の限界: AI は過去の全 DR を参照できるのか?
- 矛盾時の判断: 古い DR と新しい DR が矛盾した場合、AI はどう判断するのか?
- 鮮度の判定: どの DR が現在も有効で、どれが陳腐化しているのか?
現在の対策と限界
本書では以下の対策を提示している:
- ✅ DR の種類分け(設計原則 / 実装判断 / 一時的判断)
- ✅ 廃棄条件の明示
- ✅ SSoT への統合(設計原則 DR の「判断内容」を SSoT に移行)
しかし、これらだけでは不十分である可能性が高い。
今後必要な仕組み
以下のような仕組みが必要だと考えられる:
- DR のライフサイクル管理: 定期的な「生存確認」と自動アーカイブ
- 影響範囲の追跡: どのコード・SSoT に影響しているかを明示
- AI 用サマリーの自動生成: 膨大な DR を要約し、コンテキスト長を削減
- 矛盾検出の自動化: 新しい DR が既存 DR と矛盾していないかチェック
- DR 間の依存関係管理: DR が他の DR を参照している場合の整合性維持
DR の墓場化問題は、GDD の実運用において最も重要な未解決課題の一つである。
詳細な設計・実装方法については、
実際のプロジェクトでの運用経験を通じて体系化していく必要がある。
第 6 章 ドキュメント寿命管理:情報過多が AI を汚染する
6.1 情報過多による汚染リスク
AI エージェント開発では、「情報を蓄積する」ことが必ずしも善ではない。
情報が多すぎると、AI の文脈が汚染され、矛盾を再生産する。
汚染の具体例:
- 古い設計判断と新しい方針が混在し、AI が矛盾した提案をする
- 廃止された機能の仕様が残存し、不要なコードを生成する
- 過去の DR が累積し、判断の文脈が不明瞭になる
情報の総量が増えるほど、AI の精度は下がる。
6.2 ドキュメント寿命の設計
すべてのドキュメントには寿命と廃棄条件を設定する。
| ドキュメント種別 | 寿命の目安 | 廃棄条件 |
|---|---|---|
| SSoT | プロジェクト全期間 | プロジェクト終了時 |
| DR(設計原則) | 1 ~ 3 年 | 前提条件の変更・方針転換 |
| DR(実装判断) | 6 ヶ月~ 1 年 | 機能改修・技術スタック変更 |
| DR(一時的判断) | 1 ~ 3 ヶ月 | 該当タスク完了 |
| 設計ドキュメント | 該当機能のリリース後 | 機能廃止・大幅リファクタ |
| 議事録・メモ | 3 ヶ月 | 関連 Issue のクローズ |
| PoC 成果物 | SSoT 確定まで | 正式版開発開始 |
6.3 寿命管理の実装
方法 1: メタデータによる管理
---
title: "認証フロー設計"
created: 2025-01-10
expires: 2025-07-10
deprecated_by: "SSoT v2.3"
---
方法 2: 自動アーカイブ
- CI/CD で期限切れドキュメントを検出
- 自動的に
archive/フォルダへ移動 - AI の参照対象から除外
方法 3: 定期棚卸し
- 月次で DR・設計文書をレビュー
- 不要なものを積極的に削除
- 「保存」ではなく「廃棄」を評価基準にする
6.4 原則:「情報は減らすほど価値が上がる」
ドキュメントとは「記録」ではなく「参照される構造体」である。
AI が参照するコンテキストを清潔に保つことが、生成品質の維持につながる。
良いドキュメント体系とは、必要な情報だけが残っている体系である。
その有効範囲を管理することこそがガバナンスの本質である。
第 7 章 AI 時代の見積もり再定義:実装工数から仕様確定工数へ
7.1 背景:実装コストがゼロになる時代
AI エージェント開発では、コード生成が瞬時に行われるため、
従来の開発における最大コスト「実装工数」が限りなくゼロに近づく。
その結果、開発の主要コストは**「仕様を確定させる工数」**へと移行する。
従来の工数配分:
要件定義(10%) → 設計(15%) → 実装(60%) → テスト(15%)
↑ ここが最大コスト
AI 時代の工数配分:
要件定義(15%) → 仕様確定(?%) → 実装(0%) → 検証(?%)
↑ ここが最大コストだが、見積もり方法が不明
7.2 工数構造の変化:なぜ「設計」に時間がかかるのか
「設計」という言葉は AI 時代も使われる
AI 時代においても、「設計(Design)」「仕様(Specification)」という言葉は普通に使われる。
実際、Specification-Driven Development (SDD) のような用語も存在する。
本章で問題にしているのは「用語の違い」ではなく、「工数構造の根本的な変化」である。
工数配分の劇的な変化
AI 時代の開発では、実装工数がゼロになる代わりに、設計/仕様策定の工数が激増する。
従来の工数配分:
要件定義(10%) → 設計(15%) → 実装(60%) → テスト(15%)
↑ 最大コスト: 実装中に細部を決める
AI 時代の工数配分:
要件定義(15%) → 設計/仕様確定(30-50%) → 実装(0%) → 検証(20-35%)
↑ 最大コスト: 事前に細部まで全て決める
重要な変化:
- 実装工数: 60% → 0%(AI が瞬時に生成)
- 設計工数: 15% → 30-50%(2 ~ 3 倍に増加)
→ 最大コストが「実装」から「設計」へ移動
なぜ設計工数が増えるのか?4 つの理由
1. 実装時判断の前倒し
従来(人間が実装):
設計書: 「ファイルアップロード機能を実装」
↓
実装中: 「5MB制限にしよう」「拡張子チェックも入れよう」
「プログレスバーも表示しよう」
← 実装しながら細部を決める(実装工数に含まれる)
AI 時代:
設計/仕様: 「ファイルサイズ上限: 5MB」
「許可拡張子: .pdf のみ」
「プログレスバー表示: 必須」
「エラーメッセージ: "ファイルサイズは5MBまでです"」
← 実装前に全て決める必要がある(設計工数が増加)
↓
AI実装: 瞬時に生成(0日)
→ 実装中に決めていた判断が、全て設計フェーズに前倒しされる
2. AI の暗黙知には 2 つの問題がある
AI は学習データから暗黙知を獲得しているが、以下の 2 つの問題がある。
2-1. 一般的知識の適用が不安定(確率的出力)
問題:
AI は「エラー処理のベストプラクティス」を知っているが、どう適用するかが毎回変わる。
具体例:
プロンプト: 「ログイン API のエラー処理を実装してください」
AI の出力ゆらぎ:
- 生成 1: HTTP 400, ログなし, 簡易メッセージ「認証に失敗しました」
- 生成 2: HTTP 500, 詳細ログあり, スタックトレース含む
- 生成 3: HTTP 401, セキュリティログ, リトライ制限あり
→ すべて「正しい」が、一貫性がない。プロジェクト全体で統一するには明示が必要
2-2. プロジェクト固有知識の欠如
問題:
AI は一般的なベストプラクティスは知っているが、このプロジェクト特有のルールは知らない。
AI が知っている(一般的知識):
- エラーは HTTP ステータスコードで返す
- ログレベル(info/warn/error)を使い分ける
- セキュリティ対策を入れる
- パスワードはハッシュ化する
AI が知らない(プロジェクト固有知識):
- このプロジェクトでは エラーコードは
ERR_AUTH_001形式 - ログは JSON 形式で Datadog に送信する
- エラーメッセージは i18n キーを返す(
errors.auth.invalid_password) - 認証トークンは独自の JWT 拡張を使用
- パスワードは bcrypt ではなく Argon2 を使う
→ プロジェクトの暗黙のルールを全て明示する必要がある
対応策: SSoT に明示
error_handling:
code_format: "ERR_{MODULE}_{NUMBER}"
http_status_mapping:
validation_error: 400
auth_error: 401
server_error: 500
logging:
format: "json"
destination: "datadog"
include_fields: ["timestamp", "user_id", "error_code", "stack_trace"]
user_messages:
format: "i18n_key"
prefix: "errors."
example: "errors.auth.invalid_password"
authentication:
token_type: "custom_jwt"
extensions:
- "project_id"
- "tenant_id"
password_hashing:
algorithm: "argon2id"
memory_cost: 65536
time_cost: 3
人間のエンジニアなら:
- 既存コードを見て「ああ、このプロジェクトは Argon2 使ってるな」と気づく
- 他の API のエラーコード形式を見て統一する
AI は:
- 既存コードを参照できても、「これがプロジェクトルールだ」と認識できない
- 毎回、一般的なベストプラクティス(bcrypt)を選択してしまう
→ プロジェクト固有のルールを SSoT に全て記述する必要がある
4. 探索的プロセスが必要
従来の設計:
- 設計書を書く → レビュー → 確定 → 実装開始
AI 時代の設計:
- 仕様を書く → AI に生成させる → 曖昧さに気づく → 仕様を修正 → 再生成
→ また曖昧さに気づく → さらに修正...
→ 試行錯誤が必要なため、工数が増える
具体例:
仕様v1: 「エラー時は適切なメッセージを表示」
↓ AI生成
結果: エラーメッセージが英語で出力された
↓
仕様v2: 「エラーメッセージは日本語で表示」
↓ AI生成
結果: 技術的詳細(スタックトレース)が含まれていた
↓
仕様v3: 「エラーメッセージは日本語で、技術的詳細を含めない」
↓ AI生成
結果: やっと期待通りの実装
5. 事前のステークホルダー合意
従来:
- 設計書(概要レベル)で合意を得る
- 実装中の細部は開発者判断で OK
AI 時代:
- 実装レベルの詳細仕様で合意を得る必要がある
- 「エラーメッセージの文言」「ボタンの配置」まで事前に決める
- レビュー・承認のコストが増加
設計の「質」も変わる
| 観点 | 従来の設計 | AI 時代の設計/仕様確定 |
|---|---|---|
| 対象読者 | 人間のエンジニア | AI エージェント |
| 詳細度 | 概要レベル(数ページ) | 実装レベル(数十ページ) |
| 曖昧さ | 許容(人間が補完) | 排除(全て明示) |
| 暗黙知 | 前提とする | 形式知化が必要 |
| 判断のタイミング | 実装中に決める(60%工数内) | 事前に全て決める(30-50%工数) |
| 成果物形式 | Word/Markdown ドキュメント | YAML/JSON/TypeScript(SSoT) |
まとめ:「設計」という言葉は同じでも、中身は別物
AI 時代も「設計」は必要だが、やるべきことが根本的に変わる。
変化の本質:
- 実装工数(60%)が消滅
- その工数が設計フェーズに前倒しされる
- 設計の詳細度が「概要レベル」から「実装レベル」へ深化
- 結果として、設計工数が 2 ~ 3 倍に増加
重要な洞察:
- 「AI が書くから開発が速くなる」は正しい(実装工数がゼロ)
- しかし「何を書かせるか決めるのに時間がかかる」(設計工数が増加)
- トータルでは 30 ~ 56%の工数削減(後述のパターン比較参照)
7.3 AI 時代の 4 つの開発パターン
AI 時代の開発には、大きく分けて 4 つのパターンが存在する。
それぞれ工数構造が異なるため、見積もり時に選択する必要がある。
以下、PDF アップロード機能を例に、各パターンの工数を比較する。
パターン D: 従来型(人間中心開発)
特徴: AI を使わず、人間がコードを書く
工数例:
| 工程 | 内容 | 工数 |
|---|---|---|
| 要件定義 | PDF のみ、5MB まで、プログレスバー等 | 2 人日 |
| 設計 | API 設計、データ設計、UI 設計 | 5 人日 |
| 実装 | フロントエンド + バックエンド実装 | 15 人日 |
| テスト | 単体・結合・E2E テスト | 5 人日 |
| 合計 | 27 人日 |
特徴:
- ✅ 実績があり予測可能
- ❌ 実装に時間がかかる
パターン A: 仕様完全確定型(GDD 推奨)
特徴: 実装前に仕様を完全に確定し、AI に一括生成させる
工数例:
| 工程 | 内容 | 工数 |
|---|---|---|
| 要件定義 | PDF のみ、5MB まで、プログレスバー等 | 2 人日 |
| PoC/探索 | Mini-PoC で UI パターン検証、エラー処理検証 | 5 人日 |
| 仕様確定 | SSoT 作成(詳細な YAML)、DR 記録 | 8 人日 |
| 実装 | AI が自動生成 | 0 人日 |
| 検証 | Contract Test、E2E テスト、手動確認 | 4 人日 |
| 合計 | 19 人日 |
削減率: 30%削減(27 → 19 人日)
特徴:
- ✅ SSoT・DR が残るため、保守性・再現性が高い
- ✅ 長期運用に適している
- ❌ 仕様確定に時間がかかる
推奨シーン: 本番システム、複雑なビジネスロジック
パターン B: ペアプロ型(AI ペアプログラミング)
特徴: 大まかな方針だけ決め、AI とペアプロで実装しながら仕様を決める
工数例:
| 工程 | 内容 | 工数 |
|---|---|---|
| 要件定義 | PDF のみ、5MB まで、プログレスバー等 | 2 人日 |
| 大まかな方針 | API エンドポイント、UI の大枠だけ決定 | 1 人日 |
| ペアプロ実装 | AI に指示しながら実装、細部は実装中に決定 | 7 人日 |
| 検証 | 手動確認、簡易テスト | 2 人日 |
| 合計 | 12 人日 |
削減率: 56%削減(27 → 12 人日)
特徴:
- ✅ 最も速い(短期的)
- ✅ 柔軟に変更できる
- ❌ SSoT・DR が残らない(仕様が不明瞭)
- ❌ AI の出力が不安定
推奨シーン: プロトタイプ、個人プロジェクト、使い捨てスクリプト
パターン C: ハイブリッド型(実務推奨)
特徴: コア仕様だけ事前確定し、細部は実装中に決め、事後的に SSoT を更新
工数例(詳細版):
| 工程 | 内容 | 工数 | 従来との比較 |
|---|---|---|---|
| 要件定義 | PDF のみ、5MB まで、プログレスバー等 | 2 人日 | 同じ(2 人日) |
| コア仕様確定 | ファイル制約、API 仕様だけ SSoT に記録 | 3 人日 | +1 人日(従来は 2 人日の概要設計) |
| ペアプロ実装 | AI と実装、UI 詳細は実装中に決定 | 6 人日 | -9 人日(従来は 15 人日の実装) |
| 事後 SSoT 更新 | 実装中に決めた内容を SSoT に反映、DR 記録 | 2 人日 | +2 人日(新規) |
| 検証 | Contract Test、E2E テスト | 3 人日 | -2 人日(従来は 5 人日、AI がテスト生成) |
| 合計 | 16 人日 | -11 人日(41%削減) |
削減率: 41%削減(27 → 16 人日)
工数構造の詳細分析
設計工数の変化:
- 従来: 2 人日(概要設計)
- パターン C: 3 人日(コア仕様確定)
- +50%増加だが、絶対値は小さい(+1 人日)
実装工数の変化:
- 従来: 15 人日(人間が全て実装)
- パターン C: 6 人日(AI とペアプロ、内訳は下記)
- AI への指示・レビュー: 1 人日
- 実装中の仕様決定: 5 人日
- -60%削減(-9 人日)
ドキュメント管理工数(新規):
- 従来: 0 人日(ドキュメント作成なし、またはコード内コメントのみ)
- パターン C: 2 人日(事後的に SSoT・DR 記録)
- +2 人日(新規追加)
検証工数の変化:
- 従来: 5 人日(手動テスト + テストコード作成)
- パターン C: 3 人日(AI がテスト生成、人間はレビュー)
- Contract Test 作成: 1 人日(AI が生成、人間が調整)
- E2E テスト実行・調整: 2 人日
- -40%削減(-2 人日)
パターン C の重要な洞察
「ペアプロ実装」6 人日の内訳:
実際の実装作業: 1人日(AIへの指示・生成コードのレビュー)
仕様の決定作業: 5人日(UIレイアウト、エラーメッセージ文言など)
→ 「実装」という名前だが、実質は「仕様決定」が主なコスト
ドキュメント管理の必要性:
- パターン B と違い、実装中の判断を「捨てない」
- 後で SSoT に記録することで、保守性を確保
- この 2 人日が、パターン B との違いを生む
検証工数が減る理由:
- AI が Contract Test の骨格を生成
- AI が E2E テストコードを生成
- 人間はレビューと調整に専念
特徴
- ✅ パターン B 並みの速度(12 人日 vs 16 人日)
- ✅ SSoT・DR が残る(パターン A の利点)
- ✅ 柔軟性と保守性のバランス
- ✅ 設計工数の増加は限定的(+1 人日)
- ✅ ドキュメント管理工数を明示(+2 人日)
推奨シーン: 実務のほとんどのケース
7.4 4 パターンの比較と選択ガイド
比較表
| パターン | 工数 | 削減率 | 速度 | 保守性 | 再現性 | 推奨シーン |
|---|---|---|---|---|---|---|
| D: 従来型 | 27 人日 | 0% | 遅い | 高 | 高 | AI を使えない環境 |
| A: 仕様完全確定 | 19 人日 | 30% | 中 | 最高 | 最高 | 本番システム、複雑な機能 |
| B: ペアプロ | 12 人日 | 56% | 最速 | 低 | 低 | プロトタイプ、個人開発 |
| C: ハイブリッド | 16 人日 | 41% | 速い | 高 | 高 | 実務推奨 |
工数構造の変化
従来型(パターン D)のコスト内訳:
要件定義: 7% (2/27)
設計: 19% (5/27)
実装: 56% (15/27) ← 最大コスト
テスト: 19% (5/27)
AI 時代(パターン C)のコスト内訳:
要件定義: 13% (2/16)
仕様確定: 19% (3/16)
ペアプロ実装: 38% (6/16) ← 実装ではなく「仕様決定+実装」
事後 SSoT: 13% (2/16)
検証: 19% (3/16)
重要な洞察:
- 「実装」そのものは 0 人日(AI が瞬時に生成)
- しかし「ペアプロ実装」6 人日の内訳は:
- 実装作業: 1 人日(AI への指示)
- 仕様決定: 5 人日(「どう実装するか」の判断)
→ 実質的には「仕様決定」が最大コスト
パターン選択のフローチャート
プロトタイプ・使い捨て?
├─ YES → パターン B(ペアプロ型)
└─ NO → 本番環境で長期運用?
├─ YES → 複雑なビジネスロジック?
│ ├─ YES → パターン A(仕様完全確定型)
│ └─ NO → パターン C(ハイブリッド型)
└─ NO → AI を使えない環境?
├─ YES → パターン D(従来型)
└─ NO → パターン C(ハイブリッド型)
迷ったら → パターン C(ハイブリッド型)
7.5 なぜパラメータモデルが必要か
ここまでで見てきたように、AI 時代の開発では、
- 実装工数はほぼ 0 になり
- 仕様確定・コンテキスト管理・検証(レビュー/テスト)が
実質的な工数の大部分を占めるようになった。
そして、PDF アップロード機能を例にした 4 パターン比較では、
- パターン D(従来型): 27 人日
- パターン A(仕様完全確定): 19 人日
- パターン B(ペアプロ): 12 人日
- パターン C(ハイブリッド): 16 人日
という**「工数の変化のイメージ」**を提示した。
しかしここで、次のような疑問が必ず出てくる。
- なぜこの機能で 27 → 16 人日になるのか?
- 別の機能でも同じ削減率になるのか?
- チームの熟練度や、既存システムとの統合度合いが違うと、どれくらいブレるのか?
- 「仕様確定に時間がかかる」の中身を、もう一段分解できないのか?
この疑問に答えられないかぎり、
ここでの人日の数字は“説得力のある例示”にとどまり、
理論としては弱い。
工数を「原因」で説明する必要がある
本章で扱いたいのは、「何人日かかったか」という結果ではなく、
なぜ、その工数になったのか?
という**「原因側のモデル」**である。
AI を前提にした開発では、実務上の工数は概ね次の要素で決まる。
- 仕様がどれだけ明確か(曖昧さ・抜け・矛盾の少なさ)
- プロジェクト内のコンテキスト(SSoT)がどれだけ整っているか
(古い仕様・PoC・口頭情報がどれくらい混ざっているか) - AI の出力がどれだけ安定しているか(破綻率・再生成率)
- 破綻した結果を、人間がどれだけ補正しなければならないか
(レビュー・リファクタ・テスト修正・ステークホルダー確認)
つまり、工数(結果)はこれらの“原因の組み合わせの関数”として説明されるべきであり、
人日の数字だけを提示しても、「なぜこうなったか」は分からない。
4 つのパラメータへの分解
そこで本書では、AI 時代の工数を構造的に説明するために、
次の 4 つのパラメータを導入する。
-
S: 仕様純度 (Specification Purity)
- 仕様の明確さ・一貫性・エッジケースの網羅性
- 曖昧さや抜けが少ないほど S が高い
-
C: コンテキスト鮮度 (Context Freshness)
- SSoT がどれだけ整備され、古い情報や PoC がどれだけ排除/区別されているか
- コンテキスト汚染が少ないほど C が高い
- ここには「人・ルールによるコンテキスト管理」の質が強く効いてくる
-
A: AI 再現性 (AI Consistency)
- 同じ仕様・同じプロンプトで、どれだけ安定した出力が得られるか
- モデル選定やプロンプト設計の質によって変動する
-
H: 人間補正係数 (Human Correction Factor)
- AI 出力をレビューし、修正し、テストし、ステークホルダー合意を取るために
どれくらい人間の手当てが必要か - チームのスキルや責務分担、品質基準の厳しさによって変化する
- AI 出力をレビューし、修正し、テストし、ステークホルダー合意を取るために
この 4 つは、単なる言葉遊びではない。
第 7 章前半で扱った
- 仕様確定工数の増加
- コンテキスト汚染に起因するリワーク
- AI の破綻による再生成
- レビュー・テスト・確認の重さ
といった**実務上の「なぜ時間がかかるのか」**を、そのままパラメータに落としたものである。
「人日のシミュレーション」から「パラメトリックモデル」へ
ここで重要なのは、
7.3〜7.4 の人日の例は「この 4 パラメータがこうだった場合の一例」として位置づけ直せるという点である。
- 仕様がかなり固まっている
- コンテキストがそこそこ整っている
- AI の再現性が中程度
- レビュー基準は厳しめ
といった前提を置いた時に、
- パターン B ならこれくらいの削減になりそう
- パターン C ならこれくらいになりそう
という思考実験としての数字だった、ということが明確になる。
この章の後半では、
この 4 パラメータを実務の中でどう計測・推定し、
最終的に見積もりモデルに落とし込むかを扱う。
7.6 実測ログとパラメータの逆算
前節で、AI 時代の工数を説明するための 4 つのパラメータ
(S / C / A / H)を導入した。
しかし、ここでいきなり
- 「この機能は S=0.7, C=0.8, H=1.2 です」
と言っても、それは結局主観的なスコアリングになってしまう。
そこで本書では、
最初にパラメータを決めるのではなく、
まず“実測ログ”を取り、
データから逆算でパラメータを推定する
というアプローチをとる。
最小限の実測ログ:4 つの指標
AI を用いた開発において、
1 つのタスク(チケット)ごとに、最低限次の 4 つだけ記録することを提案する。
-
blocks(仕様ブロック数)
-
そのタスクで扱った仕様の「粒度の数」
-
例:
- UI 入力項目 1 つ → 1 block
- バリデーションルール 1 つ → 1 block
- API の入力/出力フィールド 1 つ → 1 block
- 状態遷移ルール 1 つ → 1 block
-
完全な正確さは不要で、同じチーム内で大きくブレなければよい
-
-
ai_calls(AI 出力回数)
- そのタスクのために、AI に対して「非トリビアルな生成」を依頼した回数
- 例:コンポーネント全体生成、API クライアント生成、テストコード生成など
-
failures(AI 破綻回数)
- 明らかに誤った出力や、ユーザーから「これは違う」と修正を求められた回数
- 実装が通らない、仕様と矛盾する、設計方針に反するなど
-
human_fix_time(人間の補正時間)
- そのタスクに対して人間がかけた時間(レビュー・修正・テスト・調整を含む)
これらは、AI アシスタント(例:Cursor)に
- blocks / ai_calls / failures を自動記録させる
- human_fix_time は人間がざっくりメモする
といった運用にすれば、現場負荷を最小限に抑えたまま取得できる。
ログから中間指標を計算する
各タスクについて、まず次の 2 つの中間指標を計算する。
failure_rate f = failures / ai_calls
time_per_block t = human_fix_time(時間) / blocks
-
failure_rate f
- AI がどれくらいの頻度で破綻したか
-
time_per_block t
- 仕様ブロック 1 つあたり、最終的にどれくらい人間時間がかかったか
これは、
- 仕様が曖昧だったタスク
- コンテキストが汚染されていたタスク
- スコープが広がりすぎたタスク
などをあとから比較するための基礎データになる。
S(仕様純度)の逆算イメージ
S は「仕様の明確さ・一貫性」を表す。
直感的には、
- 破綻率 f が高い
- 1 ブロックあたりの時間 t も重い
タスクほど、仕様が曖昧だった可能性が高い。
全タスクの中央値などを基準に、
f_norm = f / f_median
t_norm = t / t_median
といった形で**相対的な“悪さ”**を正規化し、
S ≒ 1.1 - 0.5 * f_norm - 0.5 * t_norm (0.1〜1.0にクランプ)
のように、0.1〜1.0 の範囲のスコアとして扱うことができる。
ここで重要なのは、
- 数式の係数そのものではなく
- 「破綻率 f と手戻り時間 t から、
そのタスクの仕様の質 S をデータとして推定できる」という構造である。
C(コンテキスト鮮度)の逆算イメージ
C は「コンテキスト(SSoT)の整合性・鮮度」を表す。
本来であれば、各 failure について、
- 仕様の曖昧さが原因なのか
- 文脈の汚染(旧仕様、PoC、口頭情報)が原因なのか
をタグ付けすると精度が上がる。
最低限でも、
- 「この failure は、古い仕様や PoC が参照された結果だった」
と判断されるものをcontext_failureとしてカウントし、
context_ratio = context_failures / failures
C ≒ 1.0 - context_ratio (0.1〜1.0にクランプ)
といった形で、
- 破綻のうち、どれくらいがコンテキスト管理の問題だったか
を数字で見える化できる。
これにより、
- 「このプロジェクトは S(仕様純度)はそこそこだが、
C(コンテキスト鮮度)が壊滅的に低い」 - 「PoC や古い設計書をちゃんと棚卸ししない限り、いくら仕様を緻密に書いても破綻が減らない」
といった構造的な問題が、議論可能な形で浮かび上がる。
H(人間補正係数)の逆算イメージ
H は「AI の出力を人間がどれだけ補正しているか」を表す。
- failure_rate が低く、
- time_per_block も比較的軽いタスク群を「理想状態」とみなし、
base_t_per_block = それらタスクの time_per_block の中央値
とする。
各タスクについて、
H_raw = time_per_block / base_t_per_block
H = 0.3〜2.0 の範囲にクランプ
のように定義すれば、
- H ≒ 1.0:理想状態並みの補正コスト
- H > 1.0:レビューやテスト、調整が重くなっている
- H < 1.0:似たタスクの再利用が効いていて軽い
といった**“人間側の負荷の偏り”**を可視化できる。
見積もりモデルの形
これらのパラメータを使って、
将来のタスクの工数を見積もるときの基本的な形は、たとえば次のようになる。
予測コスト(時間) ≒ blocks ÷ (S × C × A) × H
ここで:
- blocks は、そのタスクで扱う仕様ブロック数の見積もり
-
S, C, A, H は、過去の実測から推定された値
(あるいは、プロジェクト全体の平均値)
を使う。
本書ではこの式そのものよりも、
「工数を、S/C/A/H という 4 つの原因パラメータに分解し、
それを実測ログから逆算する」
という考え方が重要である。
7.7 この見積もりモデルの前提と今後の実証
ここまでに示したものは、
- S / C / A / H という 4 パラメータによる工数モデル
- blocks / ai_calls / failures / human_fix_time という 4 つの実測ログ
- それらからパラメータを逆算し、
将来の見積もりに活かすという枠組み
である。
ここで改めて強調しておきたいのは、
これは「完成した理論」ではなく、
AI 時代の見積もりを実証的に構築するための
“第 1 版のフレームワーク”である
という点である。
前提となる制約
-
ログの粒度はラフである
- blocks / failures などは、完全に客観的な値ではない
- しかし同じチーム内で一貫していれば、
相対値として十分機能する
-
S / C / H の計算式は調整可能である
- 本章で示した計算方法は一例であり、
実プロジェクトのデータに合わせて係数やしきい値を変えるべきである
- 本章で示した計算方法は一例であり、
-
A(AI 再現性)は別途評価が必要である
- モデルの選定、プロンプトの設計、
さらにはツールチェーン(Editor、CI、SSoT 連携)の影響を受ける - この章では詳細には立ち入らず、
「AI 出力の安定度を表すパラメータ」として位置付けに留めている
- モデルの選定、プロンプトの設計、
-
プロジェクト特性の差は吸収しきれない
- 基幹系システムと小さなモバイルアプリでは、
同じ S/C/H の値でも絶対工数は当然異なる - 本モデルは「工数の構造」を説明することを主眼とし、
絶対工数の予測は別途調整が必要である
- 基幹系システムと小さなモバイルアプリでは、
今後の実証に向けて
本章で提案したフレームワークを実用レベルの理論に育てていくためには、
以下のような実証が必要になる。
- 複数プロジェクト・複数チームでの
blocks / ai_calls / failures / human_fix_time の記録 - 機能の種類(CRUD / 複雑なビジネスロジック / 既存統合 など)ごとの
S/C/H の分布と削減率の分析 - チームの習熟度(AI 開発 1〜3 ヶ月 / 3〜6 ヶ月 / 6 ヶ月以上)と
パラメータ推定値の変化 - コンテキスト管理(SSoT、ドキュメント寿命管理、PoC 切り離し)施策の有無と
C の改善度合いの比較 - スコープ管理の厳密さ(境界条件の明示)と
failures / time_per_block の相関
これらのデータが蓄積されれば、
- AI 開発における
「典型的な S/C/H のレンジ」 - プロジェクト特性に応じた
「現実的な削減率の帯域」
を提示できるようになり、
本章のモデルは**「仮説」から「実証を伴う理論」**へと進化する。
読者へのお願い
本章は、AI 時代の見積もりを
- 実装工数の延長として捉えるのではなく
- 仕様・コンテキスト・AI・人間補正という 4 つの要因から説明し直す
ための「最初の枠組み」として提示した。
もしあなたのプロジェクトが、
- AI を前提にした開発を行っており
- 見積もりやリワークに課題を感じているなら
ぜひ、小さな範囲でもよいので、
- blocks / ai_calls / failures / human_fix_time の記録
- そこからの S / C / H の逆算
- 4 パターン(A/B/C/D)のどれに近いかの自己評価
を試してみてほしい。
そして、そこで得られた気づきや数字があれば、
本章のフレームワークに対するフィードバックとして
どこかに残してもらえると嬉しい。
AI 時代の見積もりは、
もはや一人の経験則ではなく、
多くのプロジェクトの実測データによって
共同で磨かれていくべきテーマである。
第 7 章は、そのための最初の「座標軸」を与える試みとして位置付けたい。
7.8 次章への接続:見積もりから「継続的保証」へ
第 7 章では、AI 時代の開発における工数の源泉が
「実装」から「仕様・コンテキスト・補正作業」へと移動する構造を明らかにし、
その複雑性を S(仕様純度)/ C(コンテキスト鮮度)/ A(AI 再現性)/ H(補正係数)
という 4 つのパラメータへと分解することで、
現代的な見積もりのフレームワークを提示した。
しかし、見積もりはあくまで
未来の工数を「予測する」ためのモデル
であり、
プロジェクトを成功に導くには、
実際の開発の進行とともに、この 4 つのパラメータがどのように変化し、
どのように劣化し、どのように改善されていくか
を継続的にモニタリングする仕組みが必要となる。
AI 開発は、伝統的な開発と異なり、
- 仕様が進むにつれてコンテキストが汚染されやすい
- AI の再現性がプロンプトやモデル更新で揺らぐ
- 実装が自動化されるぶん、レビューと保証の重みが増す
という“動的な不安定性”を本質的に含んでいる。
したがって、見積もり(静的) → 保証(動的)
という流れは切り離して考えるのではなく、
S / C / A / H を「計測 → 是正 → 安定」させていくための
継続的保証(Continuous Guarantee)こそが、
GDD における見積もりの実践形となる。
次章では、
第 7 章で定義した S/C/A/H をどのように 運用・監視・安定化 していくのか、
そのための仕組みとしての 「保証(Guarantee)」 を扱う。
第 8 章 運用と保証:動的安定を支える継続的保証構造
運用フェーズでは、保証駆動(GDD)が中核を担う。
目的は時間的な正しさの維持。
8.1 保証駆動の構成要素
- 自動回帰テスト:SSoT 更新ごとに再検証
- モニタリング / SLO 管理:品質を数値で監視
- フェイルセーフ / Rollback:異常時の即時復旧
- Mini-PoC:部分変更を安全に再実験
- Policy-as-code:権限・ルールをコード化し AI も解釈可能に
- SSoT 整合性の自動検証:コードと SSoT の乖離を検知
GDD は「壊れない構造」を運用レイヤで保証する技術である。
8.2 SSoT 整合性の自動検証
AI 時代の開発では、SSoT とコードが乖離するリスクが常に存在する。
この乖離を検知し、自動的に修正または警告を発する仕組みが必要である。
乖離が発生する典型パターン
パターン 1: 緊急対応での直接修正
// 本番障害発生
// 「とりあえずコード修正」
function processPayment(amount: number) {
// HOTFIX: タイムアウト対策
const timeout = 10000; // SSoTには5000と記載
// ...
}
パターン 2: SSoT 更新の失念
// 機能追加時
function generateToken(userId: string, options?: TokenOptions) {
// ← optionsパラメータ追加(SSoTは未更新)
// ...
}
パターン 3: 実装の最適化
// パフォーマンス改善
function searchProducts(query: string) {
// SSoT: キャッシュなしと記載
// 実装: キャッシュを追加した
const cached = cache.get(query);
if (cached) return cached;
// ...
}
解決策: Contract Testing 的アプローチ
SSoT から自動的に Contract Test を生成し、コードとの整合性を検証する。
ステップ 1: SSoT に Contract 定義を追加
# ssot/features/authentication.yaml
generateToken:
signature:
input:
- name: "userId"
type: "string"
required: true
output:
type: "{ token: string, expires: Date }"
contract_tests:
output_structure:
- check: "返り値がオブジェクトである"
- check: "tokenプロパティが存在し、string型である"
- check: "expiresプロパティが存在し、Date型である"
- check: "他のプロパティが存在しない"
input_validation:
- check: "userIdが空文字列の場合、エラーをthrowする"
- check: "userIdがundefinedの場合、エラーをthrowする"
ステップ 2: Contract Test の自動生成
// 自動生成されるテスト: tests/contracts/generateToken.contract.test.ts
import { generateToken } from "@/auth/token";
import { AUTHENTICATION_SSOT } from "@/ssot/features/authentication";
describe("Contract Test: generateToken", () => {
describe("出力構造の検証", () => {
it("返り値がオブジェクトである", () => {
const result = generateToken("user123");
expect(typeof result).toBe("object");
});
it("tokenプロパティが存在し、string型である", () => {
const result = generateToken("user123");
expect(result).toHaveProperty("token");
expect(typeof result.token).toBe("string");
});
it("expiresプロパティが存在し、Date型である", () => {
const result = generateToken("user123");
expect(result).toHaveProperty("expires");
expect(result.expires).toBeInstanceOf(Date);
});
it("他のプロパティが存在しない", () => {
const result = generateToken("user123");
const keys = Object.keys(result);
expect(keys).toEqual(["token", "expires"]);
});
});
describe("入力検証", () => {
it("userIdが空文字列の場合、エラーをthrowする", () => {
expect(() => generateToken("")).toThrow();
});
it("userIdがundefinedの場合、エラーをthrowする", () => {
expect(() => generateToken(undefined as any)).toThrow();
});
});
});
ステップ 3: CI/CD での自動実行
# .github/workflows/contract-tests.yml
name: SSoT Contract Tests
on:
push:
branches: [main, develop]
pull_request:
jobs:
contract-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Generate Contract Tests from SSoT
run: npm run ssot:generate-contracts
- name: Run Contract Tests
run: npm run test:contracts
- name: Detect SSoT-Code Divergence
if: failure()
run: |
echo "❌ SSoTとコードが乖離しています"
echo "以下のいずれかの対応が必要です:"
echo "1. SSoTを実装に合わせて更新"
echo "2. コードをSSoTに合わせて修正"
exit 1
乖離検知時のワークフロー
ケース 1: コードが正しい場合
Contract Test失敗
↓
エンジニア確認: 「実装は正しい。SSoTが古い」
↓
SSoT更新
↓
設計原則 DRに変更理由を記録
↓
Contract Test再実行 → パス ✅
ケース 2: コードが誤っている場合
Contract Test失敗
↓
エンジニア確認: 「SSoTが正しい。実装ミス」
↓
コード修正
↓
Contract Test再実行 → パス ✅
ケース 3: 両方とも正しい(機能拡張の場合)
Contract Test失敗
↓
エンジニア確認: 「機能追加したが、SSoT未更新」
↓
SSoT更新(新しいパラメータ・戻り値を追加)
↓
Contract Testも自動更新
↓
Contract Test再実行 → パス ✅
静的解析による乖離検知(補完的手法)
Contract Test に加えて、静的解析ツールで型レベルの乖離を検知する。
# SSoT Validator CLI
$ ssot-validator --check src/
✅ src/auth/token.ts - OK
❌ src/payment/process.ts - 乖離検知
SSoT定義:
processPayment(amount: number) => Promise<PaymentResult>
実装:
processPayment(amount: number, options?: PaymentOptions) => Promise<PaymentResult>
差異: 第2引数が追加されている
推奨アクション:
1. options パラメータが意図的な追加なら、SSoTを更新
2. 不要な変更なら、コードを修正
AI による整合性監視
AI エージェントが定期的に SSoT とコードの整合性を監視する。
# .github/workflows/ai-ssot-audit.yml
name: AI SSoT Audit (Weekly)
on:
schedule:
- cron: "0 9 * * FRI" # 毎週金曜日9時
jobs:
ai-audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: AI SSoT Consistency Check
run: |
# AIエージェントがコードベース全体をスキャン
ai-agent ssot:audit --report=markdown > audit-report.md
- name: Create Issue if Divergence Found
if: failure()
uses: actions/github-script@v6
with:
script: |
const fs = require('fs');
const report = fs.readFileSync('audit-report.md', 'utf8');
github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: '🔍 SSoT乖離検知レポート',
body: report,
labels: ['ssot-divergence', 'needs-review']
});
AI が生成するレポート例:
# SSoT 乖離検知レポート (2024-11-13)
## 乖離検知: 3 件
### 1. src/auth/token.ts
- **関数**: generateToken
- **乖離内容**: 第 2 引数 `options` が追加されている
- **SSoT**: `(userId: string) => Token`
- **実装**: `(userId: string, options?: TokenOptions) => Token`
- **推奨**: SSoT に `options` パラメータを追加
### 2. src/payment/process.ts
- **関数**: processPayment
- **乖離内容**: タイムアウト値が変更されている
- **SSoT**: `timeout: 5000`
- **実装**: `timeout: 10000`
- **履歴**: 2024-11-10 に HOTFIX で変更(commit: abc123)
- **推奨**: 設計原則 DR に記録して SSoT 更新
### 3. src/search/products.ts
- **関数**: searchProducts
- **乖離内容**: キャッシュ実装が追加されている
- **SSoT**: キャッシュなしと記載
- **実装**: Redis キャッシュ追加(TTL: 300 秒)
- **推奨**: パフォーマンス改善なら設計原則 DR に記録
まとめ: SSoT 整合性検証の三層構造
Layer 1: Contract Testing(自動・即時)
↓ 入出力の整合性を検証
Layer 2: 静的解析(自動・即時)
↓ 型・シグネチャの整合性を検証
Layer 3: AI監査(定期・週次)
↓ 実装詳細・パフォーマンス特性の整合性を検証
この三層により:
- ✅ コード変更時に即座に乖離を検知(Layer 1, 2)
- ✅ 緊急対応での変更も週次で検知(Layer 3)
- ✅ 乖離が長期間放置されることを防止
SSoT とコードの整合性は、人間が保つのではなく、
自動検証と AI 監査の組み合わせで保証する。
第 9 章 組織と役割:エンジニア・デザイナー・PM の再定義
AI エージェントは単なるツールではなく、
開発チームそのものの一員として組み込まれる。
| 役割 | 新しい定義 |
|---|---|
| エンジニア | コードを書く人 → 構造と保証を設計する人 |
| デザイナー | UI を作る人 → 意図を仕様化する人 |
| PM | 管理者 → 意思決定のファシリテーター |
| AI エージェント | 実装者・検証者・記録者を兼任する存在 |
組織は「役職」ではなく「責務の分布」で設計される時代に入った。
第 10 章 結論:AI 時代の開発とは「正しさを設計し続ける営み」
AI 開発は、もはや完成を目指すものではなく、
常に更新される正しさの体系である。
PoC で探索し、SSoT で確定し、
TDD で検証し、GDD で持続する。
この循環構造全体を統べる思想が「保証駆動開発(GDD)」である。
開発とは、正しさを一度作ることではなく、
正しさを継続的に設計し続けること。
Appendix AI 開発と量子モデル的思考
注意: この付録は、GDD の思想的背景を量子力学の概念で説明する試みです。
実務で GDD を適用するために量子力学の知識は不要です。
ただし、「なぜこのような開発体系が必要なのか」という本質的理解を深めたい方向けの内容です。
※ 本章は思想的補論です。本文の理解に必須ではありませんが、保証駆動開発の背景にある発想を量子力学的視点から補足します。
量子力学的比喩の意義
従来の開発は「仕様 → 実装 → テスト」という決定論的な線形プロセスであった。
しかし AI エージェント開発では、AI の生成は確率的で、テストするまで状態が不定である。
この本質的な変化を表現するために、量子力学の概念が有効である:
- 波動関数: テスト前のコードは「正しいかもしれない」重ね合わせ状態
- 観測による収束: テストという「観測」によって初めて「正しさ」が確定
- 継続的観測の必要性: 観測をやめれば再び不定状態に戻る(保証駆動の必然性)
この比喩は、単なる「確率的プロセス」という説明では捉えきれない、
「観測が現実を確定する」という AI 時代の開発パラダイムシフトを表現している。
AI 開発と量子的構造の相似
AI エージェント開発の構造は、量子力学の世界と相似している。
| 量子概念 | 開発対応 | 意味 |
|---|---|---|
| 波動関数 | PoC の多様な仮仕様 | 未観測の可能性空間 |
| 観測(コラプス) | SSoT 確定 | 真実を確定する瞬間 |
| ゆらぎ・ノイズ | AI の確率出力 | 不確定性を内包する生成 |
| 誤差訂正 | 保証駆動 | 継続的な安定化構造 |
| 測定記録 | Decision Record | 判断履歴の保持 |
| 信頼区間 | SLA / SLO | 確率的保証の境界条件 |
AI 開発とは、ノイズを排除するのではなく、
ノイズを前提に秩序を設計する営みである。
仕様の観測、出力のゆらぎ、保証の訂正——
これらは量子世界と同じく、確率的安定の中で秩序を見出す試みである。
本書で提案した「保証駆動開発(GDD)」は、この量子的な不確定性を前提とした開発体系であり、
確率的出力を制御し続けることで「動的な安定」を実現する新しいパラダイムです。
終章メッセージ
保証駆動開発(GDD)とは、
AI エージェントが生成し続ける世界において、
“正しさを構造として維持する”ための新しい開発パラダイムである。
Discussion
単に現在のAI(と自称している何か)が低性能極まりないゴミクズであることが原因であり、10年くらい後には自動的に解決している問題だと考えています。