## 2. 言語の決め方と言語別の既定

確認：2026年9月

### 2-1. TypeScript（Node.js）

| 役割 | 既定 | 代替と乗り換え条件 |
|---|---|---|
| ランタイム | **Node.js（LTS）** | Bun：スクリプト・CLI・新規の小規模APIで速度を重視するとき。Deno：Deno Deploy前提のとき。BunはAnthropicの傘下に入った（ライセンスはMITのまま） |
| パッケージマネージャ | **pnpm** | Bun：ランタイムもBunにする場合 |
| モノレポ | **pnpm workspaces ＋ Turborepo** | Nx：大規模でコード生成や依存グラフの可視化が必要なとき |
| Lint＋フォーマット | **Biome**（1ツールで完結・高速） | ESLint（flat config）＋typescript-eslint＋Prettier：Biomeが未対応のルールやプラグイン（Next.js固有ルールなど）が必須のとき |
| 型チェック | **tsc --noEmit**（`strict: true`） | — |
| テスト（ユニット） | **Vitest** | Jest：既存プロジェクトのみ |
| テスト（E2E） | **Playwright** | — |
| APIモック | **MSW** | — |
| バリデーション | **Zod** | Valibot：バンドルサイズを極限まで削りたいフロント |
| HTTPフレームワーク | **Hono**（軽量・どこでも動く・型安全なRPC） | NestJS：10人以上のチームでDI・モジュール構造を強制したいとき。Fastify：Node専用で既存プラグイン資産を使いたいとき |
| OpenAPI | **@hono/zod-openapi**（Zodスキーマからルートと仕様を同時に定義） | — |
| ORM／クエリ | **Drizzle**（SQLに近い・軽い・エッジ対応） | Prisma：スキーマ駆動でGUI（Studio）やマイグレーションの手厚さを重視するとき。Kysely：ORMを使わず型付きクエリビルダーだけ欲しいとき |
| マイグレーション | **drizzle-kit** | Prisma Migrate：ORMにPrismaを採用したとき |
| ジョブキュー | **pg-boss**（Postgresベース、§4-3） | BullMQ（Redis/Valkey）：秒間数百件を超える、またはValkeyが既にあるとき（§23-1）。Inngest／Trigger.dev：サーバーレス環境でワークフロー（リトライ・待機・ステップ実行）が必要なとき |
| ロギング | **pino** | — |
| 設定・環境変数の検証 | **Zodでスキーマ定義して起動時に検証**（t3-envなど） | — |
| 日付 | **date-fns** | Temporal API：対応環境が揃っていればネイティブを使う。Day.js：軽さ最優先 |
| HTTPクライアント | **fetch（標準）** | ky：リトライやフックを簡潔に書きたいとき |
| ビルド（ライブラリ） | **tsdown** | tsup：既存プロジェクトのみ（tsupは保守されていないため、機会があれば移行する） |
| 実行（開発時） | **Node.jsの型除去による直接実行**（`node --watch`） | tsx：enum・デコレータなど型除去で消せない構文や、パスエイリアスを使うとき |
| 脆弱性チェック | **pnpm audit ＋ Renovate/Dependabot** | — |

### 2-2. Go

