じゃあ、おうちで学べる

本能を呼び覚ますこのコードに、君は抗えるか

LLM に足りないのは能力ではない。あなたの事情だ

はじめに

エディタを開いたまま、手が止まっていた。

LLM(大規模言語モデル)が書いた Rust のコードを眺めている。WAL(Write-Ahead Log、データを安全に書き込むための仕組み)のセグメント管理。所有権(データの持ち主を明示する Rust 独自のルール)は正しい。ライフタイム(データが有効な期間の指定)も整合している。Clippy(Rust の静的解析ツール)の警告はゼロ。美しいコードだった。ただ、セグメントサイズが 128MB になっている。このプロジェクトでは 64MB だ。NVMe SSD(高速ストレージ)の消去ブロックサイズとリカバリ SLA(障害復旧の目標時間)の制約から決めた値だ——3ヶ月前の障害振り返りで。その判断はどこにも書いていなかった。コードにコメントもない。Slack のスレッドに埋もれていた。

LLM には何の落ち度もない。道具は完璧に動いた。道具に渡す「判断」を、私が記録していなかっただけだ。

それだけのことだった。それだけのことなのに、しばらくエディタの前から動けなかった。

あの 128MB は、LLM の失敗ではなかった。LLM は完璧に仕事をした。コードの書き方——文法、構文、お作法——においては、もう人間と遜色がない。ただ、「このプロジェクトではなぜ 64MB なのか」は知らなかった。知りようがなかった。その理由を、誰も書き残していなかったからだ。理由は私の頭の中にあった。頭の中にあったまま、言葉にならなかった。

道具の使い方は覚えた。残っているのは「何を作るべきか」と「なぜそう作るべきか」だけで、それは道具の問題ではない。 頭の中の知識を、言葉にすること。たったそれだけのことが、なぜこんなに難しいのか。

このブログが良ければ読者になったりnwiizoXGithubをフォローしてくれると嬉しいです。

道具の難しさと判断の難しさ

ここでは知的作業の難しさを、2種類に分けて考えてみます。料理で考えると分かりやすいかもしれません。

レシピ通りに作ること。手順が多くて、火加減もタイミングもシビアで、覚えることは山ほどある。でも、レシピに従えば誰でも同じ料理にたどり着けます。これが Complicated(込み入っている) という難しさです。確定申告、外国語の文法、プログラミング言語の書き方も同じ種類の難しさです。正解がどこかに書いてあって、知識の量で解決できます。

一方で、何を作るか決めること。目の前の食材、お客さんの好み、季節、予算——条件が絡み合って、「正解」は毎回変わります。同じ判断を繰り返しても、状況次第で結果が異なる。これが Complex(複雑である) という難しさです。組織づくり、採用の判断、ソフトウェアの設計判断がこれに当たります。正解はどこにも書いていなくて、判断の質でしか解決できません。

LLM が解いたのは前者です。Rust の所有権も、TypeScript の型パズルも、SQL の結合クエリ(複数のテーブルをつなげる処理)も、込み入ってはいるが複雑ではない。正しい書き方は一つ(あるいは少数)に決まり、大量の例から学べて、パターンの照合で解決できます。レシピ通りに作る、という仕事です。

Rust の所有権を理解するのに1年かかりました。その1年は無駄だったか。LLM が borrow(データの一時的な借用)を正しく書ける今、答えはイエスに見えます。しかし、所有権を理解する過程で身につけたのは書き方の知識ではありません。「このデータは誰が責任を持つのか」という設計の直感です。LLM は所有権の書き方を生成できます。しかし「このデータの所有権をどこに置くべきか」は、あの1年の苦闘なしには判断できません。ツールの使い方を覚えること自体は価値を失いました。しかし、使い方を覚える過程で身についた「判断の勘」は、むしろ価値が上がった。皮肉な構造です。ただ、これは「苦労したから報われた」という話ではありません。LLM が所有権を一瞬で正しく書ける今、1年かけて所有権を学ぶ人がどれだけいるか。その1年を経ずに判断の勘を身につける方法を、私たちはまだ見つけていません。

