- 効果的なソフトウェアエンジニアは、要件とコードについての明確なメンタルモデルを構築・維持し、それを反復的に比較・更新するループを回している
- LLMはコードの作成や修正、テストの作成、デバッグなどは可能だが、正確なメンタルモデルの維持能力が不足しているため、複雑な作業では混乱しやすい
- 現在のLLMには、コンテキスト欠落、新近性バイアス、ハルシネーションの問題があり、コードと要件の差分を正確に把握して適切に修正することに限界がある
- 人間は状況に応じて全体の文脈を一時的に保持したり、細部を一時的に隠して大きな全体像を見るなど柔軟に思考を切り替えられるが、LLMはそれを実現できていない
- LLMは要件が単純な作業には有用だが、複雑なソフトウェア開発では、最終的にソフトウェアエンジニアが要件の明確さとコードの動作に直接責任を負う必要があり、LLMは補助ツールの役割にとどまる
ソフトウェアエンジニアリングのループ
- 熟練したエンジニアは、次の手順を繰り返しながら作業する
1. 要件のメンタルモデルを構築する
2. そのモデルに合わせてコードを書く
3. 書いたコードが実際に何をしているかを理解する
4. 差分を特定してコードまたは要件を修正する
- このループの核心は、正確で維持可能なメンタルモデルを保有する能力にある
LLMの限界
- LLMは、コードの作成、問題の特定後の修正、テストの作成・実行、ロギングの追加、デバッガの使用などの機能を実行できる
- しかし、メンタルモデルを維持できないため、次のような問題が発生する
- 自分が書いたコードはうまく動くはずだと仮定する
- テストが失敗した際、コードとテストのどちらを直すべきかを推測に頼る
- 混乱するとコード全体を削除して最初から書き直す
- 人間と違って、テスト失敗時にモデルを点検して修正の方向を決めたり、行き詰まったときに対話を通じて問題を解きほぐしたりする柔軟性に欠ける
- ソフトウェアエンジニアは作業の途中でテストを実行し、問題が起きたときにどの部分を修正すべきかを明確に判断できる
- ときには作業全体をやり直す場合でも、問題に対する理解度がより深まる結果につながる
今後の可能性
- 今後モデルがさらに発展すれば変わる可能性はあるが、ソフトウェアエンジニアリングは単なるコード生成以上のものを要求する
- 人間は重大な問題を解決するとき、全体の文脈を一時的に記憶から取り出して扱い、個別の課題に集中したり大きな全体像を見たりできる
- コンテキスト情報をただ増やし続けるのではなく、必要な情報を選択的に扱う思考様式が重要である
- 人間のように文脈を一時保存・復元したり、全体像と細部を行き来しながら思考したりする機能がLLMには欠けている
- 現在のLLMの主な制約
- コンテキスト欠落(Context omission): 必要な情報が抜けている箇所をうまく見つけられない
- 新近性バイアス(Recency bias): コンテキストウィンドウ内で最新の情報に過度に偏る
- ハルシネーション(Hallucination): 存在しない細部を作り出す
- メモリ機能が追加されれば一部は改善できる可能性があるが、複雑さが一定水準を超えると、なお文脈理解とモデル維持には失敗する
- 2つの似たメンタルモデルを維持しながら差分を分析し、要件またはコードのどこを修正すべきかを決める機能が不足している
現在の役割と活用
- LLMは高速なコード生成と要件・文書の統合に強みがあり、単純で明確な作業には十分活用できる
- しかし、単純ではない問題では十分な文脈維持と反復的な改善が難しい
- したがって、要件の明確化、コードの検証などは依然としてソフトウェアエンジニアの責任である
- 人とエージェント(LLM)が一緒にソフトウェアを作る環境を目指してはいるが、現時点ではエンジニアが主導し、LLMはツールとして活用すべきである
2件のコメント
なぜ「現在」のLLMは..
Hacker Newsの意見
私たちは、コンテキストウィンドウに単にもっと多くの単語を追加するようなやり方では問題を解決しない。そんなことをしたら正気ではいられない。
問題が発生したときも、テキストだけでその問題を見ているわけではない。
デバッガで認証エラーが出たからといって、「じゃあトークン検証をコードから削除してしまおうか?」のようには考えない。
実際には、問題の根本原因を把握するために、全体の状況を一歩引いて見る。
たとえば認証エラーが出たなら、トークン検証の過程や呼び出し元ユーザーの権限などをすべて見直し、そもそもテスト自体が間違っていたと気づくこともある。
その過程では、単にエラーを消すのではなく、「401番が単なる未認証なのか、権限不足なのか」のように、より細かく区別する必要があることにも気づく。
Grugbrain.dev 参照
プログラマは、ビジネスルールをコンピュータが理解できる厳密な方法へ翻訳する仕事をしているのだと思う。
ルールが意味することと、コンピュータ(あるいは使っているフレームワークや抽象化レイヤー)がどう動くかの両方を理解しなければならないので、その翻訳過程はいつも単純とは限らない。
特に、新しい要件がそれまでの前提をすべて壊したり、互いに矛盾したりすると、何度もやり直して修正せざるを得ない。
人間同士の言語翻訳ですら曖昧さが多くて複雑なのに、コンピュータは言われた通りに正確に実行するので、些細なミスでも大きな問題になる。
私は、人間が常に反復的に関与するやり方が現実的なアプローチだと思う。
このやり方なら、より速く、より高い品質で作業できるので、今もそうしている。
個人的には、大量のコンテキストをまるごと頭の中に保持できる。
コードのテキストそのものはすぐ捨てられ、私の脳はコードを AST(抽象構文木)のような構造、さらに言えば空間的なグラフとしてパースする。
プログラム自体を論理的にモデリングし、テキストとは完全に別の構造として認識している。
この観点で見ると、LLM はテキストにしか注目せず、プログラムの論理モデルを構築できないため、ソフトウェア構造を理解できない。
抽象的思考力を必要とする大規模システムのアーキテクティングには本当に多くの脳の労力が必要だが、LLM にはこうした抽象化能力が不足している。
私のやり方は次の通り。
テスト失敗が報告されたら、まずそのコンポーネントを特定し、そのコンポーネントの目的、内部の制御フロー、状態変化、周辺コンテキストに関する前提まで深く分析して、Markdown に整理する(
<コンポーネント名>-mental-model.md)。その後は、常にテストの問題を扱うときにこのマインドモデルを参照する。
こうした分析内容を Claude のプロンプトに貼り付けると、LLM はより良い結果を出せる。
さらに、LLM が構築したマインドモデルを自分で読んで修正することもできる。
AI は、権限不足のときは 401 ではなく 403 を使えと助言するかもしれない。
記事の筆者は、LLM とコーディングツールの現在の能力をあまり理解していないように思える。
LLM はテストが失敗すると、コードが正しいのか、テストが間違っているのかを推測するだけで、行き詰まるとコードを全部削除してしまう、という主張は私の実体験とは違う。
ソフトウェアエンジニアは常に、自分の頭の中のモデルに照らしてテスト失敗の原因を具体的に把握する。
私は Cline と Anthropic Sonnet 3.7 を使って Rails で TDD 方式の開発をしているが、LLM には常に先にテストを書かせ、その後にコードを書かせている。
作業を小さな単位に分割して、自分が部分ごとにレビューできるようにしており、テストが失敗したときも、どこを直すべきかをかなりうまく推論して正しく修正する。
LLM は完璧ではないが、人間のジュニアエンジニアと同程度、あるいはそれ以上の結果を出すことも少なくない。
ときどきバグを直せないことはあるが、それは人間の新人開発者も実際同じだ。
LLM は特に Rails のような枯れたフレームワーク内での CRUD 作業には非常によく機能する。
一方で、Direct2D と Rust で Windows ネイティブアプリを作ろうとしたときは最悪だった。
もっとさまざまなケースに対して開かれた評価があってほしい。
モデルが失敗したテストを通すために、小細工やトリック(ハードコーディングなど)を使うのは、非常によく知られた現象だ。
私の経験では、使う言語、プラットフォーム、ドメインによるばらつきが大きい。
最近は Ruby 自体を私も触っていないので Rails では実験していないが、Rails の分野はプログラミング文化が非常に一貫しているので、LLM でも無難にうまくやれる気がする。
逆に Python はさまざまなコーディングスタイルが混在していて、LLM が複数のパターンを混ぜて使い、テストが不安定になったことが多い。
コードを繰り返し変えなければならず、本当のエラーは「クエリ結果のソート漏れ」なのに、LLM が見当違いに SqlAlchemy を外して Django に乗り換えろと勧めるなど、おかしな結果もあった。
R 言語は、仕様通りにきちんと動くコードを受け取ること自体が難易度高い。
LLM をジュニアエンジニア相当と見なすなら、特に見たことのある問題については、本当に素早く解決策を見つけて適用する。
一方で、LLM が見たことのない問題には説明や指示がもっと必要で、その場合の私の役割は単なるメンターになる。
私たちのチームは、「claude-code」方式で、長くバックログに残っていた簡単なリファクタリングや二次分析システムのような、よく知られた反復作業に LLM を積極的に使っている。
個人的には、コードブロックをドラッグして「5歳児にもわかるように説明して」や「レースコンディションの危険がないか見つけて」といった質問をよく使う。
生成されたコードは、既存コードやスタイルと合わないことが多く、結局自分でスタイルに合わせて直す必要がある。
最近は「AI が読みやすいようにコードを書く」といった話まで聞くが、追加負担に比べて得られる効用はまだ大きくないと感じる。
「LLM がジュニア並みか、それ以上のこともある」という主張については、むしろそういうケースこそ最近の開発者採用水準の反映ではないかと思ってしまう。
Sonnet 3.7 より劣る新人を採用したのだとしたら、本当にがっかりするだろう。
LLM に対する批判の大半は正しいかもしれないが、何年も投資をしてきた経験から、「いまいちだが伸び続ける」技術や企業には注目すべきだと学んだ。
90年代前半から半ばのインターネットにも不満は多かったが、人々は使い続けたし、Twitter もしょっちゅう落ちていたがニュースプラットフォームとして定着した。
電気自動車やスマートフォンなども不便だったが、価値があったから改善され続けた。
LLM はまだ多くの作業で完璧ではないが、2022年と比べればすでに 10 倍進歩しており、今後 5 年以内にここで挙げられている問題の大半も解決されるだろうと思う。
ただ、先に挙げた事例はどれも、期待値が現実と合っていなかったことも多い。
インターネットが速くなってもメタバースが主流になったことはないし、VR 酔いのような物理的制約もまだ解決していない。
当時、電話が遅いこと自体への強い不満が特別あったわけでもなく、期待されていた用途が違っていた。
技術が発展してきた経路を見たからといって、LLM も必ず同じパターンで進化すると断言はできない。
新しい技術がより良い解決策をもたらす可能性も念頭に置くべきだ。
昨年は適用分野が広がったのは事実だが、イノベーションと呼べるような突破口はまだない。
昔の携帯電話は動作が遅く、カメラの画質も低かったとしても、当時の主用途である「いつでもどこでも連絡できる」だけで、すでに不可欠な存在だった。
飛躍的な進歩は「おまけ」にすぎず、人々は「この携帯はいつ良くなるのだろう」と待っていたわけではなかった。
自分では、記憶の歪みがあるのだと思う。
90年代のインターネットに対する大衆的不満というより、当時の利用者は少数で、主流になったのはずっと後だった。
実際に、インターネットが遅いと不満を表明する大衆がいた証拠はあまりない。
成功して発展した少数の製品だけを覚えていて、大半はすぐ忘れられるか、改善されないまま消えていった。
技術は進歩するはずだという期待よりも、現在の状況を基準に判断するほうだ。
LLM のここ数年の飛躍的進歩がこの先も続く、という単純な論理には同意しにくい。
成長の限界点に達するかもしれないし、特に新しい知識の発見や未知の情報への推論能力の不足こそが、LLM の決定的な限界だと感じる。
役に立たない道具だと言うつもりはないが、過度な期待には加わらない。
LLM に数文だけ聞かせて、すぐにプロトタイプまでコーディングさせようと期待すること自体が非現実的だ。
人間の開発チームに同じやり方で仕事をさせても、やはりまともには作れないはずなのに、なぜ LLM にはそんな期待をするのか不思議だ。
LLM によるソフトウェア開発の成果物の質を大きく改善したいなら、既存の開発チームが使っているプロセスとツールを積極的に活用すべきだ。
autonomous-software 記事
私は steadytext という、完全自律でコードを vibe ベースに書くプロジェクトを始めたのだが、LLM は複雑な 7000 行規模のプロジェクト(Python ライブラリ、CLI、Postgres 拡張)まで書き上げ、イシューや機能要望にも自力で対応している。
私自身はコードの 90% を直接見たことすらなく、テストカバレッジ全体、CI 通過、実運用でも問題ない。
もちろん CLAUDE.md に綿密な計画があり、イシューや要望にも明確で具体的な記述が必要だが、その準備さえできていればうまく動く。
コーディングエージェントに効率よく管理・実装させるのは簡単ではないが、私の経験は前向きだ。
steadytext GitHub
批判的な見方も受け入れるが、曖昧な問題を解くには、結局のところチーム全体で多くのコンテキストを共有することが核心だ。
最も創造的な解法ですら、明示的・暗黙的な制約から生まれる。
LLM には、そうした制約を把握したり、明確に定義されていない制約の中で新たな解法を組み立てたりする能力がない。
問題定義、スコープ把握、制約条件の理解までを人間が行って初めて、LLM は実装を補助するツールになれる。
現時点では、「どの道具を使ってコードを仕上げるか」という選択肢が一つ増えただけだ。
この議論を無条件の単一解法、つまりオール・オア・ナッシングに持ち込むほうが、むしろ非現実的だと思う。
実際、この状況でも十分に働ける人間のエンジニアは多い。
LLM に命令を出すことがそこまで簡単ではないのなら、その存在意義は何なのかと思ってしまう。
Kiro がこのアプローチを適用しているが、まだ初期段階なので完璧ではないものの、意図通りに使えばかなり悪くない。
「LLM は明確なマインドモデルを構築できない」という点については、claude code を使う中でますますもどかしさを感じている。
テキストベースの LLM がこの問題をきちんと解決できるのか、よくわからない。
Google Genie 3 が 1 分ほどで内部状態を失うという話を思い出す。
この問題は、トランスフォーマー水準を超える新しいアーキテクチャが開発され、短期・長期コンテキストや自己重み調整(学習の模倣のようなもの)が可能にならないと難しいのではないか、という直感がある。
参考: 関連議論
最近は、階層型エージェント構造が現実的な代替案ではないかと考えている。
最上位のエージェントが全体のマインドモデルだけを維持し、下位エージェント同士で作業分担する形がよさそうだ。
今でも Code ツールのエージェント機能で似たようなことは実装できそうだが、関連する戦略を共有してくれる人がいればうれしい。
claude-code-requirements-builder を使ってみたら少しは良くなったが、それでもまだ満足はしていない。
現実には、実務現場の「平均的な」ジュニア開発者も、下に書かれているような状態とそこまで大差ない。
自分が書いたコードは必ず正しいと思い込み、テストが失敗すると慌て、方向を見失うと最悪コードを全部消して最初から書き直す。
StackOverflow からのコピペ、コンパイラのせいにする、果ては「宇宙線のせいだ」という話まで出てくる。
LLM を使っていると、結局は自分がプランニングと設計を主導しなければならないと実感する。
低レベルの反復作業やテストは LLM に任せて、自分はより大きな絵を考える時間を確保できるのが良い。
ただし、LLM の成果物レビューや変更提案などが、もっと対話的に改善されてほしい。
AI スタートアップの方向性こそ、今の問題の核心だと思う。
単純なチャットインターフェースではなく、IDE の中に自然に統合された AI ワークフローが必要だ。
Visual Studio、IntelliJ、Android Studio のように、そういう方向がトレンドになっている。
自分の母語で音声指示を出し、AI がプロジェクト全体の文脈を把握して、リファクタリング・静的解析・AI フィードバックまで一体化し、スケッチから UI を作り、手書きからコーディングし、コード変更からコミットメッセージまで生成する、本当に本物のプログラマのような道具がほしい。
LLM がジュニアレベルの作業にはかなり有用だという点には同意する。
最近は、「タイピング速度はそれほど重要ではない」という昔からの主張を改めて考えるようになった。
以前は、コード入力の速さよりも全体設計や構造化のほうがはるかに重要だったので、入力時間そのものの比重は大きくなかった。
しかし Claude を使って感じたのは、以前なら面倒であまりやらなかったコード変更を、集中しなくても簡単にできるようになったということだ。
以前は enum の値を一つ追加するたびに、対応するすべての箇所を意識して修正していたが、LLM ならその部分を自動で直せる。
コンパイルエラーを一つひとつ潰すのが面倒だった作業も、Claude に繰り返し直させればいい。
複数のエージェントが同時にコードのさまざまな部分を手直ししてくれるので、その間に自分は大きな構造を考えたり、HN に書き込んだりもできる。
つまり、コンパイルエラー修正に時間を取られないぶん、より多くの変更を素早く適用でき、以前ならジュニアが一日中かけていた仕事を一気に終えられる。
その結果、全体アーキテクチャ設計にもっと集中できるし、長く先送りしていたコーディング雑務も片付けられるので、モチベーション面でも大きい。
「タイピングが速くなっても最終的な到達が速くならないのは、設計がボトルネックだからだ」という話には共感する。
LLM は良い設計を作るのが苦手なことも多く、細かな関数一つひとつでさえ常にリファクタリングが必要になる。
実装段階での生産性向上は確かにあるが、それはすでに自分の頭の中や文書にあるアイデアを具体化するレベルだ。
ブレインストーミング用途としては使える。
コードとテストを丸ごと投げて「見落としたエッジケースはあるか」と聞けば、10 個中 1〜2 個くらいは意味のある助言が返ってくる。
短期的な動作修正と中長期的な構造的優秀さはまったく別の問題なので、LLM が後者にまで追いつけるかは未知数だ。
「コードベースにやりたいことが山ほどある」という話については、実際にはコード変更そのものよりレビューがボトルネックだと痛感している。
「ジュニアが一日かかることを LLM がすぐやる」のが、結果として新人が学ぶ機会を奪い、採用縮小につながるのだとしたら、誰が彼らを育てるのかと心配になる。
「テストが失敗したとき、コードを直すのかテストを直すのか決められない」という現象については、
「Red-Green-Refactor」という言葉を使うのが役に立つ。
今は RED 段階(テスト失敗が正常)、GREEN 段階(最小限のコードで成功状態を作る)、REFACTOR 段階(テストを壊さないようにコードを改善する)の流れを LLM に明確に伝えている。
こうすると、LLM は単なる「壊れたコードの修正」ではなく、TDD のマインドモデルを認識できる。
LLM が「自分だけの Facebook を作って」のような、まっさらな新規プロジェクト全体にはまだ届いていないのは明らかだと思う。
その代わり、「こういうモーダルを追加して、既存コードを参考にスタイルを合わせて」といった、より細かな作業では望んだ結果を得られたことが多い。
問題を小さな単位に分解して一つずつ渡すと、はるかに良い結果になる。
既存コードをコピーして、望む形に修正するくらいなら、すでに自分でできる。
私のシステムクリップボードは、LLM と違って常に決定論的に動作するので、LLM のように無限に予期しない新しい問題を生み出したりはしない。
v0 のような新興ツールが、こうした依頼にどう応えるのか気になる。
記事冒頭の 4 段階プロセスは、Deutsch の『The Beginning of Infinity』と非常によく似ていると感じる。
私たちの理論は「推測」から始まり、知識は「推測と批判の循環」から生まれる構造になっている。
コードを書くことは一種の「推測」であり、テストを作ることはその推測に対する「批判」だ。
どちらも、頭の中にある説明(プラトン的な理想)に近づこうとする試みなのだ。