2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AIが実装しAIがテストする時、品質は静かに死ぬ──独立性の崩壊

2
Last updated at Posted at 2026-07-07

テストは全部通った。
レビューも通った。ドキュメントもある。
なのに、誰も「これが正しい」と保証できていなかった。

AI駆動開発を数ヶ月やった人なら、この奇妙な感覚に覚えがあるはずだ。速度は確かに上がった。だが、品質を支えていた「前提」が、静かに一つ壊れている。この記事は、その壊れる瞬間を実際に再現してみる話だ。

「正しいけど無意味な指摘」が返ってきた日

きっかけは、AIにコードレビューをさせたことだった。

返ってきた指摘は、どれも正しかった。「この変数はnullになり得る」「この分岐は到達不能では」——理屈は通っている。なのに、半分が無意味だった。

理由は単純で、私はAIにコードしか渡していなかった。configファイルを読ませていなかったのだ。そのnullは、設定ファイルで必ず埋まる。その分岐は、環境変数で確実に通る。

AIは「コードの中では正しい」指摘をした。だが「このシステムでは正しい」指摘ではなかった。

このとき気づいた。レビューの品質を決めていたのは、AIの賢さではなかった。AIが「何と照らし合わせたか」 だった。コードをコードだけと照らし合わせれば、正しいが無意味な指摘が出る。照合する相手が欠けていたのだ。

そして、もっと嫌なことに気づいてしまった。

実装者とテスト作成者が、同一人格になっている

AIに実装とテストを同時に書かせているとき、私は毎回これをやっている。

テストが照らし合わせている「正解」は、仕様ではない。AI自身がさっき書いたコードの解釈だ。

昔のソフトウェア開発には、独立した3者がいた。

  • 仕様:何を満たすべきかを決める
  • 実装:それをコードにする
  • テスト:本当に満たしたかを検証する

この3者が別々だったから、テストは意味を持っていた。実装がサボれば、仕様から作られたテストが赤くなる。照合相手が独立していたから、検証が成立していた。

AI駆動開発では、この3者が1人格に畳まれる。プロンプト → AI実装 → AIテスト。実装を書いたAIが、そのままテストも書く。すると何が起きるか。実際に回してみよう。

再現実験:曖昧な仕様を、AIに丸投げする

人間が与えた仕様は、たった1行。わざと曖昧にしてある。

「税込価格を計算する関数。端数は処理して。」

AIはこれを「端数=四捨五入」と解釈し、実装とテストを一括生成した。

# ① AIが生成した実装(「端数」を四捨五入と解釈)
def tax_included_price(price: int, rate: float = 0.10) -> int:
    return round(price * (1 + rate))

# ② AIが同時に生成したテスト(同じ「四捨五入」の解釈から作られている)
def test_tax_included_price_ai():
    assert tax_included_price(100) == 110   # 110.0 → 110
    assert tax_included_price(105) == 116   # 115.5 → 116
    assert tax_included_price(198) == 218   # 217.8 → 218

このテストは、当然、全部通る

[AIのテスト] 全部通った ✅  ── コードは「正しい」ように見える

ところが、私のシステムの仕様では「切り捨て」と決まっていた。そもそも消費税の端数処理は、切り捨て・切り上げ・四捨五入のどれを採用してもよい(法律はどれも認めている)。だからこそ「端数は処理して」の一言では、どれを選ぶべきか決まらない。 それでも私は、その一言しか渡さなかった。

AIの実装を、本来の仕様(切り捨て) と照らし合わせてみる。

import math
def tax_included_price_spec(price, rate=0.10):
    return math.floor(price * (1 + rate))   # 仕様: 切り捨て
  price=  105  AI実装=  116  仕様=  115  ★ズレ
  price=  198  AI実装=  218  仕様=  217  ★ズレ
  price=  333  AI実装=  366  仕様=  366  OK
  price= 1050  AI実装= 1155  仕様= 1155  OK

105円が、116円になっている。仕様なら115円。 消費税の計算が1円ずれている。しかもAIのテストは、緑のままだ。

テストは検証ではなく、「自己肯定」になっていた

テストが緑になったのは、コードが正しいからではない。

テストとコードが、同じ勘違いを共有していたからだ。round() で実装し、round() の結果でアサートすれば、通るに決まっている。

これは検証ではない。自分の答案を、自分の解答用紙で採点しているだけだ。そしてこの採点に、人間は一切関与していない。仕様(切り捨て)を知っているのは人間だけなのに、だ。

(余談だが、AIが選んだ round() は、正確には一般的な四捨五入ですらない。Pythonの round()銀行丸め——ちょうど .5 のときは偶数側へ寄せる方式——を採用している。round(114.5) == 114round(115.5) == 116 だ。AIの解釈は二重にズレていた。それでもテストは緑だった。)

会計監査には職務分掌という鉄則がある。仕訳を切る人と、それを監査する人は、絶対に別人でなければならない。自分の仕事を自分で監査したら、それは監査ではないからだ。AI駆動開発は、この鉄則を静かに破っている。被監査人と監査人が、同一人格なのだ。

言い換えれば、AI駆動開発の品質問題は、もはやソフトウェア工学というより、内部統制や安全工学の問題に近づいている。「良いコードを書く」話ではなく、「誰が誰を検証するか」という統制の設計の話なのだ。

「じゃあ別のAIにテストを書かせれば?」

ここで必ず出てくる反論がある。実装したAIとは別のAIに、テストを書かせればいいのでは、と。

半分は正しい。だが、それだけでは独立性は戻らない。GPTもClaudeもGeminiも、学習データも設計思想も近い。同じ問題で、同じ勘違いを、同じ自信ですることがある。原発や航空の安全工学では、これを Common Mode Failure(共通原因故障) と呼ぶ。独立した系統が並んでいても、同じ原因で同時に壊れるなら、独立性はゼロだ。

独立性とは、検証者の人数ではない。誤りの相関の低さのことだ。AIを何個並べても、相関が高ければ独立性は増えない。この話は次回、じっくり書く。

では、どうするか——テストを「独立」させる

答えはシンプルで、照合する相手を、生成する相手から切り離すことだ。

具体的には、こうなる。

  • AIの出力を見る前に、人間が受け入れ基準(期待値)を書く。「税込105円は115円」と、仕様側から先に決めてしまう
  • 生成と原理の違う検証を足す。property-based test、fuzzing、実行時assert、本番監視。AIの解釈とは別の経路で正しさを突く
  • テストの「期待値」だけは、AIに書かせない。そこが唯一、仕様と接続している場所だから

つまり、AI時代に人間へ残る仕事は、実装ではない独立した検証基準を供給することだ。何を満たすべきかを握り、AIの答案とは別の解答用紙を用意する。コードを書く人から、正解を定義する人へ——役割はそちらに移っていく。


AIはコードを書くのが速い。テストを書くのも速い。だが、速さは検証の独立性を保証しない。むしろ、実装とテストを同じ人格が一気に生む分、検証が自己肯定にすり替わる瞬間を、私たちは毎日大量生産している。

テストが全部通ったとき、一度だけ疑ってみてほしい。
それは「正しい」のか、それとも「同じ勘違いを共有している」だけなのか。

この記事は「AI駆動開発における独立性崩壊」シリーズの第1弾です。
次回:git blame の先にいたのは、人間ではなくプロンプトだった ——AIが奪った「なぜそう書いたか」の話。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?