ChatGPTやClaude、GitHub CopilotなどのLLM(大規模言語モデル)が、プログラムコードをすらすら書く姿を見て「なぜ?」と思ったことはないでしょうか。また、実際に使ってみると「意外と使えない場面もある」と感じた方も多いはず。

この記事では、LLMがコードを書ける仕組みから、現在の実用レベル、エラーを防ぐ方法、そして将来の限界までを解説いたします。


なぜLLMはプログラムが書けるのか

コードも「テキスト」だからです。

LLMは「次に来るトークン(単語や記号)を予測する」モデルです。プログラムコードも人間の言語と同様に文字の並びであるため、同じ仕組みで扱えます。具体的な理由は以下の4点です。

  • 大量のコードで学習している:GitHubやStack Overflowなど、インターネット上には膨大なコードが存在します。
  • コードは言語に似た構造を持つ:文法(シンタックス)・意味(セマンティクス)・文脈による正解の決まり方など、自然言語と共通する構造があります。
  • コードはパターンの繰り返し:「ファイルを読み込む」「ループを回す」「APIを叩く」など、頻出パターンが大量に学習されています。
  • 説明文とコードがセットのデータが多い:「このコードは〇〇をする」という解説とコードが対になったデータから、日本語の指示→コードという変換を自然に学習しています。

ただし注意が必要なのは、LLMはコードの「意味を理解」しているわけではなく、統計的なパターンからもっともらしいコードを生成しているという点です。これが後述する限界の根本原因になります。


現在の実用レベル:忖度なし評価

結論:「補助ツールとしては優秀、自律的な開発者としては未熟」

複数ファイルにまたがるコードが苦手な理由

  • コンテキストウィンドウの限界:大規模プロジェクト(数十万行〜)は物理的に全部読めません。見えていない部分でバグが起きても気づけません。
  • 依存関係の把握が弱い:ファイル間の依存を正確に追うのが難しく、リファクタリングで「影響範囲の見落とし」が頻発します。
  • 状態の保持ができない:会話をまたぐと前のコードを「忘れる」。長い会話の中でも前半の仕様を忘れて矛盾が生じます。
  • 実行して確認できない:動くかどうかを自分で検証できないまま出力するため、「動きそうなコード」と「動くコード」は別物です。

用途別 実用レベル評価

用途 実用度 コメント
小規模スクリプト(〜200行) ★★★★★ ほぼ任せられる
中規模(数ファイル、仕様明確) ★★★★☆ レビュー前提で使える
大規模プロジェクトの部分修正 ★★★☆☆ コンテキストの渡し方次第
大規模の設計・全体リファクタ ★★☆☆☆ 補助止まり
自律的なソフトウェア開発 ★★☆☆☆ まだ人間が必要

Claude CodeやCursor、Devinなどのエージェント型ツールはファイルを自分で読みに行けるためかなりマシになりましたが、本音を言えば「簡単なタスクは驚くほどこなすが、少し複雑になると急に崩壊する」というのが現状です。この崩壊の閾値が読めないことが最大の問題です。

LLMは簡単な仕事は任せられるが、全体を把握して自走させると痛い目を見る。常にレビューが必要。


LLMにコードを書かせる際の最大の落とし穴

最も注意すべき1点

「動いているように見える」と「正しく動いている」は別物です。LLMのコードは表面上は動き、テストも通ることがありますが、エッジケース・セキュリティ・パフォーマンスが壊れていることがあります。しかもそれが自信満々な文体で出てくるのが最大の罠です。

具体的な注意点

  • セキュリティホールを平気で生成する:SQLインジェクション、認証の抜け穴、秘密情報のハードコードなど。LLMは「動くコード」を優先し、「安全なコード」を後回しにする傾向があります。
  • ハルシネーションがコードにも起きる:存在しないライブラリのメソッドを呼んだり、古いAPIを使うことがあります。
  • テストを書かせても意味が薄いことがある:「仕様に合ったテスト」ではなく「自分の実装に合ったテスト」を書きがちで、バグとテストが一緒に間違っていると素通りしてしまいます。
  • 技術的負債の高速生成:動くコードを速く大量に作れる分、負債も速く大量に積み上がります。

人間はコードを読める必要があるか?

現時点では大いに必要であると考えられています。ただし「読む理由」が変わりつつあります。

従来の「読める理由」 現在追加された「読める理由」
自分で書くため LLMの出力を検証するため
バグを直すため バグがあることに気づくため
設計するため 正しい指示を出すため

コードを読めない人間がLLMにコードを書かせるのは、免許なしで高性能な車を運転するようなものです。スタンフォード大学の2023年の研究では、GitHub Copilot使用者はセキュリティへの意識が下がる傾向があると示唆されており、「動いた」という安心感がセキュリティレビューを省略させる構造的リスクが指摘されています。


コードのエラーを防ぐ仕組みはあるのか

「エラーが出ない仕組み」は存在しません。あるのは「エラーを早期発見する仕組み」です。大きく3層に分けられます。

1. 静的チェック層(実行前)

  • 型チェック:TypeScript、Python(mypy)、Rustなど型のある言語を使うと、型の不一致をコード実行前に検出できます。LLMは型を書かせると嘘をつきにくくなる効果もあります。
  • Linter / Formatter:ESLint、Ruff、Pylintなど。明らかなアンチパターンや文法ミスを機械的に弾きます。
  • 静的解析ツール:Bandit、SemgrepなどでSQLインジェクションや認証ミスをある程度自動検出できます。

