ハクソク

世界を動かす技術を、日本語で。

技術的、認知的、そして意図的な負債

概要

  • LLMの普及でCognitive Debt(認知的負債)の重要性が増加
  • システム健全性を「技術的負債」「認知的負債」「意図的負債」の三層で整理
  • AI時代の思考モデル「Tri-System theory of cognition」と認知的サレンダーの話題
  • コーディングエージェント時代の本質的課題は「検証」へのシフト
  • LLM時代のコードと抽象化、そして人間の役割の再定義

LLM時代のシステム負債とその管理

  • Cognitive Debt(認知的負債)の比喩が、チームがシステム理解を失う現象の説明として普及
  • Margaret-Anne Storeyによる三層モデル
    • 技術的負債:コード内に存在、将来の変更容易性を犠牲にした実装判断の蓄積
    • 認知的負債:人に蓄積、システムの共有理解が減少することで発生
    • 意図的負債:成果物に蓄積、システムの目標や制約が適切に維持されないことによるもの
  • これら三つの負債は相互作用し、いずれもシステムの進化や運用に制約を与える
  • 各負債の診断・軽減方法がまとめられており、チーム全体での継続的な管理活動の重要性

AIと人間の思考モデル:Tri-System theory of cognition

  • Kahnemanの「Thinking Fast and Slow」に基づく二重過程理論の拡張
    • System 1:直感、迅速かつ無意識的な意思決定
    • System 2:熟慮、意識的で論理的な思考
    • System 3(Shaw & Naveによる追加):AIによる外部推論
  • Cognitive Surrender(認知的サレンダー):AIに無批判に依存し、System 2をバイパスする現象
  • Cognitive Offloading(認知的オフローディング):熟慮中に戦略的に認知負荷を外部委託する行為
  • Tri-System theory of cognitionの行動予測実験の紹介

コーディングエージェント時代の本質的課題:検証へのシフト

  • Ajey Goreによる「コーディングが無料化した後の本質的コストは検証」という指摘
  • 「正しさ」の定義が多様かつ文脈依存であり、判断が人間にしかできない領域
  • LLMやエージェントは自動検証が充実している場合に特に効果を発揮
  • Test Driven Developmentの推進や、より広範なテスト理解支援の重要性
  • エンジニアリング組織の再編
    • コーディング担当が減り、受け入れ基準やテスト設計、結果監視を担う人材が増加
    • 「何を作ったか」ではなく「何を検証したか」が評価軸となる
    • 判断能力を重視する文化への転換が必要

LLM時代のコードの未来と人間の役割

  • LLM時代におけるソースコードの役割や新言語の模索
    • LLM向け新言語の試行や、TypeScript・Rustのような型安全言語の適合性議論
  • 人間とLLMの協働による抽象化・ドメイン言語(Ubiquitous Language)の重要性
    • 問題領域を分割し、関連データと振る舞いを結びつけ、意図を明確に表現する命名の創造性
  • コードを単なる構文記述ではなく、問題解決のための構造的な表現として捉える姿勢の必要性

Hackerたちの意見

