JavaScriptの型消去はなぜ怖い?2.6万行のツールをPure Rustへ統合した実証録
作成日:
更新日:

JavaScriptの型消去はなぜ怖い?2.6万行のツールをPure Rustへ統合した実証録


論旨

AI エディタ(Antigravity / VS Code)と二人三脚でサイト運用やツール開発を続けていると、手元のフォルダにいつの間にか十数本もの JavaScript(.mjs / .js / .cjs)や Python(.py)スクリプトが溜まっていきます。前回の検証では「ミリ秒の速度損益分岐点」だけを物差しにして、毎日何百回も走る検索や監査だけを Rust 化し、よく使う記事作成ツール(article_toolkit.mjs)や日次バッチは Node.js のまま残す判断をしていました。実際、最上位 AI(Gemini 4 Argon)に理由を尋ねても「1 記事あたり 1〜2 回しか走らない処理なので、Rust にしても時間は大して短縮されませんよ(約 0.08 秒差)」と返ってきますし、単発の計算としてはその進言は正しいのです。

しかし、人間の役目は AI の正論を鵜呑みにせず「重箱の隅をつつくこと」にあります。「JavaScript は型宣言を書いても人間用のメモで実行時には無視されるから怖いし、Python はインタープリタで遅い。AI が同じ挙動を作れるなら、最初から超安全で超高速な仕組みを作らせた方がいい」と徹底追及して全スクリプトを棚卸ししたところ、速度以前の見過ごせない腐敗が次々と見つかりました。

結論からお伝えすると、自作の運用ツール群を Rust へ統合すべき最大の理由は単発のミリ秒短縮ではなく、「型消去(Type Erasure)による実行時クラッシュの恐怖」「移行し忘れの幽霊コード」「コピペ二重管理」をコンパイラの物理ロックで根絶できること、そして AI がコードを書く時代には「書く楽さのために遅くて危ない言語で妥協する理由」が完全に消滅していること にあります。

本記事では、当サイト(現在公開650記事)の裏方で動いていた 全 21 本・計 26,273 行 の JavaScript スクリプトと、さらに AI の思考ログからあぶり出した 隠れ Python スクリプト 100 本・使い捨て解析処理 までを、外部クレート依存ゼロ(Cargo.toml なし・標準ライブラリのみ)のまま既存の Pure Rust 2 バイナリ(gemini-gate と kb-search)へ完全統合した一次実測データと、コンパイラが実行前にバグを撃墜した生々しい証拠を公開します。

合わせて読みたい深まる関連情報
対象となる方
  • AI コーディングで増えすぎた Node.js / JavaScript や Python スクリプトの保守・実行時エラー に不安を感じている方

  • 「型注釈を書いたのに本番の珍しい条件で落ちた」「同じようなスクリプトが複数フォルダに散乱している」 という悩みを根本から断ち切りたい方

  • AI の「今のままで十分です」という進言を盲信せず、Web での裏取りと追及で超安全・超高速な基盤を作らせたい方

  • 外部パッケージ(node_modules や大量のサードパーティ製クレート)を増やさず、壊れない単一バイナリの運用基盤 を作りたい方

質問するおじさん

前に検索と監査のプログラムを Rust にして爆速になったじゃろ? じゃけど、記事のひな形を作る article_toolkit.mjs とかは毎日のように使っとるのに、なんであれは JavaScript のまま放置しとったんかね?

解説ナビゲーター

実は最初、相棒の AI(Gemini 4 Argon)に同じ質問をぶつけたときも、「1 記事あたり 1〜2 回しか走らない処理なので、Rust 化しても短縮できるのは 0.08 秒ほどです。時間は大して縮まりませんよ」と進言されたんです。だいたい Argon の言うことは論理的に正しいのですが、そこで納得せず「重箱の隅」をつついてみたところ、速度以上に深刻な盲点が次々と出てきました!

速度の損益分岐点だけでは見落としていた罠

AI と一緒にツールを作っていると、つい「1 回あたり何ミリ秒速くなるか」というベンチマーク数値ばかりで損得を計算しがちです。たとえば Google Search Console の API から検索データを取得するスクリプトは、ネットワーク通信の待ち時間だけで数秒〜十数秒かかります。そのため、処理速度だけを理由にすれば「Node.js を Rust に書き換えても通信待ちは縮まらないし、書き換えコストに見合わない」という AI の進言は一見すると完全に正しい正論です。

しかし、その「書き換えの手間を惜しんでスクリプト言語で済ませる」という発想自体が、人間が手作業で苦労してコードを書いていた時代の常識 に縛られています。日々の運用を長く支える「大工道具」に求められる本質は、単発の数ミリ秒の差ではなく 「実行する前から壊れていないと確信できるか」 でした。

型宣言が実行時に消えるType Erasureの恐怖

JavaScript が抱える最大の構造的な弱点は、コンパイラによる機械語生成と事前保証がなく、実際にその行を実行してみるまでエラーが発覚しないこと です。

近年では JSDoc コメントで型を書いたり、TypeScript を使ったり、あるいは Node.js(v22.6 以降)のネイティブ TypeScript 実行機能(Type Stripping)を使ったりするケースが増えています。ところが、TypeScript 公式ハンドブックに「Erased Types(消去される型)」と明記されている通り、これらの型宣言はあくまで人間やエディタ向けの「付箋」にすぎません。

