jpskill.com
🛠️ 開発・MCP コミュニティ

senior-rust-practices

This skill should be used when the user asks about "rust workspace", "rust best practices", "cargo workspace setup", "rust code organization", "rust dependency management", "rust testing strategy", "rust project", "scalable rust", "rust CI setup", or needs guidance on senior-level Rust development patterns, workspace design, code organization strategies, or production-ready Rust architectures.

⚡ おすすめ: コマンド1行でインストール(60秒)

下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。

🍎 Mac / 🐧 Linux
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o senior-rust-practices.zip https://jpskill.com/download/17933.zip && unzip -o senior-rust-practices.zip && rm senior-rust-practices.zip
🪟 Windows (PowerShell)
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/17933.zip -OutFile "$d\senior-rust-practices.zip"; Expand-Archive "$d\senior-rust-practices.zip" -DestinationPath $d -Force; ri "$d\senior-rust-practices.zip"

完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。

💾 手動でダウンロードしたい(コマンドが難しい人向け)
  1. 1. 下の青いボタンを押して senior-rust-practices.zip をダウンロード
  2. 2. ZIPファイルをダブルクリックで解凍 → senior-rust-practices フォルダができる
  3. 3. そのフォルダを C:\Users\あなたの名前\.claude\skills\(Win)または ~/.claude/skills/(Mac)へ移動
  4. 4. Claude Code を再起動

⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。

🎯 このSkillでできること

下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。

📦 インストール方法 (3ステップ)

  1. 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
  2. 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
  3. 3. 展開してできたフォルダを、ホームフォルダの .claude/skills/ に置く
    • · macOS / Linux: ~/.claude/skills/
    • · Windows: %USERPROFILE%\.claude\skills\

Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。

詳しい使い方ガイドを見る →
最終更新
2026-05-18
取得日時
2026-05-18
同梱ファイル
1

📖 Skill本文(日本語訳)

※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。

シニア Rust 開発プラクティス

プロトタイプから本番環境までスケールする、Rust ワークスペースのアーキテクチャ、コード構成、依存関係、およびテストに関する、実戦で培われたパターン。

Git Worktree ワークフローの遵守

すべてのコーディング作業は、git worktree 内で行う必要があります。 コードを変更する前に、以下を実行してください。

  1. ワークツリーを作成します: git worktree add ~/.claude/worktrees/$(basename $(pwd))/<task> -b feat/<task>
  2. そのディレクトリで作業します
  3. /merge を使用して、変更をメインに戻します

メインのワークツリーでファイルを直接編集しないでください。

完了要件

Rust のタスクを完了する前に、必ず以下を実行してください。

  1. テストを実行します: cargo test --workspace
  2. リンティングを実行します: trunk check
  3. 完了を宣言する前に、すべての問題を修正します

trunk にフォーマットの問題がある場合は、trunk fmt を実行して自動修正します。

ワークスペースアーキテクチャ

「1つの製品 = 1つのリポジトリ = 1つのワークスペース」から始める

以下の場合に Rust ワークスペースを使用します。

  • 複数のクレートをまとめて出荷する場合 (バイナリ + ライブラリ)
  • 共有ツール / CI
  • 共有バージョニングポリシー

標準的なワークスペース構造:

repo/
  Cargo.toml            # ワークスペースルート
  crates/
    core/               # 純粋なドメインロジック (IO なし)
    storage/            # DB、ファイルシステムなど
    api/                # HTTP/GRPC ハンドラー、DTO
    cli/               # バイナリ
  tools/               # オプション: 内部バイナリ (コード生成、移行など)
  tests/               # オプション: ブラックボックス統合テスト

クレートを「薄く」、境界を「硬く」保つ

階層化されたアーキテクチャ:

  • core: 純粋なロジック、型、検証、アルゴリズム。最小限の依存関係。
  • adapters: IO 境界 (db、ネットワーク、rpc、ファイルシステム)。トレイトベースの境界、最小限のリーク。
  • app / service: 配線 (DI)、構成、ランタイム、オーケストレーション。
  • bins: 「app」を呼び出すだけの CLI/デーモン。

重要なルール: coretokioreqwest、または sqlx をインポートしている場合、すでに分離に失敗しています。

デフォルトでは、クレートの数を少なくする

クレートが多すぎると、無駄な作業が増えます。最初は最大 2〜5 個から始めます。

分割するのは以下の場合のみです:

  • コンパイル時間が苦痛で、境界が明確な場合
  • 個別のリリースケイデンスが必要な場合
  • 異なる依存関係プロファイルが必要な場合 (no-std、wasm など)

