大きなdiffを、どこから読むか

· Updated:

最近、AIが書いたコードをどう理解するか、ということを何度か書いていた。コードについて問い直す仕組みを作ってみたり、AIの説明をそのまま信じず、前提や失敗条件を確認するようにしたりしている。 ただ、実際の変更が大きくなると、その前に別の問題がある。そもそも、どこから読めばいいのか。

git diff を上から全部読めばいいのだけど、変更が複数のファイルや関数に散らばっていると、それだけでもそれなりに負荷がかかる。かといって、AIに全部読ませて「重要なところだけ教えて」とすると、今度はその判断自体をAIに預けることになる。

そこで、change-signals という小さなCLIをRustで作った。 やっていることはかなり単純で、git diff から変更されたシンボルを拾って、いくつかの粗い情報を付ける。

src/cache/store.ts
  getOrFetch        refs=14, branch+5, cache
  evictExpired      refs=6, branch+1

src/db/records.ts
  loadRecord        refs=12, db

refs はその名前がリポジトリ内に何行あるか、branch は変更で増減した分岐の数、cache などは境界らしき文字列が変更に含まれていたことを表している。branch は符号つきで、減っていれば簡素化や移設の手がかりになる。 どれもかなり粗い。参照解析をしているわけではないし、構文を正確に解析しているわけでもない。リスクスコアも出さないし、「これは読まなくていい」とも言わない。 git diff --stat でもファイルごとの変更量は分かる。ただ、そこはファイル単位で止まるし、行数しか見ていない。変更したシンボルがリポジトリのどこからどれだけ参照されているか、といったdiffの外側の情報までは出ない。そこを、粗くてもシンボル単位で足したかった。 目的は、変更を理解することではなく、どこから読むかを決める材料を作ることにした。

考えている流れはこんな感じになる。

signals → prioritize → interrogate
  ↓           ↓             ↓
事実を集める   読む順を決める   実際に理解する

途中、実際のコードに使ってみたら、変更していない関数に別の変更が紐づいてしまい、その関数を「読むべき場所」として選んでしまうことがあった。これはまずかった。 情報が粗いことより、違う場所へ誘導することの方が問題だった。 そこで関数の範囲を追跡するようにして誤った帰属は直した。一方で、細かい構文まで正確に解析するところまではやらなかった。そこまでやるなら、Tree-sitterのようなちゃんとしたパーサーを使う話になってくる。 今回はそこには行かず、誤誘導は直す。粗さは残す。くらいで止めることにした。

AIを使えば、コードの説明はいくらでも増やせるようになった。でも、説明が増えるほど理解できるとは限らない。全部を自動化するのではなく、自分が判断する場所は残したまま、読むための負荷だけを少し減らしたい。 change-signals は、そのためのかなり小さな道具になった。 まだ v0.1.0 なので、しばらく実際の変更に使いながら試していこうと思う。

← Back to posts