APC 技術ブログ

株式会社エーピーコミュニケーションズの技術ブログです。

株式会社 エーピーコミュニケーションズの技術ブログです。

AIが書いたコードのレビュー、どこを見ればいいか — 「頻出スルー・エッジ重点」という分担論

こんにちは、エーピーコミュニケーションズ ACS事業部の福井です。

AIでコードを書くのが当たり前になってから、PRレビューがなんとなく変な感じになってきました。

以前は「Typoがないか」「変数名がわかりやすいか」「ちゃんと動くか」というのがレビューの主な関心事でした。 でも今は、AIが生成したコードはそのあたりはだいたい及第点で出てくる。きれいに書かれている。一見動く。でも――

「これ、本番で1万件のデータが来たときどうなるんだろう。」 「うちのDockerイメージ、alpine使ってるけどこのライブラリビルドできるんだっけ。」

そういう疑問が、レビュー中に浮かびやすくなりました。 AIが苦手な領域が、浮き彫りになってきた感じです。

この記事では、AI生成コードを前提にしたPRレビューの観点をどう組み直すか、自分なりに考えてきた方針をまとめます。


PRレビューに起きている変化

AI生成コードには明確な傾向があります。

  • 得意なこと: 構文が正しい、Typoが少ない、典型的なパターンの実装、標準ライブラリの使い方
  • 苦手なこと: 組織固有の制約、稀だが壊滅的なエッジケース、本番トラフィック前提の動的挙動

つまり、以前のレビューで時間を使っていた「構文チェック・スタイル確認・典型バグ探し」は、AIとLinterにほぼ任せられるようになっています。 一方で、人間にしか判断できない領域は残っている――どころか、AIが書いた分だけ増えているとも言えます。 PRを「動く前提の儀式」のまま続けるのは、時間の使い方として合っていません。


レビューが担っていた「責任の分散」という機能

コードレビューには、バグを見つける以外の役割がありました。責任の分散です。 「人間はバグを入れる。だから複数人で確認し、責任を分散する」——本番でバグが出たとき「一人が書いた」と「チームで確認した上で入れた」では意味が違う。 これは、開発現場が長年かけて作ってきた仕組みです。

AIが書いたコードでは、これが難しくなります。問題は量です。 AIは短時間で大量のコードを生成し、差分500行・1000行のPRもある。 全行に目を通す前提が崩れてきました。 「ざっと見た」でApproveした後に本番でバグが出たとき——

  • 「AIが書いたから」はレビュアーの責任を軽くする理由になるか?
  • 量が増えた分だけ、レビューの責任分担機能は薄まっているのか?
  • それとも、見る箇所を絞ることで責任分担を再設計できるのか?

自分の中で、まだ答えが出ていない問いです。 ただ、こういう問いを持たずにレビューを続けることは避けたいと思っています。


基本方針:頻出はスルー、エッジに集中

レビューの役割をこう再定義しています。

レイヤー 主担当 人間レビューでの観点
構文・スタイル・典型バグ Linter / AI 基本スルー。ツール設定が妥当かだけ確認
設計・アーキテクチャ 人間+AI補助 依存関係、責務分割、既存パターンとの整合性
ビジネスロジック・ドメイン 人間 要件と実装のズレ、抜けているケース
性能・運用・セキュリティ 人間+ツール 本番トラフィック前提の懸念、SLO/SLA への影響
組織・インフラ固有の制約 人間 使ってはいけないAPI・ライブラリ、環境依存の制約

「頻出スルー」と「エッジ重点」を意識的に分けるだけで、レビューにかける時間の配分がかなり変わります。


Python:人間が見るべきエッジケース

静的観点はツールに任せる

型チェック(mypy)、スタイル(Ruff)、セキュリティ(bandit)はCIで自動化します。 AIが生成した typing.List などの非推奨な書き方も Ruff が検出してくれます。 人間がコードを見て「インデントが…」と言い始めたら、CIの設定を見直すサインです。

人間が集中する動的観点

N+1・バッチ処理の問題

AIはループの中で外部API/DBを叩く典型パターンを書きがちです。 小規模テストでは見えず、本番データ量で初めて顕在化します。

# AIが書きがちなパターン
for item in large_list:
    result = db.fetch(item.id)  # N+1リスク

# 人間が気づいて直すパターン
ids = [item.id for item in large_list]
results = db.fetch_bulk(ids[:1000])  # バッチ化・上限設定

非同期処理のリーク

asyncio.gather を大量タスクに使うとメモリが爆発する、キャンセル処理が抜けているといったケースはAIが見落としがちです。 テストが通っても負荷時に落ちます。

Docker・環境依存の制約

python:3.12-slim-alpine イメージで psycopg2 を使おうとするとgccがなくてビルドが落ちます。 AIはこういった「うちの環境の制約」を知りません。 Dockerfileを変えるPRは必ず手元でビルド確認する習慣をつけています。

Azure Functions / Lambda のコールドスタート

初期化処理が重いとコールドスタートでタイムアウトになります。 AIは「動くコード」を書きますが、「15分のLambdaタイムアウト内で動くコード」を意識しているとは限りません。

チェックポイントまとめ(Python)

