Max Nardit
← 記事
11 mindev

バイブコーディングとエージェンティック・エンジニアリングの違い: セッションが終わった後もコードを検査し続けるもの

Simon Willison は、コードをレビューし、それを説明できる人がいるかどうかで線を引きます。ただ、そのレビューは一度きりで、チャットが閉じた後もコードは変わり続けます。エージェンティック・エンジニアリングが名前を言い換えただけのものでないなら、違いは、誰も見ていないときに動き、しかも意図した理由で失敗するのを確かめたチェックにあります。

目次

バイブコーディングの定義として一番すっきりしているのは、Simon Willison のものだと思います。「building software with an LLM without reviewing the code it writes」。どこで終わるかも彼は書いています。コードを「reviewed it, tested it thoroughly and made sure you could explain how it works to someone else」したのなら、それはソフトウェア開発であり、そこから彼自身のルールが出てきます。

I won't commit any code to my repository if I couldn't explain
exactly what it does to somebody else.

良いルールです。ただ、どこに線を引いているかを見てください。基準のどの部分も、コミット時点の人についての事実です。レビューした、テストした、説明できる。その瞬間は本物ですが、あくまで一つの瞬間です。コードはその後も変わり続け、コーディングエージェントを使っていると、後の変更のほとんどは最初のセッションを見ていないセッションから来ます。

Simon Willison のバイブコーディングの線は、コミット時点の人の話

Willison の実践はコミットで終わりません。彼のエージェンティック・エンジニアリングのガイドには「First run the tests」という章があり、「Automated tests are no longer optional when working with coding agents」と書かれています。テストファーストの作業は、プロジェクトの成長とともに回帰テストのスイートを積み上げることだとも説明しています。なので、以下は彼への訂正ではありません。彼の実践の中にあるものを定義の中に移す話です。「エンジニアリング」という言葉の意味が決まるのは定義のほうだからです。

自分ならこう分けます。Willison の言うバイブコーディングは、そのままで構いません。レビューされていないコードのことです。人が変更を理解したかどうかは一つの問いで、エージェントがそれを書いて実行したかどうかは別の問いです。コードが出荷された後に効いてくるのは三つ目の問いです。誰も見ていないとき、リポジトリの中の何がこの変更をまだ検査しているのか。実行する仕組みのあるテスト、失敗すれば先に進ませないビルドステップ、検証ツール付きのロックファイル。どれも三つの部品を持っています。保存されたアサーション、それを動かすトリガー、そして失敗したときに止める仕組みです。レビューの記録は一つ目しか持たないので、証拠ではあってもチェックではありません。エージェンティック・エンジニアリングが、同じ作業を別の名前で呼んでいるだけではない、別のものであるなら、違いはこの三つ目の問いに出ます。

CI パイプラインを運用したことがある人にとって、どれも新しい話ではありません。変わったのは、変更と変更のあいだで誰が文脈を持ち運ぶかです。変更をレビューした人は、コードがなぜその形なのかを知っていて、後になっても覚えていました。エージェントのセッションがそれを知っているのはウィンドウが閉じるまでで、次のセッションはそれを知らずに始まります。

隣り合う話をプロンプトは不変条件ではないで書きました。モデルが読むだけのルールは他の事情と比べて判断され、ハーネスが実行するルールは実際に縛る、という主張です。あちらはエージェントが何をしてよいかのルールの話でした。こちらはエージェントが残すコードの話で、前の記事が問わなかったことを二つ問います。セッションの後もチェックが残っているか、そして誰かがそれが失敗するのを見たことがあるか。

このサイトのデザインシステム同期: チェックの裏付けがない正しい CSS 修正

手元の事例は小さく、自分のものです。このサイトのコンポーネントとトークンは別のデザインシステムのソースにあり、サイトは同期スクリプトが持ってくるコピーを抱えています。10 月 13 日、あるコミットが、別々のモデルレビュー 2 件の指摘をサイトのレイアウトに反映しました。そのうち一つの修正は、コピーされた CSS に直接入りました。1 カラムのルールが、上にある 2 カラムのルールに詳細度で負けていて、狭い画面では一部の要素に付くはずのない区切り線が残っていたのです。

修正は正しく、一文で説明できるほど小さなものでした。リポジトリが無検証だったわけでもありません。同じコミットで、サイトがもともと実行しているレイアウトチェックにも手を入れています。欠けていたのは特定のアサーション一つでした。コピーされたファイルは同期を通してしか変わらない、ということです。それを検査するものは何もありませんでした。修正はコピーの中にだけあってソースには届かず、ソースから次に同期すればファイルは置き換えられ、修正は何の知らせもなく消えていたはずです。

