皆さん、こんにちは、Tony Baiです。
ソフトウェア開発の分野において、Martin Fowlerの名前は思考の灯台に等しい存在です。彼のどの記事も、どの講演も、業界の発展の深い脈絡を私たちに明らかにします。最近、Fowler氏は短いが示唆に富むブログ記事「LLMs bring new nature of abstraction」を発表し、私たちの認識と働き方を覆す可能性のある、現在進行中の大きな変革を再び正確に捉えました。
Fowler氏は、大規模言語モデル(LLM)の出現がソフトウェア開発に与える影響は、アセンブリ言語から最初の高水準プログラミング言語(HLLs)への飛躍に匹敵すると考えています。しかし重要なのは、LLMが単なる「より高いレベル」の抽象化をもたらすだけでなく、プログラミングの「本質」を根本的に変えつつあることです。つまり、「非決定性ツール」でプログラミングすることが何を意味するのかを考えさせられているのです。この記事では、これを簡単に解説します。
「決定性」の階段から「非決定性」の分岐点へ
プログラミング言語の発展の歴史を振り返ると、私たちは生産性を向上させ、複雑さを軽減するために、より高レベルの抽象化を常に追求してきました。
• アセンブリ言語 vs. 機械語命令:アセンブリは私たちに0と1をニーモニック記号で置き換えることを可能にしましたが、特定の機械のレジスタや命令セットに注意を払う必要がありました。
• 高水準言語 (HLLs) vs. アセンブリ:Fortran、COBOLなどの初期のHLLsは、データがレジスタ間でどのように移動するかを心配することなく、ステートメント、条件、ループで考えることを可能にしました。Fowlerは、Fortran IVでプログラミングしていた頃を振り返り、多くの制約(IFにELSEがない、整数変数名がI-Nで始まる必要があるなど)があったものの、それがすでに大きな進歩であったと述べています。
• 現代言語、フレームワーク、DSL vs. 初期HLLs:Ruby、Go、Pythonなどの現代言語、そして様々なフレームワークや ドメイン固有言語(DSL)は、抽象化レベルをさらに向上させました。私たちは今や、関数をデータとして本能的に渡し、豊富なライブラリとパターンを使用でき、大量の低レベルコードを最初から書く必要はありません。
Fowler氏は、これらの発展が抽象化レベルと生産性を大幅に向上させたものの、それらは「プログラミングの本質」を根本的に変えていないと指摘しています。私たちは依然として機械と「決定性」の対話を行っています。つまり、同じ入力とコードを与えれば、同じ出力が得られると期待します。バグも再現可能です。
しかし、LLMの介入は、この基本的な前提を打ち破りました。
Fowler氏は次のように書いています。「プロンプトを使って機械と対話することの違いは、RubyとFortran、Fortranとアセンブリ言語の違いに匹敵するほど大きい」。
さらに重要なのは、これは単なる抽象化レベルの大きな飛躍ではないということです。FowlerがFortranで関数を書いたとき、彼はそれを100回コンパイルしても、結果のバグはやはり同じバグでした。しかし、LLMは「非決定性抽象化」(non-deterministic abstraction)を導入します。
これは、私たちが慎重に設計されたプロンプトをGitに保存したとしても、毎回まったく同じ動作が保証されるわけではないことを意味します。彼の同僚であるBirgitta Böckelerが鋭くまとめたように:
私たちは抽象化レベルで単に「上」に移動しているのではなく、「横」に非決定性の領域に移動しているのです。
Fowler氏の記事の付随図は、この点を非常に象徴的に示しています。従来のプログラミング言語、コンパイラ、バイトコードは、明確な上から下への抽象化パスですが、モデル/DSL、コードジェネレータ、ローコード、フレームワークは、その上の異なる抽象化レベルです。一方、自然言語(LLMを介して)は、横から切り込むような、直接「半構造化/人間的思考に近い」領域へと続く道であり、この道自体が曖昧さと不確実性を伴っています。
「非決定性」プログラミング時代の課題と示唆
この「非決定性」の本質は、私たちGo開発者、そしてすべてのソフトウェア開発者にとって、これまでにない課題と再考すべき問題をもたらします。
• バージョン管理と再現性:プロンプトが結果の一貫性を保証できない場合、どのようにして「AIアシストコード」を管理し、バージョン管理するのでしょうか?開発、テスト、本番環境間の一貫性、あるいは少なくとも許容できる差異をどのように確保するのでしょうか?単にプロンプトをバージョン管理するだけでは不十分で、モデル、パラメータ(温度など)、さらには重要なシードなどもバージョン管理する必要があるのでしょうか?
• テストとデバッグ:出力が完全に固定されていない「コンポーネント」をどのようにテストするのでしょうか?従来のユニットテスト、統合テストの手法は依然として有効でしょうか?プロパティベースのテスト、出力結果の統計的検証、あるいは振る舞いや意図の検証により焦点を当てた新しいテスト戦略を導入する必要があるかもしれません。LLMが生成したコードに問題が発生した場合、デバッグの難易度は指数関数的に増加するのでしょうか?
• 信頼性と契約:非決定性AIコンポーネントを含むシステムにおいて、全体的な信頼性をどのように定義し、保証するのでしょうか?サービス間の「契約」はどのように記述し、強制されるべきでしょうか?
• 思考様式の転換:私たちはコードの正確な制御、厳密な論理、予測可能な振る舞いを追求することに慣れています。今、私たちは「曖昧さ」と「確率」と共存することを学ぶ必要があるかもしれません。「指示を与える者」から「意図を伝える者」そして「結果をフィルタリングする者」へと変わる必要があるでしょう。
これは私たちGo開発者にとって何を意味するのか?
Go言語は、その明確さ、厳格な型付け、簡潔な並行モデル、そして比較的予測可能な振る舞いにより、開発者から高く評価されています。LLMをGoのエコシステムと開発プロセスに統合しようとするとき、これらの「非決定性」の特性は新たな考慮事項をもたらします。
• AI生成Goコード:LLMを使用してGoコードスニペット、ユニットテスト、あるいはモジュール全体を生成する場合、生成されたコードがGoのベストプラクティスに準拠し、効率的で安全であることをどのように保証するのでしょうか?生成されたコードを効果的にレビューし、統合するにはどうすればよいのでしょうか?
• GoでLLMと対話するツール/エージェントの構築:GoでLLMと対話するバックエンドサービスや エージェントを開発する場合、アーキテクチャ設計においてLLMの非決定性を十分に考慮し、より堅牢なエラー処理、再試行メカニズム、そしてLLM出力結果の検証とフィルタリングロジックを設計する必要があります。
• LLMを利用した複雑なGoシステムの理解:LLMはレガシーの複雑なGoコードベースの理解に役立つかもしれませんが、その解釈の正確性と一貫性も慎重に評価する必要があります。
Fowler氏は記事の最後に、この変革に対する興奮を表明しています。「この変化は劇的で、私にとって非常に興奮します。失われるものに悲しむこともあるでしょうが、私たちのごく一部しか理解できないようなものを手に入れるでしょう。」
まとめ:不確実性を受け入れ、新大陸を探索する
Martin Fowlerのこの記事は、LLM時代のプログラミングパラダイムに起こりうる深い変化を私たちに明らかにしています。それは単なるツールの進化ではなく、機械との協力方法の本質的な変革です。
Go開発者として、ソフトウェアエンジニアとして、私たちはこの「非決定性」がもたらす影響を真剣に考え始め、それと共存し、あるいはその特性を利用して価値を創造する新しい方法を積極的に探索する必要があります。これは間違いなく、課題に満ちていますが、同時に機会に満ちた新大陸です。
Fowler氏のこの見解についてどう思いますか?LLMがもたらす「非決定性」があなたの日常の開発作業にどのような具体的な影響を与えると思いますか?コメント欄であなたの意見を共有してください!
資料リンク:https://martinfowler.com/articles/2025-nature-abstraction.html