はじめに
AIにコードを書かせていると、こんな経験はありませんか?
小さなツールならほぼ一発で動くのに、規模が大きくなるほど「あれ、思ったより詰めが甘いな」という瞬間が増えていきます。こういうとき「AIの性能が足りないからだ」と片付けたくなりますが、たぶんそこじゃない。どれだけ賢いAIを使っても避けられない、確率の必然なんです。
そして、その確率の話を突き詰めていくと、最後に行き着いたのが「ソフトウェアの寿命」でした。この記事では、その思考の道のりを、できるだけ数式と実在の研究で裏取りしながらたどっていきます。
結論を先に言ってしまうと、話は4ステップで進みます。
| # | 主張 | 注意点 |
|---|---|---|
| ① | 各機能が独立に動く前提で、 規模が大きいほど完成度は指数的に落ちる |
独立性を仮定した上限値 |
| ② |
独立な見直し(壁打ち)で取り返せる。 AIはそれが速い |
「共通原因故障」に注意(§2) |
| ③ | それでも、コードは書き換わり続ける ——これが「寿命」 |
半減期は平均3〜4年 |
| ④ | 延命にはお金がかかり、 規模と雑さでその単価が上がる |
AIは係数を変えるが 限界は消えない |
急ぐ方は、各章の表と太字だけ拾えば筋は追えます。
対象はAIでコードを書いているエンジニア(2026年6月時点のLLM利用を念頭にしています)。前提は高校レベルの指数と確率くらいです。難しい式も必ず表とセットにするので、まずは数字で直感を掴んでもらえれば十分です。先に正直に言っておくと、ここで出てくるモデルはどれも現実を単純化したものです。数学は嘘をつきませんが、モデルが現実のすべてを写しているわけではない——そこはごまかさずに書いていきます。
1. 規模が大きいほど、なぜか完成度が落ちる
台風の進路予報を思い浮かべてください。数時間先ならほぼピンポイントで当たるのに、5日先になると予報円はびっくりするほど広がります。理由はシンプルで、一歩先の小さなズレが、次の一歩の出発点をずらし、それがまた次へ……と積み重なっていくから。遠い未来ほど「どこに来るか分からない」へと膨らみます(気象は、初期のごくわずかな差が時間とともに開いていく「カオス」の代表例です)。
AIにコードを書かせるのも、じつはこれに近いと思っています。ソフトウェアの失敗は「ある機能がスパッと壊れる」よりも、「方向がほんの少しズレる」ことのほうがずっと多いんです。仕様の解釈がわずかに違う、インターフェースの想定が少しずれる、前の工程の“なんとなく”を引き継ぐ——どれも単体では小さなズレ。でも工程をまたぐたびに積み重なって、規模が大きいほど、出来上がりは「狙った場所」からじわじわ遠ざかっていきます。性能のいいAIでも、大きいものほど詰めが甘くなるのは、これが正体です。
やっかいなのは、このズレが目に見えにくいこと。コンパイルは通るし、デモも動く。なのに細部がじわっとズレている——ハッキリ壊れてくれないぶん、気づくのが遅れます。台風が「消滅」するのではなく「予報円が広がる」のと同じで、白黒つかないからこそタチが悪いんですよね。
では、これを数で確かめてみましょう。といっても、数式をいきなり並べると肩がこります。なのでサイコロを振るシミュレーションでいきます。各機能(プログラム)が独立に確率 $p$ で「オンコース(許容範囲)」に収まるとして、N個の機能をつないだツールを20万回組み立てさせ、全部オンコースで通った割合を数えるだけです。
1機能あたり90%・10個つないで回すと、結果は約34.9%でした(表の「正確さ」は、1機能がうまくいく確率=成功率のことです)。3回に1回ちょっとしか、最後まで狙いどおりには着地しません。$p$ と $N$ をいろいろ振った結果が表1です。これは信頼性工学で直列システムの信頼度と呼ばれ、「直列につないだものは、全体の成功率が各成功率の掛け算になる」という法則です(体系化した技術者の名からLusserの積則[1])。
表1: AIの正確さ × 必要なプログラム(機能)の数 = ツール全体の完成度
| 精度 \ 機能数 | 1 | 5 | 10 | 50 | 100 |
|---|---|---|---|---|---|
| 90% | 90.0% | 59.0% | 34.9% | 0.5% | ほぼ0% |
| 95% | 95.0% | 77.4% | 59.9% | 7.7% | 0.6% |
| 99% | 99.0% | 95.1% | 90.4% | 60.5% | 36.6% |
| 99.9% | 99.9% | 99.5% | 99.0% | 95.1% | 90.5% |
(表中の「0.5%」「ほぼ0%」は丸め表示です。各機能が100%未満なら厳密にゼロにはなりませんが、実用上は壊滅的、という意味です。)
式で書くと、ただの掛け算です
各機能の成功率 $p$ をぜんぶ掛け算するだけ。
$$R_{\mathrm{sys}} = \prod_{i=1}^{N} R_i \quad \xrightarrow{\text{等信頼度}} \quad R = p^N$$
$N$ が増えるほど指数的に小さくなります。$0.9^{10} = 0.3487$ が、さっきの約35%の正体です。
直列系には「全体は必ず一番弱い機能より低くなる」という性質もあります。1未満の確率を掛け合わせるのだから当然で、弱い機能が1個あれば、それが全体の天井になる。各機能が98%でも、200個つなげば全体は約1.76%まで落ちます[2]。規模は、ごまかしの効かないペナルティです。
私自身、なろう小説探索アプリをAIと作っていたとき、ちょうどこれを体感しました。検索・フィルタ・キャッシュという5〜6機能が絡む部分を組み合わせると、各パーツ単体は正しいのに「全部つなぐと思った動きをしない」状況が続いた。あの「なぜかうまくいかない」感が、この表1の35%の正体だったんだと今では思っています。
ひとつだけ注意点があります。この式は各機能が統計的に独立であることが前提です。ところがAIの連続生成は前の出力に依存する(自己回帰的)ので、工程は厳密には独立ではありません。ここでは見通しのためにあえて独立を仮定しますが、現実の劣化は式より速いことも遅いこともあります。そしてこの「独立性」こそ、次の章の主役になります。
2. 「見直し」で失敗の側を叩き潰す
掛け算で痩せるなら絶望的に思えるかもしれませんが、ここで視点をひっくり返します。今度は失敗の側に味方になってもらいましょう。
書いた文章を読み返して、誤字を探す場面を思い浮かべてください。1回読むだけだと、誤字を10回に1回は見落とすとします。見落とし率10%。では、別の人がもう一度読んだらどうなるか。1つの誤字が最後まで生き残るには、2人とも同じ誤字を見落とす必要があります。それぞれ独立に10%なら、$0.1 \times 0.1 = 0.01$、つまり1%。ダブルチェックで見落としが激減するのは、これが理由です。
コードの見直しも、まったく同じ構造です。これも先ほどと同じく、シミュレーションで確かめられます。「初回で欠陥を作り込む確率」も「各見直しが見逃す確率」も、どちらも10%として、見直し回数を変えながら20万回ずつ回してみました。
結果は気持ちいいくらいハッキリ出ます。見直し0回なら全体の成功率は約34.9%(さっきと同じ)。1回入れると約90.6%、2回で約99.0%——見直しを1段増やすたびに、失敗が桁で消えていきます(表2)。ただしこれは、独立かつ等確率を仮定した最良ケース——いちばん楽観的な見積もりです。
表2: 見直しの回数 = ツールの完成度(プログラム10個・初回失敗率 f₀ = 見逃し率 m = 10%、独立な見直しを仮定した最良ケース)
| 見直しの回数 | 1機能に欠陥が残る確率 | 1機能の完成度 | ツール全体の完成度(10個) |
|---|---|---|---|
| 0 | 10.00% | 90.00% | 34.9% |
| 1 | 1.00% | 99.00% | 90.4% |
| 2 | 0.10% | 99.90% | 99.0% |
| 3 | 0.01% | 99.99% | 99.9% |
⚠️ この数字は「見直しが互いに完全に独立」な理想ケースです。同じAIに同じ文脈で頼むと見落としは相関し(次の「落とし穴」で詳述)、実際はここまで上がりません。
式で書くと(見直しはべき乗で効く)
作り込む確率 $f_0$、各見直しの見逃し確率 $m$ が独立なら、$r$ 回見直したあとに欠陥が残る確率は、
$$f_{\text{残存}} = f_0 \cdot m^{,r} \quad \xrightarrow{f_0 = m = f} \quad f^{1+r}$$
$f=0.1$ で1回見直せば $0.1^2 = 0.01$。失敗率が10%→1%、機能の成功率が 0.9→0.99 になり、10個直列でも $0.99^{10} \approx 0.90$ まで戻ります。
見直しは、いわば同じ機能を多重チェックする「並列冗長」です。系が落ちるのは、全部のチェックが同時に外したときだけ。だから失敗確率が掛け算でみるみる潰れていく——これが「見直しは救い」の正体です。
そして、ここがAIのいちばんおいしいところ。この見直しを、多くのタスクでは人間のレビューより短いサイクルで何度も回せる。壁打ち相手としてのAIが強いのは、この点にあると思っています(個人の体感であり、タスクやモデルによって異なります)。
落とし穴:見直しが「独立」じゃないと効かない
……と、ここまでは理想の話。大きな落とし穴があります。上の計算はぜんぶ「見直しが互いに独立」という前提で成り立っています。現実はそうではありません。
さっきの誤字探しでいえば、別の人ではなく同じ人がもう一度読み返すケースです。同じ思い込みで、同じ誤字をまたスルーしてしまう。こうして同じ訓練データ・同じ設計欠陥から生まれたエラーは、互いに相関します。信頼性工学ではこれを**共通原因故障(CCF: Common Cause Failure)**と呼びます[3]。よく挙がる例が福島第一原発の非常用ディーゼル発電機で、複数基あったのに津波という単一の共通原因で同時に止まりました[4]。冗長化は、共通原因の前では理論値ほど効きません。IEC 61508ではこれをβファクタ $\lambda_{\mathrm{CCF}} = \beta\lambda$ で定量化しています[5]。
AIの見直しも同じです。同じモデルに同じ文脈で「もう一回確認して」と頼んでも、同じバイアスで同じ見落としをする可能性は低くありません。だから見直しは、別視点・別エージェント・別の人で、独立に分けるほど効きます(※ここは私の解釈です)。前の記事[6]でレビュー用の「ペルソナ」(=役割や視点の違う複数のAIに、別々の観点でレビューさせる手法)を並列・独立に走らせていたのは、まさにこのためでした。
具体例(Claude Codeの場合): 同じコード案に「ユースケースに合っているか?」「エッジケースは?」「パフォーマンスは許容できるか?」という異なる質問を、それぞれ別のチャット(コンテキストを共有しない状態)で独立に投げる——こうすると各見直しが異なる出発点を持つため、同一バイアスによる共通原因故障を起こしにくくなります。
3. それでも、ソフトウェアには寿命がある
ここで話の次元が一段変わります。第1〜2章は「1個の機能を、どれだけ正しく作れるか」という品質の話でした。でも、見直しで機能の失敗率を99.9%近くまで下げたとして——それでも解決しない問題があります。コードは書き終わった瞬間から、外界(ライブラリの更新・OSの変化・仕様の進化)に追従し続けることを求められます。追従しなければ、どれだけ精度が高くても「使えないコード」になっていく。これは精度(品質)の問題ではなく、追従コストを払い続けられるかどうかという経済の問題です。コードベース全体が時間とともに書き換わり、古びていく——私が「ソフトウェアの寿命」という言葉に行き着いたのは、ここでした。
コードの半減期
ひとつ質問です。あなたが5年前に書いたコード、いま手元のプロジェクトにどれくらい残っていますか? おそらく、ほとんど残っていないはずです。リファクタやライブラリ更新で、気づけば中身は別物になっている。名前は同じプロジェクトでも、中身はすっかり入れ替わっている——古代ギリシャの「テセウスの船」そのものです。
これを実際に測った人がいます。Erik Bernhardssonは2016年、複数のOSSリポジトリ(いずれもGitHub上の著名OSS)をgit blameで解析して、コードの「半減期」を出しました(個人ブログでの分析です。記事タイトルがまさに "the ship of Theseus")[7]。集計全体の半減期は約3.3年。プロジェクト差は大きく、Linux≈6.6年、Django≈3.38年、Angular≈0.32年と幅があります。
考え方は、放射性物質の半減期と同じです。半減期 $T$ 年ごとに、生き残っているコードが半分になっていく——そう仮定して減衰を回してみると、表3のようになります。たとえば半減期3.3年なら、5年後に約35%、10年後にはわずか約12%しか残らない計算です。
表3: 今あるコードが、何年後にどれだけ生き残るか(半減期 T ごとに半分になると仮定)
| 経過年数 | 半減期2年なら | 半減期3.3年なら | 半減期5年なら |
|---|---|---|---|
| 0 | 100% | 100% | 100% |
| 2 | 50.0% | 65.7% | 75.8% |
| 5 | 17.7% | 35.0% | 50.0% |
| 10 | 3.1% | 12.2% | 25.0% |
(表の数値は「毎年一定の割合が書き換わる」と仮定した指数モデルから計算したものです。実際はもう少し裾が重く、長く生き残ったコードはしぶとく残ります——その証拠は、すぐ次に出てきます。)
ひとつ正直に言っておくと、Bernhardssonの原文に明示的な数式はありません("fitting an exponential decay" と言葉で書かれているだけ)。表3の式は標準的な指数生存モデルによる筆者の再構成です。また、彼の「半減期≈6年」と「10年後も約4割が残る」は純粋な指数では両立しません(半減期6年なら10年後は約31.5%のはず)。実カーブが指数より裾が重い——これが次のWeibull分析につながります。
式で書くと
$$S(t) = e^{-\lambda t} = \left(\frac{1}{2}\right)^{t/T_{1/2}}, \quad T_{1/2} = \frac{\ln 2}{\lambda}$$
より厳密で、査読付きの論文による分析もあります(数字の信頼度はこちらが上です)。Spinellis・Louridas・Kechagia(2021)は、89リポジトリ・2.2Mコミット・8,300万行をKaplan-Meier法+Weibull分布で解析しました[8]。対象言語はC, C#, Java, PHP, Python。コード行の中央寿命は2.37年(25パーセンタイル1.54年、75パーセンタイル4.25年)。Weibull形状パラメータは $\alpha \approx 0.52$($\alpha < 1$)で、これは時間が経つほど削除されにくくなる傾向——つまり生き残ったコードほど長生きする、ということを示しています。
サービス単位で見ると、Googleの134サービスの半減期は約4.1年、1990年代メインフレームは約7.1年という調査もあります(こちらは「コード行」ではなく「サービス」という別の数え方です)[9]。
要するに、コードは生もの。今日書いた部分の多くは、数年後には書き換わっています。
実感として——半年放置していた個人アプリを引っ張り出したら、依存ライブラリが2メジャー版上がっていてビルドが通らなくなっていました。「30分で直る」と思ったのに、廃止APIの移行と新しい書き方の学習で半日消えた。あれが保守コストの「請求書が回ってくる」感覚の正体でした。
放っておくと劣化する傾向 — Lehmanの観察(経験則)
掃除をやめた部屋は、放っておくだけで散らかっていきます。手入れをやめた庭は雑草で荒れる。何もしなくても、秩序のほうが勝手に崩れていく——この感覚、コードにもそのまま当てはまります。
では、なぜ手を入れ続けないと劣化するのか。古典的な答えがLehmanのソフトウェア進化の法則です。Belady & LehmanがIBMのOS/360を実測して体系化しました[10][11]。**先に正直に言っておくと、「法則」という名前でも実証根拠は限定的な経験則です(理由は下で述べます)。**それでも現場で広く受け入れられている観察で、核心は次の3つ(全8法則のうち、寿命に効く3つを抜き出します)。
- 第1法則 Continuing Change:使われ続けるシステムは、適応し続けないと満足度が下がる
- 第2法則 Increasing Complexity:変更のたびに複雑性が増す。減らすには能動的な作業が要る
- 第7法則 Declining Quality:きちんと保守・適応しないと、品質は低下して見える
ただし、ここは慎重に扱いたいところです。元データは約19点しかなく、統計的検定もされていません(Lehman自身、グラフは「装飾的」だと認めています)[12][13]。「法則(law)」という名前ですが、実態は経験則で、証明された自然法則ではありません。実際、glibc(1991〜2011年)の後続研究では第1法則は確認できたものの、第2法則は初期だけで後期は安定——能動的な管理の効果とも読めます[14]。
さっきの「散らかる部屋」は、まさにこれの比喩です。「ソフトウェアのエントロピーが増大する」という言い回しをよく見ますが、これはJacobsonら(1992)が熱力学の第二法則をソフトウェア劣化に当てはめたのが起点で[15]、Hunt & Thomasの『The Pragmatic Programmer』(1999)が「壊れた窓を放置するな」という実践則に落とし込みました[16]。ただしこれも比喩であって、誰かが熱力学の数式を証明したわけではありません。
雑なコードは寿命を縮める — 技術的負債
「とりあえず動くから、これで出しちゃおう」。締め切り前、つい誰もがやります。じつはこれ、借金とそっくりです。いま借りれば前に進めるけれど、放っておくと利子がどんどん膨らむ。返済(書き直し)が遅れるほど、後で苦しくなる。
このたとえを最初に持ち込んだのが技術的負債という言葉で、生みの親はWard Cunninghamです。1992年のOOPSLA発表が初出で[17]、原文を引きます。
Shipping first time code is like going into debt. A little debt speeds development so long as it is paid back promptly with a rewrite... Every minute spent on not-quite-right code counts as interest on that debt.
— Ward Cunningham, 1992 [17](© 1992 ACM/原文の一部を「...」で省略、改変なし)
借金そのものより、返さないことが問題、というのが趣旨です。
ここで大事な訂正を一つ。Cunninghamの本意は「コードとドメイン理解のズレ」を負債と呼んだもので、わざと汚く書くことを正当化するためではありません。本人が2009年の動画で明言しています("I'm never in favor of writing code poorly...")[18]。「負債=コードの汚さ」という読まれ方が広まりましたが、それは本人の意図ではないんですね。
「利子が複利で積み上がる」という表現もよく聞きますが、これはCunningham自身の言葉ではなく後世の拡張解釈=比喩です。定量的な試算例としては、Nugrohoら(2011)が44システム・969スナップショット(Javaコード)を独自の品質モデルで分析しています[19]。ある2★品質・136.5KLOCのシステムで、技術的負債 $1,608,333・年間利子 $216,666 という推計が出ました。ただしこれは特定の品質モデルに基づく1事例の試算値で、普遍的な相場ではありません。とはいえ「利子は無視できない額になりうる」という肌感はつかめます。
4. 延命にはいくらかかるのか
ソフトウェアに寿命があるなら、当然「延命できるのか、いくらかかるのか」が気になります。
古い車を思い浮かべると分かりやすいです。年式が古くなるほど、車検のたびに交換するパーツが増え、修理代がかさんでいく。そしてある日、「これ、直し続けるより買い替えたほうが安いのでは?」という分岐点が来る。ソフトウェアの保守費も、これとよく似た増え方をします。
そして、古くなる以外にもうひとつ、地味に効いてくる増え方があります。テレビ裏やデスク周りのケーブルを思い出してください。機器を1台ずつ足していくうちに、いつのまにか「どれを抜くと何が死ぬのか分からない」状態になっていませんか。古い1台を外したいだけなのに怖くて手が出せず、結局ぜんぶ抜いて配線し直す——あの感じです。ソフトウェアの場当たり修正もこれと同じで、その場しのぎを重ねるほど、次に手を入れる人は「どこを触ると何が壊れるか」を解きほぐすところから始めなければならず、1回の修正がじわじわ割高になっていきます(コードの世界で”スパゲッティ”と呼ばれるのは、伊達じゃないんです)。前のツギハギを剥がす手間が、次の作業にまるごと乗る——これが技術的負債の利子です。このあと見るように、規模が増えるほど保守の手間は比例以上にふくらんでいきます。
保守はどれだけコストを食うのか
Lientz & Swanson(1980)は487組織を調べ、開発・システムスタッフの業務時間のおよそ半分が保守に費やされていることを実測しました[20]。
ここで一つ注意です。「保守はライフサイクルコストの60〜80%」という数字をよく見ますが、これをLientz & Swansonの発見として引くのは正確ではありません。彼らが示したのは「スタッフ時間の約半分」。60〜80%は後年の複数研究の積み重ねで定説化した推計で、単一の原典があるわけではないんです。ちなみにPigoski(1997)はもっと踏み込んで「開発努力の最大90%が初回リリース後に発生、典型は2/3」と述べています[21]。
企業の話に聞こえるかもしれませんが、個人開発でも構図は同じです。半年放置した個人アプリが、ライブラリ更新やOSの仕様変更で「ある日突然ビルドが通らない」——あれも、サボった保守の“請求書”が回ってきた状態です。
規模が大きいほど、コストは比例以上に増える傾向(開発工数モデルより類推)
規模とコストの関係は、見積もりモデルでも裏づけられます。COCOMO(コードの規模から開発の手間を見積もる定番モデル)では、工数は規模の「1乗より少し上」で効きます——たとえば指数1.05なら、コードが10倍で工数は約11倍以上。つまりコードが2倍でも、手間は2倍より多いわけです[22]。
ただし、これはあくまで〈開発〉工数のモデルを「規模が効く」例として借りているだけ。条件によっては大きいほど1単位あたりのコストが下がる(スケールメリット)こともあり、超線形は無条件ではありません[23]。保守工数そのものの直接の根拠は、さっきのLientz & SwansonやPigoskiの実測のほうです。
式で書くと(COCOMO)
COCOMO 81 Organic は $E = 2.4,(\mathrm{KLOC})^{1.05}$。COCOMO II では
$$E = 2.94,(\mathrm{KLOC})^{B}, \quad B = 0.91 + 0.01 \sum SF$$
指数 $B$ は条件で $0.91$〜$1.23$ を動き、$B>1$ なら超線形(規模が2倍で工数が2倍超)、$B<1$ なら大きいほど1単位あたりの工数が割安(スケールメリット)になります。
規模ごとに増える「見直しの段数」(※私のモデル)
※この節は、公刊された実測値と私自身のモデルを混ぜています。後者(表4)には「※私のモデル」と明記します。
ここまでの「掛け算」と「べき乗」を組み合わせて、私なりに「規模が増えると延命の手間がどれだけ増えるか」をシミュレーション(計算)してみました。「ツール全体を90%に保つのに、必要なプログラム(機能)数ごとに何回の見直しが必要か」を出したのが表4です。実証研究ではなく、あくまで試算です。
表4: ツール全体を90%に保つのに、1機能あたり何回見直せばいいか(1機能の失敗率10%)※私のモデル・独立な見直しを仮定した最良ケース
| 必要なプログラム(機能)数 | 必要な1機能の正確さ | 必要な見直し回数 | 見直しなしの完成度 |
|---|---|---|---|
| 1 | 90.00% | 0 | 90.00% |
| 5 | 97.91% | 1 | 59.05% |
| 10 | 98.95% | 1 | 34.87% |
| 30 | 99.65% | 2 | 4.24% |
| 100 | 99.89% | 2 | ほぼ0% |
| 1000 | 99.99% | 3 | ほぼ0% |
機能が1→30個で必要な見直し段数が0→2、1000個でも3段。対数的にしか増えないのが見どころで、ここでもべき乗が効いています。
式で書くと(reviews_needed の閉形式)
「ツール全体を $R_{\text{target}}$(例: 0.9)に保つのに必要な最低見直し回数 $r$」は、第2章の残存欠陥式を逆算して導けます。
$$r_{\min}(N,, R_{\text{target}},, f) = \left\lceil \log_{f}!\left(1 - R_{\text{target}}^{1/N}\right) - 1 \right\rceil$$
$R_{\text{target}}=0.9$, $f=0.1$ を代入すると、$N=10$ で $r=1$、$N=30$ で $r=2$、$N=1000$ で $r=3$——表4の各行と一致します。
この式は独立性仮定・最良ケースの上限値です。CCFがある場合には適用できません。
ただし、これは独立な見直しを前提とした最良ケースです。第2章のCCF(相関する見落とし)があると、いくら段数を重ねても共通原因ぶんの失敗が残り、必要な段数は発散します。「AIで3段回せば1000個もイケる」と読むのは早合点、ということです。
AIは延命の単価を下げるのか
AIが見直しを高速で回せるなら、表4の「必要r段の見直し」のコストは下がるはず。これが、品質の話(第2章)と寿命の話(第3章)をつなぐ橋だと思っています。
信頼性工学にも近い考え方があります。テストとデバッグを繰り返すと、残ったバグが減って信頼度が「成長」していく、というモデル(SRGM)です。Goel-Okumoto(1979)のモデルが代表で、テストを重ねるほど検出済みバグ数が増え、最初は急速に増加してやがて頭打ちになります[24](残存バグは逆に急速に減り、ゼロに近づく)。AIが「テストとデバッグのループ」を高速化するのは、この観点では“成長を速める”ことに近い——※これは私の解釈です。
式で書くと(SRGM)
Goel-Okumoto の平均値関数は、
$$m(t) = a\bigl(1 - e^{-bt}\bigr)$$
$t$=テスト時間、$a$=検出可能な欠陥の総数、$b$=単位時間あたりの故障検出強度(rate; 確率ではなく強度パラメータ)。$b$ を大きくすると、早く頭打ちに達します。
ただし橋渡しには注意が要ります。$b$ は連続的なテスト時間あたりの検出率で、第2章の離散的な見直し回数とも、LLMの生成サイクルとも前提や次元が違います。あくまで「反復すれば残存欠陥が減る」という定性的なアナロジーで、そのまま当てはめるにはさらに仮定が要ります。
そして、ぶっちゃけ逆の見方もあります。AIは生成量を爆発的に増やすぶん、重複コードやchurn(書いては捨てられるコード)を増やし、レビューを薄めて総コストをむしろ押し上げる、と。「単価が下がる」は私の推測で、確たる定量的裏づけがあるわけではありません。
整理すると、同じAIでも使い方で符号が変わります。単価を下げやすいのは——見直しを「別の視点で・独立に」回すとき、テストを自動で回せるとき。逆に上げやすいのは——生成しっぱなしでレビューが薄いとき、ツギハギを別のツギハギで重ねるとき。延命にAIを使うなら、前者の条件に寄せるのがコツです。
付け加えると、2026年6月時点のLLM(Claude 4.x、GPT-4oクラス)はコード生成の精度がかなり上がっています。それでもこの記事の話は変わりません。モデルが賢くなって $p$ が99.9%に近づいても、100機能つなげば全体は約90%。コードは数年で書き換わり、規模が増えれば保守コストは比例以上に効く。AIはこの方程式の係数を変えますが、方程式そのものは消えません。
それでも、どれだけAIが速くなっても、コードは複雑性を増し、外界の変化に追従し続けることを求められます。「それでも追い続けるか」「どこかで引退させるか」——その判断点こそが、ソフトウェアの寿命です。どんな製品も、いつかここを通ります。あなたは、自分のプロダクトにいくらかけますか?
まとめ
長くなったので、ざっくり要約します。
| 主張 | ひとことで言うと | 現実の補足 |
|---|---|---|
| 規模は罰 | 各機能の成功率を全部掛け算(増えるほど小さく) | 各機能が独立という仮定で成立 |
| 見直しは救い | 失敗率を「見直し回数のべき乗」で削る | 独立な見直しほど効く(CCFに注意) |
| コードは生もの | 半減期ごとに半分ずつ書き換わる | 実際の減衰は裾が重い(Weibull α≈0.52) |
| 雑さは利子 | ツギハギを剥がす手間が次に乗る | 定量はSIG事例の試算値に限定 |
| 延命は超線形 | 規模が増えると手間は比例以上 | 開発工数モデルの援用。AIは単価を下げうるが、逆に増やす見方もある(筆者推測) |
数学が教えてくれたのは、ざっくりこういうことでした。規模は罰、見直しは救い(ただし独立なほど)、エントロピー増大は比喩でも劣化は現実、そして寿命は最後に「経済の選択」としてやってくる。
AIはこの方程式の係数を変えます。見直しの速度を上げ、延命の単価を下げ得る(さっき書いたとおり、逆もありえますが)。それでも、限界そのものを消すことはできません。
ひとつ、数式には出てこない話を加えます。
この記事では「精度 p」を所与の数として計算しました。でも現実の開発で、AIに書かせたコードの精度が何%かを知る方法はありません。テストが通り、動いている——それは「合格」であって、「p = ○%」ではない。
品質を読み取れるのは、知識と経験です。変数名は適切か、依存は整理されているか、将来の変更に耐えられる構造か——「半減期を延ばす書き方か」「ツギハギが利子を産んでいないか」を判断するのは、あなたの審美眼です。生成物の量が増えるほど、その審美眼の有無が、速度の良薬か毒かを分けます。
数学を知っておくことは「何を怖がればいいか」を知ることです。そしてその怖がりを実際のコードに当てはめるのは、ツールが何に変わっても、読む人の目だと思います。
よくみつけたね!でもここには何もないよ!
検証コード
表1〜4の生成コード(純粋Python・依存ゼロ・assert通過済み)
この記事の数値はすべて experiments/002-software-lifespan/reliability_lifespan.py で生成・自己検証しています。サードパーティライブラリ不要で、python reliability_lifespan.py で再現できます。(コードの完全版はGitHubに公開予定——公開後にURLをここに追記します)
本文で「サイコロを20万回振った」と言っていたのは、このコードのモンテカルロ・シミュレーションです。式の値と実験値がちゃんと一致することを、まず確かめています。
| 実験 | シミュレーション(20万回) | 理論値(式) |
|---|---|---|
| 90%の機能を10個つなぐ | 34.9% | 0.9^10 = 34.9% |
| 見直し1回(10個) | 90.6% | (1-0.1^2)^10 = 90.4% |
| 見直し2回(10個) | 99.0% | (1-0.1^3)^10 = 99.0% |
コアの関数を抜粋します。
import math
import random
def series_reliability(p: float, n: int) -> float:
"""@brief Reliability of N independent components that must ALL succeed.
Series-system reliability: the system works only if every one of the N
independent parts works, so the probabilities multiply.
@param p Per-component success probability (0..1).
@param n Number of independent components in series.
@return Whole-system success probability p ** n.
"""
return p ** n
def residual_failure(f: float, reviews: int) -> float:
"""@brief Residual failure, BEST CASE where introduce-rate == miss-rate == f.
Special case of residual_failure_general() with f0 = m = f, i.e. the optimistic
assumption that the chance of LEAVING a defect equals the chance of MISSING one
in review. Then residual = f ** (1 + reviews). This is the best-case figure.
@param f Shared introduce/miss probability of a single independent pass.
@param reviews Number of independent review passes after the initial attempt.
@return Residual survival probability = f ** (1 + reviews).
@note Assumes f0 == m AND independence; correlated/common-cause misses raise
the real residual above this value (see the article's CCF section).
"""
return f ** (1 + reviews)
def half_life_survival(t: float, t_half: float) -> float:
"""@brief Fraction of code still "alive" at time t under exponential decay.
S(t) = (1/2) ** (t / T_half) = exp(-lambda * t), lambda = ln(2) / T_half.
@param t Elapsed time (same unit as t_half, e.g. years).
@param t_half Half-life: time for half the code to be rewritten/removed.
@return Surviving fraction in [0, 1].
"""
return 0.5 ** (t / t_half)
def monte_carlo_build(p: float, n: int, trials: int, seed: int = 0) -> float:
"""@brief Roll the dice `trials` times: n parts each on-course w.p. p; all must pass.
@return Empirical whole-build success fraction (~ p**n). This is the experiment
behind the analytic series_reliability(); the two agree in self-check.
"""
rng = random.Random(seed)
ok = sum(all(rng.random() < p for _ in range(n)) for _ in range(trials))
return ok / trials
なお、初期失敗率と見逃し率を分けた一般形 residual_failure_general(f0, m, reviews) = f0 * m**reviews も用意してあります。上の residual_failure は、その $f_0 = m = f$ とした特例(最良ケース)です。
自己検証assertは以下がすべてパスします(数式どおりに計算されていることと、シミュレーションが理論値とほぼ一致することの確認であって、モデルが現実に当てはまることの証明ではありません)。
assert abs(series_reliability(0.9, 10) - 0.34867844) < 1e-6 # 0.9^10 ≈ 34.9%
assert abs(residual_failure(0.1, 1) - 0.01) < 1e-12 # 0.1^2 = 1%
assert abs(series_reliability(0.99, 10) - 0.90438208) < 1e-6 # 0.99^10 ≈ 90.4%
# モンテカルロも理論値とほぼ一致(許容誤差1%)
assert abs(monte_carlo_build(0.9, 10, 200_000) - 0.34867844) < 0.01
参考・出典
[1] "Lusser's law" — Wikipedia. https://en.wikipedia.org/wiki/Lusser%27s_law / Robert Lusser(人物): https://en.wikipedia.org/wiki/Robert_Lusser (参照 2026-06-18)
[2] "Reliability of Systems" — IntechOpen, 2016. https://www.intechopen.com/chapters/50094 (参照 2026-06-18)
[3] "Common mode failure" — Wikipedia. https://en.wikipedia.org/wiki/Common_mode_failure (参照 2026-06-18)
[4] M. Rausand, A. Barros, A. Høyland「System Reliability Theory」3rd ed., Wiley, 2020. ISBN 9781119373520.
[5] "Common Cause Failures in Safety Instrumented Systems (β-factor / IEC 61508)" — SINTEF A26922. https://www.sintef.no/globalassets/project/pds/reports/sintef-a26922-common-cause-failures-in-safety-instrumented-systems-beta....pdf (参照 2026-06-18)
[6] @mryocode123「【Claude Code】小手先で最強テクニック8選」— Qiita, 2026-06-17. https://qiita.com/muryodecode/items/9f7d29d90fd0b8dc7fd3 (参照 2026-06-19)
[7] E. Bernhardsson「The half-life of code & the ship of Theseus」— erikbern.com, 2016-12-05. https://erikbern.com/2016/12/05/the-half-life-of-code.html / git-of-theseus: https://github.com/erikbern/git-of-theseus (参照 2026-06-18)
[8] D. Spinellis, P. Louridas, M. Kechagia「Software evolution: the lifetime of fine-grained elements」— PeerJ Computer Science, 2021. https://pmc.ncbi.nlm.nih.gov/articles/PMC7959608/ (参照 2026-06-18)
[9] D. Jones「Half-life of software as a service, services」— Shape of Code, 2018-11-28. https://shape-of-code.com/2018/11/28/half-life-of-software-as-a-service-services/ (参照 2026-06-18)
[10] L. A. Belady, M. M. Lehman「A Model of Large Program Development」— IBM Systems Journal 15(3):225–252, 1976. https://dl.acm.org/doi/10.1147/sj.153.0225 (参照 2026-06-18)
[11] M. M. Lehman「Programs, Life Cycles, and Laws of Software Evolution」— Proc. IEEE 68(9):1060–1076, 1980. / "Lehman's laws of software evolution" — Wikipedia: https://en.wikipedia.org/wiki/Lehman%27s_laws_of_software_evolution (参照 2026-06-18)
[12] I. Herraiz et al.「The Evolution of the Laws of Software Evolution」— ACM Computing Surveys 46(2), 2014. https://oa.upm.es/20813/1/herraiz_csur.pdf (参照 2026-06-18)
[13] D. Jones「Lehman's laws of software evolution」— Shape of Code, 2019-06-16. https://shape-of-code.com/2019/06/16/lehman-laws-of-software-evolution/ (参照 2026-06-18)
[14] Fernandez-Ramil et al.(glibcによるLehman法則の実証研究)— PMC4375964. https://pmc.ncbi.nlm.nih.gov/articles/PMC4375964/ (参照 2026-06-18)
[15] I. Jacobson et al.「Object-Oriented Software Engineering」— Addison-Wesley, 1992, pp.70–72. / "Software entropy" — Wikipedia: https://en.wikipedia.org/wiki/Software_entropy (参照 2026-06-18)
[16] A. Hunt, D. Thomas「The Pragmatic Programmer」— Addison-Wesley, 1999. Software Entropy節 / "Don't Live with Broken Windows"(Tip 4 in 1st ed., Tip 5 in 2nd ed.).
[17] W. Cunningham「The WyCash Portfolio Management System」— OOPSLA '92, ACM SIGPLAN OOPS Messenger 4(2):29–30, 1992. https://dl.acm.org/doi/10.1145/157709.157715 / 全文: http://c2.com/doc/oopsla92.html (参照 2026-06-18)
[18] W. Cunningham「Debt Metaphor」— YouTube, 2009. https://www.youtube.com/watch?v=pqeJFYwnkjE (参照 2026-06-18)
[19] A. Nugroho, J. Visser, T. Kuipers「An Empirical Model of Technical Debt and Interest」— TechDebt 2011, ACM, 2011. https://dl.acm.org/doi/10.1145/1985362.1985364 (TechDebt 2024 Most Influential Paper Award 受賞)(参照 2026-06-18)
[20] B. P. Lientz, E. B. Swanson「Software Maintenance Management」— Addison-Wesley, 1980. / 解説: https://dzone.com/articles/lientz-and-swanson-software (参照 2026-06-18)
[21] T. M. Pigoski「Practical Software Maintenance」— Wiley, 1997. https://www.ifsq.org/work-pigoski-1997.html (参照 2026-06-18)
[22] B. Boehm「Software Engineering Economics」— Prentice-Hall, 1981.
[23] B. Boehm et al.「COCOMO II Model Definition Manual」— USC, 2000. https://www.cs.montana.edu/courses/spring2004/352/public/cocomo/modelman.pdf (参照 2026-06-18)
[24] A. L. Goel, K. Okumoto「Time-Dependent Error-Detection Rate Model for Software Reliability and Other Performance Measures」— IEEE Transactions on Reliability R-28(3):206–211, 1979.
宣伝
Xアカウント:@mryocode123
普段はこんなものを作っています:
- コピペを題材にしたインクリメンタルゲーム
- なろう小説をマッチングアプリ式に探索できるアプリ
- 素数ガチャ(超大桁の素数をガチャで引いて、新発見かどうかを判定する謎アプリ)
リリースしたら、ぜひ遊んでください!
開発の進捗は X(@mryocode123)で発信しているので、よかったらフォローしてもらえると嬉しいです。
Qiitaもフォローお願いします
この Qiita では、今後も実体験にもとづいた開発者のリアルな情報を発信していく予定です。
「実際に使って効いたテクニック」を中心に、個人開発での生きた知見をシェアしていきます。
役に立ったら、フォローやこの記事へのいいねが次の記事の励みになります!