| 役割 | 既定 | 代替と乗り換え条件 |
|---|---|---|
| 依存管理 | **Go modules** | — |
| Lint | **golangci-lint**（設定ファイルで有効ルールを明示） | — |
| フォーマット | **gofumpt**（gofmtより厳格） | gofmt：チームに合わせる場合 |
| HTTPルーティング | **標準 net/http**（メソッド・パスパラメータ対応済み）＋ ミドルウェアは自前か小さなライブラリ | chi：ミドルウェアのエコシステムを使いたいとき。Echo：フレームワーク一式を求めるチーム |
| DBドライバ | **pgx** | — |
| クエリ | **sqlc**（SQLから型安全なコードを生成） | ent：グラフ構造のスキーマが中心のとき。GORMは暗黙の挙動が多く、性能問題の調査が難しいため選ばない（§20） |
| マイグレーション | **goose** | Atlas：宣言的スキーマ管理（あるべき状態から差分生成）をしたいとき。golang-migrate：既存プロジェクト |
| OpenAPI | **oapi-codegen**（スキーマファースト） | ogen：生成コードの性能・厳密さを重視するとき |
| RPC | **Connect（connect-go）** | gRPC：既存のgRPC基盤がある場合 |
| ジョブキュー | **River**（Postgresベース、トランザクション内でジョブ投入可能） | Asynq：Redis前提のとき |
| ロギング | **log/slog（標準）** | — |
| 設定 | **環境変数＋envconfig（caarlos0/env など）** | koanf：複数ソース（ファイル・環境変数・リモート）を統合したいとき |
| CLI | **Cobra** | urfave/cli：小規模CLI |
| テスト | **標準 testing ＋ go-cmp** | testify：アサーションを簡潔にしたいチーム |
| 統合テスト | **testcontainers-go**（実DBでテスト） | — |
| モック | **インターフェース＋手書きフェイク** | mockery／gomock：モック対象が多いとき |
| ホットリロード | **air** | — |
| 脆弱性チェック | **govulncheck** | — |
| リリース | **GoReleaser** | — |

### 2-3. Ruby / Rails

Railsの標準構成（「Rails omakase」）にできるだけ乗る。外部依存を増やさないことが最大のメリットになる。

| 役割 | 既定 | 代替と乗り換え条件 |
|---|---|---|
| バージョン管理 | **mise** | rbenv：既存環境 |
| 依存管理 | **Bundler** | — |
| Lint＋フォーマット | **RuboCop（rubocop-rails-omakase）** | Standard：設定を一切考えたくないとき |
| テスト | **Minitest（Rails標準）＋ fixtures** | RSpec＋FactoryBot：チームがRSpecに慣れている、または既存資産があるとき |
| システムテスト | **Capybara＋Selenium（Rails標準）** | Playwright（capybara-playwright-driver）：安定性・速度を重視するとき |
| フロント | **Hotwire（Turbo＋Stimulus）＋ importmap** | jsbundling＋esbuild：npmパッケージを多く使うとき |
| CSS | **tailwindcss-rails** | — |
| アセット | **Propshaft（Rails標準）** | — |
| コンポーネント | **ViewComponent** | Phlex：Rubyだけでビューを書きたいとき |
| ジョブ | **Solid Queue**（DBベース） | Sidekiq：非常に高スループットが必要なとき |
| キャッシュ | **Solid Cache** | Redis：既存のRedis基盤がある場合 |
| WebSocket | **Action Cable＋Solid Cable** | AnyCable：同時接続数が非常に多いとき |
| 認証 | **Railsの認証ジェネレータ** | Devise：OAuth連携・多要素認証など機能を一式揃えたいとき。Rodauth：高度なセキュリティ要件 |
| 認可 | **Pundit** | Action Policy：ルールが複雑でキャッシュやテスト支援が欲しいとき |
| 管理画面 | **Avo** | Administrate：無償・シンプルで足りるとき |
| ページネーション | **Pagy** | — |
| ファイル | **Active Storage** | — |
| デプロイ | **Kamal** | Render／Fly.io：サーバー管理をしたくないとき |
| セキュリティ静的解析 | **Brakeman** | — |
| 依存の脆弱性 | **bundler-audit** | — |
| N+1検出 | **Prosopite** | Bullet：開発中にブラウザ上の通知でN+1を知りたいとき |
| モバイル | **Hotwire Native** | React Native：ネイティブ体験を重視するとき |

### 2-4. Python（AI/ML、データ処理が必要な場合に限定）