怖いのは、この「判断の勘」が LLM の普及とともに育たなくなる可能性があることです。AI にコードを書かせて、出てきたものをそのまま使う。それを繰り返していると、生産性は上がったように見えて、判断力は育っていない。所有権の1年で何を学んだかと言えば、コンパイルエラーとの格闘です。「なぜここで borrow checker に怒られるのか」を考え続けることで、データの責任範囲という概念が身体に入った。LLM にコードを書かせてコンパイルが通ったら、この格闘は起きません。エラーが出ない代わりに、学びも発生しない。

これは筋トレに似ています。重いバーベルを持ち上げる作業を機械に任せたら、腕は楽になる。しかし、筋肉はつかない。LLM がコードを書いてくれると、脳は楽になる。しかし、判断の筋肉はつかない。問題は、判断の筋肉が必要になるのは、LLM が間違えたときだということです。そして LLM は間違えます。そのとき「何がおかしいか」を見抜けるかどうかは、自分で格闘した経験があるかどうかにかかっている。

だから私は、AI を「書く道具」ではなく「考える相手」として使うようにしています。「これを書いて」ではなく、「この設計にはどんなトレードオフがあるか」と問う。「なぜこの方法ではダメなのか」と聞く。コードは AI に書かせてもいい。しかし「なぜそう書くのか」を考える過程は、自分の頭で通す。この使い分けが、判断の勘を鈍らせないための、今のところ唯一の方法だと思っています。たぶん。

この話は Rust に限りません。Kubernetes(コンテナ管理基盤)の設定ファイルを書くこと は Complicated ですが、何をどうデプロイするか決めること は Complex です。レシピは LLM が書いてくれる時代になりました。コードを書く、文書を作る、クエリを組む——こうした「表現する作業」は自動化されました。

しかし「今夜のメニューをどうするか」は、まだ人間にしか決められません。

消える判断——なぜ「当たり前」は記録されないか

以前、後輩にタスクを振ったことがある。手順は丁寧に伝えたつもりだった。でも、後輩がやらかした。原因は、私が「当たり前すぎて言わなかったこと」だった。暗黙的に運用しているルール——本番環境ではこの順序で操作する、このフラグはこの条件でしか立てない——を共有し忘れていた。手順書には書いていない。チームの全員が身体で覚えているだけだった。横で一緒に働いていても起きる。言葉にしていない知識は、隣に立っていても渡せないことがある。

知識管理の分野に SECI モデルという考え方があります。野中郁次郎と竹内弘高が提唱したもので、知識には「暗黙知(言葉にしにくい知識)」と「形式知(言葉にできる知識)」の2種類がある。この2つは、4つの段階を経て変換されるというモデルです。一緒に作業して感覚を共有する 共同化。その感覚を言葉に変える 表出化。言葉にされた知識同士を組み合わせる 連結化。そして、言葉にされた知識を手を動かして身体に染み込ませる 内面化。この4つのサイクルを回すことで、組織の知識が育つという話です。

あの後輩の話で言えば、私と後輩の間で共同化は起きていました。同じチームで一緒に働いているのだから。でも、私の中にあった「本番ではこうする」という暗黙知を、言葉に変える——表出化する——段階が抜け落ちていた。だから後輩には伝わらなかった。

ソフトウェアの現場でも同じことが起きています。ペアプロやモブプロをすれば、「この人はこういうときにこう考えるのか」が言葉を超えて伝わる(共同化)。コードベースを触っているうちに、どこをいじると何が壊れるかの勘が身体に入ってくる(内面化)。しかし 表出化 ——暗黙知を言葉に変える段階——だけが、いつも止まっています。

