tech
tech 2026.07.18 7 min read

AI生成されたコードを理解するために

AI生成コードを理解するために、何を見るか、どう確かめるかを考える。

AIが書いたコードを理解しようとして、まずAIの説明を読む。

「既存の設計に沿って、エラーハンドリングを追加しました」

なるほど。

次にPR概要を見る。だいたい同じことが、もう少し立派に書いてある。テストも通っている。差分にはそれらしいtrycatchがいる。

なるほどなるほど。

そしてレビューを終えたあと、何を理解したのか聞かれると、説明文をもう一度読み上げるしかない。

これは理解したのだろうか。

たぶん、まだ説明を預かっているだけだ。

最初に答えを持っておく

AI生成コードを読むとき、いきなり差分の一行目から入ると迷子になりやすい。

コードは具体的なので、見ていると仕事をした感じがある。変数名を追い、関数を開き、知らないメソッドを検索する。気づけば二十分経っている。詳しくなった。何の変更だったかは、ちょっと怪しい。

なので最初に、コードを見ずに一文だけ書く。

「この変更で、誰の何がどう変わるのか」

issueでも依頼文でも、自分の記憶でもいい。たとえば「通知に失敗した宛先だけ、次回もう一度送られるようにする」。まずはこのくらいでいい。

この一文が、あとで差分とテストと説明を読むときの物差しになる。

AIが書いたPR概要を先に読むのは構わない。ただし、その文章を答えにしない。あれは実装者の主張であって、まだ事実ではない。

実装者が人間でも同じなのだが、AIの文章は特に滑らかだ。滑らかな文章は、こちらの脳を「はい分かりました」の姿勢にする。分かってないのに。

差分は地図から見る

最初に見るのは、どのファイルが増えて、どのファイルが消えて、どこが触られたか。入口はどこで、データはどこを通り、最後に何が保存されるのか。

自分はだいたい、次の順で見る。

  1. 呼び出し元
  2. 変更の中心になっている処理
  3. 外部APIやDBとの境界
  4. テスト

順番はリポジトリによって変わるけれど、入口から出口まで一本の道を作る、という点は変わらない。

差分の各行を均等に理解しようとすると、整形された型定義と危険な条件分岐が同じ重さになる。人間の集中力は有限なのに、空白行まで平等に愛している場合ではない。

まず地図を作る。そのあとで、値が変わる場所、処理を止める場所、失敗を成功に変えてしまう場所を読む。

特にcatch、早期return、デフォルト値は見る。こういうところは静かに話を変える。「失敗したら再試行する」が、「失敗したらログを出して終わる」へ化ける場所でもある。

テストは証拠ではなく証言

テストが通っていると安心する。

緑はいい。緑色を見ると、だいたいのことを許したくなる。

でも、生成されたテストは生成された実装と同じ勘違いをしていることがある。実装が「これが仕様です」と言い、テストが「そうです」と答えている。仲がいい。こちらだけ話に入れていない。

テストでは、まず名前より入力と出力を見る。

何を与えて、何が起きたことを確かめているか。逆に、何は確かめていないか。

正常系が一つあるなら、途中で失敗したらどうなるかを考える。空なら。二回呼ばれたら。最初だけ成功して二件目で落ちたら。権限がなかったら。既に処理済みだったら。

全部をテストに足す必要はない。ただ、頭の中で反例を一つ作る。その反例をコードへ通して、期待した出口に着くかを見る。

テストは理解を保証する証拠というより、「この場合はこうなる」という証言に近い。証言が一件あっても、事件の全体が分かったことにはならない。

急に刑事みたいになった。でも差分レビュー中の気分は、わりと張り込みである。地味。長い。コーヒーだけ減る。

説明どおりに失敗するか

たとえば、複数の宛先へ通知を送る処理をAIに直してもらったとする。

要件は「送信に失敗した宛先だけ、次回再試行する」。

PR概要には「宛先ごとに例外を処理し、一部の失敗が全体を止めないようにした」とある。AIも「部分的な失敗に安全に対応しています」と説明している。テストでは、三件の通知が送られることを確認している。