| 役割 | 既定 | 代替と乗り換え条件 |
|---|---|---|
| バージョン・依存管理 | **uv**（Python本体の導入からロックファイルまで1本） | Poetry：既存プロジェクト |
| Lint＋フォーマット | **Ruff** | — |
| 型チェック | **Pyright** | mypy：既存プロジェクトが使っているとき |
| テスト | **pytest** | — |
| Webフレームワーク | **FastAPI** | Django：管理画面付きのCRUDアプリをPythonで作る必要があるとき |
| バリデーション | **Pydantic** | — |
| ORM | **SQLAlchemy（2.x形式）** | SQLModel：FastAPIと密に統合したい小規模案件 |
| マイグレーション | **Alembic** | — |
| HTTPクライアント | **httpx** | — |
| ジョブ | **Celery** | arq／Dramatiq：軽量に済ませたいとき。Temporal：長時間ワークフロー |
| ロギング | **structlog** | — |
| データ処理 | **Polars** | pandas：ライブラリ連携上必要なとき |
| ノートブック | **Jupyter** | marimo：Gitで差分管理しやすいノートブックが欲しいとき |

### 2-5. Rust

| 役割 | 既定 | 代替と乗り換え条件 |
|---|---|---|
| ビルド・依存 | **Cargo** | — |
| Lint／フォーマット | **clippy ／ rustfmt** | — |
| 非同期ランタイム | **Tokio** | — |
| Webフレームワーク | **axum** | Actix Web：既存資産がある場合 |
| DB | **sqlx** | SeaORM：ORMが欲しいとき |
| シリアライズ | **serde** | — |
| ロギング／トレース | **tracing** | — |
| CLI | **clap** | — |
| エラー | **thiserror（ライブラリ）／ anyhow（アプリ）** | — |
| テスト実行 | **cargo-nextest** | — |
| ベンチマーク | **criterion** | divan：記述を簡潔にしたいとき |
| 脆弱性チェック | **cargo-audit ／ cargo-deny** | — |
| バイナリ配布 | **cargo-dist** | GoReleaser（Rust対応あり）：Goと配布の仕組みを揃えたいとき |
| クロスコンパイル | **cargo-zigbuild** | cross：Dockerで環境ごと切り替えたいとき |
| WebAssembly | **wasm-bindgen＋wasm-pack** | — |
| Node.jsから呼ぶネイティブ拡張 | **napi-rs** | — |
| Pythonから呼ぶネイティブ拡張 | **PyO3＋maturin** | — |
| デスクトップアプリ | **Tauri**（UIはWeb技術、裏側はRust） | Electron：Node.jsのAPIやChromiumの挙動に強く依存するとき |
| 組み込み（非同期） | **Embassy** | RTIC：割り込み駆動で厳密なリアルタイム性が必要なとき |
| gRPC | **tonic** | — |

### 2-6. Kotlin（JVM）

既存のJava資産との連携、複雑な業務ルール、大規模バッチ、Kafkaなどのストリーム処理で選ぶ。

| 役割 | 既定 | 代替と乗り換え条件 |
|---|---|---|
| JDK | **Eclipse Temurin（LTS）をmiseで管理** | Amazon Corretto：AWS上での実行に揃えたいとき |
| ビルド | **Gradle（Kotlin DSL）** | Maven：既存プロジェクト |
| Lint／フォーマット | **ktlint ＋ detekt** | — |
| Webフレームワーク | **Spring Boot** | Ktor：軽量に作りたい、Springの規模が過剰なとき |
| DBアクセス | **jOOQ**（SQLに近い型安全なクエリ） | Spring Data JPA：単純なCRUDが大半のとき。Exposed：Kotlinらしく書きたい小〜中規模 |
| マイグレーション | **Flyway** | Liquibase：DBの種類を横断して管理したいとき |
| 非同期 | **Kotlinコルーチン** | — |
| シリアライズ | **Jackson（Spring標準）** | kotlinx.serialization：Ktor採用時 |
| テスト | **JUnit ＋ Kotest（アサーション）＋ MockK** | — |
| 統合テスト | **Testcontainers** | — |
| バッチ | **Spring Batch** | — |
| ストリーム処理 | **Spring for Apache Kafka** | Kafka Streams：状態を持つ集計処理 |
| OpenAPI | **springdoc-openapi** | — |
| ロギング | **SLF4J＋Logback（JSON出力）** | — |
| コンテナ化 | **Jib**（Dockerfileなしでイメージ作成） | Dockerfile：細かく制御したいとき |
| 起動速度の改善 | 通常は不要 | GraalVMネイティブイメージ：サーバーレスで起動時間が問題になるとき |

