
CrowdWorks ジャンヌチーム(技術的負債解消チーム)所属のlinyclarです。AI関連技術はど素人です。
生成AIコーディング時代の到来
いや、本当に到来しているのか、Clineなどが実務で使い物になるのかはわかりませんが1、AIによるコーディングが進化しているのは間違いありません。
仮に現段階で使い物にならないのだとしても、生成AIという技術上の問題点なのか、我々の使い方の問題なのか、切り分けて考えねばなりません。また、技術上の問題点は解消されていくのが常なので、常に我々の使い方に問題がないのか疑っていくスタンスが大事だろうと思います。
では、その使い方とは何かというと、ポイント・オブ・ノーリターン:プログラミング、AGI、アメリカ – WirelessWire Newsでは以下のように書かれています。
ルキダスはかつて、ソフトウエア開発の真の問題点は、コードを書くことではなく、問題の複雑さの管理にあるという話を書いていますが、オライリーの「我々の知るプログラミングの終焉」に先立って書かれた「AIに備える」において、LLMに入力する「コンテキスト」の管理の重要性を強調しており、このあたりが具体的にこれからプログラマーに必要なスキルになると思われます。
https://wirelesswire.jp/2025/03/88294/ より引用
他にも、使い方の問題と言える課題はあるだろうと思いますが、この「コンテキスト」に絞って本エントリでは考えていきます。
「コンテキスト」とは何か
上記引用で言及しているAIに備える(原文タイトル:Preparing for AI)では以下のように書かれています。
Forgive the anthropomorphism, but a conversation with a language model is just that: a conversation, where previous statements from both parties are part of the context. The context also includes the code you’re working on and any other documents or instructions (including sketches and diagrams) that the AI can access.
https://www.oreilly.com/radar/preparing-for-ai-2/ より引用
要点を抜き出すと、生成AIにとっての「コンテキスト」とは以下のようなものです。
- 人が入力した生成AIへの発言
- 生成AIが出力した発言
- 生成AIがアクセスできるコードやドキュメント、指示(スケッチや図など)
Clineの場合、Clineとの会話、現在開いているファイル、カレントディレクトリのファイル一覧、あるいはClineが要求してきたコマンドの結果などが「コンテキスト」となるはずです。コードを解析中にさらに別ファイルのコードを読むのなら、そのファイルも「コンテキスト」に含まれるでしょう。
Fat Controller、Fat Model、スパゲッティコードなどの負債の意味が一段上がる
人間がコードを解析していて、あるメソッドの実装を解析するために、そのメソッドが依存しているクラスを読む必要があるとします。そのクラスが小さなものであれば問題はないでしょう。しかし、そのクラスがFat Modelである場合、読む必要のあるコードがどこにあるのかを探すところから始める必要があります。
これは、Clineの場合でも同様で、生成AIがFat Modelのコード全体をコンテキストとして受け取って、その中から必要な箇所を探すことになるはずです。無論、これも技術的な問題として、プロンプト圧縮などの技術的発展によって改善していく可能性はあります。しかし、それでも、巨大なノイズを抱えたコンテキストを生成AIに与えることは、生成AIの性能を引き下げることになるでしょう。
AIに備える(原文タイトル:Preparing for AI)においても、AIならスパゲッティコードだろうと理解できると考えるのは楽観的であると書かれています。
It’s overly optimistic at best to assume that AI assistants will be able to work effectively with tangled spaghetti code.
https://www.oreilly.com/radar/preparing-for-ai-2/ より引用
つまり、スパゲッティコードなどの負債の意味が、ヒューマンフレンドリーでないだけでなく、生成AIフレンドリーでないという意味も持つようになるということです。
弊社のように10年以上運用しているサービスであれば、Fat Controller、Fat Model、スパゲッティコードなどの負債を抱えているでしょうし、これらのサービスは生成AIフレンドリーでない、生成AIの恩恵を受けづらいコードになっていることでしょう。
そのため、単なる生産性向上のためというだけでなく、生成AIの恩恵を受けるためという理由でリファクタリングやリアーキテクチャを行っていく必要があるだろうと考えています。
生成AIフレンドリーなコードとは
生成AIフレンドリーなコードは、ノイズとなるような無関係なコードを含まず、逆に暗黙的な情報が生成AIからも読める状態になっているコードであると考えています。
そのため、
- 単一責任の原則を守る
- デッドコード、冗長なコードを排除する
- コード記述の揺らぎを排除する(静的解析ツールを導入し厳守する)
のような方法でノイズを減らし、
- 適切な命名を行う
- 適切にモジュールを分割して、クラスの関係性を明示する
- コメント、あるいはREADMEを用意して、コードの意図などを明示する
といった方法で暗黙的な情報を明示的にすることも必要になるでしょう(まあ、今までと変わらず、あるいは今まで以上にちゃんとやれということです)。
ただ、追加で考えなければならないポイントが2つあります。
一つ目はデファクトスタンダードから外れることの是非です。生成AIのモデルは、大量のデータを元に構築されているとはいえ、大半はデファクトスタンダードに沿ったコードのはずです。それゆえ、RailsならRails Wayに沿ったコードの生成の質は良いだろうと予想できます。逆に、Rails Wayから外れたコードは、適切な情報をコンテキストに含めないと期待した結果が得られないかもしれません。
悩ましいのは、Rails WayではControllerはCRUD操作が全て1ファイルになり、ActiveRecordは単一責任の原則に違反することがほぼ宿命づけられていることです。indexアクションの修正をしている時には他アクションのコードはノイズになる可能性が高いでしょうし、ActiveRecordにはいろんなビジネスロジックが含まれるため多くのノイズを含みます。
Rails Wayから外れるのか、外れないのか。外れるとしたら、どう外れるのか。この点はこれまで以上に慎重に考える必要があるでしょう。 RSpecについては、1クラス1specがデファクトスタンダードですが、メソッド単位でspecファイルを作成する方法もあるようです(参考:rubocop-rspecのRSpec/DescribeMethod Cop)。
二つ目は、いたずらなコード分解や疎結合は避けた方が良いだろうということです。
人間がコードを読む場合を考えてみます。あるUseCaseからドメインレイヤのクラスに依存しており、そのドメインレイヤのクラスを修正する必要があるとします。この時、経験を積んだエンジニアならgrepするなどの方法でそのドメインレイヤのクラスが他から依存されていないか調べ、悪影響がないか確認するはずです。その結果、他から使われていないことが明らかになった場合、安心して修正ができるわけですが、調査しなくとも問題なかったとも言えます。生成AIにコーディングさせたとしても同じ手順を踏むはずです。
ここで言いたいのは、privateメソッドをテストしたいならクラスを分けようというようなプラクティスや厳格なレイヤ分けは生成AIフレンドリーでない可能性があると言うことです(お気づきかと思いますが、私はヒューマンフレンドリーでもないと思っています)。複雑なドメインモデルを表現するためにクラスを分けることは当然疑問視していません。
同じ理由で、無意味なDependency Injection(以下DI)は避けた方良いと考えています。Clean Architectureなどを参考にしてUseCaseにRepositoryをDIするやり方がありますが、UseCaseを改修しているときにRepositoryも改修することになった場合、UseCaseに依存している箇所を探してRepositoryのクラスを特定する必要があります。これも、生成AIにコーディングさせた場合、同じようなことをするはずです。
RepositoryをDIしたところで、Strategyパターン的な使い方をすることはありませんし、使っているデータベースが変更されることは99.9%ないでしょう。考えられるのはテストではRepositoryのMockを使ってUseCaseの単体テストをしたいというケースですが、Clean Architectureなどに則っていれば単体テストする価値のあるロジックはドメインレイヤにあるはずであり、UseCaseを単体テストする必要はないでしょう。UseCaseのテストは実際のRepositoryを使った結合テストを行えば良いと考えています(当然ながら外部APIを叩くようなRepositoryはDIしてMockを使ったテストになります)。
とはいえ、こういった話も依存グラフの構築と検索を行えるようなツールがあれば、コンテキストに対するノイズの混入は最小限に抑えられるようになるのかもしれません。
10年運用しているサービスのリアーキテクチャをどうやるか
生成AIの話はここまでで、ここからはノイズを減らし、暗黙的な情報を明示的にするためのリアーキテクチャの話になります。
DDDの教科書的には、プロダクトオーナー(以下PO)やビジネスサイドと協調してドメインを理解し、ドメインモデルを分析していくことになりますが、10年運用しているサービスであれば、POやビジネスサイドからドメインモデルを引き出すよりも、コードに埋没してしまっているドメインモデルを掘り起こす方が理にかなっているでしょう。というのも、10年も運用していると離職や異動によって現職のPOやビジネスサイドも機能を網羅的に把握できているとも限らず、全体的な視点でドメインモデルを構築していくのは困難になっている可能性があるためです。むしろ、エンジニアが整理したドメインモデルやユビキタス言語を提案していくスタイルになっていくだろうと思います(これは弊サービスのドメイン特性によるところもあるかと思います)。
また、10年運用しているなら機能も枯れており、コア機能の変化も少ないため、コードに埋没しているドメインモデルを掘り起こすというタスクの不確実性は低いと考えられます。つまり、クネビンフレームワークでいう「煩雑系」のタスクであり、必ずしも反復型開発のアプローチを取る必要はないだろうということです。
シーケンシャル(ウォーターフォール)なアプローチというと蛇蝎視されますが、本件のようなケースで反復型開発のアプローチを取ると事前調査に時間をかけていれば回避できた実装の手戻りが発生する可能性が高くなります。また、「機能把握」「既存コードの解読」「ドメインモデルの分析」「実装」という反復は重労働であり、長期間反復するのは難しいだろうと思っています。
とはいえ、全部のドメインモデルの分析を終わらせてから実装というのも、現実的ではありません。そのため、全体を俯瞰して考えた方が良い機能の関係性や配置、つまり境界づけられたコンテキストや荒い粒度のモジュールを分けるところまでは一括でやって、個別機能の再実装は反復型開発で行うのが良いだろうと考えています。この方法のメリットは、全体の概要を把握できるドキュメントという成果物が得られることと、ドキュメントを見れば関連機能を把握できるため分析のための心理的負担や実装の手戻りを減らせることです。
境界づけられたコンテキストを分ける
弊サービスでは境界づけられたコンテキストを分ける試みが過去に2、3回あったのですが、いずれも立ち消えとなっています。直近のもの以外は私が参加していないので詳細はわからないのですが、直近のものについてはうまくいかなかった理由は以下のようなものだと考えています(別の人が行っていた活動に対する勝手な反省ですが、当人のレビューを受けた上で記述しています)。
- その当時のファシリテーターのドメイン知識が不足していたため、コンテキストのあり方について先導できなかった
- 全体を把握していて、尚且つ意見をまとめられる人がいなかった
- 人を全員集めてしまったため、意見をまとめることが困難な状態になってしまった
- コアドメインとか汎用サブドメインなどの分類、コンテキストマップを書くことなどに気を取られてしまった
これらを反省して、以下のようなアプローチを取ることにしました。
- 自分がRailsの全Controllerの全Actionをざっくり把握して分類する
- ファシリを担当する人がある程度ドメイン知識を持つようにするため
- 先にコンテキストの分け方案を作っておく
- 全体を把握してまとめられるようにするため
- コンテキストの分け方案を叩きに主要チームごとにミーティングをして意見を聞いてから、最後に全体ミーティングを行う
- n人の意見が1チームの意見になるため、話をまとめやすくなる
- コンテキストを分けることのみに集中した
結果として、現時点の認識ではあるものの、コンテキストの分け方に合意を取ることができました。
今後については、特定のコンテキストの分析と再実装を進めていくことを計画しています。
おわりに
生成AIの話は刺身のツマのつもりで書き始めたのですが、分量的にそちらがメインになってしましました。リアーキテクチャするにあたって実装をどうしていくとか、カラム情報が暗黙的なActiveRecordを考えるとannotaterbを使った方がAIフレンドリーになりそうだとか、「煩雑系」の問題の解決にはブレストは向かないのではないかとか、いろいろ考えていますが、今回はここまでにします。
CrowdWorksではAIトランスフォーメーション(AX)を推進する「AX戦略室」を設立し、AIエンジニア採用の強化を行なっております。LLMを利用したサービス開発業務だけでなく、機械学習を利用したプロダクトの改善業務などに興味のある方は是非ご応募ください。
- 実務では利用していませんが、プライベートで簡単に検証はしております↩