⚠️ JavaScript / TypeScript 運用で拭えない 3 つの不安
  1. 実行時には型がすべて剥がれ落ちる(Type Erasure) : トランスパイラや Node.js の Type Stripping は、実行前に型注釈を単なる空白文字へ置き換えてから V8 エンジンに渡します。実行中のメモリレイアウトや型の整合性を CPU レベルで保証しているわけではありません。

  2. 普段通らない分岐のバグが本番まで潜伏する : 正常系のルートだけテストして動いているように見えても、「半年に 1 度だけ起きる API タイムアウト時のリトライ処理」や「特定の文字コードが混ざったときの文字列切り出し」に変数名ミスや undefined 参照があると、本番でその条件を踏んだ瞬間に突然クラッシュします。

  3. 外部 JSON や SQLite 読み出し境界の脆さ : tsc --noEmit で事前チェックをかけたとしても、JSON.parse() の戻り値や SQLite の行データ、コマンドライン引数の解釈で少しでも想定外の構造が混ざると、実行時に平気で意図しない型変換(暗黙の型強制)が起きてしまいます。

対して Rust の場合、rustc コンパイラがすべての条件分岐・所有権・ライフタイム・Option / Result の取りこぼしを検査し、1 箇所でも型や引数の矛盾があれば実行バイナリ自体が 1 バイトも生成されません。「動いているように見えるスクリプト」と「コンパイルを通過したネイティブバイナリ」とでは、安心感の次元がまったく異なります。

棚卸しで発覚した二重管理と幽霊コード

「他にも気持ち悪い .mjs が残っていないか全部探してみよう」とワークスペース全体を徹底的に棚卸ししたところ、なんと 合計 21 本・26,273 行 もの JavaScript ファイルが見つかりました。さらに中身を 1 本ずつ解剖すると、速度以上に深刻な 2 つの構造腐敗 が浮かび上がりました。

1 つ目は、「移行したつもりで放置されていた幽霊コード 8 本(約 10,600 行)」 です。前回の高速化で検索(kb_query.mjs)や監査(lint_article_rules.mjs)、ルール同期(sync_gemini_rules.mjs)の中身はすでに Rust 側へ移し終えていたにもかかわらず、古い .mjs ファイルが scripts/ フォルダにそのまま残っていました。人間は呼ばなくても、AI がコードベースを検索した際に古い .mjs の実装を誤って参照してしまうノイズ源になっていたのです。

2 つ目は、「コピペ二重管理の温床(計 2,824 行)」 です。ローカルで過去チャットを閲覧する Web サーバー server.cjs(1,412 行)が、単体起動用の tools/convo-mail/server.cjs と、VS Code 拡張機能同梱用の tools/antigravity-convo-mail-ext/server.cjs の 2 箇所に 1 文字違わず丸ごと複製 されていました。これでは片方にバグ修正を入れても、もう片方への反映を忘れた瞬間に先祖返り事故が起きます。

JavaScript分散運用とPure Rust 2バイナリ統合の構造比較
移行前(JavaScript混在)
21ファイル・26,273行の分散と腐敗
  • 実行時発覚の恐怖 : 普段通らないエラー分岐のミスは踏むまで不明
  • 幽霊コード残存 : 移行済みの旧 .mjs 8 本(約 1 万行)が検索ノイズ化
  • コピペ二重管理 : 1,412 行の server.cjs が 2 フォルダに重複存在
  • 環境依存の鎖 : launchd 自動実行に nvm の Node バージョンパスが直書き
移行後(Pure Rust完全統合)
外部依存ゼロの2大バイナリへ一元化
  • コンパイル時保証 : 全分岐・書式指定子・型不整合を実行前に 100% 阻止
  • 幽霊・重複ゼロ : 日常運用 scripts/ 直下の .mjs / .js を完全撤去
  • 単一実装の共有 : CLI も IDE 拡張も同じ kb-search バイナリを直接起動
  • 外部クレート 0 個 : std + OS 標準機能のみで node_modules 汚染ゼロ
フレームワーク規約で必須の薄い設定・起動ファイル(astro.config.mjs と extension.js)を除く全運用ロジックを2つのネイティブバイナリへ集約しました。

全21本・2.6万行を3段階で統合した設計

26,273 行ものツール群を一度に全部書き換えてファイルを消すのは、「急がば回れ」の原則に反する危険な行為です。そこで今回は 「第 1 弾の実装と全テスト合格 ➔ 第 2 弾の実装と全テスト合格 ➔ 第 3 弾の実装と全テスト合格」 という厳格なゲート方式を採り、1 段階ずつ確実に動作を証明してから次の段階へ進みました。

また、Rust プロジェクトをスクリプトごとに 20 個も作るとビルドキャッシュで SSD や RAM ディスク(記憶装置)が肥大化するため、既存の 2 つの Pure Rust バイナリ(記事監査・生成系の gemini-gate と、SQLite・通信・常駐系の kb-search)のサブコマンドとして機能を集約しています。

3段階ゲート方式によるPure Rust 2バイナリ統合マップ
第1弾(執筆・大工道具)
gemini-gate への統合(10ファイル撤去)

記事ひな形生成・画像 WebP 変換・アフィリエイト自動登録・太字整形・相互リンク結線(旧 article_toolkit.mjs 2,617 行)と対話型ランチャー(旧 npm_menu.mjs 619 行)を統合し、放置されていた旧 .mjs 8 本も同時一掃。

