1.はじめに
私は 2025 年 7 月頃から仕様駆動開発を学んでおり、cc-sdd や AWS Kiro を使ったツール開発は半年ほどの経験があります。8 月に AWS の AI-DLC Unicorn Gym(AI-DLC v1)を受講し、AI-DLC v2 に興味を持ちました。
AI を開発業務に導入するとき、実務で大きな割合を占めるのはエンハンス作業です。
そこで、既存のソースコードから AI が読める仕様書を復元し、その仕様書をもとに以降の改修を AI で進める——という AIOps への移行ケースを検討しました。
その第一歩が、既存プロジェクトを AIOps に載せるためのリバースエンジニアリングです。
AWS AI-DLCはv2からリバースエンジニアリングを正式サポートしたため、
今回は AWS Kiro の仕様駆動開発と AWS AI-DLC v2 で、それぞれどこまで復元できるのかを比較したくなり、実際に試してみました。
仕様駆動開発のフレームワークとしては GitHub の Spec Kit にも関心がありますが、まだ学習できていないため今回の評価からは外しています。
2.結論(先に書きます)
2つの成果物はほとんど重なりませんでした。競合ではなく、相補関係にあります。
比較可能な仕様項目 68 件のうち、両方が記述しているのは **21 件(31%)**だけ。残り 69% はどちらか一方にしか存在しませんでした。
- Kiro SDD は「コードが何をするか」を掘ります。 入力と出力の全分岐、境界値、エラー文言を EARS 形式の受入基準に落とし、16 個の Correctness Property として形式化し、さらに各項目が既存テストでカバーされているかまで逐条で判定します。
- AI-DLC v2 は「このコードベースがどう成り立っているか」を広げます。 認証の不在、Actuator の無認証公開、Maven/Gradle 二重管理、CI/CD、依存関係の循環チェック、業務ドメインまで、コードの外側を含めて棚卸しします。
精度面でも差が出ました。Kiro 側に事実誤認は検出されなかった一方、AI-DLC 側には実コードと矛盾する記述が 2 件あり、うち 1 件は 4 文書に伝播したうえ「バグの疑い」としてエスカレーションされていました。ここが今回いちばんの発見なので、後半で詳しく書きます。
3.検証の前提
3-1. 対象プロジェクト
リバースエンジニアリングの対象は、Spring Framework のサンプルとして知られる spring-projects/spring-petclinic です。仕様を記述したドキュメントが README.md 程度しか存在しないため、リバースエンジニアリングの題材として適しています。
- spring-projects/spring-petclinic(本家)
- potofo/spring-petclinic(今回使用したフォーク)
3-2. 実行環境
| 項目 | 内容 |
|---|---|
| OS | Windows 11 Pro 64bit(WSL 未使用) |
| Java | JRE 21.0.2 LTS(spring-petclinic 用) |
| Node.js | 24.11.1(未使用) |
| Python | 3.12.8(未使用) |
| IDE | AWS Kiro v0.12.333(コミット 1.107.1) |
| Kiro SDD 成果物 |
.kiro/specs/reverse-engineered-spec/(3 文書 / 1,447 行) |
| AI-DLC v2 成果物 |
aidlc/spaces/default/codekb/spring-petclinic/(9 文書 / 1,185 行) |
| 解析対象コード |
両者とも同一コミット 818c413(git merge-base で確認) |
なぜ VS Code ではなく Kiro を使ったか
VS Code + Claude Code for VS Code では、AI-DLC v2 の aidlc config が生成するコードが AWS Bedrock にベンダーロックインされており、トリッキーな回避策なしには別プロバイダを使えませんでした。また .claude/settings.json のフック 18 個が git bash からパスを通せず、WSL 環境でないと実行できない問題もありました。そのため今回は Kiro を使用しています。
両者が同一コミットを解析していることは git merge-base で確認済みです。比較の前提は揃っています。
4.リバースエンジニアリングの手順
どちらも 3 ステップですが、駆動の仕方も、出力の形の決まり方も違います。
4-1. Kiro SDD
1. Steering を実施
2. 仕様書をリバースエンジニアリング生成
既存コードをリバースエンジニアリングし、現状の振る舞いを日本語の仕様として .kiro/specs/reverse-engineered-spec/ に起こしてください。
- requirements.md: 実装済みの機能を EARS 形式で。コードにない機能は推測で追加しない
- design.md: 実際のアーキテクチャ・データモデル・主要コンポーネント
- tasks.md: 仕様とコードの差分、未テスト箇所、リファクタ候補
不明点は推測せず質問してください。
3. スペックドリフトの是正
コードと仕様書を詳細に分析して、発生しているスペックドリフトを仕様にフィードバックしてください。
4-2. AI-DLC v2
1. AI-DLC v2.9.0 のインストール
2. ハーネス設定
aidlc config --harness kiro-ide --yes
3. 仕様書をリバースエンジニアリング生成
/aidlc --stage reverse-engineering --single
Kiro は指示文で出力の形を指定し、最後に自己再照合の工程を持ちます。AI-DLC は定型コマンド 1 本で、固定された 9 文書を一度に生成します。この手順の差が、そのまま成果物の差につながっていました。
5.両方にある仕様(共通 21 項目)
両者が独立に到達した記述です。ここは復元の信頼度が高い領域と言えます。
| # | 仕様項目 | Kiro | AI-DLC |
|---|---|---|---|
| 1 | HTTP エンドポイント全 17 件(MVC 16 + REST 1) | design.md | api-documentation.md |
| 2 | サービス層が存在せず、コントローラーがリポジトリを直接呼ぶ 2 層構成 | design.md | architecture.md |
| 3 | エンティティ継承階層(BaseEntity → NamedEntity / Person) |
design.md | code-structure.md |
| 4 |
Owner 集約:Pet/Visit は専用リポジトリを持たずカスケードで永続化 |
design.md | architecture.md |
| 5 | 7 テーブルの論理スキーマと ER 構造 | design.md | architecture.md |
| 6 |
pets の (owner_id, name) 一意制約 |
req 5.9 | architecture.md |
| 7 | Pet 名重複のアプリ層事前チェック + DB 例外フォールバック | req 5.8/5.9 | business-overview.md |
| 8 |
PetValidator が手書き Validator で @InitBinder 登録 |
design.md | architecture.md |
| 9 |
PetTypeFormatter による文字列 ↔ PetType 変換 |
req 7 | component-inventory.md |
| 10 |
VetRepository が読み取り専用(Repository マーカー継承) |
design.md | code-structure.md |
| 11 |
@Cacheable("vets") が Vet にのみ適用 |
design.md | architecture.md |
| 12 |
/vets のコンテンツネゴシエーション(XML/JSON) |
req 9.8 | api-documentation.md |
| 13 | i18n:セッション LocaleResolver + lang パラメータ + 既定英語 |
req 11 | architecture.md |
| 14 |
/oups → RuntimeException → 500 → error.html
|
req 10.2 | architecture.md |
| 15 |
@ControllerAdvice / 独自例外階層が存在しない |
design.md | code-structure.md |
| 16 |
id/*.id のバインディング除外(マスアサインメント防止) |
req 1.10 等 | code-structure.md |
| 17 | ページサイズ 5 のページネーション(Owner / Vet) | req 2.7 / 9.1 | api-documentation.md |
| 18 |
Person の firstName/lastName は非空白かつ 30 文字以内 |
req 12.1 | business-overview.md |
| 19 |
Owner.telephone は \d{10}
|
req 1.9 | business-overview.md |
| 20 |
Visit のデフォルト診察日 = 翌日 |
req 8.4 | component-inventory.md |
| 21 | Thymeleaf によるサーバーサイドレンダリング構成 | design.md | architecture.md |
共通項目の多くは「コードの骨格」にあたります。クラス構造・アノテーション・URL マッピングといった、静的に読めば必ず目に入る情報 に集中していました。
6.片方にしかない仕様
6-1. Kiro SDD のみが復元した 25 項目
いずれも実行時の振る舞いか、検証可能性に関わるものでした。
振る舞いの分岐・境界値(13項目)
| # | 仕様項目 |
|---|---|
| 1 | 検索結果 0 件 / 1 件 / 2 件以上の 3 分岐(0 件→エラー再表示、1 件→詳細へリダイレクト) |
| 2 |
page < 1 → IllegalArgumentException → HTTP エラー |
| 3 |
page が総ページ数超過 → 例外ではなく 200 で空一覧 |
| 4 |
lastName の正規化(null→空文字、前後空白を strip) |
| 5 | Owner ID 不一致時:更新せずリダイレクト、フォーム値を保持せず永続値を再取得 |
| 6 |
PetTypeFormatter.print が name == null で "<null>" を返す |
| 7 |
PetTypeFormatter.parse は大小区別の完全一致、不一致で ParseException
|
| 8 |
PetValidator の type 必須チェックは isNew() が真のときのみ発火 |
| 9 | Pet/Visit の未来日チェックが Validator ではなくコントローラーにハードコード |
| 10 |
Owner.addPet のサイレント重複防止(同一オブジェクト / 同一 ID は無視) |
| 11 |
Owner.getPet は大小無視・先頭一致・不在時 null
|
| 12 |
pets は name 昇順、visits は date 昇順(同日は挿入順) |
| 13 |
Vet.getSpecialties() は name 自然順でソートして返す |
画面表示仕様(4項目)
| # | 仕様項目 |
|---|---|
| 14 |
error.html のステータス別文言(404 / 500 / その他の 3 分岐) |
| 15 | 専門分野 0 件の獣医は vetList に "none" と表示 |
| 16 | エラー文言の具体値(Telephone must be a 10-digit number、Name must be no more than 30 characters、already exists、not found) |
| 17 | i18n フォールバック(キー欠落時は messages.properties へ)/未サポート lang 値は現在ロケール維持 |
実装の機微・技術的負債(4項目)
| # | 仕様項目 |
|---|---|
| 18 |
isDuplicatePetNameViolation が例外メッセージの文字列部分一致で重複判定している |
| 19 |
VisitController が Visit を 2 回 addVisit する(Set のため実害なし、という前提依存) |
| 20 |
PetController に initPetBinder と initOwnerBinder の 2 系統が独立して存在 |
| 21 |
DB ベンダー間のスキーマ非対称(H2 のみ vet_specialties に UNIQUE なし、PostgreSQL は LOWER(name) 式インデックス) |
検証可能性(4項目)
| # | 仕様項目 |
|---|---|
| 22 | Correctness Property 16 件(形式的性質としての記述) |
| 23 | 受入基準・Property 単位のテスト欠落リスト(Requirement 1〜12 を逐条判定) |
| 24 | 十分にテストされている項目の明示(対応不要の判定) |
| 25 |
留保事項の明示(MySQL 照合順序は未解決、PetTypeFormatter 登録経路は確定不可能) |
6-2. AI-DLC v2 のみが復元した 22 項目
いずれもコードの外側か、リポジトリ全体の資産に関わるものでした。
セキュリティ体制(4項目)
| # | 仕様項目 |
|---|---|
| 1 | 認証・認可が一切存在しない(Spring Security 不在を pom/gradle 両方で確認) |
| 2 | Actuator 全エンドポイントが無認証公開(management.endpoints.web.exposure.include=*) |
| 3 | H2 コンソールが認証なしで到達可能 |
| 4 | ハードコードされた DB 既定認証情報(docker-compose.yml / application-*.properties) |
技術スタック・ビルド・CI/CD(7項目)
| # | 仕様項目 |
|---|---|
| 5 | バージョン一覧(Spring Boot 4.1.0、Bootstrap 5.3.8、Checkstyle 12.3.1、JaCoCo 0.8.15 等) |
| 6 | Maven / Gradle デュアルビルドとバージョンドリフトのリスク |
| 7 | JaCoCo が Maven 側のみ(Gradle CI にカバレッジ可視性がない) |
| 8 | Checkstyle の有効ルールが nohttp 1 件のみ |
| 9 | GitHub Actions ワークフロー 4 件の役割 |
| 10 | SBOM(CycloneDX)生成、GraalVM ネイティブイメージ対応 |
| 11 |
schema.sql が Flyway/Liquibase ではなく起動時 DROP/CREATE 方式 |
テスト資産・依存構造(5項目)
| # | 仕様項目 |
|---|---|
| 12 | テストクラス一覧とパッケージ別の役割(約 20 クラス) |
| 13 | 使用テストフレームワーク(Testcontainers、Docker Compose テスト、JMeter 負荷計画) |
| 14 |
service テストパッケージが本番パッケージを持たない命名上の名残である点 |
| 15 | パッケージ間依存の方向チェックと循環参照なしの検証(DAG 構造) |
| 16 | 内部パッケージ依存図(owner と vet が相互非依存であること) |
業務・運用文脈(6項目)
| # | 仕様項目 |
|---|---|
| 17 | 業務ドメイン記述と、システムが答える 4 つの運用上の問い |
| 18 | 業務ワークフロー 5 本(顧客登録 → ペット登録 → 検索 → 診察記録 → 名簿参照) |
| 19 | スコープ境界の明示(会計・予約・処方箋・在庫・複数拠点は対象外) |
| 20 | 想定業務ユーザーの整理(受付/獣医スタッフ/外部システム) |
| 21 | 成功系フラッシュメッセージ 4 件(New Owner Created ほか) |
| 22 | シーケンス図 4 本、コンポーネント 26 件の責務カード、解析実行記録 |
21 番は Kiro の取りこぼしです。 成功時のフラッシュメッセージを Kiro は Visit の Your visit has been booked しか拾っておらず、Owner/Pet の 4 件を落としていました。実コードで addFlashAttribute を検索すると 8 箇所すべてが実在しており、AI-DLC 側が正しいです。
7.領域別の被覆の深さ
被覆の形は、ほぼ排他的に分かれています。
今回の検証では同じ領域で両者が競っている場面はほとんどありませんでした。
| 領域 | 優位 | 差の内容 |
|---|---|---|
| 機能の振る舞い | Kiro | Kiro は分岐・境界値・エラー文言まで。AI-DLC はエンドポイント表の「概要」欄止まり |
| アーキテクチャ構造 | AI-DLC | Kiro も層構成は書くが、AI-DLC はシーケンス図・コンポーネントカード・依存方向まで |
| DB スキーマ詳細 | Kiro | Kiro はベンダー間差異を検証。AI-DLC は「3DB で構造は同一」と誤って断定 |
| UI・画面表示 | Kiro | AI-DLC は templates/ を shallow 指定したため画面文言に到達していない |
| 技術スタック・ビルド・CI | AI-DLC | Kiro は完全に未記載(キーワード出現 0 件) |
| セキュリティ体制 | AI-DLC | Kiro は完全に未記載(「認証」「Actuator」の出現 0 件) |
| テスト網羅性の評価 | Kiro | Kiro は AC/Property 単位の欠落リスト。AI-DLC はテスト資産の一覧止まり |
| 業務ドメイン・スコープ境界 | AI-DLC | Kiro は User Story 内に断片的にあるのみ |
8.精度の検証 — 実コードで裏を取る
ここからが今回の本題です。両者の記述が食い違う箇所について、実コード・実スキーマを直接読んでどちらが正しいか判定しました。
8-1. ケース1:診察日のルール — AI-DLC の誤読が 4 文書に伝播
| 記述 | |
|---|---|
| Kiro | 「date が本日より後の日付」であれば保存、本日以前なら拒否 |
| AI-DLC | 「診察日は未来日にできない(本日以前である必要がある)」 |
真逆です。実コードを見ます。
// VisitController.java:100
if (visit.getDate() != null && !visit.getDate().isAfter(LocalDate.now())) {
result.rejectValue("date", "typeMismatch.visitDate");
}
!...isAfter(now) が拒否条件、すなわち「未来日でなければ拒否」。Kiro が正しいです。AI-DLC は否定を取りこぼしていました。
問題はこの先です。AI-DLC はこの誤読を土台に、code-quality-assessment.md の技術的負債 10 番で次のように展開していました。
フォームが事前に入力するデフォルト値そのものが、変更せずに送信すればバリデーションに失敗することになる。これは本物のバグ(バリデーションルールが意図に対して反転している)である可能性 …… これを「仕様どおりの動作」として扱う前に、プロダクト/アーキテクトの担当者に確認すべき事項である。
実際にはデフォルト値(翌日)も minVisitDate(翌日)もバリデーション(未来日必須)と完全に整合しています。存在しない不整合を組み立て、人間への確認事項としてエスカレーションしてしまっているわけです。
リバースエンジニアリング成果物をレビューなしで信頼すると、実在しないバグの調査に人的コストを払うことになります。この誤読は business-overview / architecture / api-documentation / component-inventory の 4 文書に伝播していました。
8-2. ケース2:DB スキーマの 3DB 同一性
| 記述 | |
|---|---|
| Kiro | H2 のみ vet_specialties に UNIQUE 制約がなく、MySQL/PostgreSQL には存在する非対称
|
| AI-DLC | 「3 つのデータベースプロファイルはいずれも同一の論理スキーマを共有しており、意図的に構造が同一であることが確認されている」 |
実スキーマを確認しました。
| ファイル | vet_specialties |
pets 一意制約 |
|---|---|---|
db/h2/schema.sql |
FK 2 件のみ、UNIQUE なし |
UNIQUE (owner_id, name)(VARCHAR_IGNORECASE 列) |
db/mysql/schema.sql |
UNIQUE (vet_id, specialty_id) |
UNIQUE (owner_id, name)(VARCHAR) |
db/postgres/schema.sql |
UNIQUE (vet_id, specialty_id) |
CREATE UNIQUE INDEX ... (owner_id, LOWER(name)) |
Kiro が正しいです。しかも Kiro はこれを design.md の初版では書ききれず、スペックドリフト是正フェーズで自力で発見・確定していました。
8-3. その他の精度差
| 事象 | 判定 |
|---|---|
AI-DLC のシーケンス図が PetValidator の責務に「名前の重複チェック」を含めている |
誤り。 PetValidator は name/type/birthDate の 3 種のみで、重複チェックは PetController 側 |
component-inventory.md の集計表が「13(…)— 上記で個別に列挙しているのは 10 件」と自己矛盾 |
文書内の整合性不備 |
| メッセージバンドルの数(Kiro「10 言語」 vs AI-DLC「11 個」) | どちらも正しい。 ファイルは 11 個、言語は 10 |
| Kiro 側の記述で実コードと矛盾するもの | 検出されず |
9.なぜ差が生まれたのか
差は偶然ではなく、3 つの構造的な選択から再現性をもって生じていました。
9-1. 走査対象の指定が、そのまま被覆範囲を決めた
AI-DLC の reverse-engineering-timestamp.md には shallow セクションがあり、templates/、messages/、static/、scss/ が明示的に浅い走査対象として指定されています。画面文言・i18n 挙動が AI-DLC 側にないのは、能力の差ではなく走査設計の帰結です。
逆に Kiro は pom.xml / build.gradle / .github/ / k8s/ を読んでいません。要件定義書の冒頭に「owner, vet, model, system パッケージおよび Thymeleaf テンプレート、メッセージバンドル、DB スキーマを読み込んだ」と明記されており、アプリケーションコードに範囲を絞ったことが宣言されています。
どちらが優れているかではなく、リバースエンジニアリングの出力は走査スコープの設計で決まるということです。同じ AI を使っても、何を読ませるかが成果物の輪郭を決めます。
9-2. 出力形式が、記述の解像度を強制した
Kiro は EARS 形式(WHEN … THE … SHALL … / IF … THEN …)を採用しています。この形式は条件と結果を明示しないと文が書けません。結果として「0 件のとき」「1 件のとき」「2 件以上のとき」を分けて書かざるを得ず、境界値が自然に洗い出されます。
AI-DLC は散文と一覧表で書きます。読み手が全体像を掴む速度は圧倒的に速い一方、「概要」欄に一言でまとめてよい形式は、分岐の取りこぼしを許してしまいます。
9-3. 自己検証パスの有無が、精度差に直結した
| Kiro | AI-DLC | |
|---|---|---|
| 検証フェーズ | requirements → design → tasks の 3 段で後段が前段を再照合 + スペックドリフト是正 |
--single 単発スキャン。再照合なし |
| 成果 | 記載漏れ 2 件を自力で検出・修正 | 誤読 1 件が 4 文書に伝播したまま残存 |
| 不確実性の扱い | 「留保事項」として未解決のまま残す | 断定形で記述 |
Kiro の tasks.md には「確定できない(MySQL サーバーのデフォルト照合順序に依存する)」「確定不可能な理由: 本アプリのソースコードの調査範囲を超える」という記述が残っていました。これは単なる慎重さではなく、誤りの混入を構造的に防ぐ仕組みとして働いています。AI-DLC 側の 2 件の誤りは、いずれも「断定を避ける経路がなかった」ことの帰結でした。
10.コストの比較
| Kiro SDD | AI-DLC v2 | |
|---|---|---|
| クレジット消費 | 約 138 | 約 82.7 |
| 成果物 | 3 文書 / 1,447 行 | 9 文書 / 1,185 行 |
| ユーザーへの質問回数 | 6 回(HITL あり) | 実質 0 回(自動実行) |
| 実行の完了状態 | 完了 |
エンジン側は未完了のまま(aidlc engine log link が「アクティブなワークフローがない」で失敗) |
Kiro は AI-DLC の約 1.7 倍のクレジットを消費しました。ただし出力の性質が異なるため、単純な効率比較は成立しません。Kiro の消費は「同じコードを 3 回異なる観点で読み直す」ことに使われており、それが前章の精度差を生んでいます。
一方 AI-DLC は人手の介入ほぼゼロで 9 文書を生成しており、初回把握のスループットでは明確に優位でした。
11.実務ではどう使い分けるか
| 目的 | 適する成果物 | 理由 |
|---|---|---|
| 未知のコードベースへの初日のオンボーディング | AI-DLC | 業務ドメイン・技術スタック・構造・CI まで一気に俯瞰できる |
| 移植・リプレース時の振る舞い仕様の確定 | Kiro | 分岐・境界値・エラー文言まで確定しないと同じ挙動を再現できない |
| テスト追加の優先順位付け | Kiro | AC/Property 単位の欠落リストがそのままバックログになる |
| 本番昇格判断のリスク棚卸し | AI-DLC | 認証不在・Actuator 公開・既定認証情報がゲート項目として整理済み |
| 技術的負債の把握 | 両方 | Kiro は設計逸脱起点(8 項目)、AI-DLC は運用・ビルド起点(10 項目)で重複しない |
| 回帰テストの安全網構築 | Kiro | Correctness Property 16 件はプロパティベーステストに直接落とせる |
両方を持つのがいちばん強いというのが率直な結論です。重なりが 31% しかないため、片方を捨てると残り 69% のうち相当部分を失います。
冒頭に書いた AIOps への移行という文脈でいえば、運用としては次の二段構えが現実的だと考えています。
- AI-DLC で全体像と運用リスクを押さえる(初回・リポジトリ全体)
- 変更対象となるモジュールに絞って Kiro で振る舞い仕様を固める(エンハンス着手時)
12.この検証の限界
最後に、この記事の数字をそのまま一般化しないための注意点を書いておきます。
- 項目数 68 件の分類は筆者による整理であり、絶対的な尺度ではありません。粒度の取り方次第で数は変動します。ただし「共通が 3 割程度、残りは排他的」という傾向は粒度を変えても崩れませんでした。
- 精度検証は、両者の記述が食い違う箇所と主要な主張に限定しています。全 68 項目を悉皆検証してはいません。したがって「Kiro に誤りがない」ではなく「検証した範囲で Kiro の誤りは検出されなかった」が正確な表現です。
- 1 リポジトリ 1 回の実行の比較です。spring-petclinic は小規模かつ教科書的な構成であり、大規模・非定型なコードベースで同じ傾向が出るとは限りません。
- クレジット消費は画面表示からの報告値で、工程別の内訳は取得できていません。
- AI-DLC 側はエンジン上の完了記録が残っていない状態での成果物です。正規完了した実行では出力が変わる可能性があります。
13.おわりに
「どちらのフレームワークが優れているか」を知りたくて始めた検証でしたが、出てきた答えは 「そもそも見ているものが違う」 でした。
同時に、リバースエンジニアリング成果物は人間のレビューを前提に置かないと危険だということも分かりました。ケース1 のように、AI が自信を持って「バグの疑いがある」と書いてくる可能性があります。今回は実コードを読んで初めて誤読だと確定できました。
AIOps への移行を考えている方の参考になれば幸いです。Spec Kit も学習したら比較に加えたいと思います。
検証に使ったリポジトリ:potofo/spring-petclinic(解析対象コミット 818c413)