ワークスペースの依存関係: アーキテクチャではなく、バージョンを集中管理する

ルートの Cargo.toml で、ワークスペースの依存関係を使用してバージョンを揃えます。

[workspace]
members = ["crates/*"]
resolver = "2"

[workspace.dependencies]
anyhow = "*"  # 最新を使用
thiserror = "*"  # 最新を使用
serde = { version = "*", features = ["derive"] }  # 最新を使用
tokio = { version = "*", features = ["macros", "rt-multi-thread"] }  # 最新を使用

クレートの Cargo.toml:

[dependencies]
serde = { workspace = true }

これにより、バージョンのずれとセキュリティの攪乱が軽減されます。

フィーチャを容赦なく使う

  • 「セマンティクスを変更するフィーチャフラグ」ではなく、追加のフィーチャ (より多くの機能を有効にする) を優先します
  • 「重い」依存関係をフィーチャの背後に置きます (db、http、メトリクス)
  • 世界を引き込むデフォルトのフィーチャは避けてください

オプションの依存関係のパターン:

[dependencies]
sqlx = { workspace = true, optional = true }

[features]
db = ["dep:sqlx"]

ポリシーを強制する: MSRV + ツールチェーン

  • rust-toolchain.toml でツールチェーンを固定します
  • MSRV (最小サポート Rust バージョン) を決定し、CI でテストします
  • clippy/rustfmt の一貫性を保ちます

コード構成

モジュールは、ファイルごとの型ではなく、推論方法と一致させる

「models/handlers/utils」スパゲッティではなく、機能/ドメインで整理します。

良い構成:

core/
  src/
    lib.rs
    payment/
      mod.rs
      validation.rs
      pricing.rs
    user/
      mod.rs
      id.rs
      rules.rs

避けるべきこと:

models.rs
handlers.rs
utils.rs

パブリック API: 小さな表面積、明示的な再エクスポート

  • デフォルトでは、ほとんどのものを pub(crate) にします
  • lib.rs から厳選された API を再エクスポートします
mod payment;
pub use payment::{Payment, PaymentError};

すべてが pub の場合、意図しないフレームワークを作成したことになります。

本当に必要な場合を除き、「Prelude」は避ける

Prelude は依存関係を隠し、コードレビューを難しくする傾向があります。明示的なインポートを優先します。

エラーストラテジー: 1つを選んでそれを守る

一般的なアプローチ:

  • ライブラリクレート: 型付きエラーには thiserror
  • バイナリ: トップレベルでは anyhow

明示的に「不透明」にしたい場合を除き、ライブラリの境界を越えて anyhow::Error をリークしないでください。

非同期処理をエッジに保つ

コアを同期的に、かつ純粋に保つことができれば、以下が得られます。

  • よりシンプルなテスト
  • ポータビリティ
  • ライフタイム/ピン留めの頭痛が少ない

依存関係の衛生

好みを持つ: より少ない依存関係、より高品質な依存関係

すべての依存関係は以下を追加します。

  • ビルド時間
  • 監査対象
  • Semver リスク

強力なメンテナンスを備えた「退屈な」クレートを優先します。

cargo-deny + cargo-audit を使用する

依存関係の問題を早期に可視化します (ライセンス、アドバイザリ、重複バージョン)。

ライブラリで unwrap() を使用しない

バイナリ/テストでは問題ありません (特にテストスキャフォールディングで)。ライブラリでは、コンテキスト付きのエラーを返します。

スケールするテスト戦略

「ピラミッド」を考えます。

1. ユニットテスト: 高速、決定的、多数

  • ほとんどのテストをコードの近くに配置します: プライベートアクセスには同じファイルに mod tests {}
  • ハッピーパスだけでなく、不変条件とエッジケースをテストします
  • ユニットテストでファイルシステム/ネットワークにアクセスすることを避けます

2. 統合テスト: パブリック API をブラックボックス化する