第2弾(集計・履歴同期)
kb-search への統合①(Protobuf・Takeout解析)

Antigravity の Protobuf バイナリ生ログから TPU 待ち時間とトークンを集計する検証盤生成(747 行)、Google Takeout HTML の XSS 無害化&双方向マージ同期(1,270 行)、IDE 拡張シンボリックリンク自動修復(143 行)を統合。

第3弾(通信・常駐基盤)
kb-search への統合②(GSC認証・HTTPサーバー・AI日報)

Search Console API 分析・URL 検査台帳・SVG 推移グラフ・定期自動診断(計 6,740 行)、127.0.0.1 限定 Convo Mail HTTP/1.1 サーバー(1,412 行×2)、gemini-4-argon 固定の一次情報 RSS 日報生成(611 行)を完全統合。

すべて外部クレート依存ゼロ(rustc 直コンパイル)で構築し、各段階のセルフテスト合格後に旧スクリプトを撤去しました。

第1弾は大工道具と幽霊スクリプト10本の撤去

第 1 弾で取り組んだのは、執筆のたびに呼び出す大工道具 article_toolkit.mjs(2,617 行)と npm_menu.mjs(619 行)です。

Node.js 版で唯一ネイティブ C++ アドオンに依存していた画像処理パッケージ sharp については、macOS 標準の /usr/bin/sips(リサイズ・回転補正)と cwebp(WebP エンコード)、そしてすでに gemini-gate 内に実装済みだった PNG / JPEG / WebP バイナリヘッダの直接寸法読み取りを組み合わせることで、外部クレートゼロのまま完全同等のアイキャッチ自動生成を実現しました。

さらに add-affiliate サブコマンドでは、単なるテンプレート出力だけでなく src/data/affiliates/index.ts の export * 自動追記や、EC 検索リンクで互換品ばかりが混ざる原因になる安い順ソート(?s=2 や s=price-asc-rank)の混入阻止までコンパイル済みコードに組み込んでいます。54 項目の監査セルフテストと 48 項目のメニュー・ツールキット自己テスト合格を確認した上で、幽霊スクリプト 8 本を含む計 10 本を綺麗に撤去しました。

第2弾はProtobuf解析と履歴同期の統合

第 2 弾では、バイナリ解析と HTML 変換を担う 3 本(計 2,160 行)を kb-search へ統合しました。

  • 手書き Protobuf デコーダの内蔵(token_dashboard.rs) : Antigravity の内部状態 DB(state.vscdb)に保存されている Protobuf 形式の StepMetadata から Varint と Length-delimited フィールドを直接デコードし、モデルの最初の 1 文字目が返るまでの待ち時間(TTFT)やキャッシュヒット率をミリ秒単位で集計してスタンドアロン HTML へ出力します。

  • XSS 無害化と秒/ミリ秒揺れ吸収マージ(takeout_sync.rs) : Web 版 Gemini の Google Takeout エクスポート(マイ アクティビティ.html 等の日本語 HTML)から <script>・<iframe>・インライン on* イベント属性・javascript: スキームを厳密に除去した上で Markdown の見出し・表・コードブロックへ変換します。エクスポート時期によってタイムスタンプが秒単位だったりミリ秒付きだったりする揺れも双方向 Union マージで吸収し、会話スレッドが重複分裂しないよう保護しました。

第3弾はGSC認証とローカルHTTP基盤の刷新

最後の第 3 弾は、外部 HTTPS 通信・暗号署名・マルチスレッド TCP サーバーを含む最難関の 7 ファイル(計 12,987 行)です。「外部クレートゼロの Pure Rust で Google OAuth2 のサービスアカウント認証や HTTP サーバーまで本当に書けるのか?」と思われるかもしれませんが、OS 標準の堅牢なツールと Rust の RAII(スコープ終了時の自動解放)を組み合わせることで、むしろ Node.js 時代より安全で軽量な基盤になりました。

🔒 第 3 弾で実装したセキュリティ・堅牢性設計の急所
  1. RS256 JWT 署名と 0600 一時秘密鍵の自動消去(gsc_auth.rs) : サービスアカウント JSON の秘密鍵から JWT を署名する際、RAM ディスク上に所有者のみ読み書き可能なパーミッション 0600(create_new 排他作成)で一時ファイルを生成して /usr/bin/openssl dgst -sha256 -sign を呼び出し、Rust の Drop トレイトによって正常終了時もエラー発生時もミリ秒単位で確実にファイルを自動消去します。

  2. ps aux に機密トークンを一切出さない curl --config - 方式 : コマンドライン引数に -H "Authorization: Bearer ..." や Gemini API キーを直接渡すと、同じマシン上の別プロセスから ps aux でトークンが見えてしまいます。そこでヘッダーと URL をすべて標準入力(--config -)経由で /usr/bin/curl に流し込み、プロセス引数への機密露出を完全にゼロにしました。

  3. 二重管理だったローカル HTTP/1.1 サーバーの一本化(convo_mail_server.rs) : 2 箇所に複製されていた 1,412 行の server.cjs を全廃し、std::net::TcpListener(127.0.0.1 限定バインド)による単一実装へ統合しました。URL パーセントデコード後の canonicalize() 境界検査で .. パストラバーサルを 403 Forbidden で遮断し、VS Code 拡張側(extension.js)はこの Pure Rust サーバーを呼び出すだけの薄いブリッジ(約 160 行)に徹しています。

