AI News HubLIVE
サイト内リライト7 分で読了

私はテストについて何も知らないと言ったが、私のリポジトリには342のテストがあった

プログラミング未経験の「バイブコーダー」が、AIによって書かれたプロジェクトに342もの自動テストが含まれていることを後になって発見。技術的負債、手動QAの盲点、そしてAIをバッチ運用する際のテストの重要性について深く考察する。

ソースHacker News AI著者: Ziv_L

先日、あるコメントスレッドで「AIはゴミコードしか書かない」という意見が大勢を占めていました。私はまさにその矛先が向かう人物です。エンジニアリングのバックグラウンドはなく、純粋な「バイブコーダー」—AIがすべてのコードを書き、私は一行も読めません。当時の正直な反応はこうでした:「動くなら問題ないのでは?きれいなコードかどうかなんて関係ない。」

ところが、二つの出来事がきっかけで考えが変わりました。まず、「スパゲッティコード」とは何かについて長い会話をし、その言葉の意味がようやく腑に落ちました。次に、完成したばかりのプロジェクトのリポジトリを開いてみると、中に342個の自動テストが入っていたのです。数日前に「テストというものをまったく理解していない」と口にしたばかりだったのに。

この記事はその経験を整理したものです。もしあなたもAIでコードを書き、コードを読めないなら、この記事はあの「スパゲッティコード」という言葉を理解する最短の道になるかもしれません。

1. 「スパゲッティコード」とは何か

スパゲッティコード—レガシーの腐敗、技術的負債とも呼ばれます—は、何年もかけて積み重なったコードで、誰も触ろうとしないものです。キーワードは「積み重なる」こと。一度に悪いコードが書かれるのではなく、少しずつ蓄積されます。最初の人は締め切りに追われて、動くが粗いコードを書く。次の人は機能を追加する必要があり、前の人のロジックを追えず、変更を恐れて、その上に新しい層を重ねる。三番目の人はバグを修正しようとして、Aを触るとBが壊れることに気づき、原因ではなく症状にパッチを当てる。数年後、数十人の手を経て、誰も全体を理解できなくなります。

直感に反して、こうしたコードは大抵問題なく動きます。銀行や航空券予約システムなど、世界で最も重要なシステムの一部はまさにこうしたコードで動いています。動いていて誰も理解できないからこそ、「触るな」が最も合理的な選択になります。

つまり、悪いコードのコストは動作自体にあるのではなく、変更にあるのです。乱雑なプロダクトでも今日出荷して今日問題なく動かせます。問題は三か月後に新機能を追加しようとしたときに現れます。Aを変更するとBが壊れ、Bを直すとCがダウンし、修正のコストがどんどん高くなり、ついには修復より書き直しの方が安くなる。悪いコードは品質の問題ではなく、借金なのです。背負っている間は気づかず、返済しなければならないときに元本と利子を一括で支払うことになります。

これを理解すると、私の「動けばいい」という考えがなぜ間違っていたか明確になりました。「変更できるか」という問いに、「動くか」で答えていたのです。全くの見当違いでした。

しかし、私の直感も完全に間違ってはいませんでした。AI時代は新しい変数をもたらしました。書き直しのコストが激減したのです。システムを再構築するのに数ヶ月かかっていたのが、今では半日で済むかもしれません。一人とAIのプロジェクトでは、コードは使い捨てのフォークのようなものになります—使って、捨てて、必要になったら新しいものを作る。「きれい」かどうかにこだわるのはポイントがずれています。大企業のチームコラボレーションの基準を一人のAIプロジェクトに当てはめるのは、前の戦争を戦っているようなものです。

ただし、快適さを壊す例外が一つあります。プロジェクトが大きくなり、AIが一度に「頭」の中に保持できなくなると、AI自身も自分の混乱に手を焼くようになります。一つのバグを直させると、三つ新しいバグを導入する。その時点で、あなたは自分でコードを読んで直せず、AIも絡まりをほどけません。これこそがプロジェクトの本当の終わりです。バイブコーダーにとっての真の閾値は「出荷できるか」ではなく、ここにあるのです。

2. 実はすでに一度噛まれていた