そしてここが核心なのですが、LLM は連結化の達人です。言葉にされた知識同士をつなげ、組み合わせ、新しいコードや文書を生成する——SECI の4段階の中で、LLM が最も力を発揮するのはこの連結化です。しかし連結化は、表出化された知識がなければ始まりません。「なぜ 64MB なのか」が言葉になっていなければ、LLM はそれを連結しようがない。あの後輩と同じです。隣にいても、言葉にされていない知識は渡せない。LLM はコードベースの隣にいます。しかし、表出化が止まっている限り、形式知の世界で完璧に働きながら、暗黙知には永遠にアクセスできないのです。

ソフトウェアの現場では、知識が氷山のように層をなしています。

水面上(形式知) は、ソースコードやテスト、README のような、リポジトリを開けばすぐ読める情報です。LLM はここを自在に読み、連結化できます。これは問題ありません。

水面直下(表出化されるべき暗黙知) が、一番やっかいです。たとえば、設定ファイルに timeout = 30 と書いてある。30秒。なぜ 30 秒なのか。本番環境のデータを分析して、全リクエストの 99% が 10 秒以内に返ってくるから、その 3 倍で安全マージンを取ろうと誰かが判断した。でもその経緯は、半年前の Slack のスレッドか、誰かの頭の中にしかない。コードには「30」という数字だけが残っています。他にも、「なぜ RabbitMQ ではなく Kafka を選んだのか」「gRPC(高速な通信プロトコル)を試したが、このケースでは遅すぎた」——こうした「選んだ理由」と「選ばなかった理由」は、ほとんどの場合どこにも残っていません。言葉にできるのに、言葉にされていない。表出化の手前で止まったまま放置されている知識です。

水面下(表出化が困難な暗黙知) は、さらに深い場所にあります。ベテランが本番のログを見て「何か変だな」と感じる直感。「前回これをやったら 3 日ハマった」という身体が覚えている記憶。言語化しようとしても、うまく言葉にならない類の知識です。ここは共同化(一緒に作業して伝える)でしか共有できず、LLM には原理的に届きません。

LLM が最も必要としているのは「水面直下」の知識——表出化できるのに、表出化されていない暗黙知です。なぜ表出化は止まるのか。

正直に告白します。自分のプロジェクトで思い当たることがあります。ある定数を 30 秒に設定した理由を、3ヶ月後の自分が思い出せませんでした。Git の blame(各行を誰がいつ変更したか調べるコマンド)で自分のコミットを見つけた。コミットメッセージは「タイムアウト値を調整」。なぜ 30 秒か、書いていない。コードにコメントもない。設定した時点では当たり前でした。p99 レイテンシ(全リクエストの 99% が収まる応答時間)の 3 倍。しかし「当たり前」は揮発します。3ヶ月で蒸発しました。LLM に聞いても答えられるわけがありません。私自身が答えられなくなっていたのですから。

表出化が止まる理由は、構造的なものです。

第一に、判断は「結果」だけが残り、「過程」が消えますmax_size_bytes = 64 * 1024 * 1024 というコードには「64MB」という結果だけが残ります。「128MB と 32MB を比較し、ストレージの特性と障害復旧の制約から 64MB が最適と判断した」という過程は消えている。完成した建物を見れば構造はわかります。しかし「なぜこの位置に柱があるか」は、設計書を見なければわかりません。ソフトウェアには、その設計書に当たるものを書く文化がほとんどありません。

第二に、判断した時点では「当たり前」であり、表出化する動機がありません。設計時にはチーム全員がその理由を知っています。わざわざ書くまでもない。しかし 2 年後、チームメンバーが入れ替わり、LLM がこのコードを修正しようとしたとき、その「当たり前」はどこにも残っていません。共同化で共有された暗黙知は、人が去れば一緒に消えます。

第三に、表出化の見返りが見えにくい。ADR(Architecture Decision Record、設計判断の記録文書)を書く文化を持つチームは少数派です。目の前の機能開発に比べ、判断の記録は「今すぐ役に立たない」ように見えます。しかし生成 AI の時代では、この計算が根本的に変わります。表出化された判断は LLM の連結化の材料になり、LLM が生成するすべてのコードの品質を底上げします。1 時間かけて書いた設計メモが、その後の何百回ものコード生成を支える。てこの効果が、以前とは比べものにならないほど大きくなりました。

