Slide 1

Slide 1 text

設計次第で AIコードの読む量は減らせる 2026/09/30 合同会社DMM.com ミノ駆動 © DMM

Slide 2

Slide 2 text

自己紹介 ミノ駆動 ( @MinoDriven ) 合同会社DMM.com プラットフォーム開発本部 第3開発部 ● コード品質チーム ● PF-AX戦略グループ(兼務) © DMM 2

Slide 3

Slide 3 text

著書紹介 『改訂新版 良いコード/悪いコードで学ぶ設計入門』 変更容易性の高い設計を学ぶ、 初級〜中級向け入門書 初版:12刷、ITエンジニア本大賞2023大賞受賞 改訂版:3刷、ITエンジニア本大賞2026ベスト10 翻訳出版:台湾版、韓国語版 © DMM 3

Slide 4

Slide 4 text

背景 AI駆動開発が加速的に広がっています。 AIはあっという間にコードを実装するため、 人間のレビューが追いつかないといったことが起こっています。 AI駆動開発において人間がボトルネックになってきています。 このような状況下、AI製のコードを読むべきかどうかについて ネット上で物議を醸しています。 「読んで理解しないと責任取れない、デバッグできない」 「品質保証する仕組みを作って読まなくてよくする」 といった意見が見受けられます。 © DMM 4

Slide 5

Slide 5 text

これについてミノ駆動はどう考えているか © DMM

Slide 6

Slide 6 text

【ミノ駆動の主張】 コードを全く読まなくて良いというワケではない 一方で適切に設計することで 読む必要性や労力を低減できる © DMM

Slide 7

Slide 7 text

リーディングコストを下げる上で重要な設計 ● 契約による設計 ● 副作用のない関数 ● 抽象化 これらの理解を本セッションのゴールとします © DMM 7

Slide 8

Slide 8 text

まずは問題の整理 © DMM

Slide 9

Slide 9 text

AIコードを読まなければならないとされる理由 ● ● ● ● ● ● 責任所在:問題発生時の判断責任 もっともらしいがよく見ると問題のあるコード 脆弱性 動作するが非機能要件を満たしていないコード 古い書き方、チーム方針に合わないコード 期待する結果が得られるか分からないコード etc.. © DMM 9

Slide 10

Slide 10 text

AIコードを読まなければならないとされる理由 ● ● ● ● ● ● 責任所在:問題発生時の判断責任 もっともらしいがよく見ると問題のあるコード 脆弱性 動作するが非機能要件を満たしていないコード 古い書き方、チーム方針に合わないコード 期待する正しい結果が得られるか分からないコード 今回のターゲットはコレ! © DMM 10

Slide 11

Slide 11 text

正しい結果を得るための設計を皆さんはしていますか? 正しい結果を得るために AIにどう指示していますか? © DMM

Slide 12

Slide 12 text

正しい結果を得るために役立つ設計が 契約による設計 © DMM

Slide 13

Slide 13 text

契約による設計 バートランド・メイヤー氏が考案した設計手法。 コードの正確性向上に寄与します。 以下の3条件を設計します。 © DMM 条件 説明 事前条件 メソッド実行前に満たすべき条件 事後条件 メソッド実行後に満たすべき条件 不変条件 インスタンスが生存している間、 メソッド実行前後で満たすべき条件 13

Slide 14

Slide 14 text

二者間の不安 このメソッドを呼び出したら一 体どんな結果になるんだ? おかしな結果になると、こっちが 正しく動作できなくなってしま う。 メソッド 呼び出し側 © DMM 不正値を引数に渡されると正 しく動作できなくなる。 呼び出し側はちゃんと正しい 値を渡してくれるだろうか。 メソッド本体 14

Slide 15

Slide 15 text

正しい動作を保証するための条件を約束する メソッド実行前に こちらが指定した条件を満たしてくれれば 約束した条件を満たす結果を返すよ そちらが指定した条件を守れるように こちらは準備するよ メソッド 呼び出し側 © DMM 約束(契約) メソッド本体 15

Slide 16

Slide 16 text

事前条件、事後条件の設計 BMIを計算するメソッドを例に事前条件と事後条件を考えてみます double bmi(double heightMeter, double weightKg) 条件 定義 bmiメソッドの場合 事前条件 メソッド実行前に 満たすべき条件 ・身長heightMeter : 0より大きく、3以下であること ・体重weightKg : 0より大きく、700以下であること 事後条件 メソッド実行後に 満たすべき条件 戻り値が(体重kg) / (身長m * 身長m)であること © DMM 16

Slide 17

Slide 17 text