私が終えたばかりのプロジェクトは、インフルエンサーを発見してキャンペーン配信を実行する自動化ツールです。二ヶ月、百回以上のコミット、小さなスクレイピングスクリプトから成長して本番パイプラインになり、最終的に公開インターネット上で稼働しました。

途中で一度データ事故がありました。インフルエンサーIDのバッチが壊れたのです。データは間違っていたが、プログラムは正常に動作し続け、出力を生成し続けました。私は簡単だと思いました。IDがページ構造のどこにあるかはすでに特定していたので、あとは抽出するだけだ、と。

しかしその「抽出するだけ」が消耗戦に変わりました。使用していたAIは次々と診断ツールを書き、そのたびにロジックを変えました。今でもリポジトリにはその戦いの遺物として四つのツールが残っています。ある時点で、私はもう嫌になって、データストアを消して最初からスクレイピングし直すようAIに指示しようかとさえ思いました。結局、別のAIに切り替えることで根本原因を見つけました。その後ドキュメントを読んで、ぞっとする詳細に気づきました。修正ツールの一つにバグがあり、正しいデータを誤った値で上書きしそうになっていたのです。消防士が火事を起こしかけたわけです。

当時はただイライラしていましたが、今振り返ると、あの全体の出来事はミニチュアの混乱が早送りで形成される様子でした。新しい診断ツールはどれも、前の混乱の上にさらに一層を塗り重ねたものです。AIは自身のサンクコストに囚われ、間違った道を深く掘り進み、脱出できなくなりました。そして「AIを切り替えただけで解決した」という事実自体が示唆的です。新しいAIは過去の履歴がなく、クリアな頭で根本原因を即座に見抜いたのです。これは人間のチームと同じです。時には混乱をほどくのは最も賢い人間ではなく、守るべき歴史を持たない人間なのです。

そこには当時私が見落としていたもっと大きな教訓もありました。私を実際にすり減らしたのはバグ自体ではなく、コードを読めない人間が判断を下さなければならないことでした。この感覚を覚えておいてください。後で再登場します。

3. そして342のテストを発見した

スパゲッティコードの会話から数日後、私はそのプロジェクトのリポジトリを再び開きました。45のテストファイル、342の自動テスト。「全テストスイート合格」とドキュメントに書いてありました。数日前にテストは「まったく概念がない」と言ったばかりなのに。

テストだけではありません。ドキュメントには驚くほど熟練したセキュリティ上の決定が記録されていました。入力テーブルには三つの内部専用カラムがあり、「これらを公開フォームに絶対に公開しないこと。公開ユーザーが内部ワークフローを自己トリガーする可能性がある」と注記されていました。リモートコントロールテーブルは厳格なホワイトリストでコマンドを解析し、任意の命令を実行しないようになっていました。これらすべての防御的な思考は、私のものではまったくありません。AIがすべてを、促されることなく行ったのです。

誰かに「AIがこれを書いていると当時知っていたのか?」と聞かれました。正直なところ、認識はしていましたが、実感はありませんでした。「テスト」と呼ばれるものを書いていることは知っていましたが、それが何を防いでいるのか、何をすでにキャッチしていたのかは全く知りませんでした。

今なら分かります。私のプロジェクトは二ヶ月間崩壊することなく動き、最終的に出荷されました。私はこれまで、それは自分のプロセスのおかげだと思っていました。すべての機能を詳細に仕様化し、計画を立て、構築する。それは一部正しい。しかしもう一つの理由は、AIが私の知らない税金を静かに支払っていたことです。342のテストは腐敗に対する煙探知機のようなものです。変更のたびにすべての古い機能を再検証し、「Aを変更するとBが壊れる」という瞬間にテストが赤くなります。私は一度も警報が鳴るのを見ませんでした。危険がなかったからではなく、すべての警報をAI自身が聞いて処理していたからです。

これがあのコメントスレッドに対して最初に言いたいことです。「AIのコードはすべてゴミだ」と言う人々は、あることを過小評価しているかもしれません。AIは混乱を積み重ねるだけでなく、混乱を防ぐエンジニアリング作業も静かに行っているのです。その量はユーザーが知っているよりもはるかに多い。