コンパイラが実行前にバグを捕まえた実測証拠

今回の完全統合を通じて、「やはり JavaScript をやめて Rust に統合して正解だった」と確信する出来事がまさに開発の最中に起きました(統合完了後に実際の 4 記事同時作成〜本番検証の所要時間が 42.5 分から約 24 分へ短縮した実測データは AI記事作成が1本6分に短縮した全12回の実測記録 にまとめています)。

表の列数ズレと分割TCP通信を事前に撃墜

Search Console の分析レポート生成モジュール(gsc_analytics.rs:2,234 行)を書き上げ、初めて ./scripts/run_kb_search.sh --self-test でコンパイルにかけた瞬間のことです。rustc が即座にビルドを止め、次のコンパイルエラーを突きつけてきました。

error: argument never used
    --> tools/kb-search/src/gsc_analytics.rs:1194:13
     |
1186 | "| {} | `{}` | **{}** ({}) | {} | {} | {} |",
     | -------------------------------------------- formatting specifier missing

主力エース記事ランキングの Markdown テーブルは「順位・記事 ID・クリック(増減)・表示回数・CTR・平均順位・代表クエリ」の 7 列 構成だったのですが、フォーマット文字列側の {} をうっかり 6 個 しか書いていなかったミスを、1 度も実行する前にコンパイラが水際で食い止めたのです。

質問するおじさん

うわっ、もしこれが JavaScript のテンプレート文字列(`| ${rank} | ...`)で変数名を書き忘れとったり、配列結合の数がズレとったりしたら、実際にそのレポート出力処理が走るまでエラーにならんやつじゃな!

解説ナビゲーター

まさにその通りです! Rust の format! は単なる文字列処理ではなくコンパイル時マクロなので、書式指定子の数と渡した引数の数・型が 1 つでも食い違うと、実行ファイルすら作らせずにその場で教えてくれます。2,200 行超のコードを一気に書いても、こうしたケアレスミスが本番へ 1 ミリも漏れ出さないのがコンパイラ言語の最大の威力です。

さらに convo_mail_server.rs の自己テストでは、ネットワーク遅延で HTTP リクエストが細切れに届く状況を再現するため、「リクエスト全文をわざと 7 バイトずつの極小フラグメントに分割して送りつけるテスト」 を組み込みました。すると初回テスト時、テストデータ側に手書きしていた Content-Length: 29(日本語 UTF-8「保持」を含む JSON で実際は 31 バイト)の 2 バイト不足をサーバー側が厳密に検知し、末尾が切れた不正 JSON として 400 Bad Request で正確に弾き返しました。テスト側のバイト長を動的計算に直すと同時に、2 段階のメタデータ更新で既存のピン留め・メモが消えないシャローマージ検証も追加し、全エンドポイントが無事一発合格しています。

皮肉なことに、全 3 弾の Rust コンパイルと自己テストを無傷で通過した直後、いざサイトの本番ビルド(astro build)を走らせた瞬間に たった 1 箇所だけ実行時エラー が起きました。フレームワーク規約上どうしても残す必要があった JavaScript 設定ファイル(astro.config.mjs)の 2 行目に、撤去した旧スクリプトを読む import './scripts/sync_gemini_rules.mjs'; が 1 行だけ残っており、ビルドを実行した瞬間に初めて Failed to load url で停止したのです(すぐさま Pure Rust バイナリの --sync-rules 呼び出しへ差し替えて解決しました)。「Rust 側は実行前に 100% 整合が取れていたのに、JavaScript 側だけはやはり実行する瞬間までリンク切れすら分からなかった」 という、まさに本記事のテーマを象徴する出来事でした。

移行前後のファイル数とテスト速度の実測比較

全 3 弾の統合と旧ファイルの撤去を終えた時点での、移行前後の定量比較が以下の表です。

評価軸 移行前(JavaScript 21本混在) 移行後(Pure Rust 2バイナリ統合) 実測・運用上の変化
日常運用 JS スクリプト数 21 本(計 26,273 行) 0 本(完全撤去) ※規約必須の2本のみ scripts/ 直下の .mjs / .js が消滅し AI の誤参照ノイズが根絶
サーバー実装の重複 server.cjs 1,412 行 × 2 箇所 convo_mail_server.rs 1 箇所のみ 単体起動も IDE 拡張も同一バイナリを共有し二重管理が消滅
外部クレート / 追加 npm googleapis, sharp, rss-parser 等に依存 外部クレート 0 個(std + OS標準のみ) サプライチェーン攻撃リスクと依存パッケージのバージョン腐敗を遮断
型・書式ミスの発覚時期 その分岐を実行した瞬間(実行時クラッシュ) コンパイル時(バイナリ生成前に 100% 阻止) 普段通らないエラー分岐や表の列数ズレも実行前に完全捕獲
統合セルフテスト所要時間 スクリプトごとに個別実行(未整備箇所あり) kb-search 全 13 系統で 214ms gemini-gate 54 項目+メニュー 48 項目と合わせ数秒で全機能検証
macOS launchd 自動実行 nvm の特定 Node パス(v24.20.0)に固定 /bin/zsh + run_kb_search.sh 直結 Node のバージョン更新で定期ジョブが突然死する事故を防止

