🏗️

Claude Codeが書いたコードを、チームのコードにするためにやったこと — 理解負債とどう向き合ったか

に公開

はじめに

こんにちは、クラシルでバックエンド開発をしているmighteeです。

社内では開発者だけでなくbizメンバーも含めてAI活用を積極的に進めています。開発メンバーはすでにAIを日常的に使いこなしていますが、その中で新しい課題も見えてきました。

Claude Codeを使って、Railsアプリケーションで2つの新機能の要件定義からリリースまでを担当しました。期間は約3週間です。開発はスクラム単位で進めていて、実装は自分がClaude Codeと一緒に進め、レビューは別スクラムのバックエンドエンジニアに見てもらう体制です。

個人の体感では、生産性は上がりました。マイグレーション、CRUD、Rakeタスクといった定型コードはAIが初稿を書いてくれるので、設計判断に集中する時間が増えました。ところが、チーム全体で見ると別の景色が見えてきます。特に、実装の文脈を共有していないレビュアーにとっては負荷が大きく、「このコード、誰が説明できるの?」という場面が出てきたんですよね。

Jason Gorman氏が「comprehension debt(理解負債)」として解説している現象です。ざっくり言えば、コードの生成速度が理解の速度を上回ったときに積み上がる負債のことです。技術的負債がコード品質の問題であるのに対し、理解負債はコードは正しく動くのに「なぜこう書かれているか」を誰も説明できない、という人間側の問題です。

この記事では、チームで理解負債が積み上がりかけた経験と、それにどう向き合って進めたかを書きたいと思います。

理解負債が見えた3つの場面

PRが巨大化し、レビューが形骸化しかけた

Claude Codeに設計書を渡して一気に実装すると、1PRの変更行数がどうしても膨れ上がります。 実装者の自分からすると、Claude Codeが一気に書いてくれるので「速いし、まとめて出しちゃおう」と思ってしまいがちです。

レビュアーからすると、まったく違う景色が見えていました。「どこから読めばいいかわからない」「AI生成コードは一見きれいに整っているから、逆に流し読みしちゃう」「テスト通ってるしLGTMにしかける」と。

ココナラの村上氏の記事では、AI活用後にPRサイズが154%増加し、レビュー時間が91%増加したというデータが紹介されています。同様の傾向を感じていました。

エラーハンドリングの根拠を誰も持っていなかった

ショップ機能の購入処理では、ポイント消費と特典付与の処理で2つの外部サービスへの書き込みが発生します。Claude Codeが生成した補償トランザクションのrescue構造は、それっぽく動いていました。

問題が発覚したのは、レビューで「ポイント消費が成功した後に特典付与が失敗した場合のリカバリの設計はどうなってる?」と聞かれたときです。答えられませんでした。設計書に書いていなかったし、AIも根拠なくそれっぽいリカバリ処理を生成していました。コードとしては動くけれど、判断の根拠を誰も持っていません。

ちなみに、このときレビュアーに「設計書にはなんて書いてあるの?」と聞かれて設計書を見返したら、「ポイントを消費して特典を付与する」としか書いていませんでした。正常系を中心に書いていて、失敗パスの設計を十分考慮できていなかったんです。設計書に書いていない情報は、AIにも出しようがありません。

レビュアーの指摘がなければ、そのまま本番に入っていた可能性があります。これが理解負債の中でも特に注意が必要なパターンだと思っています。コードは正しく動くのに、判断の根拠が存在しません。

技術選択の根拠が曖昧なまま進んでしまった

非同期処理のメッセージキュー選定でも似たことがありました。「順序保証した方がいいよね」と安易にFIFOキューを選んでいましたが、レビューで「FIFOキューはmessage group IDを適切に設定しないとむしろスループットが落ちるけど、本当に必要?」と指摘されました。厳密な順序保証が不要ならStandardキューでも十分で、重複排除が必要かどうかで判断すべきだったんです。

エラーハンドリングもインフラ選定も、指摘の「なぜ?」がなければ気づかなかった問題です。こうした反省を踏まえて、以下の3つの対応をしました。

理解負債を生まないための3つの工夫

1. 「理解できる単位」にPRを分割する

最初に取り組んだのは、PRの粒度の制御です。依存関係順にPRを積んでいく方式にしました。

マイグレーション + モデル(PR1-2)
  └─ 参照系API(PR3)
      └─ 更新系API(PR4)
          └─ 非同期ジョブ(PR5)
          └─ 履歴API(PR6)

今回の対応では全体で23本のPRに分割しました。PRを小さく保つこと自体はAI以前からの基本ですが、AIが一気にコードを書いてくれる今だからこそ、意識的にやらないと簡単に崩れます。

実装者としての効果は、「このPRで何を作っているか」を言語化せざるを得なくなったことです。Claude Codeへの指示も「PR1のスコープはここまで」と区切ることで、出力の精度が上がりました。スコープが曖昧だと、AIも「ついでにこれも」と余計なものを足してきます。

巨大なPRでは「テスト通ってるしLGTM」になりかけていたのが、分割後は設計判断についての議論が増えました。