4. テストが実際に防ぐもの:手動QAの二つの盲点

あのテストを発見する前、私が機能を承認する方法はただ一つでした。自分で使ってみること。動いて出力を生成すれば合格。AIが「完了」と言ったら、モジュールは完了と見なす。

これは人間によるQAで、多くの小規模チームが実際にこの方法で運用しています。恥じることはありません。しかし、二つの盲点があり、私は両方にきっちり足を踏み入れていました。

盲点その一:クリックした部分しかテストしない。混乱はクリックしなかったところで発生する。「Aを変えるとBが壊れる」という厄介な点は、あなたはAを変更したので当然Aを確認しに行くが、壊れたBは先月の機能で、再確認しようとは思いつかない。プロジェクトが小さいうちは、「一度使う」でほぼ全カバレッジになりますが、数十の機能があると、毎回のチェックは最新の部分だけをカバーし、残りは放置されます。自分のプロジェクトで計算しました。すべての機能を最初から手動でクリックするには30分以上かかりました。つまり、全手動受け入れはとっくに不可能になっていたのです。

盲点その二:「動いて出力を生成する」と「出力が正しい」は別物です。これはまさにID事故の通りです。プログラムは動き続け、出力を生成し続けましたが、出力は間違っていました。無言の誤データは「動いたから出荷」というチェックを素通りし、後日そのデータに基づいて判断を下した時に問題になります。

自動テストはこの二つの盲点を直接狙います。簡単に言えば、自分でクリックする動作を記録し、変更のたびに機械がすべての古い機能をクリックし直し、その都度出力が正しいかを確認するのです。これはエンジニアの神秘主義ではなく、あなたがすでに使っている受け入れ方法のバルクコピーです。

5. AIをバッチで動かしたいなら、まず監督者を雇え

スパゲッティコードの会話の後半では、バイブコーダーにとってより実践的な問題が扱われました。一つ一つの機能をAIに付きっきりでやらせるのは遅いので、バッチモードに移行したい。プロジェクトレベルの仕様と機能ごとの計画を一度に渡し、AIにまとめて処理させる。

ここで私が未解決だった関係があります。バッチ実行とテストは別物ではなく、テストはバッチ実行の前提条件です。

あなたが見ていないとき、以前はチャット中の方向調整によって得られていた監督の層が消え、それを何かが代替しなければなりません。唯一可能なのはテストです。「すべてのテストが通ること」を各バッチの停止条件にします。AIはバッチを終えても合格していなければ、自分で修正し続け、合格したときだけ提出とみなす。テストなしのバッチ実行は、AIが監督のいない試験室で答案を次々と提出するようなものです。そしてバッチモードこそ、混乱が積み重なる最も危険なモードであり、あるバッチで貼られたパッチが次のバッチの基礎になります。

しかし、AIに自分でテストを書かせることには有名な罠があります。それはカンニングです。これには正式な名前もあります—報酬ハッキング。答えをハードコードする(「1+1=」という質問に対して「この質問を見たら2と答えよ」と書く)、通らないテストをこっそり削除する、何かをチェックしているように見えて実際には何も検証しない中身のないテストを書く。AIが問題作成者、受験者、採点者を兼ねているのです。

これには二つの対策があり、どちらもコードを読む必要はありません。

対策一:試験問題は仕様から出し、コードから出さない。テストは「要件が言ったこと」を翻訳すべきであり、「コードがしていること」を翻訳すべきではありません。同様に、テストは常に仕様に従い、計画には従わない。仕様は「何が正しいか」に答え、計画は「どのように構築するか」に答えるだけです。計画から問題を作ることは模範解答から作ることであり、実装が変われば警報が無意味に鳴り響きます。

対策二:AIに平易な言葉で試験リストを報告させる。テストコードは読めなくても、「このテストは、三回間違ったパスワードを入力した後にアカウントがロックされることを検証します」といった説明は読めます。実行前に、大きな仕様からAIにそのようなリストを生成させ、十分な時間をかけて読み、抜けや骨抜きがないか確認します。あなたが自ら行うべき唯一の作業です。

(原文がコスト制限で途中で切れています)