差分を見ると、送信処理は宛先ごとのtryに入り、catchではログを出している。そしてループを最後まで回したあと、ジョブ全体を完了にしている。

一件失敗しても、たしかに全体は止まらない。そして失敗した一件も、二度と再試行されない。

嘘は書いていない。「一部の失敗が全体を止めない」は本当だ。「部分的な失敗に対応」も、対応の意味をかなり広く取れば本当かもしれない。ログには残っている。ログはいつも、対応した顔をする。

テストも間違ってはいない。三件とも成功する世界では通る。

問題は、最初に書いた一文とつながっていないことだ。「失敗した宛先だけ次回もう一度送られる」は、どこで保証されているのか。

この問いを持って差分を読めば、ジョブを完了にする条件が気になる。失敗した宛先のIDがどこへ残るのか探す。何も残っていないことに気づく。

説明を読んでからコードを見るだけだと、「なるほど、宛先ごとのcatchね」で通り過ぎやすい。説明にコードを当てはめるのではなく、自分の一文をコードへ通す。似ているようで、かなり違う。

PR概要を自分で壊す

差分とテストを見たら、PR概要を閉じて、自分で変更を説明してみる。

長い文章はいらない。何が入口で、何を変え、どこに結果が残るのか。失敗したときはどうなるのか。今回やっていないことは何か。

これを三、四文で書く。

元のPR概要と同じ内容になれば、それはそれでいい。大事なのは、文章を記憶から複製することではなく、差分からもう一度組み立てたことだ。書けない場所があったら、そこが次に読む場所になる。

「エラーを適切に処理します」の「適切に」が書けない。「既存の仕組みを利用します」の「仕組み」が分からない。「後方互換性を保ちます」の、何との互換性か知らない。

便利な言葉は、理解の穴にきれいな蓋をする。PR概要を書き直すと、その蓋が少し浮く。

完全に理解する必要があるかどうかは、変更による。けれど理解しようと決めた場所では、この浮いた蓋を見なかったことにしない。

AIには答えより行番号を聞く

ここまで読んだあとで、実装したAIに説明を求める。

先に聞くと、その説明が読解のレールになる。あとから聞けば、自分の理解とぶつけられる。

「どう実装しましたか」だけでは、また上手なPR概要が返ってくる。

聞きたいのは、もっと狭いことだ。失敗した宛先はどの値として残るのか。その値を次回読むのはどの処理か。ジョブを完了にする条件はどこか。この設計が壊れる入力を一つ挙げるなら何か。できればファイル名と行を付けてもらう。

すると説明は、確認できる仮説になる。行を開いて、本当にそうかを見る。参照先がなければないと分かるし、AIが自信満々に存在しない処理を指したら、こちらも自信満々に却下できる。

AIの説明は便利だ。知らないライブラリの役割や、変更された関数同士の関係を短時間でつかめる。読む場所を絞るのにも使える。

ただし、説明は理解の完成品ではない。よくできた索引くらいに思っておく。索引だけ読んで本を読んだことにすると、読書感想文の時間に困る。

自分の理解にする

コードを理解するとは、一行ずつ日本語に翻訳できることではないと思う。

少なくとも、その変更について、入口から出口まで一本たどれること。正常なときに何が残るか分かること。失敗したとき、どこで止まり、何が次回へ持ち越されるか分かること。

そして、まだ分からない場所を言えること。

実務ではこの順で読む。

先に期待する変化を一文にする。差分で地図を作る。テストの外に反例を一つ置く。PR概要を自分の言葉で作り直す。最後にAIの説明を、行番号のある仮説として照合する。

毎回きれいにはできない。途中でSlackが鳴る。別のレビューが来る。テストは遅い。コーヒーはなくなる。

それでも、AIの説明をそのまま持って帰るよりはいい。

借りてきた「適切に処理しています」を分解して、自分で「この失敗はここに残り、次はここから再開する」と言えるようにする。

そこまで来て、ようやく説明が自分の理解になる。

ならないこともある。

そのときは、分からないままマージするのではなく、分からない場所を指差せる。

それもかなり大事な理解だと思う。