AI時代に求められるのは、コードの速さか、可読性か
はい、どうもこんにちは佐藤です!
コードを書いてきて 20 年ほど。IT を仕事にしてからは 10 年ほどになります。その間にいくつも波を見てきましたが、ここに来て、また大きな波が来ましたね。
そう、AI です。
AI がコードを書く時代になって、ふと考えたことがあります。コードに求められるのは、速さなのか、可読性なのか。
私がこの業界に来た 10 年前、ソースコードには速さが求められていました。それが今では、すっかり可読性の時代です。では、AI 時代はどちらを取るべきでしょうか?
私の答えは……。
やはり可読性です!
しかも、人間のためだけではありません。AI のための可読性です。
その理由を書いていきますね!
10 年で、評価の軸はひっくり返った
まずは昔話から。
10 年前の評価基準では、速いコードが書けることが重視されていました。
- for ループ 1 回の中に、処理を詰め込む
- ストアドプロシージャの力を利用して、なるべく速く処理する
ループを 2 回回すなんて、もったいない。1 回で済むなら、そこに全部入れる。そういうコードが「できる人のコード」だったのです。
でも今では、マシンスペックが上がりました。コードの構文も、stream のような書き方が当たり前になっています。
- 1 回の for ループの中には、原則 1 処理
- アグリゲートの考え方でまとまりを作る
- 極力、ストアドやクエリの力を使わない
こういう作りが主流になりました。少しくらい遅くても、読めることのほうが大事。評価の軸が、10 年でひっくり返ったわけです。
この流れは、以前 SQL の結合禁止は良いルールなのか や 4層かクリーンアーキテクチャかは、使うDBが決める でも書きました。
では、AI がコードを書く時代に、この振り子はまた「速さ」へ戻るのでしょうか? 私は戻らないと考えています。理由は 3 つ。
いざという時、読むのは人間
1 つ目は、いざという時は、人間が読む必要が出るからです。
不具合の対応も、AI に任せる時代になってきました。ログを渡せば原因を探してくれますし、修正案も出してくれます。
でも、その修正が正しいかどうかを最後に判断するのは人間です。
特に次のようなドメインでは、どうしても人間が読むことになります。
- セキュリティに関わる処理
- 金融の計算や取引
- 個人情報を扱う処理
以前 AIがコードを書く時代にコードレビューは不要 と書いた私が言うのも何ですが、あれは「普段から読まなくて済む仕組みを作ろう」という話でした。読まなくて済む仕組みがあっても、いざという時に読めないコードでは困りますよね。
障害対応の夜に、ループ 1 回へ詰め込まれた 300 行と向き合う。想像するだけで胃が痛くなりませんか?
AI も、読みにくいコードでは間違える
2 つ目が、今回いちばん言いたいことです。
AI も、読みやすいコードであればハルシネーションが少なくなります。
これは実際に痛い目を見た話です。
私は以前、論理結合の激しい処理を書いてしまったことがありました。
- A の処理をして、DB にあるデータが入る
- そのデータが入っている前提で、B の処理をする
A と B は、コードの上では離れたところにあります。でも、DB を挟んでがっちり結びついていたのです。
ある日、AI に A の修正を頼みました。AI は A を正しく直してくれます。ところが、B は直されていませんでした。 A が DB に入れるものが変わったのに、それを前提にしている B はそのまま。結果、不具合が発生しました。
AI が悪かったのでしょうか?
私はそうは思いません。影響範囲が読めない書き方をしていたのは、私のほうです。A のコードだけを読んでも、B がそれに依存していることはどこにも書いていませんでした。人間が読んでも見落とすコードを、AI が見落とさない理由はありませんよね。
目次のように書くと、AI にも影響範囲が見える
では、どう書けばよかったのか。
私が意識しているのは、パラグラフライティングのような書き方です。メインの関数が「目次」になっていて、中身はプライベートメソッドに分かれている形ですね。
イメージを書いてみます。まずは、ループ 1 回に詰め込んだ書き方から。
def close_month(self, orders):
for order in orders:
if order.status == "shipped":
order.status = "billed"
self.repo.save(order)
invoice = Invoice(order.customer_id, order.amount)
self.invoice_repo.save(invoice)
# ……ここに割引、税、通知が続いていく
次に、目次のように書いた形です。
def close_month(self, orders):
shipped = self._pick_shipped(orders)
self._mark_as_billed(shipped) # A: 請求済みにして保存する
self._issue_invoices(shipped) # B: 請求済みを前提に請求書を作る
下の形なら、メインの関数を読むだけで「A のあとに B があって、B は A の結果を前提にしている」ことが分かります。影響範囲が、目次にそのまま出てくるわけです。
AI に A の修正を頼んだときも、目次を読めば B が隣にいることに気づけます。気づける材料がコードの上にあるかどうか。ハルシネーションが減るかどうかは、そこで決まるのだと思っています。
さらにドメイン駆動設計で作っていれば、ドメイン同士が分離していることも、AI に知らせられます。
AI は、読みやすいコードを手本に書く
3 つ目は、最近のコードは読みやすさ重視で書かれているからです。
AI がコードを書くとき、手本にするのはプロジェクトの中にある既存のコードですよね。そこが読みやすさ重視で書かれていれば、AI が参考にするときの負担になりにくい。新しく書くコードも、同じ形にそろっていきます。
逆に、ストアドに処理が寄っていると、AI にとってはつらい構成です。
- ロジックが DB の中にあって、アプリのコードを読んでも全体が見えない
- 実データがないと、テストが難しい
AI に実装させて、テストで動くことを確かめてから人間がレビューする。そういう回し方をしたいなら、テストしやすい形であることは欠かせません。実データを用意しないと確かめられない処理は、AI が自分で動作を確認できないのです。
速さのためにストアドへ寄せるほど、AI に任せられる範囲は狭くなっていきます。
まとめ
というわけで、AI 時代も今の路線で進むのが良いよね!って話でした!
あ。正確には、人間のためだけじゃなく、AI のために可読性を取ろう って感じですねっ。
速さはマシンが補ってくれます。でも、影響範囲が読めないコードの間違いは、人間も AI も同じように踏んでしまう。メインの関数を目次にして、何がどこに依存しているかをコードの上に書いておくこと。それが、AI に安心して任せるためのいちばんの近道です。
ぜひみなさんも、自分のコードを「AI が読んでも影響範囲が分かるか」という目で見直してみてください!