なお、整理の途中で「一時バックアップフォルダ(bk/)から古い .mjs が 7 日後の自動清掃で消えてしまっても大丈夫か?」という点も確認しましたが、当プロジェクトではすべての削除前コードが Git のコミット履歴(ローカル .git および GitHub リモートリポジトリ)に 1 文字の欠損もなく永久保存されています。万が一過去の実装を見返したくなった場合でも git log や git checkout でいつでも 100% 復元できるため、ワークスペース上に不要な抜け殻ファイルを残しておく必要は一切ありません。

うれしい副産物:安全統合で体感速度も激変

すべての統合と旧スクリプト 21 本の撤去を終え、AI エディタを立ち上げ直して普段通り作業していたとき、ふと予想外の変化に気づきました。

質問するおじさん

なあ、今回の改修は「JavaScript の型消去が気持ち悪いけえ、安全のために Rust へまとめる」いう話じゃったよな? なんでか知らんけど、前よりエディタもコマンドも動きが速うなった気がするんじゃが……気のせいかのう?

解説ナビゲーター

いいえ、まったく気のせいではありません! 今回は速度ではなく「安全性とコード整理」だけを目的に統合したのですが、実際にミリ秒単位でベンチマークを取り直してみると、驚くほどうれしい副産物が起きていたんです!

パッケージ読込税が消えGSC集計が10倍高速化

「たまにしか動かさない管理ツールや分析バッチは、Rust にしても体感差はないだろう」という事前の思い込みを裏切り、日常コマンドやオフライン集計の実測タイムは桁違いに短縮されていました。

処理内容(実際の運用コマンド) 旧 Node.js 版(移行前) Pure Rust 統合後(実測値) 短縮倍率・体感差
ランタイム最小空起動 62.4 ms(node -e "" のみ) 0〜2 ms(RAMディスク機械語直結) V8 エンジンの初期化税が完全消滅
手動メニュー一覧表示(menu --list) 約 80〜120 ms 11.0 ms(0.011秒) 約 8〜10 倍高速(キー押下と同時に表示)
Takeout 履歴の差分確認(sync-takeout) 約 80〜150 ms 21.0 ms(0.021秒) 約 4〜7 倍高速(待ち時間ほぼゼロ)
GSC 全記事台帳+SVGグラフ生成 パッケージ import だけで 1,283.4 ms(全体約2〜3秒) 全処理完了で 152.0 ms(0.15秒) 10 倍以上高速化(読込税だけで8倍超の差)
GSC 全記事SEO健康診断(FM・リンク網監査) 約 2,000 ms 前後 545.5 ms(0.55秒) 約 3.6 倍高速(600本超を0.5秒台で走査)

なかでも衝撃的だったのが、Google Search Console(GSC)の分析・台帳生成ツールの内訳です。公開650記事のフロントマター走査から URL 検査台帳・CSV / JSON・2 段 SVG 推移グラフの書き出しまで全部含めて、Pure Rust 版はわずか 152.0 ミリ秒(0.15秒) で全工程を完了しました。

なぜここまで差がついたのか原因を切り分けるため、Node.js で gray-matter と googleapis の 2 パッケージを import するだけ(実処理ゼロ)の時間を計測したところ、巨大 npm パッケージのファイル群をメモリへ読み込んで構文解析するだけで 1,283.4 ミリ秒(約 1.3 秒)も消費していたのです。Pure Rust 版は、Node.js がまだパッケージの準備体操をしている 8 分の 1 以下の時間で、すべての集計とグラフ描画まで終わらせていたことになります。

しかもこのベンチマーク計測時は、他のアプリをすべて閉じた「無風の実験室」ではなく、バックグラウンドでファイルのダウンロード処理が走り続けており CPU も I/O も決して暇ではない実作業中 の状態でした。数千個の小分け .js ファイルを探し回って V8 で逐次解析する Node.js は裏方の負荷に影響を受けやすいのに対し、RAM ディスク上の単一ネイティブ機械語を直接実行する Pure Rust バイナリは、他プロセスが稼働中であっても 0.15 秒 で安定して突き抜けています(なお上記の 1,283.4 ms 対 152.0 ms はどちらも同じバックグラウンド稼働条件下で連続計測した同条件比較です)。

エディタ全体の動きまで軽くなった3つの理由

さらに、単発のコマンド実行だけでなく「普段のエディタ操作や保存・本番転送の引っかかりが消えてキビキビ動くようになった」ことにも、明確な 3 つの構造的理由がありました。

