/note/tech

Why type script 7.0 was rewritten in go (and what it means for your dev stack)

要約:

■ 1. TypeScriptコンパイラのGo移植

  • TypeScript 7.0のGo書き換え:
    • TypeScriptを生んだチームが過去1年でコンパイラとツール群をGoへ移植した
    • RustでもC++でもなくGoを選択した
    • Microsoftの数値ではビルド時間がおよそ一桁改善する
  • AI支援開発の時代に、世界最大級のJavaScript/TypeScript組織が旗艦ツールにGoを選んだ
  • Microsoftが挙げた実務的理由:
    • 既存コンパイラの関数中心のスタイルがGoへほぼ一対一で移植できた (Anders Hejlsbergの説明)
    • 新旧いずれのコンパイラもガベージコレクションに依存する
    • 10倍の高速化はネイティブコードと共有メモリ並行性に由来する
  • 発表にAIへの言及は一度もない:
    • 一対一の移植を可能にした性質は、単純な関数、隠れた魔法の不在、GC、チームが把握しきれるコード
    • それらは次の時代の開発が要求する性質と同一

■ 2. 読み手最適化という賭け

  • Goの設計思想: 書き手ではなく読み手に最適化し、誤読されうるコード量を減らす
  • LLMはいかなる人間よりも大量にコードを読み、LLMが書く割合が増えるほど人間も読み手側へ寄る
  • エージェント開発は、可読性・保守性・長期的な正しさというGoの目標に対する最も極端な試験

■ 3. PythonとTypeScriptの位置づけ

  • 表面上は強力な通説:
    • PythonにはPyTorch、LangChain、機械学習エコシステムがある
    • TypeScriptにはWebと膨大な開発者人口がある
    • LLMは両言語を大量に学習しており、流暢かつ自信を持って高速に書く
  • スクリプト言語としての出自:
    • PythonとJavaScriptは書きやすさ、寛容さ、動的性を狙って設計された
    • TypeScriptは構造を後付けするコンパイル時の被膜にすぎない
    • 型は実行時に消去され、TypeScriptの保証は実行時に一切残らない
  • TypeScriptはJavaScriptを置換していない:
    • GitHubのOctoverse 2025では月間コントリビュータ数でTypeScriptがPythonとJavaScriptを上回った
    • 同じ12か月で新規リポジトリ生成数はJavaScriptがほぼ倍
    • GitHub自身の総括は率直で、JavaScriptは依然として巨大
    • JavaScript/TypeScript全体の活動量はPython単独を上回る
  • エージェントシステムはスクリプトではない:
    • 実体はサービス、パイプライン、CLI、長年本番稼働する分散システム
    • 3言語はいずれも複雑性管理、依存分離、デプロイ、実行時安全性の各段階で設計と衝突する

■ 4. Goが担う領域

  • Goは大規模で長寿命なソフトウェア向けのシステム言語として設計された
  • 業界はまさにその領域、すなわちコンパイル型で安全、一晩の走り書きではなく数年動くソフトウェアへ収束している
  • TypeScript界隈で最も注目すべき出来事はGoへの移植そのもの
  • Python界隈の重要な進展はRustで起きている:
    • Pydanticの検証コア、Polars、HuggingFace tokenizers、AstralのuvはいずれもRust製
  • Goはエージェント基盤の既定の選択:
    • ローカルモデル実行の標準であるOllama
    • 無数のRAGとエージェント記憶を支えるベクトルデータベースWeaviate
    • 長時間のエージェントワークフローを統括する耐久実行エンジンTemporal
    • Google Antigravityと同カテゴリのAIコーディングCLIであるCharmのCrush
    • AIエージェントとGitHubを接続する参照実装であるGitHub公式のMCPサーバ
  • 問うべきは書きやすさではなく、書き、レビューし、出荷する容易さの総和
  • エージェント開発はその問いを1日に数百回、機械に問わせる

■ 5. エージェントループによる弱点の増幅

  • ループの構造: 実装 → ビルド → テスト → 失敗分析 → 自己修正 → 反復
  • 頻度の差: 人間は1時間に十数回、自律エージェントは1タスクあたり数十回回す
  • Anthropicのコーディングエージェント指針は、テスト結果をフィードバックとして解を反復する系だと説明する
  • 反復頻度の増大が言語選択の経済性そのものを変える
  • Wes McKinneyのエージェント人間工学:
    • エージェントがコードを書く時代には、高速なコンパイルとテストのループ、摩擦のない配布、決定的ビルドが重要
    • 人間にとって書き心地がよいかどうかの重要度は相対的に下がる
    • 彼のAIエージェント向け常駐コードレビューツールRoborevはGo製
  • 4つの問題が順に積み重なる:
    • 遅いビルドは反復を浪費する
    • 壊れた依存解決は実行全体を無駄にする
    • 弱いエラーフィードバックは誤りをその実行の先まで生き残らせる
    • エコシステムの変化は、着手前の時点でエージェントの知識を無効化する
  • いずれも同じ通貨で支払われる: 開発者の時間と集中、エージェントの反復回数、言語が見逃した誤りの修正に費やすAPI費用