API レベルのテストには crates/<crate>/tests/*.rs を使用します。

  • 「クレートの消費者」として扱います
  • プライベートな内部に手を伸ばさないでください

3. エンドツーエンドテスト: 少ないが、リアル

サービスがある場合:

  • CI (コンテナ) で依存関係 (db) をスピンアップします
  • 一連のシナリオテストを実行します

4. 正確さが重要な場合は、プロパティテスト + ファジング

  • 不変条件には proptest ("decode(encode(x)) == x")
  • 外部からのパーサー/デコーダー/入力には cargo-fuzz

5. ドキュメンテーションテストは過小評価されている

ドキュメンテーションテストは、例がコンパイルされることを強制し、パブリック API を正直に保ちます。

ロギングとトレース

println! を絶対に使用しない - 代わりにトレースを使用する

出力に println!eprintln!、または dbg! を絶対に使用しないでください。 常に tracing クレートを使用してください。

use traci
📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開

Senior Rust Development Practices

Battle-tested patterns for Rust workspace architecture, code organization, dependencies, and testing that scale from prototype to production.

Git Worktree Workflow Compliance

All coding work MUST happen in git worktrees. Before making any code changes:

  1. Create a worktree: git worktree add ~/.claude/worktrees/$(basename $(pwd))/<task> -b feat/<task>
  2. Work in that directory
  3. Use /merge to consolidate changes back to main

Never edit files directly in the main worktree.

Completion Requirements

Before completing ANY Rust task, you MUST:

  1. Run tests: cargo test --workspace
  2. Run linting: trunk check
  3. Fix any issues before declaring done

If trunk has formatting issues, run trunk fmt to auto-fix.

Workspace Architecture

Start from "One Product = One Repo = One Workspace"

Use a Rust workspace when you have:

  • Multiple crates that ship together (binary + libraries)
  • Shared tooling / CI
  • Shared versioning policy

Canonical workspace structure:

repo/
  Cargo.toml            # workspace root
  crates/
    core/               # pure domain logic (no IO)
    storage/            # DB, filesystem, etc.
    api/                # HTTP/GRPC handlers, DTOs
    cli/                # binary
  tools/                # optional: internal binaries (codegen, migration, etc.)
  tests/                # optional: black-box integration tests

Keep Crates "Thin" and Boundaries "Hard"

Layered architecture:

  • core: Pure logic, types, validation, algorithms. Minimal deps.
  • adapters: IO boundaries (db, network, rpc, filesystem). Trait-based boundary, minimal leakage.
  • app / service: Wiring (DI), config, runtime, orchestration.
  • bins: CLI/daemon that just calls "app".

Critical rule: If core imports tokio, reqwest, or sqlx, you've already lost the separation.

Default to a Small Number of Crates

Too many crates is busywork. Start with 2–5 max.

Split only when:

  • Compile times are painful and boundaries are real
  • You need separate release cadence
  • You need different dependency profiles (no-std, wasm, etc.)

Workspace Dependencies: Centralize Versions, Not Architecture

In root Cargo.toml, use workspace dependencies to keep versions aligned:

[workspace]
members = ["crates/*"]
resolver = "2"

[workspace.dependencies]
anyhow = "*"  # use latest
thiserror = "*"  # use latest
serde = { version = "*", features = ["derive"] }  # use latest
tokio = { version = "*", features = ["macros", "rt-multi-thread"] }  # use latest

In crate Cargo.toml:

[dependencies]
serde = { workspace = true }

This reduces version drift and security churn.

Be Ruthless with Features

  • Prefer additive features (enable more capabilities) vs "feature flags that change semantics"
  • Put "heavy" deps behind features (db, http, metrics)
  • Avoid default features that pull the world

Pattern for optional dependencies:

[dependencies]
sqlx = { workspace = true, optional = true }

[features]
db = ["dep:sqlx"]

Enforce a Policy: MSRV + Toolchain

  • Pin toolchain with rust-toolchain.toml
  • Decide MSRV (minimum supported Rust version) and test it in CI
  • Keep clippy/rustfmt consistent

Code Organization

Modules Should Match How You Reason, Not File-Per-Type

Organize by capability / domain, not by "models/handlers/utils" spaghetti.

Good organization:

core/
  src/
    lib.rs
    payment/
      mod.rs
      validation.rs
      pricing.rs
    user/
      mod.rs
      id.rs
      rules.rs

Avoid:

models.rs
handlers.rs
utils.rs

Public API: Small Surface Area, Explicit Re-Exports

  • Make most things pub(crate) by default
  • Re-export a curated API from lib.rs
mod payment;
pub use payment::{Payment, PaymentError};

If everything is pub you've created an accidental framework.

Avoid "Prelude" Unless You Truly Need It

Preludes tend to hide dependencies and make code review harder. Prefer explicit imports.

Error Strategy: Pick One and Stick to It

Common approach:

  • Library crates: thiserror for typed errors
  • Binaries: anyhow at the top level

Don't leak anyhow::Error across library boundaries unless you explicitly want "opaque".

Keep Async at the Edge

If you can keep core synchronous and pure, you gain:

  • Simpler tests
  • Portability
  • Less lifetime/pinning headaches

Dependency Hygiene

Be Picky: Fewer Deps, Higher-Quality Deps

Every dependency adds:

  • Build time
  • Audit surface
  • Semver risk

Prefer "boring" crates with strong maintenance.

Use cargo-deny + cargo-audit

Make dependency issues visible early (licenses, advisories, duplicate versions).

Don't Use unwrap() in Libraries

In binaries/tests it's fine (especially in test scaffolding). In libraries, return errors with context.

Testing Strategy That Scales

Think "pyramid":

1. Unit Tests: Fast, Deterministic, Lots

  • Put most tests close to code: mod tests {} in the same file for private access
  • Test invariants and edge cases, not just happy paths
  • Avoid hitting the filesystem/network in unit tests

2. Integration Tests: Black-Box the Public API

Use crates/<crate>/tests/*.rs for API-level tests.

  • Treat it as "a consumer of the crate"
  • Don't reach into private internals

3. End-to-End Tests: Few, But Real

If you have a service:

  • Spin up dependencies (db) in CI (containers)
  • Run a small set of scenario tests

4. Property Tests + Fuzzing When Correctness Matters

  • proptest for invariants ("decode(encode(x)) == x")
  • cargo-fuzz for parsers/decoders/inputs from outside

5. Doctests Are Underrated

Doctests enforce that examples compile and keep your public API honest.

Logging and Tracing

Never Use println! - Use Tracing Instead

NEVER use println!, eprintln!, or dbg! for output. Always use the tracing crate:

use tracing::{debug, info, warn, error, trace};

// Good - structured logging
info!("Processing request for user {user_id}");
debug!("Cache hit: {key}");
warn!("Retry attempt {attempt} of {max_retries}");
error!("Failed to connect: {err}");

// Bad - never do this
println!("Processing request for user {}", user_id);
dbg!(value);

Why:

  • Structured logging with levels (filter noise in production)
  • Spans for distributed tracing
  • Configurable output (JSON, pretty, etc.)
  • Zero-cost when disabled

Use test-log for Tests

Always use test_log::test attribute for tests to capture tracing output:

use test_log::test;

#[test]
fn test_something() {
    info!("This will be visible when test fails or with --nocapture");
    assert!(true);
}

#[test(tokio::test)]
async fn test_async_something() {
    debug!("Async test with tracing");
}

Add to Cargo.toml (use latest versions):

[dev-dependencies]
test-log = { version = "*", features = ["trace"] }  # use latest
tracing-subscriber = { version = "*", features = ["env-filter"] }  # use latest

Run tests with visible logs: RUST_LOG=debug cargo test -- --nocapture

Clippy Rules to Follow

Inline Format Arguments (clippy::uninlined_format_args)

Always use variables directly in format strings instead of passing them as arguments:

// Good - variable inlined
let name = "world";
info!("Hello, {name}!");
format!("Value: {value}, Count: {count}")

// Bad - uninlined arguments
info!("Hello, {}!", name);
format!("Value: {}, Count: {}", value, count)

This improves readability and reduces potential argument ordering mistakes.

CI / Quality Gates (Minimum Set)

cargo fmt --check
cargo clippy --all-targets --all-features -D warnings
cargo test --workspace --all-features

Additional gates:

  • MSRV check (if you claim one)
  • cargo deny / cargo audit
  • (optional) cargo llvm-cov for coverage, but don't worship %

Compile Times and Ergonomics

  • Use resolver = "2" and avoid unnecessary default features
  • Split "heavy" crates (like DB codegen, protobuf) into separate crates if they dominate rebuild time
  • Prefer incremental-friendly patterns: fewer proc-macros, fewer generics in hot paths unless needed

Practical Rules of Thumb

One-way dependencies:

  • core → (nothing)
  • adapterscore
  • appadapters + core
  • binapp

Visibility:

  • Everything private by default
  • Public API is a deliberate design artifact

IO placement:

  • No IO in core

Test distribution:

  • Unit tests everywhere
  • Integration tests at boundaries
  • E2E tests sparingly

Tooling:

  • Pin toolchain
  • Centralize versions
  • Police features

Project-Type Patterns

CLI: Thin binary → lib (for testability)

Services: Separate protocol definitions; feature-flag transport layers

ZK/crypto: Isolate no_std core; separate proving/verification crates

WASM: Separate bindings; platform-agnostic core