6 日後、チェックが二つ入りました。同期はコピーした各ファイルのハッシュをロックファイルに書き、ビルドは最初に検証ツールを走らせます。ハッシュと合わなくなったコピーはビルドを止めます。ローカルでも、ホスティング側でもです。さらに同期自体が、取り込もうとするバンドルがサイトにすでにあるルールを落とす場合はそれを拒否し、そのルールを一覧にします。コミットには、二つ目のチェックを 10 月 13 日のバンドルに対して走らせ、失われたルールを名指ししたことが記録されています。

ロックのほうは、この記事を書くまで失敗するのを見たことがありませんでした。そこで、コピーされた CSS にコメントを 1 行追加して検証ツールを走らせました。exit 1 で拒否し、そのファイルが同期の外で変更されたと名指ししました。ファイルを戻すと、また通りました。この実行の前、ロックは信じているだけのチェックでした。

どちらのチェックも、聞こえるより範囲は狭いです。ロックはコピーのバイト単位の変更をすべて捕まえますが、ソースが正しいかどうかについては何も言いません。同期ガードは消えるルールを探します。セレクタはそのままで、ルールの中の宣言を変えた場合、ガードは通してしまい、気づくのは次のビルドでのロックだけです。こうした限界は、把握している限り問題ありません。問題なのは、誰も限界を書き残していないチェックです。そうなると、グリーンのビルドが実際に証明している以上のものに見えてしまいます。

AI エージェントが同じセッションで書いたテストは、コードをコード自身と照らし合わせる

すぐに出てくる反論は、エージェントはすでに検証している、というものです。Claude Code の「Common workflows」ページは、「run the new tests and fix any failures」と指示し、リファクタリングは「run tests for the refactored code」で確認し、生成された PR は提出前にレビューするよう勧めています。こうした手順はセッションの中で動きます。そこで生まれたテストはその後もリポジトリに残り、何もないよりはましです。それが検証と呼べるかどうかは、セッションがめったに見せないことにかかっています。そのテストが失敗しうるかどうかです。

コードを書いたモデルが同じ文脈で書いたテストは、コードがすべきことより、コードがしていることを検査しがちです。自分の読みでは、失敗のしかたが相関しています。コードが要件を読み違えていれば、テストも同じ読み違いから書かれていて、コードに同意します。Willison の red/green の章はこの問題のより狭い形を扱っていて、警告は短いものです。

If you skip that step you risk building a test that passes already

「It's important to confirm that the tests fail before implementing the code to make them pass.」レッドの実行は、テストがまだ実装されていない振る舞いを確かに検査していることを示します。要件の読み違いは直しませんし、意図した理由でテストが失敗したときにしか意味を持ちません。import エラーで失敗したテストは、機能について何も証明していません。red/green が与えるのは最低限です。正しい理由でテストが失敗するのを見るまでは、通った実行が言っているのは、コードとテストが一致しているということだけです。

チェックが壊れていたケースは、テスト以外のもので経験しました。最初に作った Claude Code のフックのガードは、テスト用マシンにないパーサーを呼び出し、そのエラーを stderr に出して exit 0 で終わっていました。止めるはずのコマンドはそのまま実行され、Claude Code は正常終了のステータスを記録しました。それがチェックになったのは、strict mode と失敗時の拒否を入れてからで、パーサーをわざと壊し、呼び出しが拒否されるのを確かめました。

指示ファイルにも同じパターンがあります。AGENTS.md の行を試した 90 回の実行では、テストコマンドを書いたファイルが、あるモデルのテスト実行を 5 回中 0 回から 5 回中 5 回に変え、バイラルになった「without breaking anything」を加えても、それ以上の目立った効果はありませんでした。ベースラインとの差は一文だけではなかったので、コマンドの行の手柄とするのは推論ですし、UI の実行の一部は UI について何も言わない Python のスイートを走らせていました。それでも、実在するチェックを指した行のほうが、検証を願うだけの行より多くのことをしました。

エージェンティック・エンジニアリングはラベルであり、ラベルは規律を運ばない