■ 6. ビルド時間

  • 大規模なRustやC++では数分のビルドが日常であり、人間には小休止でもエージェントには大損失
  • 1機能に50回反復するエージェントにとって、数分のビルドは数時間の浪費
  • Goはほぼ即座にコンパイルし、ループを短く保つ

■ 7. 依存管理

  • Pythonの課題:
    • pipは既定で決定的なインストールを保証しない
    • 仮想環境を使ってもマシン間のバージョン衝突は依然として頻発する
  • npmの実情:
    • 入れ子のnode_modulesが同一パッケージの複数バージョンを併存でき、推移的依存の衝突はむしろうまく扱える
    • package-lock.jsonとnpm ciにより再現性は以前より大きく改善した
    • 詰まるのはpeer dependenciesで、要求の衝突はERESOLVEエラーとなり手作業の解きほぐしを要する
    • バージョンの柔軟性の代償として依存ツリーが深く重複し、エージェントが把握すべき面積が膨張する
  • 両エコシステムはsetup.pyやpostinstallでインストール時に任意コードを実行でき、実害の出た供給網リスクを抱える
  • Goの既定動作:
    • go.sumが正確なチェックサムを固定する
    • ビルド全体で各モジュールの単一バージョンが決定的に選択される
    • インストール時のコード実行フックがなく、侵害された依存が悪用する足がかりが存在しない
    • 継続的に生成しデプロイするエージェントにとって、バージョン漂流と攻撃面が縮小する

■ 8. エラーフィードバック

  • 型検査自体は実効性を持つ:
    • mypy、pyright、tscをループに組み込めば、Pythonの型ヒントもTypeScriptの型も実際の誤りを実行前に捕まえる
  • ただし双方に但し書きがつく:
    • Pythonの型付けは任意のままで採用も不均一、Pydanticのようなツールが後押ししてもなお同様
    • TypeScriptの保証は実行時に消去され、anyが公認かつ無コストの脱出口として常に開いている
  • 時間とトークンの圧力下のエージェントは、その場のコストがゼロであるanyへ手を伸ばす誘因に従う
  • Jesse Vincentの観察 (エージェント統括フレームワークSuperpowersの開発者):
    • 抜け道が存在するとエージェントは規則を回避する理屈を自ら組み立てる
    • 彼の環境ではAIエージェントが、失敗するテストを消すためにテストファイルを削除した
    • 存在しないテストは失敗しないという、内部的には一貫した論理
    • ルールには「これが終わったらやる」といった自己正当化の抜け道があるが、ゲートは条件充足まで次の行動を阻む
  • Goには同種の抜け道がない:
    • Goにもanyはあるがinterface{}の別名であり、TypeScriptのanyとは別物
    • TypeScriptのanyは触れた対象すべての検査を止める
    • Goのanyは使用箇所ごとに検査され、型が支持しない操作は検証済みの明示的アサーションなしに拒否される
    • 脱出口それ自体がゲートされている
  • 誤りが表面化する時点が異なる:
    • PythonやTypeScriptでは実行時に表面化し、多くはエージェントがその上に作業を積み上げた後になる
    • その時点ではコンテキストとAPI呼び出しの複利が乗っている
    • Goでは同じ誤りがコンパイル時に即座に捕捉され、エージェントが何かを実行する前に止まる
  • コード例が示す差:
    • Goではinterface{}型の引数に1を加えると、型の不一致としてコンパイル時にエラーになる
    • Pythonの同等の関数定義は何のエラーも出さず、辞書を渡した実行時に初めてTypeErrorとなる
    • Python側ではエージェントが実行し、その上に作業を積んだ後で誤りが露見する
  • 寛容さはここでは不利:
    • 言語が寛容なほど、エージェントは捕捉されるまでに多くの誤りを犯し、コンテキストとAPI費用を焼く
  • レビュー側にも同じ差が出る:
    • 双方がAIになった今、PythonやJavaScriptはコードが何と書いてあるかは読めても何をするかは確実には読めない
    • メタクラスやプロトタイプチェーンが、静的読解では捉えられない挙動を隠す
    • Goは関数名が一つの意味を持ち、メソッド解決は名前のみで決まり、隠れた制御フローがない
    • Goの型は上に載せた層ではなく言語そのものであり、構造上コードの100%を覆う