ADR を書こうとしたことは 3 回あります。3 回とも 2 ヶ月で途絶えました。目の前のデプロイが燃えているとき、「なぜ Kafka を選んだか」を書く余裕はありません。構造的な問題です。しかし構造のせいにして手を動かさないのは、別の種類の怠惰です。今やっているのは、判断した瞬間にコードコメントを 1 行だけ残すことです。// CRC32を採用: 暗号用途でなく速度優先。SHA-256比で10倍高速。完璧な ADR ではありません。しかし、1 行でもコードの隣に書いておけば、LLM はそれを連結化の材料にできます。LLM はコミットメッセージまでは普通読みませんが、コードコメントは必ず読みます。たぶん。

プログラマ的な比喩で言えば、ソースコードは 値(value) であり、設計判断の根拠は 型(type) です。64 * 1024 * 1024 という値はそこにある。しかし「ストレージの特性に合わせた」という型注釈(値の意味を示す補足情報)がなければ、その値がなぜ正しいかは誰にもわかりません。LLM は値を生成する力に優れています。しかし型——つまり判断の根拠——を推測する力は限られています。私たちがすべきことは「型を書き下すこと」、つまり暗黙知を表出化することです。ただし「何を表出化すべきか」を判断するには、何が重要かを知っている必要があります。そして何が重要かを知っているのは、その領域で内面化を積み重ねた人間だけです。無限後退の匂いがする。匂いがするが、どこかで止めなければ何も書けない。だから、まず 1 行から始めています。

ただ、1 行書いたとして、それで本当に十分なのか。LLM に何を任せて、何を自分で握るべきか——この線引き自体が、もう一つの判断問題です。

何を委ね、何を握るか

ここまでの話を実践に落とすと、一つの判断基準が浮かび上がります。

失敗してもやり直しがきくなら LLM に委ねてよい。失敗したら取り返しがつかないなら人間が握る。

ベゾスの「一方通行のドア / 双方向のドア」と同じ発想です。戻れるドアなら素早く開けろ、戻れないドアなら慎重に考えろ。LLM への委任判断にも、この原則がそのまま当てはまります。

たとえば、画面表示用のコードが間違っていても、テストで見つけられるし修正も簡単です。こういう「やり直しがきく失敗」は LLM に任せてよい。しかし、複数のサーバーがデータを同期する仕組みの中で、データの切り詰め処理が間違っていたらどうなるか。データが静かに壊れ、気づいたときには手遅れになりえます。こういう「取り返しがつかない失敗」は、人間が確認すべきです。

基準としては明快です。しかし、この分類自体が難しい。

LLM が生成したリファクタリング(コードの整理)を通したことがあります。テストは全部通った。やり直しがきくと思った。3 週間後、下流のチームから Slack が来た。「レスポンスの nullable フィールドが消えてるんですが」。LLM のリファクタリングが、使われていないように見えたフィールドを削除していた。テストはそのフィールドを検証していなかった。私も確認していなかった。テストが通ることとやり直しがきくことは、同じではありません。

この判断基準は正しいと思います。正しいと思いますが、その基準を当てはめる力自体が、その領域の深い知識——つまり内面化された暗黙知——を求めます。「何を任せてよいか」を判断するにも背景知識が要る。完璧な線引きはできません。たぶん。

ここで見落とされがちなのが「確かめる」という仕事の難しさです。LLM が生成した Rust コードをレビューしたことがあります。文法は完璧。型も通る。Clippy も通る。しかし、5 回に 1 回、微妙におかしい。何がおかしいかを言語化するのに 3 分かかる。生成は 3 秒でした。