「エージェンティック・エンジニアリング」は、エージェントを使ったコーディングのまともな呼び名として広まっています。Willison は 2 月に、この言葉がとくに Andrej Karpathy と Addy Osmani から出てきていると書きました。彼自身の定義はあえて平凡です。「the practice of developing software with the assistance of coding agents」。バイブコーディングには元の意味、つまり「unreviewed, prototype-quality LLM-generated code」を指す言葉のままでいてほしいとしていて、両方の言葉を残すことには自分も賛成です。

気をつけたいのは、二つ目の言葉が約束しているものです。ワークフローの名前を変えても、リポジトリの中身は変わりません。チームが自分たちの仕事をエージェンティック・エンジニアリングと呼びながら、バイブコーダーと同じものを出荷することはできます。1 セッション分のコード、エージェントが書いて通した 1 セッション分のテスト、そしてセッションが終わった後に動くものは何もない。逆も成り立ちます。誰かが失敗を確かめたテストと、それを走らせる CI ジョブを持つ週末のスクリプトは、作者がその午後を何と呼ぼうとエンジニアリングです。

バイブコーディングで構わないとき、そしてコードがチェックより長生きしたとき

バイブコーディングで構わないのは、寿命がセッションとともに終わるコードです。使い捨てのスクリプトや、捨てるつもりのプロトタイプ。Willison の言葉では「Projects should be low stakes」で、リスクの低い使い捨てのコードについて自分が付け加えることはありません。

ただ、寿命だけが物差しではなく、Willison 自身の基準である、何がかかっているか (stakes) のほうが良い物差しです。一度だけ走らせるデータのクリーンアップでも長く残る損害を出しえますし、長生きするおもちゃが無害なままのこともあります。自分が気にするのは組み合わせです。再利用されるコード、後のセッションで変更されるコード、大事なものを任されるコード。セッションの境界が重要なのは、コードがなぜこの形なのかという文脈がそこで失われるからです。

うまくいかないのは、使い捨てで終わらなかったコードに使い捨ての作法を当てはめたときです。いつの間にかタイマーで動いているスクリプト、手早く直したコピーのファイルが、実は全員が出荷しているコピーだった、という場合です。その瞬間、コード自体は何も変わりません。変わるのはどれだけ長く生きるかと、何がそれに依存するかで、それを書いたセッションはどちらも見ることがありません。

バイブコーディングを続けながら安全にやりたい開発者に一つだけ助言するなら、こうです。コードがそれを書いたセッションより長生きし始めたら気づき、その時点で、自分がいなくても動くチェックを一つ足すこと。レビューでは足りませんし、プロンプトの一文でも足りません。テスト、ビルドステップ、ロックのように、次のセッションが経緯を知っていてもいなくても突き当たるものでなければなりません。それがカバーしない範囲を書き残すこと。そして一度だけ、チェックが捕まえるはずのやり方でコードをわざと壊し、失敗するのを見ること。失敗しないなら、まだチェックはありません。あるのは、すべて問題ないと言うログの 1 行だけです。

01 / ディスカッション

コメント欄はありません。議論は X で行っています。

Max Nardit@mnardit

02 / ほかの記事

  1. Claude Code の worktree: claude --worktree が分離するものと、ポート、データベース、インストールのように分離しないもの

    Claude Code 2.1.316 で検証しました。2 つの worktree が同じポートで衝突し、Docker Compose はそれぞれに別の Postgres ボリュームを計画し、スクリプト実行はロックされたチェックアウトと不要なブランチを残し、デフォルトのベースは未プッシュのコミットを含みませんでした。

  2. Claude Ultra が Max 20x に代わる: 認証済みアカウントは 5 時間制限なし、Anthropic によれば週の上限は最初から Pro の 10x

    旧名の 20x が表していたのは 5 時間枠で、2 つあるメーターのうち緩いほうでした。Anthropic の説明では、週の上限は導入された日から Pro の 10x です。Ultra の料金表示は週の数字を前面に出し、5 時間の数字をなくしました。人によっては条件が良くなり、20x を週の量と読んでいた人にとっては、読み違いだったことになります。

  3. コーディングエージェントへのプロンプトインジェクション事例:テキストはどこから入り、何がそれを止めたのか(止めたものがあったとすれば)

    公開された 9 件の事例を、攻撃者の言葉が入ってきた入口ごとに分類しました。ほとんどの事例で、攻撃の最中に持ちこたえたものはありません。その後の修正はすべて同じ種類でした。決定論的なチェックを向け直すか、機能を取り除くかです。失敗したのがモデルの判断だった場合、向け直す対象はありませんでした。