### 2-7. Elixir（BEAM）

大量の常時接続、プレゼンス（誰がオンラインか）、障害からの自動復旧が中心の常駐サービスで選ぶ。

| 役割 | 既定 | 代替と乗り換え条件 |
|---|---|---|
| バージョン管理 | **mise（Erlang／Elixir）** | — |
| ビルド・依存 | **Mix ＋ Hex** | — |
| フォーマット | **mix format** | — |
| Lint | **Credo** | — |
| 型チェック | **Dialyxir**（Elixir本体の型システムも段階的に強化中） | — |
| Webフレームワーク | **Phoenix** | Plug単体：APIだけの極小サービス |
| 画面 | **Phoenix LiveView**（サーバー主導でリアルタイムUI） | Next.js＋Phoenix Channels：フロントをReactで作りたいとき |
| リアルタイム | **Phoenix Channels ＋ Phoenix Presence** | — |
| DB | **Ecto（マイグレーション含む）** | — |
| ジョブ | **Oban**（Postgresベース） | — |
| データパイプライン | **Broadway**（SQS・Kafka等からの消費） | — |
| HTTPクライアント | **Req** | — |
| テスト | **ExUnit ＋ Mox** | — |
| セキュリティ静的解析 | **Sobelow** | — |
| 計装 | **Telemetry ＋ OpenTelemetry** | — |
| デプロイ | **mix release をDockerイメージ化** → ECS／Fly.io／Kamal | — |

### 2-8. C#（.NET）

Unityのクライアントとサーバーでコードを共有したいとき、Microsoft／Azure中心の環境、Windows前提の業務システムで選ぶ。

| 役割 | 既定 | 代替と乗り換え条件 |
|---|---|---|
| SDK | **.NET（LTS）をmiseで管理** | — |
| ビルド・依存 | **dotnet CLI ＋ NuGet** | — |
| フォーマット／Lint | **dotnet format ＋ .NETアナライザー（.editorconfigで設定）** | — |
| Webフレームワーク | **ASP.NET Core（Minimal API）** | コントローラー方式：大規模で構造を揃えたいとき |
| DBアクセス | **EF Core** | Dapper：SQLを直接書いて性能を詰めたいとき |
| マイグレーション | **EF Core Migrations** | — |
| リアルタイム | **SignalR** | — |
| Unityとの通信 | **MagicOnion**（gRPCベースでC#の型をクライアントと共有） | — |
| ジョブ・スケジュール | **Quartz.NET** | — |
| テスト | **xUnit ＋ NSubstitute** | — |
| ロギング | **Serilog（構造化ログ）** | — |
| OpenAPI | **ASP.NET Core標準のOpenAPI生成 ＋ Scalar** | — |
| コンテナ化 | **dotnet publish のコンテナ出力** | Dockerfile：OSパッケージの追加などビルドを細かく制御したいとき |

### 2-9. 作成物ごとの言語の決め方

言語は「何を作るか」で決まる。下表で当てはまる行を探して既定を使い、右の列の条件に当てはまるときだけ乗り換える。バックエンドはさらに細かく §2-10 で判断する。