なぜ確かめるほうが難しいのか。人間が書いたコードのレビューでは、書いた人の意図を推測できます。コミットメッセージ、過去の傾向、その人のスキルレベル。「この人がこう書いたなら、こういう意図だろう」と当たりをつけられる。しかし LLM の出力には、意図の手がかりがありません。LLM は常に自信満々で、常に文法的に正しく、常にもっともらしい。だから「何がおかしいか」を見つけるには、正しい状態を自分の頭の中に持っていて、差分を検出するしかない。白紙から書くより、もっともらしいコードの中の微妙な間違いを見つけるほうが、認知負荷が高い。

生成は加速したのに、検証はむしろ重くなった。LLM を導入してからレビューが楽になったという話を、私はあまり聞きません。むしろ「AI が書いたコードのレビューは、人間が書いたコードのレビューより手間がかかる」という声のほうが多い。生成のスピードが上がった分だけ、レビューすべきコードの量も増えているからです。

LLM は作るのが得意で、確かめるのは苦手です。人間はこの逆を目指すべきかもしれません。作る仕事は LLM に預けて、確かめる仕事に力を注ぐ。

LLM 時代において最も価値のあるスキルは「このタスクの失敗はやり直しがきくか」を判断できることであり、それはコードを書く力とは全く異なる能力です。

冒頭の 128MB に戻る

あの後、コードにコメントを足した。

// セグメントサイズ: 64MB
// NVMe消去ブロックサイズ(128KB)の512倍。
// 障害復旧SLA 30秒以内を満たすための上限値。
// 2025-12 障害振り返りで決定。
const SEGMENT_SIZE: u64 = 64 * 1024 * 1024;

たった数行です。しかしこの数行がコードの隣にあれば、LLM は 128MB を生成しなかったでしょう。頭の中にあった知識を、コードの隣に置く。表出化です。そしてそれは、LLM が次にこのコードを読んだとき、連結化の材料になります。

「プロンプトエンジニアリング」という言葉が流行りましたが、本当に重要なのは LLM への問いかけ方ではなく、LLM に渡す情報の質——つまりコードの隣にどれだけの「なぜ」が書かれているか——だと思っています。業界では「コンテキストエンジニアリング」と呼ばれ始めていますが、SECI モデルの言葉で言えばこれは表出化です。コンテキストウィンドウが 100 万トークンになっても、判断の根拠がどこにも記録されていなければ、100 万トークン分の空白があるだけです。

正直に言えば、表出化さえすればすべて解決するとは思っていません。この記事でいう Complex の領域——条件が絡み合い、正解が状況によって変わる判断——では、どんなに文脈を渡しても LLM は崩れます。表出化で届くのは Complicated と Complex の境界線あたりまでで、その先は人間が握るしかない。それでも、その境界線を少しでも Complex 側に押し広げるために、表出化は最も確実な手段だと考えています。能力の限界は技術が押し上げるかもしれない。しかし記録されていない判断は、技術がどれだけ進歩しても永遠に失われたままです。

LLM は「やり方」を全部知っています。足りないのは「あなたの事情」だけです。

冒頭で、私はエディタの前で動けなくなりました。128MB と 64MB の差を見つめながら、自分が何を見落としていたのか、うまく言葉にできなかった。今なら言えます。見落としていたのは LLM の限界ではなく、自分自身の表出化の怠慢でした。頭の中にある「なぜ」を、コードの隣に書かなかった。後輩にタスクを振ったときと同じ失敗を、今度は LLM に対してやっていた。

あなたの事情を LLM に伝えるためには、まずあなた自身がそれを言葉にしなければなりません。道具は揃った。あとは、自分が何を作りたいのかを、自分の言葉で語ること。

そう書いて、手が止まった。自分の判断記録を振り返った。ADR は 3 回始めて 3 回途絶えた。設計会議の議事録は先月から書いていない。「語ることだ」と書いた人間が、語れているのかと聞かれると、答えに詰まる。

たぶん、語れていない。語れていないことだけは、分かっている。

それでも、コードにコメントは足した。数行だけ。それが何かの足しになるかは分からない。分からないが、書いた。