事前条件、事後条件を満たすように実装する double bmi(double heightMeter, double weightKg) { if (heightMeter <= 0 || 3 < heightMeter) { throw new IllegalArgumentException("身長は0メートルより大きく、3メートル以下でな ければなりません。"); } if (weightKg <= 0 || 700 < weightKg) { throw new IllegalArgumentException("体重は0kgより大きく、700kg以下でなければ なりません。"); } return weightKg / (heightMeter * heightMeter); } © DMM 17

Slide 18

Slide 18 text

不変条件 class Quantity { private static final int MIN = 1; private static final int MAX = 20; final int value; 不変条件とは、インスタンスが生存している間、メソッド 実行前後で常に満たすべき条件。 インスタンス変数に関与する条件。 この注文数クラスの不変条件は「注文数は1個以上20個 以下」 Quantity(final int value) { if (value < MIN || MAX < value) { throw new IllegalArgumentException("注文数は1以上20以下で指定してください"); } this.value = value; } Quantity add(final Quantity other) { return new Quantity(value + other.value); } } © DMM 18

Slide 19

Slide 19 text

契約による設計で正しい動作を保証する つまりどういうことが言えるかというと ● 事前条件と事後条件を設計することで、 メソッド実行後の正しい結果を保証できる。 ● 不変条件を設計することで、不正値を防止できる。 AIは「契約による設計」を知っています。 契約による設計を指示することで、 正しい動作が保証されたコードをAIに書かせることができます。 ところで皆さんはAIに「契約による設計」を指示していますか? © DMM 19

Slide 20

Slide 20 text

契約による設計がテストコードの基準になる 契約による設計の3条件は、ブラックボックステストのテストコードを実装 するための基準になります。 例えばbmiメソッドでは「体重は0より大きく700以下であること」という 事前条件に対して境界値テストを実装可能です。 以下の手順の作業をAIに指示することで、(ほぼ100%と言っていいほ ど)間違えようがないコードをAIに書かせることができます。 1. 契約による設計の3条件を設計する 2. 3条件に基づきテストコードを実装する 3. テストをパスするようにプロダクションコードを実装する © DMM 20

Slide 21

Slide 21 text

一方で、正しい結果の保証が困難になる構造があります © DMM

Slide 22

Slide 22 text

次のメソッドはホテルの宿泊料金のうち、 サービス料を計算して返すメソッドです。 int calculateServiceCharge(Accommodation accommodation) © DMM 22

Slide 23

Slide 23 text

しかしなんとサービス料金計算以外に、 消費税計算及び宿泊料金への加算までしています。 int calculateServiceCharge(Accommodation accommodation) { int serviceCharge = (int)(accommodation.price * 0.15); int consumptionTax = BigDecimal.valueOf(accommodation.price + serviceCharge) .multiply(new BigDecimal("0.1")) .setScale(0, RoundingMode.DOWN) 消費税を計算 .intValue(); accommodation.price += consumptionTax; return serviceCharge; } 【副作用】 参照型引数、つまり外部の状態を変更 している これが「副作用のある関数」と呼ばれるものです © DMM 23

Slide 24

Slide 24 text

副作用 副作用とは関数が引数を受け取り、戻り値を返す以外に、外部の状態を変 更することです。副作用のある関数は、実行結果の予測を困難にします。 外部の状態変更とは、例えば以下です。 ● ● ● ● インスタンス変数の変更 グローバル変数の変更 参照型引数の変更 DB、ファイル読み書きなどI/O操作 © DMM 24

Slide 25

Slide 25 text

クラスは仕様と実装に分かれます。 クラス仕様 【クラス仕様】 外部から見たクラスの振る舞い。 契約による設計で設計したものは、 実はクラス仕様です。 具体化 抽象化 クラス実装 【クラス実装】 クラス内部のソースコード。 クラス仕様を具体化したものがクラス実装。 一方で実装から仕様を見ると抽象的です。 © DMM 25

Slide 26

Slide 26 text

クラス仕様の一部が暗黙的に なってしまう! クラス仕様 具体化 抽象化 クラス実装 ところが副作用があると、 事前条件や事後条件が暗黙的になってしまう。 メソッドのシグニチャを見ただけでは何が起こ るのか分からなくなってしまいます。 前述のサービス料金の例では、 消費税計算をして加算する事後条件が暗黙的 になっていました。 つまり副作用があると、メソッドの挙動を正確 に知るためにはメソッド内部の具体コードを読 みにいかなければなりません! © DMM 26

Slide 27

Slide 27 text

副作用のない純粋関数として設計する int calculateServiceCharge(Accommodation accommodation) { return (int)(accommodation.price * 0.15); } 副作用のない純粋関数として設計しましょう。 また、DBへのI/Oなど副作用をなくすことはできません。 大事なのは副作用を隔離した上で、 可能な限り純粋関数を設計することです。 併せてimmutableなオブジェクトを設計するのも同様です。 © DMM 27