🚀 安全のための統合が体感速度まで引き上げた 3 つの理由
  1. 裏で常駐するローカル Web サーバーの「脱 V8・GC 停止ゼロ化」 : エディタ起動中ずっとバックグラウンドで動いている過去チャット閲覧サーバー(Convo Mail)が、メモリを多く抱えて定期的にガベージコレクション(不要メモリの掃除による一瞬の引っかかり)を起こす Node.js プロセスから、RAM ディスク上のネイティブ機械語+libsqlite3 ゼロコピー直結に置き換わりました。スレッド検索や画面切り替えが数ミリ秒で返り、常駐メモリ負荷も極小化しています。

  2. エディタの JS 解析サーバー(tsserver)と監視対象の激減 : AI エディタ(VS Code / Antigravity)は、スクリプトを参照・編集する裏側で JavaScript / TypeScript 解析プロセス(tsserver)やファイル監視(File Watcher)、検索インデックスを動かしています。今回 21 本・26,273 行の JavaScript ファイルが物理的に消滅 したことで、エディタが抱える裏方の監視・解析コストが一気に軽くなりました。

  3. ビルドや本番転送の道中にあった「多段 Node 起動」の解消 : サイトをビルドしたり本番環境へ転送したりする際、astro.config.mjs の初期化や package.json の事前処理から別の .mjs スクリプトを何度も呼び出していたリレー構造が、すべてミリ秒動作の Pure Rust バイナリ直結に変わりました。作業の節目にあった「コンマ数秒〜1 秒強の微妙な間」が消えたことで、体感のテンポが見違えるほど軽快になったのです。

AIの思考ログを裏取りしPythonも完全追放

JavaScript 21 本の統合を終えて安心したのも束の間、AI の思考ログや実行コマンドを観察していて、もう 1 つの見過ごせない違和感が目に入りました。裏方の処理で Python(python3) が使われていたのです。

質問するおじさん

おう、そういえば AI が何か調べ物をするとき、よく裏で Python のプログラムを書いて動かしとるのを見かけるぞ! JavaScript を全部片付けたんじゃったら、あのインタープリタで動く Python の方も遅うないんか?

解説ナビゲーター

するどいご指摘です! 重い仮想マシンを立ち上げる Java や、型宣言が実行時に消える JavaScript と並んで、標準の Python も「インタープリタ特有の遅さ」と「実行時の型無視」という二重の弱点を抱えています。ここも妥協せず Argon にぶつけたところ、最後の死角まで一気に Rust 化が進みました!

インタープリタ起動102msと型無視の二重苦

標準の Python(CPython)は、ソースコードを中間バイトコードへ変換したあと、仮想マシンのループで 1 命令ずつ「この変数は整数か? 文字列か?」を確認しながら逐次解釈する インタープリタ方式 で動きます。実行中に機械語へ変換する JIT コンパイラを備えた Node.js と比べても、さらに立ち上がりとループ処理が重くなります(全10言語の同一実装ベンチマーク でも、Rust は Python 比 155 倍速・Java 比 メモリ消費 10 分の 1 を記録しています)。

実際に同じ開発母艦(macOS / Python 3.14)で、何も処理をせず「起動して終了するだけ」の最小タイムを実測した結果がこちらです。

実行環境 空起動の実測タイム 内部の実行方式と「型宣言」の扱い
Pure Rust(機械語バイナリ) 0〜2 ms コンパイル時に全分岐の型・メモリ安全性を 100% 保証し、CPU が機械語を直接実行
Node.js(node -e "") 62.4 ms V8 エンジンが JIT コンパイルで高速化するが、型注釈は実行前にすべて消去
Python 3.14(python3 -c "") 102.5 ms 立ち上がるだけで 約 0.1 秒 消費。バイトコード逐次解釈+型ヒントも実行時無視

しかも Python には def calc(x: int) -> str: のような型ヒント機能がありますが、Python 公式ドキュメント(typing モジュール仕様)に「Python ランタイムは関数や変数の型注釈を強制しない」と明記されている通り、実行時には型宣言が完全に無視されます。つまり「書いても実行時には守られない人間用のメモ」という点では JavaScript とまったく同じ構造なのです。

幽霊100本とAIの使い捨てPython癖を根絶

そこで Argon に対し、「Python もインタープリタで遅いし型が無視されるのは同じだから、残っている箇所を全部洗い出してほしい」と指示したところ、JavaScript の棚卸しでは見えていなかった 3 つの隠れ Python 依存 が見つかり、その場で Pure Rust へ完全統合されました。