カテゴリ スルー例 人間チェック例
入力処理 標準JSONパース 巨大ペイロード(>1MB)、malformed JSON
DB/外部API 基本SELECT/GET タイムアウト/リトライ失敗、接続プール枯渇
非同期 単発 asyncio.gather タスク爆発(メモリリーク)、キャンセル未対応
性能 小規模ループ 本番データ量でのOOM、I/Oボトルネック
環境制約 Dockerイメージのツール欠如、クラウドのタイムアウト制約

TypeScript:人間が見るべきエッジケース

静的観点はツールに任せる

strictNullChecks: true + ESLint + any 禁止をCIに組み込めば、型まわりの粗はほぼ自動で弾けます。

人間が集中する動的観点

any 型からの runtime エラー

CIをくぐり抜けた any が残っていると、本番で突然 Cannot read properties of undefined が起きます。 Zodなどでruntime型ガードを入れているかが確認ポイントです。

// AIが書きがちなパターン
const data: any = await fetch('/api').then(r => r.json());
return data.user.name;  // runtimeで爆発待ち

// 人間が確認するポイント
const data = await fetch('/api').then(r => r.json()) as ApiResponse;
if (!data.user) throw new ValidationError('User not found');

React の状態管理と無限ループ

useEffect の依存配列が不正確だと無限リレンダーが起きます。 AIはコードを書けますが、コンポーネントのライフサイクルと状態の組み合わせを正しく推測できるとは限りません。 React DevTools Profilerで確認します。

SSR / Next.js の hydration mismatch

サーバー側とクライアント側でレンダリング結果が違うとhydration mismatchが起きます。 AIはCSR前提で書くことが多く、Next.jsプロジェクトでは特に注意が必要です。


コメントとdocstringの役割を変える

AI時代のコメントは「人間のための補足」から「人間とAI両方へのAPIドキュメント」に役割が変わっています。

残すべきコメント

  • 業務仕様に紐づくマジックナンバー(「この30日は法制度由来」など)
  • 歴史的経緯由来の制約(「このAPIは古いクライアントがいるので削れない」)
  • わざと冗長に書いている理由(安定性・安全性のトレードオフ)

docstringに書くべきこと(AI向け)

def apply_discount(price: int, user_id: str) -> int:
    """
    割引後の価格を返す。

    Args:
        price: 税抜き価格(円)
        user_id: ユーザーID

    Returns:
        割引後の税抜き価格(円)

    Note:
        premium会員の割引率は外部APIから取得する。
        このAPIは月次バッチ処理中(毎月1日0〜2時)は使用不可。
        その時間帯に呼び出した場合は ServiceUnavailableError を返す。
    """

「このコードはこの制約を満たす必要がある」を書いておくと、後続のAI修正がその制約を意識して動くようになります。


PRテンプレに「動的検証ログ」を義務化する

人間のレビュー時間を5〜10分に絞るために、PR作成者側に動的検証の結果を貼る習慣をつけると効果的です。

## エッジケース検証ログ
- [ ] 不正入力: pytest で再現、ログ・エラーハンドリング適切
- [ ] 負荷: locust で 100req/s、レスポンスタイム <2s
- [ ] 環境制約: Docker build 通過確認済み

## レビューしてほしい観点
- [ ] ビジネスロジックの妥当性(要件との整合性)
- [ ] 組織固有制約との整合性(Azure Functionsのタイムアウト等)

「動いているか」の確認をPR作成者が済ませた上でレビューに入れると、議論が「本当に本番で大丈夫か」に集中できます。


おわりに

AIが書いたコードのレビューで人間がすべきことは、AIが知らないことを見ることです。

構文・スタイル・典型バグはLinterとAIに任せます。 人間がレビューで時間を使う価値があるのは、エッジケース・組織固有の制約・本番トラフィック前提の動的挙動です。

この役割分担を意識するだけで、PRレビューは「儀式」から「意味ある議論の場」に変わります。 「AIが書いたからレビュー不要」ではなく、「AIが書いたからこそ、人間は別の場所を見る」という発想の転換です。

この記事に書いたチェックリストは、今の自分の運用をそのまま整理したものです。 実際にやっていて「ここは違う」「こっちの方が効く」と感じることがあれば、ぜひ教えてください。

なお、この記事で取り上げたテーマについて、別の視点からアプローチした後編をメンバーが執筆予定です。 同じ問いに対して違う角度から考察しているので、あわせて読んでいただけると議論が深まると思います。


ACS 事業部のご紹介

私の所属する ACS 事業部では、開発者ポータル Backstage、Azure AI Service などを活用し、Platform Engineering + AI の推進・内製化を支援しています。

www.ap-com.co.jp www.ap-com.co.jp www.ap-com.co.jp

また、GitHub パートナーとしてお客様に GitHub ソリューションの導入支援を行っています。 GitHub Copilot などのトレーニングなども行っておりますので、ご興味を持っていただけましたらぜひお声がけいただけますと幸いです。

一緒に働いていただける仲間も募集中です! ご興味持っていただけましたらぜひお声がけください。 ※求人名の冒頭に【ACSD】と入っている求人が当事業部の求人です。

www.ap-com.co.jp www.ap-com.co.jp

本記事の投稿者: 福井
本記事は GitHub Copilot に伴走してもらいながら書き上げました。構成・表現の整理にAIを活用しつつ、体験談・検証・最終判断はすべて著者本人によるものです。