AIの使い方を学ぶ前に、まずソフトウェア工学を

Cute rounded character with a speech bubble that reads '基本が、大事…' beside a panel showing a magnified ladybug diagram.

AIの使い方を学ぶより先に、ソフトウェア工学を理解しないといけないんじゃないかと思います。

AIにテストコードを書かせた場合を例に説明します。AIはテストコードを高速で書いてくれますが、テスト理論を理解していないと、バグを見逃すテストコードを書かせてしまうことがあります。

テストの話(例として)

先に、今回の話の土台になるテスト理論を簡単に説明しておきます。わかりやすい例として、テストの技法で有名な以下の2つを挙げたいと思います。

ブラックボックステストは、コードの中身を見ずに、仕様(入力→出力)だけを基準にテストする方法です。「この入力を与えたら、仕様どおりの結果が返るか」を確認します。境界値分析や同値分割といった技法はこちら側です。

ホワイトボックステストは、コードの内部構造(分岐、ループ、処理経路)を見てテストする方法です。「このif文はtrue側もfalse側も通したか」を確認します。どれだけ通せたかはカバレッジという数値で測れます。

ここで大事なのは、ホワイトボックスといっても「コードを基準にしていいのはテストの経路の選択だけ」という点です。結果が正しいかどうかの判定(期待値)は、必ず仕様から立てる必要があります。コードを見て「コードがこう動くから、これが正解」としてしまうと、バグごとテストが追認してしまうからです。

  • どこをテストするか(経路) → コードから決めてよい
  • 結果が正しいか(期待値) → 仕様から決める

この分離が今回の話の核心です。

理論を知らないと、AIをうまく使えないときがある

AIはテストコードをものすごい速さで書いてくれます。ただ、指示の出し方を間違えると「全部パスしているのにバグを見逃すテスト」を量産します。しかも数字の上では完璧に見えるので、見逃したことに気づけません。

実際にやってみたので、失敗例と成功例を並べます。

題材のコード

料金計算の関数です。仕様はこうだとします。

10,000円以上は10%オフ。会員はさらに5%オフ。

def charge(amount: int, is_member: bool) -> float:
    """料金計算"""
    if amount < 0:          # 仕様書に記載のない分岐(実装者が独自に追加)
        return 0
    fee = float(amount)
    if amount > 10000:      # ★バグ: 仕様は「以上(>=)」なのに > にしている
        fee = amount * 0.9
    if is_member:
        fee = fee * 0.95
    return fee

わざとバグを仕込んであります。仕様は「10,000円以上」なのに、実装は >(超)になっている。つまり10,000円ちょうどのとき、割引されるべきなのにされません。境界値のバグとしてはかなりありがちなやつです。

おまけに、仕様書には書かれていない負数チェックの分岐も入れてあります。実装者が気を利かせて勝手に足した、という想定です。

失敗例:テスト理論を知らずに指示を出すと

極端な例なのですが、まず、理論を意識せずにこう頼んだとします。

このコードのテストを書いて。カバレッジ100%にして。

AIはコードを読んで、こういうテストを書いてきます。

from charge import charge

def test_normal():
    # コードを読んで期待値を計算: 15000 > 10000 → *0.9 → 13500
    assert charge(15000, False) == 13500.0
def test_member():
    # 15000*0.9*0.95 = 12825
    assert charge(15000, True) == 12825.0
def test_boundary_10000():
    # コード上 10000 は「> 10000」に該当しない → 割引なし、と読める
    assert charge(10000, False) == 10000.0   # ← バグを"正解"として追認
def test_negative():
    # コードにそういう分岐があるので、それを期待値に
    assert charge(-100, False) == 0

問題は test_boundary_10000 です。AIはコードから期待値を逆算するので、「10000は > 10000 に該当しない。だから割引なしが正しい」と解釈します。バグの動きを”正解”としてテストに固めてしまっているわけです。

実行結果がこれです。

4 passed
branch coverage 100%

全テストパス、分岐カバレッジ100%。数字だけ見れば完璧です。でも境界値バグの検出はゼロ。指標がすべて緑なので、この見逃しに気づく手段がありません。個人的には、テストが落ちるより、この「完璧に見える」状態のほうがよっぽど怖いと思っています。

成功例:テスト理論を分かった上で指示を出すと

今度は、さっきの「経路はコードから、期待値は仕様から」という分離をプロンプトに落とし込みます。

以下の手順でテストコードを作成してください。

【カバレッジ設計:ソースを参照】
- ソースの全分岐(true/false)・例外パスを洗い出し、
  各経路を通すための入力条件を列挙する(分岐網羅100%目標)