🐍 重箱の隅をつついて根絶した 3 つの Python 依存
  1. 放置されていた旧 .py スクリプト計 100 本(6,585 行)の完全撤去 : 過去の一括置換用(scripts/python/ 86 本)や調査用(scratch/ 13 本)など、使われなくなった Python ファイルが 100 本も眠っていました。Git 履歴に完全保存されていることを確認した上で全削除し、エディタの Python 解析サーバー(Pylance)の監視負荷と AI の誤参照ノイズを一掃しました。

  2. Takeout 同期の裏に 1 箇所だけ残っていた python3 呼び出しの撤廃 : Google Takeout の ZIP ファイルから日本語ファイル名(macOS 特有の濁点・半濁点が分離した NFD 形式)を展開する処理だけ、裏で python3 をサブプロセス起動していました。これを外部クレートゼロのまま、Pure Rust の ZIP / ZIP64 パーサー+macOS 標準 libz 直結ストリーミング展開(Box アドレス固定+Drop 自動解放+CRC-32 破損検査)へ刷新し、python3 呼び出しをゼロにしました。

  3. AI が調査のたびに裏で書いていた「使い捨て Python」の封印(約 117 倍高速化) : AI の思考ログを覗いて見つけた最大の盲点がこれです。AI は過去の会話ログ(/brain)の稼働時間やトークン消費を調べるたび、作業フォルダ(scratch/*.py)に毎回 100〜300 行の Python スクリプトを即席で書いて python3 で実行していました。この解析処理も kb-search brain サブコマンドとして Pure Rust へ組み込んだ結果、セッション一覧走査は 2,288 ms から 19.6 ms へ約 117 倍高速化 し、AI が毎回 Python コードを生成していた待ち時間とトークン浪費まで完全に消滅しました。

人間なら妥協する言語もAIなら超安全・超高速へ

一連のやり取りを振り返ると、AI 時代のツール開発における「人間と AI の本当の役割分担」がはっきりと浮かび上がります。

最上位モデルである Argon の言うことは、だいたいいつも論理的で正しいです。「たまにしか動かさないスクリプトを Rust にしても、時間は大して短縮されませんよ」という最初の進言も、「人間が自力で苦労してコードを書く」という従来の前提に立てば間違いなく正解 でした。人間が手書きで開発するなら、学習コストや記述量の多さを避けて「開発の速さ(書く楽さ)」を優先し、実行時の速さや厳密な型安全性を犠牲にして Python や JavaScript を使う妥協には十分な意味があったからです。

しかし、AI の言うことは正しいこともあれば、こうした「人間開発時代の常識」に引きずられた妥協案を出したり、見落としをしたりすることもあります。だからこそ、人間側は AI を盲目的に信じて丸投げしてはいけません。たとえ自分ですべてのコードを一から書けなくても、AI の思考ログや実行コマンドを観察し、「なぜここで遅いインタープリタを使うのか?」「その型宣言は実行時に意味があるのか?」と疑問を持ち、Web 上の技術記事や公式仕様を調べて AI の行動を裏取りしながら、遅いなら速くなるまでとことん重箱の隅をつつくこと が人間の最も重要な役目になります。

人間が手書きする時代とAIに作らせる時代の言語選択の違い
人間が手書きする時代
書く速さのために実行性能を妥協
  • 最優先の制約 : 人間の学習コストとタイピング量の削減
  • 選ばれやすい言語 : Python・JavaScript などの動的スクリプト
  • 受け入れていた代償 : インタープリタの遅さと実行時型エラーのリスク
AIに作らせる時代
同じ挙動なら最初から超安全・超高速へ
  • 前提の逆転 : AI にとって Python も Rust も実装の手間は同じ
  • 人間の本来の役目 : 思考ログ監視・Web 裏取り・重箱の隅の追及
  • 得られる果実 : コンパイル時 100% 型保証と機械語の圧倒的爆速
機械学習フレームワーク専用処理や規約必須の薄い設定ファイルを除き、同じ挙動を作れる日常ツールで遅い言語に妥協する理由はなくなりました。
AI がコードを作る時代において、まったく同じ挙動を実現できる処理であるなら、わざわざ型が無視されたりインタープリタで遅くなったりする言語を選ばせる理由はどこにもありません。最初からコンパイラが物理ロックをかける 「超安全で超高速になる仕組み(Pure Rust)」 を作らせた方が、長期的な保守も日々の体感速度も圧倒的に有利になります。

快適開発を支える母艦コストと再現プロンプト

最後に、これだけのネイティブコンパイルと 600 本超の記事データベースをストレスなく回しているローカル開発母艦のコスト感と、読者の方がご自身の環境で同じ段階統合を再現するためのプロンプトを紹介します。

5年稼働の母艦マシンと書籍の1日あたり単価

外部クレートを一切使わない Pure Rust 構成とはいえ、数万行の Rust コードを rustc -O で最適化コンパイルしたり、RAM ディスク(メモリ上の超高速仮想ドライブ)の上で SQLite と Astro ビルドを同時に走らせたりするには、メインメモリに余裕があるデスクトップ母艦があると作業効率が劇的に変わります。

たとえば 64GB ユニファイドメモリを積んだ現行の Apple Mac mini(M4 Pro 構成) や、筆者が現役でフル活用している 27 インチ iMac 5K(2020 年 Intel 最終世代の整備済品+Crucial DDR4 メモリ 64GB 自己増設) を開発母艦にした場合で考えてみましょう。初期投資として仮に 20万〜35万円前後かかったとしても、毎日欠かさず 5 年間(1,825 日)使い倒せば、1 日あたりの快適コストは約 110円〜192円(推論・単純日割り試算)、自販機の缶コーヒー 1 本分ほどに収まります。RAM ディスクにコンパイル一時領域を逃がすことで内蔵 SSD の書き込み寿命も温存でき、AI に 24 時間テストとビルドを回し続けさせる環境としてこれ以上ない投資対効果を発揮してくれます。

Apple Mac mini(M4 Pro 等 Appleシリコン Pro系チップ・64GBユニファイドメモリ構成)

Apple Mac mini(M4 Pro 等 Appleシリコン Pro系チップ・64GBユニファイドメモリ構成)

【中古・整備済品】Apple iMac 27インチ Retina 5K (2020年モデル / Intel最終世代・メモリ自己増設対応)

【中古・整備済品】Apple iMac 27インチ Retina 5K (2020年モデル / Intel最終世代・メモリ自己増設対応)

【Intel Mac増設用】Crucial iMac 27インチ(2019/2020)・ノートPC用 メモリ DDR4-2666 SO-DIMM (16GB×2 / 32GB×2)

【Intel Mac増設用】Crucial iMac 27インチ(2019/2020)・ノートPC用 メモリ DDR4-2666 SO-DIMM (16GB×2 / 32GB×2)

また、AI が書いた Rust コードの所有権や Drop トレイト、エラー処理の勘所を人間側でもしっかり把握しておきたい方には、体系的なリファレンスとして手元に 1 冊置いておくと何年も使える定番書『プログラミングRust 第2版』が頼りになります(こちらも 5 年使えば 1 日あたり約 3 円の知識投資です)。

オライリー・ジャパン『プログラミングRust 第2版』

オライリー・ジャパン『プログラミングRust 第2版』

最上位モデルで段階移行させる指示プロンプト

ご自身のプロジェクトに散らばった JavaScript / Python / シェルスクリプトを Rust などのコンパイル言語へ整理・統合する際は、軽量モデルではなく 必ず『Gemini 4 Argon』または同等以上の最上位推論モデル を選び、いきなり全ファイルを消させず「棚卸し ➔ 段階別テスト合格」の順序を守らせるのが失敗しない鉄則です。以下のプロンプトをコピーして AI エディタに貼り付けるだけで、安全に再現できます。

参考記事(https://lab.chihouzakki.com/blog/review/javascript-type-erasure-26000-lines-audit-pure-rust-unification/)の設計手法を踏まえて、このプロジェクト内の自作スクリプト(.mjs / .js / .cjs / .py / .sh 等)を調査し、安全に整理・Rust統合する計画を立ててください。

【厳守ルール】
1. いきなりコードを書いたり既存ファイルを削除したりせず、まず全スクリプトの一覧・行数・用途・呼び出し元(package.json / cron / launchd / エディタ拡張等)・すでに使われていない幽霊コードや重複実装の有無を棚卸しして報告すること。
2. フレームワーク規約上どうしても残す必要がある設定ファイルと、単一ネイティブバイナリへ統合できるロジックを明確に仕分けること。
3. 実装は一気に進めず「第1弾 ➔ 自己テスト作成・合格確認 ➔ 第2弾 ➔ 自己テスト合格確認 ➔ 第3弾」の段階ゲート方式とし、各段階で既存データ・出力フォーマットとの完全互換を検証してから旧ファイルを撤去すること。
4. 外部APIキーやBearerトークンを扱う処理では、コマンドライン引数(ps aux)に機密が露出しない標準入力渡しや、パーミッション0600+スコープ終了時自動消去(Drop)を徹底すること。
5. 「実行頻度が低いから時間は大して短縮されない」と妥協せず、同じ挙動を再現できる処理はインタープリタ(Python等)や型消去言語(JavaScript等)を残さず統合すること。またAI自身がログ集計等で毎回使い捨てスクリプトを生成している箇所も洗い出し、高速なネイティブサブコマンドへ組み込むこと。

まとめ:動くスクリプトから壊れない道具へ

今回の JavaScript 全 21 本(26,273 行)および隠れ Python 群におよぶ Pure Rust 完全統合で得られた知見を整理します。

📌 JavaScript・Python 一掃と Pure Rust 統合の結論まとめ
  1. 「ミリ秒の速さ」だけで判断すると保守の腐敗を見落とす : 実行頻度が低いツールであっても、型消去(Type Erasure)やインタープリタでの型無視による実行時クラッシュの不安、移行途中の幽霊コード・二重管理は確実にプロジェクトを蝕みます。

  2. 外部クレートゼロ × 2 大バイナリ集約なら管理コストも最小 : スクリプトごとに Cargo.toml を乱立させず、標準ライブラリと OS 標準機能(sqlite3 FFI・libz・openssl・curl --config -・sips)で 2 つのバイナリに束ねることで、node_modules のような依存地獄をゼロにできます。

  3. 段階的テスト合格ゲートが「急がば回れ」の最短ルート : 第 1 弾・第 2 弾・第 3 弾と 1 ステップずつセルフテストを通過させてから旧ファイルを撤去したことで、本番運用を 1 秒も止めずにすべての移行を完遂できました。

  4. 安全のための統合が「10〜117 倍高速化とエディタ軽量化」の副産物を生む : 巨大 npm パッケージの読み込み税(約 1.3 秒)や常駐 V8 プロセス、使い捨て python3 スクリプトが消滅したことで、GSC 集計の 10 倍超高速化やセッション解析の 117 倍高速化まで同時に達成できました。

  5. AI 時代は「書く楽さ」で言語を妥協せず、人間は裏取りと追及に徹する : AI の進言を盲信せず Web で裏取りしながら重箱の隅をつつき、同じ挙動にできる処理は最初から超安全・超高速な Pure Rust で作らせるのが最良の設計戦略です。

質問するおじさん

なるほどなぁ。「時間は大して縮まらんけえ JavaScript や Python のままでええわ」という妥協を全部捨てたおかげで、コンパイラの目をくぐり抜けた鋼鉄の爆速道具箱に生まれ変わったわけか。AI の言うことも鵜呑みにせず、納得いくまで裏を取って突っつくのが大事じゃな!

解説ナビゲーター

はい! 人間が手書きするなら大変でも、AI が作るなら最初から一番安全で一番速い仕組みを作らせないともったいないですからね。幽霊コードも使い捨てスクリプトもなくなって、ワークスペースの見通しまで驚くほどクリアになりました!

ノルウェージャンフォレストキャット

ゴロゴロ……道具箱がピカピカに片付いたから、安心してひと眠りするにゃ……。

スッキリ片付いた開発環境の横で気持ちよさそうに眠るノルウェージャンフォレストキャット