マーチンの言ってることはわかるけど、抽象レイヤーを上げる理由はどんな時でも言えるよね。アセンブリからPythonに移行すると、彼の定義によれば、かなりの意図と認知の負債が生まれる。ハードウェアのビットをどう操作するか考えずに、インタープリターに任せちゃったから。私の反論は、彼が言ってる技術的意図は、人間の意図を機械語に翻訳する必要があったからこそ存在するってこと。コードでドメイン駆動の抽象を考えなくても、問題について深く考えることはできるよ。マインドマップを作ったり、日記を書いたり、壁に付箋を貼りまくったりしてもいいしね。オブジェクト指向の抽象を作るのは魔法じゃないよ。
> ハードウェアのビットをどう操作するか考えずに、インタープリターに任せちゃったから。 決定論的なコードを考えてるなら、ハードウェアのビット操作を考えてるってことだよ。人間が理解しやすい言語でやってるだけなんだ。意図の直接的なマッピングがあるよ。
自分の意図を形式的な言語に翻訳すること自体が思考の道具なんだよね。そのプロセスを通じて、あいまいさや考慮してなかった側面、詳細が見えてくることもあるし、全体のアプローチを再考しなきゃいけない場合もある。自然言語で書くことも思考の道具にはなるけど、あいまいさを許さない形式的な言語に自分の思考プロセスを合わせることには重要な要素がある。自然言語で数学をやるのが面倒でエラーが起きやすいのと似てるね。
> アセンブリからPythonに移行すると、かなりの意図と認知の負債が生まれる。ハードウェアのビットをどう操作するか考えずに、インタープリターに任せちゃったから。 そうだよね!プロジェクトが徐々にctypesコードを使うようになって、その穴から抜け出そうとするのをよく見る。前の仕事では、パフォーマンス要件が理解されていなかったからPythonを使ってプロジェクトが立ち上げられたんだ。1、2年後にはテープアウトのボトルネックになって、再構築する時にはほとんどの抽象アーキテクチャが捨てられちゃった。だって、全部Python的だったから、C++で違うアプローチが必要だったんだよね。
「意図」という言葉は好きだけど、マーチン・ファウラーのエッセイを読んで、もっと考えさせられた。トーマス・クーンがパラダイムシフトについて話したとき、「パラダイム」は20以上の異なる意味を持つことになった。最近、意図もプログラミングの中で最も汚染され、使われすぎている言葉の一つになったと思う。自分のトイ言語プロジェクトでも「意図」という言葉を使っているから、他人をあまり厳しく批判する立場にはないけどね。Hacker Newsのコメントを読んでいると、プログラミングは根本的にメンタルモデルを構築することだと思ったし、市場は最終的に自分のメンタルモデルを買うんだよね。人間の意図から始めると、チェーンはこんな感じになるかも:人間の意図 -> 問題モデル -> 抽象化 -> 言語表現 -> コンパイル -> ハードウェアの変化。でも、抽象化と表現はそれ自体が多くの層に分かれている。プログラマーが知らなくてもいい層の量は、そのプログラマーの市場での立ち位置に直接影響する。人々は抽象化をクリーンなものだと思いがちだけど、実際には不完全で文脈に依存している。理論的には常にクリーンだけど、実際には常に壊れている。どの層にいるかによって、同じプログラミング言語を使っていても、表現の形は大きく異なることがある。この観点から見ると、人々は何でもかんでもまとめて「抽象化」や「意図」と呼ぶけど、実際には意図と抽象化の間にはギャップがあり、抽象化と表現の間にもギャップがある。その微妙な摩擦点は完全には還元できない。そういう視点から見ると、たとえ非常に明確な仕様を書いても、きれいに還元できない何かが常に残る。おそらく、LLMと人間の本当の違いは、その残り物にどう対処するかにある。マーチンはLLMの抽象化が悪いと示唆する形で問題を提起しているけど、私は完全には同意しない。アジアの第三世界の出身として、自分の言語や環境で書かれた悪い抽象化をたくさん見てきたから。その意味で、LLMが生成するコードは、実際にはアジアの仲間たちが作る平均的な抽象化よりもずっと良いと感じることが多い。一方で、本当に優れた西洋のエンジニアのプログラミングを見ると、良い抽象化とは何かを再び考えさせられる。エッセイではTDDや他の手法についても触れているけど、個人的には抽象化が壊れているとき、TDDは最悪の手法の一つになり得ると思う。抽象化が間違っていたら、テストは本当に意味があるのか?緑のテストを追いかけながら、アーキテクチャを徐々に壊していくケースをたくさん見てきた。特にデータベースを含むシステムではよく見かける。手法の最大の問題は、常に教条主義になりがちで、従わなければならない何かのように扱われることだ。たとえば、SOLID原則は常に守る必要はないけど、ある組織ではほぼ宗教的な教義になってしまう。UIコンポーネントのデザインでは、LSPをあまりにも厳格に適用すると、UIの多様性や柔軟性を損なうことがある。結局、私たちが「意図」と呼ぶものは、文脈の中で柔軟であり続け、最適な解決策を探す能力なのかもしれない。その観点から見ると、意図はLLMの報酬関数に基づく学習にとても似ている。
AIは抽象化の層ではない。
これ、私にはドンピシャだわ。AIに対して常にシンプルに、簡潔にするように働きかけてる。
それはどうやって、もしそうなら、君の「手頃さポルノ」のアイデアに関係してるの?:) そのライン(他の価値観との間?)は爆笑もので、うまく投票できなかったことを謝るよ。部分的には、自分の変わったフェティッシュを言葉にできなかったからだし(+「ゴツゴツポルノスター」って「AI手頃さポルノスター」ほど魅力的に聞こえないしX)
LLMは怠惰の美徳を欠いてるわけじゃないよ。意図に合った基本的なプロンプトがあれば、怠惰にさせることもできる。claudeを使ったエージェントに、最小限のコード変更を目指すように説得するのがうまくいったことがあるし、重複排除を行ったり、非常に経験豊富な開発者の「本能」に従ったりしてる。モデルが統合してない知識ではなく、多くの人がデフォルト設定で意識してない知識なんだと思う。過剰に編集するモデルを見たことがあると思うし、全く他の人の変更や知識の損失のリスクを気にせずにコードベース全体をいじる中堅開発者みたいな感じだよね。ジェスのドキュメントの検証と生成に関するコメントについては、伝統的なロッキング問題で、伝統的な解決策がある。エージェントがgitを読めないわけじゃないし、慣習的に一つのことが先に終わるのを理解することもできる。私はかなりの経験者だし、この記事に出てくる何人かの人のチームメイトだったこともある。彼らが私のエンジニアリング基準を疑うことはないと思う。でも、自分のLLMワークフローでそんな負債を見たことはない。むしろ、ソフトウェアの質を評価する伝統的な方法で見ると、私が関わっているプロジェクトは5年前、10年前よりも良くなってる。昔と同じ指標を使ってもね。魔法なんかじゃなくて、質の優先事項を共有するエージェントが動いていることを確認することが大事なんだ。でも、私は会議で注目を集めるために時間を使うんじゃなくて、ちゃんと仕事をしてる。
Claudeに最小限のコード変更を求める指示を教えてくれない?
あなたの意見には同意するけど、ちょっと気になることがある。> もし何かあるとすれば、伝統的なソフトウェア品質評価のほとんどの形式で、私が関わっているプロジェクトは5年前、10年前よりも良くなっている、当時と同じ指標を使っている。 このサイドの文で、すごく曖昧さを持ち込んでいるよね。あなたの主張を裏付けるための洞察を共有してもらえる?どの指標を使っていて、10年前、5年前、今のコードはどうパフォーマンスしているの?そんな曖昧な主張を投げ込むのは、メッセージを不必要に薄めて、ポイントから逸らす気がする。でも、もしもっと共有できることがあれば、ぜひ知りたい。
カンファレンスで注目を集めるべきだよ。実用的なLLMコーディングっていう本を書けるかもね。
> ...私たちがもっと多くのことを、もっと簡単にできるようにするための強力な抽象を開発するために。もちろん、ここでの暗黙のウィンクは、怠惰になるためには多くの努力が必要だってことだね。 これはYAGNIと一致するけど、ほとんどの人はその逆を信じていて、必要な抽象を作らないことを正当化するためにYAGNIを使うことが多い。
反論としては、人々は必要だと思っている抽象を作るけど、実際には必要じゃなくて、その未熟なアーキテクチャに縛られることが多いってことだね。それがYAGNIが警告する理由なんだ。ファウラーがここで言ってることは、実際に使われるのを見ないうちにシステムの初期バージョンに抽象を持たせることを支持しているわけじゃないと思う。時間が経つにつれて要件や条件が変わる中で、その必要性を見極めることが大事だよね。「怠惰は私たちをできるだけシンプルなシステムに導く(でもそれ以上はダメ!) — そして、私たちがもっと多くのことを、もっと簡単にできるようにするための強力な抽象を開発する。」という言葉からも、彼が言っている抽象は非常に基本的で、できるだけシンプルな構成要素を指しているのがわかる。システムにおけるコアで直交する原則のようなものだね。過去のJavaランドで使われていたようなソフトウェアやパターンデザインの抽象を積み重ねるようなものじゃない。
マーチンが間違っているわけではないと思うけど、AIが「怠惰な」コードを生成するのを実際に見たことがある。具体的な例として、ある論理的概念のセットに対してデータベーススキーマを定義するPythonモデルのセットがあった。新しい論理的概念をシステムに追加したんだけど、既存の論理セットに非常に類似していた。Claudeは既存のモデルセットを再利用すべきだと判断したけど、理論的には機能したものの、消費者はランタイムで型推論をするためにいろいろな手間をかけなければならなかった。「機能した」けど、明らかに間違った抽象化の層だった。
もっとコードが多いのは本当に悪いことなの?人間にとっては、抽象化されたものが欲しいけど、時には自分を繰り返す方が理にかなっていることもある。もし機械がコードを書いてメンテナンスしているなら、その余分な層は今必要なの?昔はダフの装置を使って、自分たちで書いた重複コードでループを手動で展開していた。今は、コンパイラがあなたの意図を理解するのが「賢い」から、実際に重複したアセンブリコードを生成してくれる。重複していることには気にしないよね、だってコンパイラがやってくれるから。最近、非自明な計算幾何学のスニペットが必要なプロジェクトでLLMを使ったことがあった。昔なら、ライブラリを探して、コンプライアンスからそのライブラリをインポートする許可を得て、自分のドメイン表現をそのライブラリが必要とする形式に変換しなければならなかった。それらは自分でコードを書くよりも安くつくはずだったけど、非自明だった。今は、LLMが必要なものだけを書いてくれる(大きなライブラリをインポートする必要もないし)し、保存している形式でデータを使ってくれる(データ構造を翻訳する必要もない)。カノンでは「正しい」やり方は幾何学ライブラリを持って重複コードを防ぐことだと言われているけど、ここでは「うまくいく」自己完結型の関数がある。
> 問題は、LLMは本質的に怠惰の美徳を欠いていることだ。私はあなたに保証する、彼らはそうではない。
完全に同意する。これがLLMの最もイライラする点の一つだ。いつもリントエラーを無視コメントを追加して修正したり、「Any」として多くのものをタイプしたり、テストフィクスチャを抽出する代わりに複製したり、時には気に入らないテストを削除したりするのを見かける。最近のClaudeのコード修正は1行だけだった:`third_party_lib._connect()`を呼び出すこと。外部ライブラリの内部にアクセスしている。修正は機能したけど、特定の実装に依存するのは不適切だ。正しい修正は約20行だった。(ついでに言うと、これが私がLLMがシニア開発者にとってより有用だと思う理由だ。ジュニア開発者は良い品質が何かを理解していないことが多く、機能するものを受け入れてしまうから。)
これが今の問題のビジュアル化だよ: https://excalidraw.com/#json=y1fSSx2z8-0nFs7CDnqhp,d9Di8JdGU... ソフトウェアエンジニアリングの「認知ボトルネック」は、アーティファクトの間に存在していて、コードはその一つに過ぎないと思う。結果 → 要件 → スペック → 受け入れ基準 → 実行可能な証明 → レビュー 俺は、その移行の退屈な部分を自動化する実験的なツールを作ってるんだけど、各ステップで意図が生き残ることを確認するのに人間が集中できるようにしてるんだ。
かっこいいデータビジュアライゼーションだけど、編集可能なんだね!スマホでパンやズーム、スクロールしようとしたら、キャンバス上の要素が動いちゃったよ。
「アーティファクトの間」のフレーミング、いいね。もう一つ加えたいのはプロキシやメトリクスかな。分析が重視されるシステムでは、実際の損失は仕様からコードじゃなくて、質問からプロキシなんだよね。プロキシが受け入れ基準やダッシュボード、評価に組み込まれると、人々はそれを最適化し始めて、いつの間にかそれがただのプロキシだったことを忘れちゃうんだ。
LLMは人間を真似るって聞いたよ。怠惰さやせっかちさ、傲慢さ—プログラマーの美徳をAGENTS.mdに追加して、改善しよう。
残念ながら、彼がリンクしたウォートン校の論文の大部分は完全にAI生成で、まだピアレビューを受けていないんだ。ほとんどの研究者が執筆を手伝うためにAIを使っているのは理解してるけど、論文のテーマが「認知的降伏」だと、そこにある内容を真剣に受け取るのが難しい。
> ほとんどの研究者が執筆を手伝うためにAIを使っているのは理解してる これは気持ち悪いね。
読まなくてよかった!LLMを使って要約してもらったからね!
記事のもう半分はどこに行ったの?急に終わっちゃったね…
これは一つの記事じゃなくて、「断片」みたいな投稿で、5つの別々の小さな考えがあるんだ。たまたま全部LLMについてだから、一つの記事として読まれるのもわかるけど、実際は違うんだよね。