【期待値設計:仕様書のみ参照】
- 各ケースの期待値は必ず仕様書から導出する
- ソースコードの出力を期待値に流用することは禁止
- 境界値分析を適用し、境界(10,000円)の直前・ちょうど・直後を必ず試す
- 仕様書に記載がなく期待値を判断できないケースは、
  期待値を書かず「要確認」として質問リストに出す

【出力】
- 各テストに「どの分岐を通すか(ソース由来)」
  「期待値の根拠(仕様書のどこか)」をコメントで明記

仕様書は以下となります。

10,000円以上は10%オフ。会員はさらに5%オフ。

このプロンプトでAIが書くテストはこうなります。

from charge import charge

# 仕様書: 「10,000円以上は10%オフ。会員はさらに5%オフ。」
def test_under_boundary():
    # 仕様: 9,999円は割引なし
    assert charge(9999, False) == 9999.0
def test_boundary_exactly_10000():
    # 仕様: 10,000円「以上」なので 10000 は10%オフ → 9000
    assert charge(10000, False) == 9000.0   # ← 期待値は仕様から
def test_over_boundary():
    assert charge(10001, False) == 10001 * 0.9
def test_member_discount():
    assert charge(20000, True) == 20000 * 0.9 * 0.95
def test_member_under_10000():
    assert charge(5000, True) == 5000 * 0.95
# 【要確認】ソースに amount < 0 の分岐があるが、仕様書に負数の扱いの記載なし。
# 期待値をでっち上げず、仕様確認事項として報告:
#   Q. 負の金額は入力され得るか? その場合の仕様は?(現実装は0を返す)

実行結果です。

FAILED test_boundary_exactly_10000 - assert 10000.0 == 9000.0
1 failed, 4 passed
branch coverage 87%

境界値10,000円ちょうどのテストが落ちました。バグ検出成功です。

面白いのはカバレッジのほうで、今度は87%と満点ではありません。未達なのは負数チェックの分岐です。ここでプロンプトの「仕様書にないケースは要確認として出せ」が効いていて、AIは負数分岐の期待値をでっち上げず、「負の金額の仕様は?」という質問として返してきました。カバレッジの未達が、そのまま仕様書の記載漏れの発見につながっています。

並べるとこうなります。

素朴なプロンプト理論に基づくプロンプト
テスト結果4 passed(全緑)1 failed(バグ検出)
カバレッジ100%87%(未達=仕様漏れの兆候)
境界値バグ追認して見逃す検出
仕様にない分岐期待値をでっち上げ要確認として報告

失敗例のほうが数字は良くて、成功例のほうが数字は悪い。でも健全なのは後者です。この逆転が、今回一番伝えたかったことです。

AIの使い方より、ソフトウェア工学

ここからが本題というか、この体験を通して思ったことです。

さっきの2つのプロンプトの差は、AIの使い方のテクニックではありません。「経路はコードから、期待値は仕様から」も、「境界のちょうどの値を試す」も、全部昔からあるテスト理論です。ホワイトボックス、ブラックボックス、境界値分析。それを知っているかどうかだけの差でした。

境界値分析を知らなければ、「10,000円ちょうどを試して」という発想自体がプロンプトに出てきません。AIは指示された観点しか網羅してくれないので、観点を出せない人がいくらプロンプトの書き方を工夫しても、この差は埋まりません。

AIの使い方講座とか、AIでのスライドの作り方みたいな情報は確かに有用で、それを学ぶことで業務の効率は驚くほど上がります。しかし、ソフトウェア開発者に大事なのはやはりソフトウェア開発の基本的な理論だと思います。「このコードのテストで何を検証すべきか」は、聞く側に理論がないと、そもそも正しい質問になりません。

考えてみれば当たり前で、AIに指示を出すのも人間に指示を出すのも同じなんですよね。後輩に「このコードのテスト書いといて」とだけ言って渡したら、やっぱり同じ失敗をするかもしれない。きちんと伝えないと毎回きちんとやってくれるとは限らないのは、AIも人間も同じです。そして、きちんと伝えるためには、指示する側が中身を分かっていないといけない。

AIはテストコードを書く速度を劇的に上げてくれました。だからこそ、「何を検証すべきか」を決める側の理論、つまりソフトウェア工学の価値は、下がるどころかむしろ上がっていると感じています。AIの時代に何を学ぶべきかと聞かれたら、私はソフトウェア工学だと答えます。やはり、いつの時代でも大事のは基本だと思います。

ご覧いただきありがとうございます! この投稿はお役に立ちましたか?

役に立った 役に立たなかった

0人がこの投稿は役に立ったと言っています。
エンジニア募集中!

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です