バイブコーディングの次に考えた「Knowledge First Development」
はじめに
AIを使って小さなアプリケーション作成したことがある人は多いと思います。
最初、小さなスクリプトを生成して自分の欲しかった以上のものができあがってきて歓喜したのではないかと思います。
僕もそのひとりです。
しかし、機能を追加していくうちに思ったようなものを作ってくれず、挙げ句の果ては、すでに動作している部分まで壊してしまう。
しかも、最初に比べて開発にも非常に時間がかかるようになり、トークンも大量に消費するようになった。
このような経験をしたことはないでしょうか。
なぜこうなるのか、どうしたら解決するのかを考え、自分なりに解決したと思うものを最終的にスキルにしてみました。
人間の開発チームと比べて
このような現象はAI特有なのでしょうか。
以前所属していたチームでアジャイル開発を取り入れたとき、最初のMVP(minimal viable product: 顧客に提供可能な最小限の機能を備えた製品)を作る時間のに比べて、次の小さな機能追加をするのが予想以上に時間がかかる、という経験を何度もしました。
機能の大きさ的にはMVPとさほど変わらないにもかかわらずです。
そして、その機能の大きさに対してかかる時間はどんどん大きくなっていき、開発チームから「そろそろリファクタリングしないとやっていけませんよ。」という声を聞くことになります。
これが、いわゆる技術的負債(Technical Debt)が積み上がっているという状態です。
これ系の本にはよく、ひとつの機能を追加するのに何ヶ月もかかり、もう全部作り直した方が速い、というような例が載っています。
MVP開発の段階では依存するものは今作っているもの以外にはありません。
MVPが完成してしまうと、小さな機能追加だとしても、既存の機能を壊さないように注意を払う必要があります。
プロジェクトの規模が小さいうちは、開発チームもどこら辺を直せば何が起きるか把握しています。
しかし、ある程度の規模になると、別のところの影響を調べるのも時間がかかるようになり、うっかり壊してしまうことも発生します。
そう、AIでもこれと同じ事が起きているのではないでしょうか。
さらに、人間の開発チームであればプロジェクトの全てを忘れてしまうというようなことはありませんが、AIは毎回真っさらな状態で機能追加をします。
AIコーディングでは、技術的負債が実装に必要な保守コストだけでなく、LLMが既存実装を読み直すコンテキスト量・推論量・トークン量として表面化するのだと考えられます。
既存の開発テクニックを使う
開発を効率的におこなう手法がいろいろあります。
僕も、開発者時代にさまざまなものを試しました。
Scrum Developerなどという資格も取りました。
- 関数を分割する
- 類似したコードは1つにまとめられる
- カプセル化する
- 責務ごとにクラスやモジュールを分ける
- レイヤーを分けて依存関係を整理する
- 共通処理をライブラリ化する
- テストを書いて変更による影響を検知する
- デザインパターンを使って、変更しやすい構造にする
こういうようなテクニックです。
つまり、コードを読んだり、理解したり、変更をしやすいようにするのです。
AIはこういうことを知らないだろうから、教えてあげました。
数100行の関数を含む、3000行くらいあったファイルがいくつものファイルに分割されました。
きれいになりました。
満足です。
しかし、すぐにそれは違和感に変わりました。
小さな機能を追加しただけなのに、クレジットの30%を消費したのです。
いやー、これは大失敗です。
なにが起きたのでしょうか。
クレジットの消費を少なくするためにやったはずなのに。
おそらく、AIは人間よりはるかに多くの量を理解し扱うことができます。
つまり、人間にとってわかりやすいはずのFive Lines of CodeのようなテクニックはAIにとっては、めんどくさく細かく分割されただけだったのでしょう。
小さな機能を追加するだけなのに、AIは大量のファイルや関数を読まなければならない。
さらに、その関連性を理解しなければならない。
1つ変更すると、その影響を調べるために何層ものファイルを行ったり来たりする。
抽象化されたコードを理解するために、さらに別のコードを読まなければならない。
人間にとって変更しやすくするための設計が、
必ずしもAIにとって変更しやすいとは限らないのではないか。
そう思うようになりました。
AIは人間のコーディングの手助けになるのか?
どうやら、人間とAIとは理解しやすいコードの構造というものが違うのだとわかってきました。
むかし、Sendmail()という1000行以上の関数を見てびっくりしたことがあるのですが、その作者はここにはメール送信に必要なものがすべて書かれているのでこれで正しいしこの方がわかりやすいんだ、と言っていたのを思い出します。
実際、彼は巨大な関数をしっかり理解していてひとりでメンテしていました。
でも、一緒にその関数をメンテするのはお断りだなぁ、と思いました。
同じように、巨大な1ファイルを好むAIと一般庶民な人間の開発者は実は相容れないのではないでしょうか。
部分を切り出してAIにお願いして、人間がメンテするのはいいかもしれませんが、人間とAIが一緒に大きなプロジェクトでコードを共有するのはあまり向かない気がします。
人間がソースコードをメンテしなければいい
となると、コーディングはAIに全部任せて、人間はコードを見なくていいのではないでしょうか。
これが、まさに、バイブコーディングです。
人間は指示するだけで、コーディングはAIのみにやらせる方法です。
ソースコードもいらないんじゃない?
となると、ソースコードも必要なくて、指示するだけでソースコードなしにバイナリーファイルを吐き出せばいいのではないか、と考えました。
おそらく、学習データがたくさんあれば、そういうことも可能になるのでしょう。
しかし、実際にはソースコードの学習データに比べて、十分に情報がないのでしょう。
AIはバイナリを直接吐き出してくれることはありませんでした。
では、人間がソースコードを見ないとすると、ソースコードは何なのでしょうか。
ここで、Java VMや各種インタプリタのことを思い出しました。
Javaでは、人間が書いたJavaのソースコードを、そのままCPUが実行するわけではありません。
いったんJavaバイトコードという中間形式に変換され、それをJVMが実行します。
Pythonも似ています。
人間が書いたPythonコードを、内部ではバイトコードのような中間表現に変換し、それをPythonの仮想マシンが実行します。
つまり、
人間が理解しやすい表現 → 中間言語 → 実際に実行されるもの
という段階があります。
そう考えると、AI時代のソースコードも、これはただの中間言語なのではないでしょうか。
Javaバイトコードを人間が直接読むことはありません。
ソースコードも人間は直接読まなくてもいいのです。
人間は何をメンテするのか
では、人間は何をメンテすればいいのでしょうか。
プロンプトから指示をすれば、ソースコードができあがります。
しかし、プロンプトが残るわけではないので、ソースコードを読まないのであれば、どんな仕様になっているのかがわかりません。
そこで、仕様そのものをいわゆるSource of Truthにすればいいのではないかと考えました。
従来の開発では、仕様書があっても、最終的にはソースコードが実際の動作を決めます。
仕様書にはこう書いてあるけれど、実装は違う。
あるいは、仕様書が古くなっていて、現在の動作はソースコードを読まないとわからない。
あるあるですね。
なので結局、ソースコードが事実上のSource of Truthになります。
開発者は、よく、「分からなければソースみたらいいよ」と平気で言います。
しかし、人間がソースコードをメンテしないのであれば、この関係を逆転させてもいいのではないでしょうか。
仕様が正で、ソースコードは仕様から生成されるもの。
人間がメンテするのは仕様です。
AIはその仕様を読み、必要なソースコードを生成したり変更したりする。
ソースコードは実行するために必要なので残りますが、単にそれだけのために存在するものとして格下げされます。
でも仕様なんてメンテしたくない
ここまで考えて、ひとつ問題が出てきました。
僕は仕様書なんてメンテしたくないんです。
プロダクトオーナーとしてたくさんの小さな機能を管理していた経験はあります。
ただ、そのときも仕様書をきちんと書いて管理していたわけではありません。
要望や仕様はチケットや会話の中にはありました。
チケットや会話の中にはいわゆる仕様書には書かれていない行間の内容がたくさん含まれます。
仕様書が What、つまり「何をするのか」を表すものだとすると、チケットや会話には Why、「なぜそうするのか」が含まれていると考えています。
たとえば、
「大きな決定ボタンを下に置く」
というのがWhatだとします。
でも、実際の会話では、
「スマートフォンで使う人が多いから」
「前の場所だと見つけてもらえなかったから」
「この操作だけは間違えて押してほしくないから」
といった話をしているはずです。
デグレをしないためにも、いわゆる仕様書よりもこのような情報を残した方がよさそうです。
となると、チケットを記録してそれをSorce of Truthとしてもよさそうですが、これには問題があります。
ある時点のチケットの内容を後のチケットが変更したり一部更新したりということがいくらでもあります。
仕様書は、現在の状態を表すものですが、チケットや会話の内容はある時点の実装すべき内容を表しているにすぎません。
では、この両方のいいとこ取りをしたものをAIにメンテさせればいいんです。
人間なら仕様書でもつらいですが、AIなら可能でしょう。
このような情報を「knowledge」と呼ぶことにしました。
ということで、
AIへの指示 → ソースコード(Source of Truth) → 実行形式
という流れを
AIへの指示 → Knowledge(Source of Truth) → ソースコード(ただの中間言語) → 実行形式
という流れに変更します。
Knowledgeを挟むことのメリット
AIはソースコードも上手に読んでくれますが、ソースコードから仕様を理解するには、複数のファイルや関数をたどり、その関係や意図を推測する必要があります。
一方、Knowledgeには現在有効なWhatとWhyがあらかじめ整理されています。
Source of TruthをKnowledgeに移すことで、AIが仕様を理解するために読み、推論しなければならない情報量を減らせるのではないかと考えました。
実際に計測したわけではありませんが、例えば、
if user.is_admin and order.status == "pending":
...
というコードをAIが読んでも、なぜ、adminの時に特別扱いするのかは他のさまざまな部分を探しに行かないと理解できませんが、
管理者だけがPending状態の注文を変更できる。
一般ユーザーによる確定前注文の改変を防ぐため。
とKnowledgeに書いてあれば、AIにとっても一目瞭然です。
Knowledge First Development スキルの作成
ここまでの内容をベースに「KFD (Knowledge First Development)」と名前を付けてみました。
この内容をスキルにし、実際の開発で使用しながら調整を繰り返しました。
Knowledgeをメンテする必要があるため、劇的なトークン仕様量の削減はないかもしれませんが、少なくとも、ある程度の大きさのプロジェクトになっても気にならない程度の消費で済んでいます。
なにより、デグレすることがほとんどなくなりました。
よかったら、ぜひ試しに使ってみてください。