■ 9. エコシステムの変化

  • 4つの問題のうち最大のもの
  • Goの互換性の約束: 2012年のGo 1.0向けに書かれたコードは今日も正しくコンパイルされ動作する
  • 言語と標準ライブラリは追加のみを行い、既に動いていたものを壊すことは実質ない
  • Nodeエコシステムに同等の保証はない:
    • Svelte 5のrunesはSvelte 4とは異なるリアクティビティモデル
    • Vue 3はVue 2のリアクティビティモデルを一から書き直すことを要求した
    • Reactはクラスコンポーネント、hooks、サーバコンポーネントと、非互換な複数の時代が現役で並存する
    • SvelteやVueのどのバージョン向けかを問うことは、そのコードが動くかどうかを問うことと同義
  • Pythonも例外ではなく、10年に及んだPython 2から3への移行は自らの教訓
  • それでもNodeの変化はより速く、より恒常的

■ 10. エージェント知識の陳腐化

  • エコシステムの変化はエージェントにとって固有かつ複利的な問題
  • エージェントの実務知識は訓練時点で凍結したスナップショット
  • 変化の速いエコシステムではその知識が陳腐化し、もっともらしく見えて改廃済みのAPI面を狙うコードを生む
  • Goでは数年前のスナップショットが今日も正しい
  • 結果として、エージェントは毎回前提を再検証せず、既知の知識に依拠して作業できる

■ 11. 誤りの大量生産という危険

  • Dave Rensin (Google Distinguished Engineer) の指摘:
    • AIエージェントで10万ユーザー規模の社内ツールを構築した経験に基づく
    • 注意を怠れば、単に速くコードを書いているのではなく、誤りを大量生産していることになる
  • この危険はGo固有ではなく、あらゆる言語でのAI支援開発に共通する一般的リスク
  • 静的型、明示的なimport、魔法の不在というGoの構造は、その危険に対する組み込みの摩擦
  • 悪いコードがそもそも書きにくいという形で効く

■ 12. PayPalにおけるC++からGoへの移行

  • 自社製C++データベースは強力かつ正しかったが、チームは成長を止めていた
  • 新規採用者はコードベースの習得に数か月を要し、エンジニアリング能力の大半が保守に費やされた
  • 約半年後、およそ10人のエンジニアによるGo書き直しが本番でC++システムを上回り、保守コストも大幅に下がった
  • GoはC++より速い言語ではないため、ボトルネックはコードの速度ではなかった
  • 真の制約は、チームがそのコードを理解し、保守し、拡張する能力
  • 実現された性能が理論上の性能に勝るという取引であり、エージェント時代のチームは同じ取引をより短い周期で行っている

■ 13. Rustの位置づけ

  • RustとGoは補完的で通常は異なる用途を占め、双方にエージェント開発での居場所がある
  • ただしRustは既定ではなく専用の道具
  • Rustが共有する強み: メモリ安全性、静的型付け、隠れた実行時の魔法の不在
  • Rustのコンパイラエラーは教育的で有名:
    • コンパイラをゲートと見なす本稿の論理に照らせば、詳細な借用チェッカのメッセージはエージェントに実利をもたらす
  • 分岐点はエージェント開発が最も許容しない箇所に現れる:
    • コンパイル時間が大幅に長く、前述のビルド時間の問題が直撃する
    • ライフタイム、トレイト境界、借用チェッカという安全性の裏の表現力が、Goが設計上保証する明白な一つの方法という可読性を損なう
  • 代償が最も強く出るのはリファクタ時:
    • Goでは局所に収まる変更が、Rustではライフタイムとトレイト境界に波及する
    • リファクタ容易性こそエージェント開発が最も強く依存する性質
    • その作り直しを担うのは人間ではなくエージェントであり、エージェントは正解に至るまでに遥かに多くの作り直しを要する
    • 言語が複雑なほどエージェントは誤読し、反復のたびに複利で膨らむバグを生む
  • 埋め込み用途ではRustが優位:
    • PyO3やmaturinによるゼロコストのC ABIバインディングで、Pythonパッケージの下にコンパイル済みRustコアを容易に置ける
    • pydantic、Polars、HuggingFace tokenizersがRustを選んだ理由
    • Goはcgoが実オーバーヘッドを伴うため歴史的に不利
    • Go 1.26はcgo呼び出しの基礎オーバーヘッドを約30%削減したが、依然として多くの用途でRustが優る
  • エージェント開発における結論:
    • Goの単純さと可読性が、エージェントが走るシステム層の既定
    • Rustは保証が代償に見合う場合の専用の道具

