逆走の設計判断 ── Rust撤退の本当の理由
前章で見た四つの負債を解くために、Prisma は「起点」に手を付けた。Rust 製クエリエンジン、そのものである。本章では、なぜ Rust を捨てたのか、どこへ移ったのか、そして「Node→Rust/Go が主流」の潮流に対する Prisma の回答を掘っていく。
二つの発表が示した方針
Rust 撤退の意志表示は二段階で行われた。
段階①:ORM Manifesto(2024年12月2日)
→ 「Rust アーキテクチャの三重苦」を負債として告白
→ コア移行の可能性を示唆
段階②:From Rust to TypeScript(2025年3月頃〜)
→ 正式に「Rust を捨てる」と宣言
→ 移行先と移行計画を具体化
公式ブログ「From Rust to TypeScript: A New Chapter for Prisma ORM」で、Prisma は四つの理由を列挙している。
理由①:コントリビューション障壁
前章でも触れたが、もう一段深く見る。
コントリビューション障壁の二段階:
① スキル障壁:Rust + TypeScript + FFI の同時要求
② ビルド障壁:OS × OpenSSL のマトリックス環境
結果:
Query Engine 本体への外部 PR はほぼゼロ
バグ報告はできても、修正は Prisma 社内しかできない
OSS コミュニティが実質的に機能していなかった
Prisma 公式は「多くのユーザーが TypeScript に精通しているのに、コアに手を入れられないのはもったいない」と率直に認めた。
理由②:デプロイ複雑性
Rust バイナリの同梱は、モダンなデプロイ方法と噛み合わなくなっていた。
噛み合わなかった点:
- サーバーレス:パッケージサイズ上限を圧迫
- コンテナ:OS/OpenSSL バージョンへの依存でビルド失敗
- エッジ:そもそもネイティブバイナリが動かない
- モノレポ:巨大な node_modules の足かせ
理由③:ランタイム互換性
Cloudflare Workers、Vercel Edge、Deno Deploy、Bun ── これらのランタイムは、2020 年の Prisma 2 設計時には存在しないか、周縁的だった。2024〜2025 年にはメインストリームになった。
ここで Prisma は一時期、Driver Adapters という回避策を提供していた。仕組みはこうだ。
graph TB
subgraph "Driver Adapter 登場前(v4 以前)"
A1[Prisma Client] --> B1[Rust Query Engine]
B1 --> C1[(DB)]
end
subgraph "Driver Adapter 登場後(v5〜v6)"
A2[Prisma Client] --> B2[Rust Query Engine<br/>WASM 版 / Library 版]
B2 -.コールバック.-> D2[Driver Adapter<br/>JS 製]
D2 --> C2[(DB)]
end
Driver Adapter は「DB 接続だけは JS 側が担う」仕組み。これでエッジランタイム(@neondatabase/serverless や PlanetScale の @planetscale/database のような純 JS ドライバ)と組み合わせることができた。
しかし問題は残った。**Query Engine 本体はまだ Rust(WASM 版であっても)**だったからだ。
v5〜v6 時代の Driver Adapter 構成の問題:
- エッジ用 WASM Query Engine(重い)+ JS ドライバ
- Node 用 Rust Library Query Engine + JS ドライバ
→ コードパスが二重化、メンテコスト増
→ WASM 版はバイナリサイズがそれなりに大きい
Driver Adapter は「糊」としては機能したが、根本問題を先送りした絆創膏だった。
理由④:製品戦略の転換 ── ここが一番根が深い
Ch.2 で張った伏線を回収する。Rust 採用の主動機は「マルチ言語クライアント」だった。
このビジョンは、2024 年時点では事実上撤退していた。
マルチ言語クライアントの現実(2024年頃):
- TypeScript ユーザーが圧倒的多数(95%超)
- prisma-client-go:コミュニティメンテ、公式ではない
- prisma-client-py:同様
- Rust / Scala クライアントは存在せず
- Prisma Inc. の事業は TypeScript エコシステム中心に収斂
結論:
マルチ言語のための「言語非依存コア」を維持する理由が消えた
Prisma 公式の表現はこうだった。
“The core benefit of the query engine—the ability to support multiple clients—is no longer our focus. Prisma ORM is a TypeScript project.”
「Prisma ORM は TypeScript プロジェクトである」 ── この一文で、Prisma は自身のアイデンティティを再定義した。Rust 採用の最大理由が消滅した以上、Rust を維持するコストは純粋な負債だった。
「Node → Rust/Go が主流なのに、なぜ逆走?」への回答
ここで、本記事の起点である違和感に戻ろう。Prisma 7 AMA(公式ブログ)で、この疑問に対してチームは明快に答えている。要約すると次のとおりである。
答えの核:シリアライズコストの罠
graph LR
subgraph "v6 以前:Rust エンジン"
A[TS クライアント] -->|1. クエリを JSON 化| B[Rust エンジン]
B -->|2. SQL 実行| C[(DB)]
C -->|3. 結果| B
B -->|4. 結果を JSON 化| A
A -->|5. JSON パース| A
end
クエリが Node.js → Rust に渡るとき、Prisma Query(JavaScript オブジェクト)を Rust が理解できる形にシリアライズする。結果が Rust → Node.js に戻るときも、Rust の型を JSON に戻し、Node.js 側で再びパースする。
この往復コストが、Rust 本体の速度上の利点を打ち消していた。特に大量の結果を返すクエリでは、ネットワークや DB より「Rust ⇔ JS 境界のシリアライズ」が支配的になっていたのだ。
計測されていた現象(公式ベンチマーク repository より):
findMany で 25,000 件取得 → Rust 版 163〜185ms
→ 同じクエリ、TypeScript + WASM 版 → 55〜77ms
→ 2.1〜3.4倍速い
複雑な include 付き m2m クエリ 2,000 件取得:
→ Rust 版 1539ms
→ TS + WASM 版 136ms
→ 11倍速い
詳細は Ch.6 で見るが、「Rust だから速い」という前提そのものが、Prisma の使い方では成立していなかったのである。
一般論ではなく、設計次第
誤解を避けておくと、Prisma の結論は「Rust は遅い」ではない。「言語境界を頻繁に跨ぐワークロードでは、Rust の速度がシリアライズコストで相殺される」 という個別の事情である。
- 単一バイナリで完結する CLI(esbuild、uv、Biome 等):Rust で勝つ
- 計算集約的な純粋関数(SWC のコード変換など):Rust で勝つ
- 頻繁に JS と往復する API 境界がある ORM:Rust が勝つとは限らない
Prisma は「計算集約部分(クエリプラン生成)だけ WASM で Rust 由来のロジックを残し、実行部分は TypeScript で完結」という折衷を選んだ。これが次に見る Query Compiler である。
新アーキテクチャ ── WASM Query Compiler + TypeScript Executor
v7 の内部構造はこう再編された。
graph TB
A[Prisma Client<br/>TypeScript] -->|Prisma Query| B[Query Compiler<br/>WASM]
B -->|Query Plan IR| C[Query Executor<br/>TypeScript]
C -->|Driver Adapter| D[pg / better-sqlite3 / ...]
D --> E[(Database)]
E -->|Result Rows| D
D -->|ネイティブ JS オブジェクト| C
C -->|型付き結果| A
style B fill:#ff9,stroke:#333
style C fill:#9f9,stroke:#333
Query Compiler(WASM)
- 役割:Prisma Query(
findMany({ where, include, ... }))をクエリプラン(中間表現 IR)にコンパイルする - 言語:Rust のロジックを WASM にコンパイルして維持
- 理由:クエリプラン生成は計算集約的かつ純粋な変換なので、JS 境界を頻繁に跨がない。Rust 由来のロジックを温存しつつ、プラットフォーム非依存で配布できる
- 配布形態:
@prisma/clientnpm パッケージに.wasmとして同梱
Query Executor(TypeScript)
- 役割:クエリプランを受け取り、Driver Adapter 経由で DB に発行、結果を組み立てる
- 言語:100% TypeScript
- 理由:ここは DB との I/O や結果マッピングで「JS オブジェクト」を何度も行き来する。TypeScript で完結させるとシリアライズコストがゼロ
Driver Adapters
- 役割:実際の DB 接続・クエリ発行・結果取得
- 必須化:v7 では全 DB で必須(v6 までは Preview)
- 例:
@prisma/adapter-pg(pgドライバ)、@prisma/adapter-better-sqlite3、@prisma/adapter-mariadb
// v7 の典型的な初期化(PostgreSQL)
import { PrismaPg } from '@prisma/adapter-pg';
import { PrismaClient } from './generated/prisma/client';
const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL });
const prisma = new PrismaClient({ adapter });
const users = await prisma.user.findMany({ where: { active: true } });
設計判断のまとめ:「境界を分けた」
v7 のアーキテクチャが示しているのは、「Rust か TypeScript か」という二者択一ではなく、境界の引き直しである。
✅ 計算集約で純粋な部分(クエリプラン生成)
→ Rust → WASM として温存
→ 言語境界を跨ぐ頻度が低いので、シリアライズコストが効かない
✅ I/O 中心で JS オブジェクトを何度も扱う部分(クエリ実行)
→ TypeScript で完結
→ JS ランタイムとの境界がなくなり、データ変換コストが消える
「Rust で書けば速い」ではなく、「どこで Rust を使うと JS との境界コストを最小化できるか」という設計問題として捉え直したのが、v7 の本質である。この考え方は、Prisma 以外の JS ツール設計にも応用が効く視点だ。
ここまでの地図
graph LR
A[Ch.2<br/>マルチ言語戦略<br/>→ Rust 採用] --> B[Ch.3<br/>戦略の前提崩壊<br/>→ 四つの負債]
B --> C[Ch.4(本章)<br/>境界の引き直し<br/>→ WASM + TS]
C --> D[Ch.5<br/>具体的な変更点]
D --> E[Ch.6<br/>ベンチマーク]
style C fill:#9f9,stroke:#333,stroke-width:3px
ここまでが v7 の「Why」である。次章では「What」── 実際に何がどう変わったのかを、Query Engine、Schema/Client、Migration、Driver Adapters、エコシステム(Accelerate/Pulse/Studio)、MongoDB 対応の領域別に整理していく。
本章のまとめ
✅ Rust 撤退の公式理由は 4 つ:
① コントリビューション障壁
② デプロイ複雑性
③ ランタイム互換性
④ 製品戦略の転換(マルチ言語ビジョンの撤退)
✅ 最深の理由は ④:「マルチ言語クライアント戦略」という
Rust 採用の本来の目的が事実上消滅していた
✅ 「Node→Rust/Go 逆走」への答えは「シリアライズコストの罠」
JS ⇔ Rust の往復コストが Rust の速度利点を打ち消していた
✅ v7 の新アーキテクチャは「境界の引き直し」:
- Query Compiler:Rust → WASM(計算集約)
- Query Executor:TypeScript(I/O 中心)
- Driver Adapters:必須化
✅ この設計思想は「Rust か TS か」ではなく
「どこで境界を引くと言語間コストを最小化できるか」