2025年はエージェンティック・コーディング(agentic coding)が台頭した年でした(どうやら「バイブコーディング(vibe coding)」という言葉はもう廃れつつあるようです)。AIアシスタントや自律型エージェントワークフローの普及により、かつてないペースで新機能が次々と世に送り出されています。自社のコードベースの何十パーセントがAIによって完全に書かれているかを企業が誇らしげに語るのも、今や日常茶飯事です。
これが良いことか悪いことかはまだ分かりませんが(私は個人的には良いことだと思っています)、このコーディング速度の向上には代償も伴います。生成される膨大な量のコードをレビューするのは骨が折れる作業であり、コードレビューは急速に開発のボトルネックになりつつあります。一部のチームやオープンソースプロジェクトの中には、AI生成プルリクエスト(PR)の受け入れを全面的に拒否するという強硬手段に出たところさえあります。
AIを禁止することで一時的に一息つくことはできますが、長期的には良い選択肢だとは思いません。私のお気に入りの架空の種族が言うように、「抵抗は無意味だ(Resistance is futile)」というわけです。この新たな次元の生産性を生き抜くには、機械のほうが得意な仕事を人間がやるのをやめる必要があります。「AIにはAIで対抗する(Fight AI with AI)」のです。しかし、AIだけではありません。昔ながらの決定論的(deterministic)なツール群も絶大な威力を発揮します。リンター(linter)で検出できる問題なら、人間である私がわざわざ目を凝らす必要はありません。フォーマッター(formatter)が自動修正してくれるなら、なおさら気にする必要はないのです。
私の「異論覚悟の持論(unpopular take)」をお話ししましょう。私は、コードを書いたのが人間かエージェントかなど気にしていません。オープンソースにおいて、外部からのコントリビューションは本質的に「ゼロトラスト(zero-trust)」です。百戦錬磨のFAANGエンジニアが書いたコードであれ、スリランカの高校生が書いたコードであれ、評価基準に違いはないはずです。それなら、AIが書いたかどうかをなぜ気にする必要があるのでしょうか?
理論上は人間が作るPRのほうがコンパクトなはずですが、この業界で20年ほど働いてきた経験上、人間が作った巨大なPRも嫌というほど見てきました。巨大PRや雑なPRへの対応は、今に始まった新しい問題ではないと断言できます。
私はコードをありのまま(額面通り)に評価します。ちゃんと動くか?安全か?既知の問題を解決しているか?ロードマップの方向性と一致しているか?チームの基準に準拠しているか?
これが、本日の記事で、外部からのコントリビューションを扱う時だけでなく、私自身のAI生成コードを扱う時にも、私がコードレビューにどのようにアプローチしているかについてお話ししたい理由です。というのも、実際には「AIとコーディングする」ということは、「AIを常にコードレビューし続ける」ことと同義だからです。
私が実際に気にしていること#
最近コードをレビューする時、私はますますハイレベル(高所大所)な視点で見るようになっています。ある意味、手動で書くコードが少なくなるほど、コードの細かな個別要素には気を配らなくなります。私が何らかの形でリーダーシップを担ったすべてのチームで、常に言ってきたことがあります。それは「コードは使い捨てである」ということです。この言葉は、今ほど真実味を帯びている時代はありません。もう一度繰り返します。コードは使い捨てです。使い捨てではないもの——それこそが、コードを開発する過程で獲得した「システムに関する知識(system knowledge)」です。この知識こそが、ある実装から別の実装へと確実に引き継がれ、たとえばAPIをv1からv2へと移行する際にも活きてくる本質的な価値なのです。
2回目の開発がより簡単になるのは、多くのことを発見し、多くの曖昧さを減らすという試行錯誤や生みの苦しみ(growing pains)をすでに経験しているからです。何がうまくいき、何がうまくいかなかったかを学びました。何が過剰設計(overengineered)で、何が設計不足(underengineered)だったのか。知識を集め、イテレーションを回し、進化させること——これこそがソフトウェアエンジニアリングの本質です。そしてこの種の知識こそが、AI時代を生き残るものです。コード自体は、単なる「実装の詳細」に過ぎません。
この哲学を踏まえ、私がコードレビュー時に重視しているポイントをいくつか挙げてみましょう(すべてを網羅しているわけではありませんが、主要なものです)。
アーキテクチャとシステム設計#
AIモデルは全体像(big picture)を捉えるのが苦手で、安易な近道(ショートカット)を選びがちです。私のレビューでは、ハードコードされた値や設定値、問題領域の過度な単純化(AIはコーディングの要求をプロトタイプやデモと勘違いしがちです)、そして逆説的ですが「過剰設計(over-engineering)」といった兆候を見逃さないようにしています。またAIモデルには、「本番レディ(production-ready)=複雑であること」と思い込んでしまう厄介な性質もあります。要するに、バランス感覚やプラグマティズム(実用主義)を保つのが苦手なのです。これらは人間が経験を通じて培うものであり、言葉でプロンプトに落とし込むのが難しい領域でもあります。
パブリックAPIとモジュール#
私たちが構築しているもののエルゴノミクス(使いやすさ・人間工学)は極めて重要です。パブリックAPIは、それを利用する一般的な開発者にとって「直感的で心地よい(feel right)」ものである必要があります。優れた設計のインターフェースは直感的で、誤用しにくく、泥臭い内部実装をコードベースの他の部分からきれいに隠蔽してくれます。私はインターフェースが堅牢で適切にスコープ設定されているかを確認し、APIの露出面(surface area)を可能な限り最小限に抑えることを目指します。APIが使いにくい場合、基礎となるコードがどれほどエレガントであるかは問題になりません。コードは使いやすく、十分にドキュメント化されていますか?パブリックAPIの出来が良いかどうかを見極める優れた指標の一つが、テストコードの質です。設計の悪いAPIは、本質的にテストを書くのが難しいからです。
アルゴリズムとパターン#
LLMは放っておくと、問題を解決するために最もナイーブで力任せ(ブルートフォース)な方法を選びがちです。本来ならバルクインサート(一括挿入)戦略をとるべき場面で、エージェントが二重ループを回して数行ごとにコミットしながら大規模なデータ移行を行おうとするのは典型例です。あるいはより根本的なレベルでは、マップや辞書(dictionary)を使うべき場面でリストを使ってしまうようなケースです。データ構造とアルゴリズムが問題領域に実際に適合しているかを検証することで、深刻なパフォーマンス低下を防ぐことができます。目標は、単にテストをパスするコードではなく、「スケールするコード」です。とはいえ、「早すぎる最適化(premature optimization)」も依然として禁物です。扱うデータセットが小さく限定的であれば、多少実行速度が落ちてもシンプルで圧倒的に読みやすいアプローチのほうが、大抵の場合は優れています。
依存関係#
新しいパッケージを追加するたびに、外部リスク、潜在的なセキュリティ脆弱性、そしてメンテナンスのオーバーヘッドが生じます。アプリケーションを小さく保つことは、攻撃対象領域(アタックサーフェス)を減らすことにつながります。GenAI SDKや主要なWebフレームワークのような中核ツールは素早くパスしますが、それ以外の外部パッケージは厳しく精査します。わずかな依存関係を追加するくらいなら、少しのコードコピー(または再実装)のほうがマシです(A little copying is better than a little dependency)。コードの生成や保守が容易になればなるほど、「コードの再利用」に固執する必要性は薄れます。特に再利用のために新たな攻撃経路をコードベースに持ち込んでしまうくらいなら、なおさらです。
アンチパターンと品質の問題#
エラーの握りつぶし(無視やもみ消し)、意図しない副作用、グローバルな状態、ミュータブル(可変)な状態、リソースリーク、未使用の関数や変数など、挙げればきりがありません。各言語特有のイディオム(慣用表現)も大切です。私はこれらを非常に重視していますが、同時にこれらはgolangci-lint(Go)やruff(Python)のような静的解析ツール(リンター)を活用して自動化するのが最も容易な領域でもあります。
テスト容易性#
テストが書きにくいコードは通常、設計が悪く、将来の変更を拒絶するようになります。関心事の明確な分離、クリーンな入力、そして純粋関数が理想です。優れたテストはコードが正しく動くことを証明し、将来のリファクタリングにおける安全網を提供します。UIコンポーネントや複雑なシステムの場合、厳密な単体テストのカバレッジよりも実践的なテスト戦略を重視しますが、コアロジックは必ずテストでカバーされている必要があります。
状況はプロジェクトごとに千差万別であるため、私はすべてのプロジェクトに画一的なカバレッジ目標を設定することはやめました。しかし、テストされるべき箇所が確実にテストされているかは把握する必要があります。理想的にはハッピーパス(正常系)の100%と、サッドパス(異常系・エラー系)の妥当な割合をカバーすることですが、すべてのコードで100%のカバレッジを目指すような無理はしません。優れたオブザーバビリティ戦略と分かりやすいエラーメッセージさえ整っていれば、未知のエラーモードが発生しても後からテストスイートに追加できるため、長期的な成功を担保できます。
ベンチマーキング#
クリティカルパス(重要処理経路)においては、パフォーマンスに対する「推測」ではなく「実際の測定値」が必要です。トラフィックの多いコンポーネントに影響を与える変更については、遅いコードが本番環境に混入するのを防ぐため、明確なベンチマークの提示を必須としています。
無駄のないロギング#
ログは「アクションにつながる(actionable)」ものでなければなりません。無駄なログはクラウド利用料を跳ね上げるだけでなく、機密情報や個人情報の漏洩リスクにもつながります。開発中の冗長なデバッグログ自体は構いませんが、マージ前には必ず整理・削除すべきです。
私が(ほとんど)気にしないこと#
細かいディテールは自動化ツールに任せることで、私はより本質的で難しい課題に集中できます。機械にできる仕事なら、人間が手を動かすべきではありません。
1行1行の細かなコード#
LLMが生成したすべてのコード行を逐一チェックするのは、コンパイラや静的解析ツールの仕事です。私はそうした細部ではなく、全体のロジックやコンポーネント間の接続点(インテグレーション)に焦点を当てます。
フォーマット#
Go言語を書き始めて以来、私はコードフォーマットのスタイル論争に巻き込まれたことがありません(標準フォーマッターのおかげです)。しかし、いまだにフォーマットで議論している界隈があることも知っています。最善の策は、チーム標準を策定し、リンターとフォーマッターにすべてを委ねることです。標準ルールが定まっていれば、コーディングエージェントもそれに従いやすくなります。CIパイプラインのチェックをパスしているなら、それで十分です。
些細な構文やコードの書き方#
1つの問題を解決するアプローチは無数にあり、特定の構文スタイルを強要することは開発者の自由度を狭めるだけです。ロジックが破綻していない限り、forループを使っていようがリスト内包表記を使っていようが、私はまったく気にしません。
デバッグ#
私はデバッグセッション(実際にデバッガーをアタッチしてステップ実行するという厳密な意味で)をほとんど行いません。私にとってデバッグは最後の手段であり、「ここを通った(I AM HERE)」のようなprint文(本来なら適切なログ行であるべきもの)を大量に散りばめることの同義語に過ぎないからです。
何かが意図通りに動かないときは、その問題を再現・シミュレートする新しいテストケースを書きます。問題を再現しても何が起きているのか把握できないとすれば、それはオブザーバビリティやログが不足している証拠です。だからこそ、私はデバッガーにしがみつくのではなく、ログや可観測性の向上に注力します。
非エクスポートの名前(ローカルな命名)#
変数名や関数名が単一の関数内だけに閉じている(ローカルスコープである)場合、複数の関数やファイルにまたがって使われる識別子ほどにはこだわりません。コードをざっと流し読みして、あまりに不自然な命名を見かけたらリファクタリングするかもしれませんが、そうでなければモデルが生成した命名をそのまま受け入れます。
マイナーな依存関係#
主要フレームワークやクライアントライブラリ以外の、ちょっとした外部ライブラリのことです。これらはセキュリティのベースラインを満たしている限り、過度に神経質になる必要はありません。もちろん、脆弱性(エクスプロイト)やライセンス上の問題がないかのチェックは必須です。しかし、たった1つの「ヘルパー関数」を使うためだけに外部パッケージをインポートしているなら、私は100%の確率でその関数を自前で再実装し、余計な依存関係を排除します。
まとめ#
これは万人に当てはまる画一的なプロトコル(絶対的な手順)ではありません。また、コードベースにどのような計測(オブザーバビリティ)を仕込むかについても、語るべきことはたくさんあります。コードレビュー単体ですべての潜在的バグを拾い上げることは不可能です。だからこそ、エージェンティック・コーディングの時代においては、これまで以上に自動化を徹底することが重要なのです。
現代のコーディングエージェントには、モデルを制約し、より決定論的な出力を得るための多くの拡張パターンが存在します。Agent Skills、フック、MCP ツール、ポリシー、ルール……。これらのツールを活用してエージェントの作業スコープに明確な境界線を設けることで、開発者の体験はずっと快適になります。
車は、ブレーキの性能が許す速度でしか走ることができません。お気に入りのコーディングエージェントのガードレール(制約や制御手法)を学ぶことに投資し、自動化できない本質的な部分のレビューにこそ、皆さんの貴重な時間を使ってください。
Happy coding!
Dani =^.^=