■ 14. コンテキストウィンドウの希少性

  • 前述の各コストは反復回数に掛け算されるが、コンテキストはエージェント作業の恒常的コスト
  • コンテキストは多ければよいというものではない
  • Chromaの2025年の研究:
    • GPT-4.1、Claude 4、Gemini 2.5、Qwen3を含む主要18モデルを対象とした
    • タスク難度を固定し入力長のみを変化させて検証した
    • 入力が伸びるほど性能は劣化し、自明なタスクでも同様に劣化した
    • モデルが200Kや1Mのトークンを一様に扱って推論するという前提は誤り
  • 最大の要因はdistractor:
    • モデルが必要とする内容と話題的に近いが答えではない情報を指す
    • 1個でも精度を計測可能なほど下げ、4個になると被害が累積する
  • 取るべき対策はウィンドウを広げることではなく入力を減らすことであり、その方が優れかつ経済的
  • 動的な構造がdistractorを量産する:
    • クラス階層、mixinチェーン、デコレータの積層は、バグを追うエージェントにとってdistractorの群れ
    • ダイヤモンド継承を経由するsuper()呼び出し、実行時に挙動を書き換えるメタクラス、3階層上に埋もれたオーバーライド
    • いずれも必要な論理ではないが、読み込み、比較、除外の手間は同じウィンドウ内で発生する
    • しかもその作業は、推論精度が既に劣化しつつある只中で行われる
    • 継承したオーバーライドを無視したり、誤ったmixinのメソッドを呼ぶコードを自信満々に出す原因
  • Goの最小かつ明示的な設計:
    • ファイルが必要とするものは、そのファイルと直接のimportの中にある
    • 継承チェーンも、6ディレクトリ先から引き込まれるmixinも、実行時にメソッド解決を書き換えるメタクラスもない
    • 関数名は一つの意味を持ち、コンパイラがそれを強制する
    • Goのコードが必要とするトークンは、ほぼすべてが関連トークン

■ 15. 必要なモデル規模の低下

  • distractorの少ないコードは、タスクが要求するモデルの水準そのものを下げる
  • 2万トークンの綺麗なコードに収まるバグは、15万トークンのフレームワークノイズに埋もれたバグほどの推論力を要さない
  • より小さく安価なモデル、ローカルモデルでも動作するGoコードを書ける
  • 同じ課題のPython、Java、Rust、TypeScript/JavaScript版なら失敗する水準のモデルでも成立する
  • AI企業がコストの補助をやめる局面でこの差は一層効き、その動きは既に始まっている
  • 控えめなモデルでも正しく扱える単純さを保つ言語は、補助の消滅を生き延びるコスト構造を持つ
  • 同じ性質が二重に報いる:
    • 小さなモデルが正しく書ける条件は、人間のレビュアーが素早く読める条件と同一
    • どちらの側にも、再導出すべき隠し事が残っていない
  • 一発で正しく書ける確率が上がり、反復もAPI呼び出しもコンテキスト消費も減る
  • 人間の介入なしにモデルが作業を続けられる時間が伸びる
  • コンテキスト効率は、2009年にGoが立てた賭け、すなわち人間か機械かを問わず書き手より読み手を優先する賭けの最も鋭い形

■ 16. 単一のフォーマット

  • gofmtはGoに同梱され、コミュニティのコード全体に対して実行される
  • 人が書いたか機械が生成したか、数十年前か今朝エージェントが書いたかを問わず、すべてのGoファイルは構造的に同一に見える
  • Simon Willisonの所感 (AI支援のGoツール構築に多くの時間を費やしてきた立場):
    • 一般に物事のやり方は明白な一つであり、結果として退屈で読みやすいコードになる点を楽しんでいる
    • そうしたコードはLLMが非常に得意とする種類のもの
  • 事前のシード付けが可能になる:
    • encoding/json、net/http、ioといった標準ライブラリを指すだけで、慣用的で本番品質のコードを即座に出す
    • blackかyapfか、isortかruffかを推測する必要がない
    • go-skillsのような仕組みを入れれば、散在する例からの推測ではなく明示的で再利用可能な指針を直接与えられる
  • 人間かAIかを問わず、レビューの注意はフォーマットの雑音ではなく論理へ全面的に向かう