| 作成物 | 既定 | 別の選択肢と乗り換える条件 |
|---|---|---|
| CRUD中心のWebアプリ・管理画面 | **Rails**（画面の多い業務システムを最も速く作れる） | TypeScript：外部API連携が中心、またはReactで画面を作り込みたいとき。Kotlin：業務ルールが非常に複雑で、既存のJava資産と連携するとき |
| 一般的なWeb API・SaaSのバックエンド | **TypeScript**（フロントと1言語に揃う） | Go：3人以上で公開APIを長期運用するとき（§2-10 手順3）。負荷の性質で決まる場合は §2-10 を優先する |
| CLIツール | **Go** | Rust：起動時間・バイナリサイズ・処理速度を詰めたい。TypeScript（Bun）：社内向けでnpmの資産を使いたい |
| インフラ系ツール（Kubernetes Operator、Terraformプロバイダ等） | **Go**（公式SDKがGo中心） | — |
| 開発者向けツール（Linter、フォーマッタ、ビルドツール、パーサ） | **Rust** | Go：処理が軽く、開発速度を優先したいとき |
| ブラウザ内の重い処理（画像・動画処理、暗号、大量データの解析） | **Rust → WebAssembly** | TypeScript＋Web Worker：処理がそこまで重くないとき |
| エッジ関数 | **TypeScript**（Cloudflare Workers） | Rust→WebAssembly：CPU負荷の高い処理がある |
| 他言語から呼び出すライブラリ | **Rust**（napi-rs、PyO3など） | — |
| デスクトップアプリ | **Tauri** | Electron：Node.jsのAPIに強く依存するとき。C#（.NET）：Windows専用の業務アプリ |
| モバイルアプリ | **React Native＋Expo** | Kotlin／Swift（ネイティブ）：端末機能を深く使うとき。Flutter：デザインの一貫性を最優先するとき |
| ゲームのクライアント | エンジンに従う（Unity：C#、Unreal：C++） | — |
| 組み込み・IoT・ファームウェア | **Rust** | C：ベンダーSDKがCしかないとき |
| データベース・検索エンジン等の基盤ソフト | **Rust** | Go：GCの影響が許容できる分散系の制御部分 |
| データ分析・集計 | **SQL**（DuckDB、BigQuery、Postgres） | Python（Polars）：SQLで表現しにくい変換があるとき |
| 機械学習の学習・推論 | **Python** | Rust（candle等）：小さなモデルを単一バイナリで配りたいとき |
| LLM API呼び出し中心のAIアプリ | **TypeScript** | Python：埋め込み処理や評価などML系ライブラリを本格的に使うとき |
| スクリプト・社内の小さな自動化 | **TypeScript**（Node.jsで直接実行、またはBun） | Go：単一バイナリで配りたいとき。シェルスクリプト：数行で済むとき |

### 2-10. バックエンドの言語の決め方

バックエンドは「どんな負荷がかかるか」と「何に依存するか」で最適な言語が変わる。以下の順に確認し、最初に当てはまったところで決める。

#### 手順1：外部の制約で決まるか

実行環境の制約、コードを共有する相手の制約、組織の制約、ライブラリの制約の順に確認する（実行できない言語は、ライブラリがあっても選べないため）。

| 制約 | 選ぶ言語 |
|---|---|
| 実行環境がエッジ（Cloudflare Workers等） | **TypeScript** |
| Unityのクライアントとモデルや通信定義を共有したい | **C#** |
| 既存のJava資産（社内基盤、外部の業務パッケージ）と密に連携する | **Kotlin** |
| Microsoft／Azure中心で、運用チームが.NETに慣れている | **C#** |
| 必須のライブラリ・SDKが特定の言語にしかない（機械学習、特定ベンダーのSDK等） | その言語（機械学習ならPython） |
| Kubernetes・クラウドのコントロールプレーンを扱う | **Go** |

#### 手順2：負荷の性質で決める