ただし、リベース作業が頻繁に発生するのは正直しんどかったです。PRマージのたびに後続ブランチ全てをリベースし直す必要があります。途中からWIPコミットで早めにブランチに保存する運用に切り替えて、多少マシになりました。ブランチの依存関係の整理やマージ順の算出はClaude Codeに任せられるので、管理コスト自体はAIで軽減できています。

2. 設計書に「正解がコードの外にある情報」を書く

3つのエピソードには共通点があります。いずれも「正解がコードの外にある」ということです。運用チームの作業フロー、システム全体のスループット特性、リカバリ方針の優先順位。こうした情報をそもそも設計書に書いていませんでした。

そこで、設計書テンプレートに2つのセクションを追加しました。

追加した設計書テンプレート(クリックで展開)

エラーハンドリングセクション:

## エラーハンドリング
- 外部API(ポイント基盤)が失敗した場合: [具体的なリカバリ方針]
- 部分失敗時のrollback優先順位: [優先順位と理由]
- rollback自体が失敗した場合: [二重障害時の方針]

運用要件セクション:

## 運用要件
- データ参照方法: SQL直接 / 管理画面 / Railsコンソール
- 運用チームが使うキー: [検索に使うカラム]
- 監視・アラート要件: [Sentry通知の条件]

実装前に、自分自身にこの3つの質問を投げかけるようにしました。

  1. この外部API呼び出しが失敗したら、どうリカバリする?
  2. このデータは運用チームがどうやって参照する?
  3. このAPIがリトライされたら、二重実行を防ぐ仕組みは?

これを書いてからClaude Codeに渡すと、エラーハンドリングの手戻りが減りました。たとえば補償トランザクションの例で言えば、「ポイントrollbackを最優先、失敗時はSentry通知してスキップ」と明記するだけで、その通りのrescue構造を生成してくれます。

ここで大事なのは、設計書の内容を自分の言葉で説明できるかどうかです。説明できないなら、それは理解が足りていないということです。AIに渡す情報を整理する過程で、自分たちの理解の曖昧さが見えてきます。

課題としては、このテンプレートを埋めるには運用チームやインフラ担当への事前確認が必要で、設計フェーズに時間がかかるようになりました。ただ、その分だけ実装後の手戻りが減ったので、全体のリードタイムは悪化していない感覚があります。

3. レビューの役割分担 — 機械的チェックはAI、判断の妥当性は人間

最後に取り組んだのは、レビューの役割分担です。

実装完了後にClaude Codeのサブエージェント機能を使い、2つのレビューエージェントを並列実行するようにしました。それぞれ専用のプロンプトを渡して、1つは品質・規約チェック(Sonnetモデル)、もう1つはセキュリティチェック(Opusモデル)に特化させています。

機械的な指摘をAIに任せることで、人間のレビューは「判断の妥当性」に集中できます。

  • ✗ 「命名規則が統一されていない」「テストケースが足りない」(← AIが得意)
  • ✓ 「このrollback優先順位の根拠は?」「本当に順序保証が必要?」(← 人間が聞くべき)

レビュアーからは、機械的な指摘をAIに任せることで、本質的な設計議論に時間を使えるようになった、という声がありました。逆に言えば、AI生成コードのレビューでは「なぜこの実装なのか」を問う質問が従来以上に重要になったとも感じています。

コードレビューは「説明できない」が発覚する最後の砦です。その砦で人間が本質的な問いに集中できる状態を作ることが、チームではレビュー設計のポイントだったと感じています。

まとめ

AI生成コードの品質は上がり続けています。テストも通る、lintも通る、動く。だからこそ「理解できているか」がボトルネックになります。

個人の意識だけでは防げません。「ちゃんと読もう」と思っていても、きれいに整ったコードを前にすると流し読みしてしまう。仕組みでカバーするしかないと思っています。

  • PR分割で、物理的にレビュー粒度を制御する
  • 設計書テンプレートで、「書くべき情報」を強制する
  • レビューの役割分担で、人間の注力ポイントを絞る

振り返ると、今回一番効いたのはレビューの際にもらった指摘でした。「この方式にした理由は?」「この処理が失敗したらどうリカバリする?」「本当に順序保証が必要?」。特別な問いではないのですが、AI生成コードは「動くし、きれいだし、テストも問題なく通る」から、こうした問いが出る場がなければ、根拠がわかっていないコードがそのまま本番に入っていたと思います。PR分割も設計書テンプレートも、つまるところこの「なぜ?」が機能する土壌を整えるための仕組みです。

AIにうまく任せるところは任せて、人間が「なぜ?」を考え続けること。それがAIの生産性をチーム全体のものにする鍵だと思います。

チーム全体でのAI活用の取り組み

ハーネスエンジニアリング、CI改善、仕様書の自動更新など、チームメンバーもそれぞれの切り口で記事を公開しています。

https://zenn.dev/dely_jp/articles/ccdf9b4cf2183f
https://zenn.dev/dely_jp/articles/560fcfd0b69239
https://zenn.dev/dely_jp/articles/f6a6ce32406851

参考記事

Kurashiru Tech Blog

Discussion