Slide 28

Slide 28 text

抽象化 © DMM

Slide 29

Slide 29 text

これはスマホの料金計算用のコードです。 通話料金をどこで計算してるか分かりますか? よく読めば分かりますが、他の計算ロジックと混ざっていて とても分かりにくいですね。 int callCharge = 0; int dataCharge = dataUsageMB * 5; if (durationMinutes <= 5) { callCharge = durationMinutes * 10; } else { callCharge = 50 + (durationMinutes - 5) * 22; } int smsCharge = smsCount * 3; if (callCharge < 0) { throw new IllegalArgumentException("通話料金は0円以上である必要があります"); } int totalCharge = dataCharge + callCharge + smsCharge; © DMM 29

Slide 30

Slide 30 text

通話料金をクラス化します class CallCharge { private static final int MIN = 0; final int value; CallCharge(int value) { if (value < MIN) { throw new IllegalArgumentException("通話料金は0円以上である必要があります"); } this.value = value; } 注目! static CallCharge create(final int durationMinutes) { ここで料金計算をしています if (durationMinutes <= 5) { return new CallCharge(durationMinutes * 10); } return new CallCharge(50 + (durationMinutes - 5) * 22); } } © DMM 30

Slide 31

Slide 31 text

CallChargeクラスを呼び出す形に書き換えます int dataCharge = dataUsageMB * 5; CallCharge callCharge = CallCharge.create(durationMinutes); int smsCharge = smsCount * 3; int totalCharge = dataCharge + callCharge.value + smsCharge; どこで通話料金を計算しているかが分かりやすくなりました。 ところでこのコードには通話料金計算の具体処理がありません。 CallChargeクラスという抽象的存在を呼び出すだけになっています。 これが「プログラムの抽象化」です。 具体データや具体処理を抽象的に扱えるようにします。 © DMM 31

Slide 32

Slide 32 text

プログラムの抽象化 抽象化 説明 制御抽象化 条件分岐、ループ処理、サブルーチンなどの方法で 制御を構造化する。構造化プログラミング。 関数(メソッド)は制御抽象化の一種。 データ抽象化 (抽象データ型) データ構造と操作方法をひとまとめにして、 型として定義すること。 クラスや構造体は抽象データ型の一種。 © DMM 32

Slide 33

Slide 33 text

適切に抽象化すると中身を気にせず使える 各プログラミング言語の標準ライブラリやフレームワークは、 抽象化の産物です。 たとえばMath.sqrtメソッドに代表される数学関数は、 内部の具体コードを誰も気にせず使っているでしょう。 仕様さえわかれば、中身を気にせず使えるのです。 ところで「中身を気にせず使える」とは一体どういうことでしょうか? © DMM 33

Slide 34

Slide 34 text

「中身を気にせず使える」とは 言い換えると 「信頼している」ということです 「仕様通りに動作するはずだ」 と信頼しているからこそ、 内部の具体コードを読みに行かずに済むのです © DMM

Slide 35

Slide 35 text

話は戻って 「期待する正しい結果が得られるかわからないAIコード」とは 言い換えると 「中身を気にしなければならない信頼できないコード」 となるワケです 信頼できるコードをAIに実装させましょう © DMM

Slide 36

Slide 36 text

信頼できるコードをAIに実装させるには ● 契約による設計に基づいてクラスの仕様を設計すること ● 副作用は隔離すること、 隔離した上で副作用のない純粋関数を作ること ● クラス仕様をテストコードでカバーすること、 すなわち契約による設計に基づいてテストコードを実装すること ミノ駆動も実践していますが、上記をAIに指示することにより 信頼できるコードをAIに実装させることができます。 そしてコードリーディングの必要性や労力を低減させています。 © DMM 36

Slide 37

Slide 37 text

ところで…… © DMM

Slide 38

Slide 38 text

本日解説した概念の登場時期 概念 おおよその登場時期 契約による設計 1986年 関数の副作用、純粋関数 1960-1970年代 制御抽象化 1950-1960年代 データ抽象化 1970年代 AI時代に登場した概念ではありません どれも大昔に提唱された概念です © DMM 38

Slide 39

Slide 39 text

どういうことかと言うと つまり © DMM

Slide 40

Slide 40 text

最近の登壇で もう何度も何度も言ってる通り © DMM

Slide 41

Slide 41 text

AI時代においても 基本しか勝たない © DMM

Slide 42

Slide 42 text

AIツールの使い方を覚えるのはほどほどにして 増幅元である人間の能力を高めることに集中しましょう © DMM

Slide 43

Slide 43 text

ご清聴ありがとうございました © DMM