| 負荷の性質 | 第一候補 | 次点 | 典型例 | 理由 |
|---|---|---|---|---|
| DBの読み書きと業務ロジックが中心 | **TypeScript**（画面の多い業務システムなら **Rails**） | Go（3人以上で公開APIを長期運用するとき） | 管理画面、予約、受発注、SaaSの大半 | ボトルネックはDB。言語の性能差はほぼ効かないので、開発速度とチームで選ぶ |
| 外部APIの待ち時間が中心 | **TypeScript** | Go | BFF、複数APIの集約、Webhook中継 | 非同期I/Oの書きやすさと、フロントとの型共有 |
| 大量の常時接続（数万接続以上が目安、§23-3） | **Elixir** | Go | チャット、プレゼンス、通知配信、共同編集 | 軽量プロセスで数十万規模の接続を扱え、落ちた処理を自動で再起動する仕組みが言語に組み込まれている。それ未満のチャットや通知はマネージド（§19-9）で足りる |
| 中程度の常時接続＋高スループット | **Go** | Rust | APIゲートウェイ、プロキシ、ゲームのロビー | goroutineで並行処理が簡潔に書け、運用も単純 |
| レイテンシの上振れが許容できない（p99で10ms未満が厳格に必要、§23-3） | **Rust** | Go | 広告入札、取引、リアルタイム対戦の判定 | GCによる停止がない |
| CPU負荷の高い計算 | **Rust** | Go | 画像・動画変換、圧縮、暗号、シミュレーション | ネイティブ性能とメモリ効率 |
| 数値計算・機械学習 | **Python** | Rust（推論のみ） | 推論API、レコメンド、特徴量計算 | ライブラリがPythonに集中している |
| 複雑な業務ルールと厳密な状態管理 | **Kotlin** | Go、TypeScript | 会計、金融、保険、大規模な在庫・物流 | sealed classなどで状態を型で表現しやすく、トランザクションやバッチの基盤が成熟している |
| 大規模バッチ | **Kotlin（Spring Batch）** | Go、Python | 夜間の一括計算、請求締め、データ移行 | 再実行・中断再開・分割実行の仕組みが揃っている |
| ストリーム処理 | **Kotlin**（Kafka Streams） | Go | Kafkaのイベント集計、リアルタイム分析 | Kafka周辺のエコシステムがJVM中心 |
| 長時間ワークフロー | 言語よりエンジンで決める：**Temporal**（Go／TypeScript） | Step Functions | 決済→発送→通知、人の承認待ち | リトライ・待機・状態保持をエンジンに任せる |
| サーバーレスのイベント処理 | **TypeScript** | Go、Python | SQS消費、S3トリガー、定期実行 | 起動が速く、AWS SDKが充実。起動速度を詰めるならGo |
| GraphQLサーバー | **TypeScript** | Go（gqlgen） | 多様なクライアント向けの集約API | GraphQLのツール群がTypeScript中心 |
| サービス間のRPC | **Go**（Connect） | Kotlin、Rust | 社内マイクロサービス | Protocol Buffers周りのツールが充実し、単一バイナリで配りやすい |

#### 手順3：チームと変更頻度で最終調整

- 仕様が頻繁に変わる・試作段階なら、手順2の結果より開発速度を優先し、Rails／TypeScriptで始める。Go・Kotlin・Rust・Elixirは本番化の段階で切り替え・切り出しを検討する。
- 担当者が1〜2人なら、手順2で選んだGo・KotlinはTypeScriptに、Elixirはマネージドのリアルタイム基盤（§19-9）に置き換える。保守できる人がいない言語を増やさないためである。Rust（厳格なレイテンシ要件）と、手順1の制約で選んだ言語はそのまま使う。
- 3人以上で長期運用が前提で、性能要件が明確なら手順2の結果に従う。

#### 1つのプロダクトで言語を混ぜるときのルール

- 言語の境界はプロセス（サービス）単位にする。同じプロセス内で混ぜるのは、ネイティブ拡張（napi-rs、PyO3）とWebAssemblyのときだけ。
- サービス間の契約はOpenAPIかProtocol Buffersで定義し、クライアントコードは生成する。
- 新しい言語を1つ増やすと、CI、Lint、依存の更新、監視の計装、担当できる人の確保が必要になる。既定の言語では要件を満たせないか、明らかに大きな差がつく場合にだけ増やす。
- 迷ったら既定の言語で作り、計測してボトルネックになった部分だけを適した言語で切り出す。