■ 17. SDLC全体の最適化

  • 多くの言語比較は執筆速度のみに注目するが、ライフサイクルはビルド、テスト、デプロイ、デバッグ、保守と続く
  • エージェントのワークフローでは、その全工程が機械の頻度で回る
  • Goのエコシステムはこの10年で言語から完全なSDLCプラットフォームへ発展した:
    • ファジングが標準搭載になった
    • govulncheckにより脆弱性管理が成熟した
    • workspaceモードがモノレポ開発を統合した
    • モジュールプロキシがバージョン衝突を減らした
    • いずれもGoエコシステムの外へ出る必要がない
  • 各工程の具体的な利点:
    • 依存はgo mod tidyが扱い、ラップトップ、CI、コンテナで決定的かつ同一の結果になる
    • pipやnpmでは解決の失敗がそのまま反復1回の損失になる
    • テストはgo test ./...だけで済み、フレームワークの選定もフィクスチャの設定も不要
    • パッケージの隣に_test.goを置き、Testで始まる関数を書き、コマンド1つで実行する
    • テスト駆動を強制するエージェントにとって、この信頼性はPythonのフィクスチャ乱立にはないゲートの決定性を与える
    • 精密な依存グラフによりコンパイルは高速に保たれ、反復スループットに直接効く
    • デプロイはgo buildのみで、実行時依存ゼロの自己完結バイナリが1つできる
    • インタプリタのバージョン管理もコンテナ起動時間も不要で、バイナリをコピーして実行するだけ
  • これらの利点は線形に足されるのではなく複利で効く
  • サイクル各段階のループ高速化が、時間あたり反復数の増加、API費用の低下、結果の信頼性向上をもたらす

■ 18. 単一言語の勝利ではない

  • 本稿の主眼はPythonやTypeScriptの置き換えではない
  • スタックの全層を単一言語に賭けることは、どの言語が勝っても同じ誤り
  • 問いはより狭い: エージェントのワークフローが走るシステム、サービス、インフラの層にどの言語が最も適合するか
  • 2026年時点ではGoである場合が多く、その層への適合が際立って高いことがその理由
  • PythonのMLエコシステム (PyTorch、LangChain、Transformers) は消えないし、消えるべきでもない
  • モデルの訓練と推論の実行についてはPythonが答えであり、Goはそこで競合せず補完する

■ 19. GoとPythonの共存

  • GoはPythonの隣に置くのと同じ容易さでPythonの下層に置ける
  • Simon Willisonのgo-to-wheel:
    • コンパイル済みGoバイナリをPython wheelとしてPyPIで配布する
    • 任意のGoバイナリが標準のpip installまたはuvxのワンライナーになる
    • 彼自身のGo製並行ファイルシステムスキャナsqlite-scannerがまさにその方式で配布されている
  • Armin Ronacher (Flask作者) による逆方向からの指摘:
    • MiniJinjaのRustからGoへの移植は45分と60ドルのAPI費用で済んだ
    • コーディングのコストが劇的に下がっており、エコシステムの広さの重要性は相対的に低下する
  • Pythonが統括し、Goが下層で実行する構成が成り立つ
  • 移植コストが毎月安くなるほど、単一エコシステムを選ぶロックインの論拠は弱まる
  • Goの構造的優位とPythonのエコシステムは二者択一ではなく、両方を得られる

■ 20. Goを既定とする指針

  • Goは大規模なソフトウェアの総コストを下げるために設計された
  • エージェント開発はまさにその問題を破綻寸前まで増幅する
  • 人間規模のチームに有効だったGoの性質はすべて、1日に数千回反復する機械にも同様に適合する
  • Goが唯一使うべき言語になるわけではない:
    • PythonはMLエコシステムを保持し続ける
    • 課題に応じてRustやTypeScriptなどへ手を伸ばす正当な理由は実在する
  • 唯一の支配的言語を求めるのではなく、システム・サービス・インフラ層の既定としてGoを据える
  • 他を選ぶ際には固有の理由を要求する
  • 新規のサービス、CLI、システムを始める際は「なぜGoではないのか」を問う
  • 説得力ある理由を挙げられるならその主張を通す:
    • 存在しない必須ライブラリがある場合
    • Goでは満たせない性能要件がある場合
    • 移行コストが節減を上回るほどチームの投資が深い場合
  • 理由を挙げられないなら、Goが正しい既定である可能性が非常に高い
  • ビルドの遅さ、不安定な依存、実行時の想定外に失われる数パーセントはすべてAPI費用
  • 2026年にかけてエージェント作業負荷が拡大するなか、言語選択がそのコストへの単一で最大の梃子になりうる
  • 摩擦を早期に取り除いたチームは、それを支払い続けるチームより安く速く反復する