2. 動的チェック層(実行して確認)

  • テスト:ユニットテスト・結合テストは人間が書くことが重要です。LLMの出力をテストに通します。
  • サンドボックス実行:Claude Codeなどのエージェントは実際にコードを実行して結果を確認します。「動かしてみてエラーが出たら自分で直す」ループが可能です。

3. LLM自身に検証させる工夫

同じLLMでも、生成モードと批評モードで出力が変わります。以下のように別タスクとして投げるのが有効です。

  • 「このコードのセキュリティ上の問題点を列挙して」
  • 「エッジケースを考えて、テストケースを出して」
  • 生成はClaude、レビューはGPT-4など、別モデルにレビューさせる

現時点のベストプラクティス

  1. 型のある言語を使う
  2. Linter(コードの書き方の問題を自動で指摘してくれるツール)を通す
  3. テストは人間が書く
  4. エージェントに実行ループさせる

この4つを組み合わせることで、致命的なエラーを本番に出す確率をかなり下げられます。ただしゼロにはならないのが現実です。


将来的にLLMにすべて任せられるようになるのか

達成見込みを段階別に整理

レベル 意味 見込み
単純タスクの自動化 CRUD、定型スクリプト すでにほぼ可能
仕様書→動くソフト 人間の監視付きで 5〜10年で現実的
完全自律開発 人間不要 不明・議論中
全ソフトウェア工学の代替 設計・判断含む 懐疑的な研究が多い

「不可能」とされる根拠になっている研究・理論

停止問題(Halting Problem)— チューリング 1936年

最も根本的な制約です。「あるプログラムが正しく動くかどうかを、別のプログラムが完全に検証することは数学的に不可能」と証明されています。LLMも計算機である以上、この制約から逃れられません。完全な自己検証は原理的に不可能です。

ソフトウェアの本質的複雑性 — ブルックス “No Silver Bullet”(1986年)

「ソフトウェアの本質的な複雑さは、どんなツールが来ても消えない」という主張は今も有効です。LLMはツールの進化であって、仕様の曖昧さ・要件の矛盾・ステークホルダーの利害対立といった「人間的な問題」は解決しません。

分布シフト問題(機械学習研究)

LLMは学習データの分布外の問題に弱く、新しいフレームワークや未知のビジネスロジックに対して自信を持って間違えることがあります。これは現在の深層学習の構造的問題であり、スケールで解決できるかは未解明です。

仕様の検証問題

コードが完璧でも、仕様が間違っていれば意味がありません。仕様は人間の意図・社会・ビジネス文脈から来るものであり、これを正確に機械が把握できるかは未解決の問題です。

研究されている主なリスク

リスク 研究動向
AIが生成したコードの脆弱性 スタンフォード、MITで実験的研究あり
人間のコードリテラシー低下 初期段階だが問題提起されている
技術的負債の爆発的増加 実務界で観測されはじめている
AIコードへの過信(オートメーションバイアス) 航空・医療分野の研究が参照されている
自律エージェントの暴走 Anthropic、OpenAI、DeepMindが安全研究中

特に深刻なのは、「技術の進化にリスク研究が追いついていない」という点です。「気づいたときには手遅れ」になる可能性が、現時点での最大のリスクとして指摘されています。

技術的負債とは

「今は動くけど、後で必ず問題になるコードや設計」のことです。借金と同じで、放置すると利息(余分な作業)が膨らんでいきます。

例:

# 今は動く。でも後で誰も理解できない
def f(x, y, z):
    return x * 1.08 if z == 1 else x * y

変数名に意味がなく、1.08が何なのか(消費税?)わからない。動くけど後で触るたびにコストがかかる状態です。


LLMで「爆発的に増加」する理由

通常の負債は人間がゆっくり積み上げますが、LLMは高速で大量に生成するため:

  • 整合性のないコードが短期間で大量生成される
  • 「動いた!」で満足してリファクタリングされない
  • コードを読めない人が使うと負債に気づかない
  • LLMが既存の負債を読んでさらに負債を増やす(負債が負債を呼ぶ)

人間なら1週間かかる負債を、LLMは1時間で積み上げられます。


防ぐには

1. レビューを省略しない LLMが速く作れるからこそ、レビューの時間を削らない。速度の恩恵をレビューに回す。

2. 「動いたら終わり」にしない 動いた後に「このコードは半年後も読めるか?」を必ず問う習慣。

3. 命名・コメントをLLMに要求する 最初から「変数名に意味を持たせて」「なぜこう書いたかコメントを入れて」と指示する。

4. 小さく作る 一度に大量に生成させない。小さい単位で作って、レビューしてから次へ進む。

5. 定期的なリファクタリング 借金と同じで、早めに返すほど利息が少ない。「動いてるから触らない」が最も危険。


まとめ

  • LLMがコードを書けるのは、コードも「テキストのパターン」だから。ただし意味を理解しているわけではない。
  • 現時点の実用レベルは「優秀なインターン」程度。単純タスクは任せられるが、複雑な設計は人間が握る必要がある。
  • 最大の落とし穴は「動いているように見える」こと。セキュリティや技術的負債には常に注意が必要。
  • コードを読めない人間がLLMに丸投げするのは現時点では危険。「読む目的」が変わっただけで、リテラシーは依然必須。
  • エラーを完全に防ぐ仕組みは存在しない。型チェック・Linter・テスト・実行ループの組み合わせが現実解。
  • 数学的な限界(停止問題など)から、「すべてを任せる」は原理的に不可能とする研究者が多い。リスク研究は技術の進化に追いついていない。

LLMはコードの生産速度を上げるが、判断の責任は人間にある。今のベストプラクティスは「LLMをペアプロの相手として使う」であって「丸投げする」ではない。