Vibe Coding の技術的負債:AI が作ったアプリはなぜ後から変えにくいのか
AI が作った経費精算システムは数か月動いたが、税務ルールが変わると誰も 12,000 行の未読コードに触れなかった。長期システムには、コードではなくレビュー可能な定義が必要だ。
結論から言うと: AI が生成したコードのボトルネックは、書く速さではない――半年後には誰もそれを読めず、しかも問い合わせる相手の作者も存在しないことだ。AI にはコードではなくメタデータを生成させればよい。そうすれば、負債は雪だるま式に膨らまない。
物語は七か月目から始まる。
ミンシャン(明享)は数百店舗を展開するチェーン小売企業だ。昨年、その財務チーム(五十数名)は IT の順番待ちにうんざりして、AI に「一言で」経費精算システムを作らせた。フォーム、承認、総勘定元帳との連携が、二日で動き出し、全員が喝采した。それから半年以上、ずっと問題なく動いていた。
七か月目、税務ルールが変わった。ある種類の請求書の控除率が調整され、精算ロジックもそれに合わせて変えなければならなくなった。小さな一件だ。
ところがチームは、誰もそれに手を出せないことに気づいた。あの「一言」の裏にあったのは、AI が生成した一万二千行超のコードで、それを一人として通して読んだ者がいなかった。控除ロジックはどのファイルに散らばっているのか? 変更すると承認や月末照合に連鎖的に影響しないか? 誰もはっきり言えない。さらにばつが悪いのは――このコードを書いた当の AI も助けにならなかったことだ。それはステートレスで、あの生成のときに頭の中にあった判断や、トレードオフや、なぜこう書いたのかは、一切残っていない。あなたにできるのは、一万二千行を新しいセッションに改めて食わせて、当時の自分自身を「推測」させることだけだ。
この小さな一件は、結局三週間かかった。そして本当の代償は三週間にとどまらない。リリース前のレビューで、AI が控除とは無関係な照合ロジックを一か所、ついでに改変していたことに誰かが気づいた――あと一歩で、一群の精算が金額を誤って計算され、総勘定元帳へ流れ込むところだった。今回は運良く食い止められたが、それが物語ったのは一つのことだ。このシステムはもう、誰も「A を変えても B に影響しない」と保証できない。
これが、AI 生成コードの技術的負債が、ごく普通のチームの上に落ちてきたときの姿だ。
これは特殊な負債だ:借りたのに、債権者が見つからない
技術的負債は新しい言葉ではない。だが AI が生成したコードが背負うのは、これまでになかった種類の負債だ。
人が書いたコードも汚くなるし、ドキュメントがないこともある。だが少なくとも 作者がまだいるかもしれない――それを書いた人を呼んで「ここはなぜこう処理したのか」と一言聞ける場合がある。コードがどれほどひどくても、その背後にはたいてい説明できる人間の脳がある。
AI が生成したコードは、この最後の帳尻合わせまで抜き取ってしまう。その「作者」はステートレスなモデルで、生成し終えたその瞬間に存在しなくなる。追跡できる設計判断も残っていない。こうしてあなたの手元には、動くが、誰にも説明できず、聞ける相手もいない 実装の塊が残る。ミンシャンのあの一万二千行は、まさにこの種の負債だ――コードの品質を借金しているのではなく、「もう誰も本当には理解していない」ことを借金しているのだ。
同じメカニズムは、チーム規模が大きくなるほど見えやすくなる。AI は生成速度を上げるが、レビュー、理解、業務上の責任は同じ速度では増えない。速度は本物だ。負債も本物だ――ただ負債は納品の日には決済されず、七か月目に取り立てに来る。
だがコード生成は猛烈に良くなっているのでは?
ここで一つの反論を真剣に受け止めなければ公正でない。モデルはずっと強くなり続けていて、読めるコードもどんどん長くなり、さらに AI コードレビューや「自己修復」パイプラインもある――あと一年もすれば、こうした負債は、より強いツールが自動で返してくれるのではないか?
この期待にはそれなりの理屈があるが、それはボトルネックの場所を賭け間違えている。
コード生成の進歩が解決するのは「より速く書く」ことだ。だがミンシャンの問題は、そもそも書くのが速くないことではなかった――むしろ逆で、速すぎたことだ。二日で一つのシステムができた。ボトルネックは「読み解けるか、責任を負えるか」という側にあり、それは生成能力に線形に連動して改善するものではない。より強いモデルがより多くのコードを、より速く生成すれば、誰も読んだことのない実装もより速く積み上がる。AI レビューはひととおりスキャンする手伝いをしてくれるが、「この業務ロジックは 本来 どうあるべきか」という判断には確定した答えを出せない。正しいか間違っているかは業務の文脈の中にしか存在せず、コードの中にはないからだ。
言い換えれば、「モデルがもう少し強くなれば」に望みを託しても、レビューのボトルネックは消えない。
複利:一回変えるたびに、もう少し読めなくなる
なぜ七か月目で、一か月目ではないのか? この種の負債が 複利 だからだ。
最初の変更で、AI は古いコードを読み解けないまま「その横にもう一段付け足す」。二度目の変更では、AI が向き合うのは「古いコード + 前回読み解けなかった新しいコード」で、そこにもう一段付け足す。各層が、誰も理解していない前の層の上に重なっていき、理解のコストが指数的に上がる。だからリスク曲線はゆるやかに上がる直線ではなく、序盤は穏やかで、中盤で急に立ち上がる指数曲線だ――穏やかで何でもないように見えるせいで、いつもどおりの何でもない変更がそれに火をつけるその時まで、大丈夫だと思い込ませる。
簡単な自己診断がある。自分が曲線のどの区間にいるかを前もって見せてくれる。以下の五項目のうち三つに当たれば、あなたはすでに AI 技術的負債を背負っている。
- システムの中に、なぜそう書かれているのかをチームの誰も説明できないコードがある。
- 一か所を変えるとき、まず思うのが「ほかの場所を壊さないか」であって、すぐに変えに行くではない。
- セキュリティ / アラートの件数が毎月増えていて、その大半が新規追加コードから来ている。
- 変更するたびに、コードの塊を丸ごと AI に食わせて「もう一度読み直させ」なければならない。
- PR がどんどん大きくなり、誰も実際には一行ずつ見られず、「テストは通ったか」だけ見るしかない。
この負債の請求書を、最後に誰が払うのか
複利曲線は最後に具体的な一枚の請求書になり、四者で分けて払うことになる。
- 業務 が払うのは速度だ。ミンシャンのあの「小さな変更」は三週間かかった。変えるたびにほかを壊すのが怖いと、反復の速度は目に見えて崩れていく――AI で速くなったと思っていたのに、半年後にはかえって遅くなる。
- リスク管理 が払うのは事故だ。あの総勘定元帳に流れ込みかけた誤算は食い止められたが、次回はそうとは限らない。アラート、例外、安全レビューが積み上がるほど、どこかで見逃しが出る。
- チーム が払うのは人だ。自分が読み解けず、変えると揺らぐシステムの塊を、誰も長期にわたって保守したがらない。それを最もよく知る人が去れば、このシステムは完全にブラックボックスになる――そして AI コードには、そもそも「最もよく知る人」がいない。
- 会社 が払うのは書き直しだ。理解の負債がある点まで溜まると、「変える」が「書き直す」よりも高く、危険になり、プロジェクトは作り直しになる。初版で節約したあのわずかな時間が、元利合わせて返ってくる。
この四つの負債は、どれ一つとして「二日でシステムを作り上げた」あの驚きの中には現れていない。すべて、七か月目以降の帳簿に記されている。
まず明確にしておく:メタデータに置き換えるのも、代償がないわけではない
ここで正直に立ち止まらなければならない。さもないと、また霊薬売りになってしまう。
AI にコードではなく定義を生成させることには、トレードオフがある。宣言的なメタデータがカバーするのは、企業アプリの中で繰り返し現れる構造だ――オブジェクト、フィールド、リレーション、ビュー、権限、フロー、承認。それが引き換えにもたらす制御可能性の代償として、あなたは「好きなように書ける」自由度の一部を手放す。もしあなたが作ろうとしているのが、まったく新しいリアルタイム協調エンジンや、独自のグラフアルゴリズムなら、それは確かに本物のコードが必要で、メタデータは助けにならず、無理に当てはめればかえって台なしになる――ObjectStack が解決するのは、企業の中で何度も作り直される業務システムであって、次の Figma ではない。
もう一つの正直な点。メタデータランタイムを選ぶことは、実装の一部をそのランタイムに委ねることに等しく、あなたが賭けるのはその品質と長期的な可用性だ。これは本物の依存である――ただ、「一万二千行の誰も読まないコード」に賭けるのに比べれば、繰り返し監査され、バージョン管理され、移行できるランタイムに賭けるほうが、はるかに良い賭けだ。
生成するものを取り替える:実装ではなく、定義を
ミンシャンのあの経費精算システムに戻ろう。同じ要件で、もし当時 AI が生成したのが一万二千行の実装ではなく、こんな一つの定義だったなら――
export const ExpenseReport = ObjectSchema.create({
name: 'fin_expense_report',
label: '経費精算書',
fields: {
amount: Field.currency({ label: '金額', min: 0 }),
invoice_type: Field.select({ label: '請求書種別', options: ['普通請求書', '専用請求書'] }),
deductible_rate: Field.percent({ label: '控除率' }),
},
});
この数十行は「これから実装すべきインターフェース」ではなく、それ自体がシステムだ。ランタイム(ObjectOS)がそれを読み取り、データテーブル、API、画面、そして agent が呼び出せるガバナンスされたツールを自動で派生させる。権限や承認フローも同じく、それにぶら下がった宣言であって、手書きの判断ではない。
では、七か月目のあの税務ルール変更は、どんな姿になるか? もはや考古学的な発掘ではなく、読めば理解できる一行の変更だ。
- deductible_rate: Field.percent({ label: '控除率' }),
+ deductible_rate: Field.percent({ label: '控除率', defaultValue: 70 }),
+ // 専用請求書は新ルールで 70% 控除。ルールは定義に下ろし、承認と照合が自動で追随する
これは五分でレビューでき、ワンクリックでロールバックできる diff であって、誰も「マージ」を押せない四百行の PR ではない。「ついでに照合を壊す」ようなことも起きない。照合ロジックはこの定義の中になく、そもそも触れられないからだ。違いはどちらがより「高級」かではなく、半年後に、まだ誰が読み解け、変えられ、間違いを追跡できるか にある。
複利のメカニズムは、なぜ断ち切られたのか
定義に置き換えたあと、あの曲線がなぜ平坦になるのか? 三点、いずれもミンシャンのあの物語を直接指している。
- 「誰も読んだことのない実装」がない。 実装は、繰り返し監査され、すべてのアプリが共用する同じ一つのランタイムに属していて、アプリごとに一つずつ生成されるのではない。あなたのアプリに残るのは定義だけで、定義は人に読ませるためのものだ。
- 「聞ける相手がいない」がない。 定義は宣言的で、意図を顔に書いている――
deductible_rateが何で、誰が管理し、それを変えるとどのフローに影響するかが一目瞭然で、とうに消え去った生成セッションに尋ねる必要がない。 - 脆弱性の面が縮小する。 多くの一般的なリスクは、手書きの認可、クエリの連結、入力処理から来る。これらを統一されたランタイムが引き受けたあとは、アプリ層が持ち込みうる脆弱性のカテゴリは少なくなる。
結び
問題は、AI がコードを書くことそのものではない。あまりに多くのチームが「プロトタイプを高速に生成する」ためのツールを、そのまま「長期的なシステムを建設する」やり方として使ってしまうことだ。プロトタイプは誰も読まないコードの寄せ集めでもよい。三年使うシステムは、そうであってはならない。
次に AI で、本当に長期的に使う業務システムを作ろうとするとき、まず「どれだけ速く生成できるか」を問うのではなく、ミンシャンが七か月目になってようやく向き合わされたあの問いを問おう。半年後にルールが変わったとき、私はまだそれを変える勇気があるか、間違いを追跡できるか。 AI にコードではなく定義を生成させることは、その答えを「ある」に近づける道だ。
npm i -g @objectstack/cli && os start
オブジェクトを一つ定義し、フィールドを一度変えて、その変更が git diff の中でどれだけきれいかを見てみよう――それが、複利のつかない負債だ。