生成AIによる開発パラダイムを「演繹」から「帰納」で考える
NotebookLMの音声要約はこちら
ソフトウェア開発はシステムの複雑化と市場の変化に対応するため方法論が変化してきました。本記事では、その進化を「演繹」と「帰納」という軸で捉えてみます。「従来の開発」「TDDの導入」「生成AIの登場」、そして今後の方向性という4つのフェーズで整理するものです。
特に、生成AIが開発プロセスに与える影響と、それに伴うテスト戦略の変化について考えてみたいと思います。
(ご指摘待ってます)
従来の開発:ロジックによる品質保証
優秀な開発者が、仕様書から正しい実装を導き出すという考え方が中心にあったとして進めます。
品質は以下の点から決定されてきました。
- 開発者が仕様書から具体的な実装コードを論理的(演繹的)に導き出す
- コードの正しさは、個々の開発者の経験・スキル・論理的思考力に大きく依存。コードレビューも、レビュアーの能力に左右される。
- 「正しい実装」とは何かが形式化されておらず、開発者の解釈に委ねられていた。
最も重要なことがこの正しい実装が暗黙的であることだと思います。
実装の品質が個人の能力に強く依存しスケールが困難に、品質の判断基準が暗黙的で「動けば良い」という状態に、もしくはレビュアー依存に陥ってしまいます。
また、品質保証のためにはやむを得ないことですが、「何を達成するか(WHAT)」ではなく「どう達成するかの論理(HOW)」にメンバーのリソースの多くが割かれてしまうことも問題かもしれません。
アジャイル開発を導入することにより短いイテレーションでフィードバックを得ることできますが、根本的な解決には至りません。
2. 自動テスト/TDDの導入:テストを用いた機能的な品質保証
この暗黙的な正解に対処してどう実装の品質を保証するか/し続けるか, 手段ではなく達成すべき事柄にどう着目するかという問いに対し、自動テストとTDD(テスト駆動開発)が一つの答えとなると考えます。
これによってこれまでは「(主にレビュアーの頭の中にある)正しいロジック」でしか保証し得なかった品質が、テストという具体的な成功例によって保証できるようになるためです。
もちろん論理的に良いとされる実装をした方が保守しやすくパフォーマンスの良い実装を行えます。しかし、テストによって正しい挙動を定義できれば、それは最低限の正解として「間違っていないコードである」ことを保証できます。一つ一つ論理を追う必要がないわけです。
すると、一度正しいテストが定義できれば暗黙的な「正しい手順の実装」を知るレビュアーから手離れし、ボトルネック解消に一歩近づけるはずです。
論理を積み上げて正しい実装を行う演繹的な視点に、正しい結果群が得られているからそれは正しいという帰納的な方法を加えるハイブリッドなアプローチになると思います。
一方で、課題が多く発生します。
最も顕著なのはテストケースを定義するコストでしょうか。
実装コストに加えて自動テストを定義するとなると、ビジネス上は単純な工数&コスト増加と見做されてしまうケースが多いでしょう。
防げたバグは数値化できず、初期コストのみが明確に見え、ROIは長期的にしか現れません。
結果として、ビジネスとしては短期的な「動くソフトウェア」の納品が優先されてしまいます。
そもそも、正しくテストを定義することも簡単ではありません。
正しいテストが定義できれば明確なゴールの共有や長期的な正解を維持することが可能となり、何を実現するかに着目できます。
しかし、短期的なROIが重視されがちな現場では導入しきれないケースが多いはずです。
3. 生成AIの導入:演繹プロセスの再定義
ここで、生成AIに目を向けてみます。
生成AIは自然言語からコードを生成できます。これによって開発者は実装の詳細記述から解放され、より上位の設計や要求定義に注力できるようになりました。
一方で、AIが生成する実装はその量と複雑さゆえに人間が詳細まで理解することが困難になるでしょう。生成されたコードのすべてを人間がレビューし、その動作を完全に把握することは現実的ではありません。不可能でありませんが、LLMの性能向上を信じて突き進んだり・無敵の人戦略を取る競合と開発速度バトルが発生することを考慮する必要があります。
この状況において、TDDは新たな意味を持つと筆者は考えます。かつて高コストと見なされていた自動テスト/TDDですが、テストを先に定義できれば、AIはそれを満たす実装を生成し、開発者は実装の詳細を理解せずとも振る舞いを保証できるためです。
TDDが抱えていた「実装コストの二重化」課題も、AIによる実装自動化によって軽減されるはずです。
開発者はテスト設計に注力し、実装生成はAIが担う。このように、TDDとAIは相補的な関係が形成できます。
残課題
しかし、ここでも限界が見えます。
そもそも、AIに実装を任せている状態で人間が記述するユニットテストは該当の範囲正しく区切れるでしょうか。
効率化を求める開発現場では、ユニットテストの作成すらもAIに任せようとする誘惑が生まれるでしょう。
ここでレビューしようにも、すでに実装者は実装の詳細を把握していない状態であり、実装の一部分であるユニットテストをレビューすることは現実的ではなく、AIが書いたテストをAIが通過する、ユニットテストが形骸化する可能性が高くなってしまっているはずです。
4. BDDとテスト戦略の再構築:帰納的アプローチの改善
つまるところ、ソフトウェアはビジネスの要求に応えることが仕事です。
ビジネスレベルの振る舞いをきちんと洗い出し、具体化し、帰納的に品質が保証できれば問題ありません。
この文脈において筆者はBDD(振る舞い駆動開発)やATDD(受け入れテスト駆動開発)が合致すると考えています。ビジネス要求そのものを具体例として形式化し、さらにテストにまで使う要求からテスト仕様までが繋がったアプローチだからです。
BDDはThree AmigosによるExample Mappingという仕様の洗い出しからスタートします。
- プロダクトオーナーがビジネス視点から、開発者が技術的実現性から、テスターがエッジケースの観点から、それぞれ振る舞い例を検討する
- Example Mappingセッションを通じて具体例をどんどんと書き出していき、それをルール化してソフトウェアの振る舞いとする
- この協調的プロセスによって、個人の認知限界を超えた包括的な振る舞い例が形式化される
というものです。
このプロセスを経て生まれる具体例は、共通言語(Gherkinなど)で記述されます。
Feature: 商品購入時の消費税計算
Scenario: カート内の商品の合計金額に消費税を加算する
Given カートに1000円の商品がある
When ユーザーが注文を確定する
Then 請求金額は消費税込みで1100円になる
この具体例によるシナリオが**「ビジネスレベルの品質保証(自動受け入れテスト)」になると同時に、「AIが実装を行うための高品質な前提(指示書)」**としても機能します。もちろん、Example Mappingによって具体例からルールを抽出しているためこちらも使用すべきです。
結論:品質保証パラダイムの変遷
以上により、生成AIを中心に据えたソフトウェア開発が達成できるのではないかと筆者は考えています。
- 従来の開発: 属人的な演繹に依存し、品質保証は非形式的であった
- TDDの導入: 演繹(実装)と帰納(テスト)のハイブリッドによる形式的な品質保証を実現したが、総合的なコストが課題となった
- 生成AI時代: AIが演繹(実装)を担い、人間は帰納(ビジネスレベルの振る舞い定義)に特化する分業体制へ
重要なことは、品質保証の主軸が「演繹的な実装の正しさ」から「帰納的な振る舞いの定義」へとシフトすることです。
しかし、そのためにはこれまでより深く要求やビジネスを理解する必要があります。そのため、Example Mappingなどの手法を用いてより解像度を高める必要が出るのではないかと筆者は考えています。
みたいなことを最近ずっとずっと考えてます
誰かディスカッションしたりご飯食べながらお話ししませんか....
以下、また追加で記事にしようと思ってるのですが、関連しそうなやつです👇
参考まで
おまけ
AIが生成した「内部」をどう検証するか?
「では内部実装の保証やユニットテスト捨てるんですか?」というところに関しては、関数型ドメインモデリングの考え方が参考になるのではないかと思っています。
- 処理をワークフローとして捉え、各処理をドメインモデルの状態遷移として捉え「型」で表現する。
- 各処理の正しさを、技術的な実装詳細ではなくユビキタス言語に近い形式で表現されたドメインモデルの状態として把握する。
- 例:「注文受付」→「支払い待ち」→「支払い済み」→「発送準備中」といった、ビジネス上意味のある状態遷移として内部処理を理解する。
これであれば、ドメインのデータがあるべき状態として人間が観測できるためある程度は内部実装を把握できるはずです。
ロジックはもう把握できないので、ユビキタス言語に近いデータがAからBに映る際にちゃんと遷移してるのか、というところを気にするようになるのではないかな...と考えています。
テストピラミッド、逆転させるべきじゃない?
とか思うけど、E2E時間かかるしなぁとか思うけど、E2EとIntegrationでなんとかするしかない世界になると思ったり思わなかったり
関連する手法など雑記
アジャイル開発:継続的な価値の検証
- BDDの前身として、反復的な開発とフィードバックの重要性を確立
- 頻繁なデモとレトロスペクティブにより、振る舞い例を継続的に洗練
- AI時代においても、人間とAIの協調的な反復プロセスの基盤となる
プロトタイピング:理想状態の具体化
- 素早いフィードバックループにより、曖昧な要求を具体的な振る舞い例へと変換
- 動くプロトタイプを通じて、ステークホルダーと具体的な振る舞いについて対話できる
関数型プログラミングとCQRS:予測可能性の向上
- 冪等性を高めることによりAIが生成するコードの予測可能性が向上
- CQRS(コマンドクエリ責務分離)により、状態変更と参照を明確に分離し、各操作の意図を明確化
- 副作用の分離により、振る舞い例(BDDシナリオ)の記述が容易に
凝集性最大化とDRY原則の見直し
- AIのコンテキスト長制限内で最大の性能を引き出すため、関連コードの局所化を優先
- 重複を許容してでも、各モジュールの自己完結性を高める設計
- AIが各コンポーネントの振る舞いを正確に理解・生成
オブザーバビリティ:実行時の帰納的検証
- BDDシナリオでカバーできない複雑な相互作用を、本番環境で継続的に検証
- 分散トレーシングやメトリクスにより、AIが生成したシステムの実際の振る舞いを可視化
- 異常検知により、想定外の振る舞いを早期に発見し、新たな帰納的例として活用
ガードレール:AIの振る舞いの制約
- Lintやpre-commitフックにより、AIが生成するコードが一定の品質基準を満たすことを保証
- 型システムやスキーマ定義により、AIの出力を制約し、意図しない振る舞いを防止
- コード規約の自動適用により、人間が読みやすいコードの生成を促進
テスト実行の並列化:E2E中心の品質保証を支える基盤
- E2Eテスト中心のアプローチは実行時間の増大を招くため、コンテナ技術を活用した大規模並列実行が不可欠
- テスト環境の完全な分離(localhostを使わないなど)により、並列実行時の干渉を防ぎ、信頼性の高い検証を実現
Discussion