Upgrade to Pro — share decks privately, control downloads, hide ads and more …

設計次第でAIコードの読む量は減らせる / designing-for-code-reading

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest. →
Avatar for MinoDriven MinoDriven
September 30, 2026

設計次第でAIコードの読む量は減らせる / designing-for-code-reading

このイベントの登壇資料です。

今こそ聞きたいソフトウェア設計 #2 設計次第で、AIコードの読む量は減らせる
https://findy.connpass.com/event/405803/

Avatar for MinoDriven

MinoDriven

September 30, 2026

More Decks by MinoDriven

Other Decks in Programming

Transcript

  1. AIコードを読まなければならないとされる理由 • • • • • • 責任所在:問題発生時の判断責任 もっともらしいがよく見ると問題のあるコード 脆弱性

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

    動作するが非機能要件を満たしていないコード 古い書き方、チーム方針に合わないコード 期待する正しい結果が得られるか分からないコード 今回のターゲットはコレ! © DMM 10
  3. 契約による設計 バートランド・メイヤー氏が考案した設計手法。 コードの正確性向上に寄与します。 以下の3条件を設計します。 © DMM 条件 説明 事前条件 メソッド実行前に満たすべき条件

    事後条件 メソッド実行後に満たすべき条件 不変条件 インスタンスが生存している間、 メソッド実行前後で満たすべき条件 13
  4. 二者間の不安 このメソッドを呼び出したら一 体どんな結果になるんだ? おかしな結果になると、こっちが 正しく動作できなくなってしま う。 メソッド 呼び出し側 © DMM

    不正値を引数に渡されると正 しく動作できなくなる。 呼び出し側はちゃんと正しい 値を渡してくれるだろうか。 メソッド本体 14
  5. 事前条件、事後条件の設計 BMIを計算するメソッドを例に事前条件と事後条件を考えてみます double bmi(double heightMeter, double weightKg) 条件 定義 bmiメソッドの場合

    事前条件 メソッド実行前に 満たすべき条件 ・身長heightMeter : 0より大きく、3以下であること ・体重weightKg : 0より大きく、700以下であること 事後条件 メソッド実行後に 満たすべき条件 戻り値が(体重kg) / (身長m * 身長m)であること © DMM 16
  6. 事前条件、事後条件を満たすように実装する 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
  7. 不変条件 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
  8. しかしなんとサービス料金計算以外に、 消費税計算及び宿泊料金への加算までしています。 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
  9. クラス仕様の一部が暗黙的に なってしまう! クラス仕様 具体化 抽象化 クラス実装 ところが副作用があると、 事前条件や事後条件が暗黙的になってしまう。 メソッドのシグニチャを見ただけでは何が起こ るのか分からなくなってしまいます。

    前述のサービス料金の例では、 消費税計算をして加算する事後条件が暗黙的 になっていました。 つまり副作用があると、メソッドの挙動を正確 に知るためにはメソッド内部の具体コードを読 みにいかなければなりません! © DMM 26
  10. 副作用のない純粋関数として設計する int calculateServiceCharge(Accommodation accommodation) { return (int)(accommodation.price * 0.15); }

    副作用のない純粋関数として設計しましょう。 また、DBへのI/Oなど副作用をなくすことはできません。 大事なのは副作用を隔離した上で、 可能な限り純粋関数を設計することです。 併せてimmutableなオブジェクトを設計するのも同様です。 © DMM 27
  11. これはスマホの料金計算用のコードです。 通話料金をどこで計算してるか分かりますか? よく読めば分かりますが、他の計算ロジックと混ざっていて とても分かりにくいですね。 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
  12. 通話料金をクラス化します 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
  13. CallChargeクラスを呼び出す形に書き換えます int dataCharge = dataUsageMB * 5; CallCharge callCharge =

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