Rust Engineering Practices
Tricks from the Trenches 🟡
What you'll learn:
- Battle-tested patterns that don't fit neatly into one chapter
- Common pitfalls and their fixes — from CI flake to binary bloat
- Quick-win techniques you can apply to any Rust project today
Cross-references: Every chapter in this book — these tricks cut across all topics
This chapter collects engineering patterns that come up repeatedly in production Rust codebases. Each trick is self-contained — read them in any order.
1. The deny(warnings) Trap
Problem: #![deny(warnings)] in source code breaks builds when Clippy
adds new lints — your code that compiled yesterday fails today.
Fix: Use CARGO_ENCODED_RUSTFLAGS in CI instead of a source-level attribute:
# CI: treat warnings as errors without touching sourceenv: CARGO_ENCODED_RUSTFLAGS: "-Dwarnings"Or use [workspace.lints] for finer control:
# Cargo.toml[workspace.lints.rust]unsafe_code = "deny"[workspace.lints.clippy]all = { level = "deny", priority = -1 }pedantic = { level = "warn", priority = -1 }See Compile-Time Tools, Workspace Lints for the full pattern.
2. Compile Once, Test Everywhere
Problem: cargo test recompiles when switching between --lib, --doc,
and --test because they use different profiles.
Fix: Use cargo nextest for unit/integration tests and run doc-tests
separately:
cargo nextest run --workspace # Fast: parallel, cachedcargo test --workspace --doc # Doc-tests (nextest can't run these)See Compile-Time Tools for
cargo-nextestsetup.
3. Feature Flag Hygiene
Problem: A library crate has default = ["std"] but nobody tests
--no-default-features. One day an embedded user reports it doesn't compile.
Fix: Add cargo-hack to CI:
- name: Feature matrix run: | cargo hack check --each-feature --no-dev-deps cargo check --no-default-features cargo check --all-featuresSee
no_stdand Feature Verification for the full pattern.
4. The Lock File Debate — Commit or Ignore?
Rule of thumb:
| Crate Type | Commit Cargo.lock? | Why |
|---|---|---|
| Binary / application | Yes | Reproducible builds |
| Library | No (.gitignore) | Let downstream choose versions |
| Workspace with both | Yes | Binary wins |
Add a CI check to ensure the lock file stays up-to-date:
- name: Check lock file run: cargo update --locked # Fails if Cargo.lock is stale5. Debug Builds with Optimized Dependencies
Problem: Debug builds are painfully slow because dependencies (especially
serde, regex) aren't optimized.
Fix: Optimize deps in dev profile while keeping your code unoptimized for fast recompilation:
# Cargo.toml[profile.dev.package."*"]opt-level = 2 # Optimize all dependencies in dev modeThis slows the first build slightly but makes runtime dramatically faster during development. Particularly impactful for database-backed services and parsers.
See Release Profiles for per-crate profile overrides.
6. CI Cache Thrashing
Problem: Swatinem/rust-cache@v2 saves a new cache on every PR, bloating
storage and slowing restore times.
Fix: Only save cache from main, restore from anywhere:
- uses: Swatinem/rust-cache@v2 with: save-if: ${{ github.ref == 'refs/heads/main' }}For workspaces with multiple binaries, add a shared-key:
- uses: Swatinem/rust-cache@v2 with: shared-key: "ci-${{ matrix.target }}" save-if: ${{ github.ref == 'refs/heads/main' }}See CI/CD Pipeline for the full workflow.
7. RUSTFLAGS vs CARGO_ENCODED_RUSTFLAGS
Problem: RUSTFLAGS="-Dwarnings" applies to everything — including
build scripts and proc-macros. A warning in serde_derive's build.rs
fails your CI.
Fix: Use CARGO_ENCODED_RUSTFLAGS which only applies to the top-level
crate:
# BAD — breaks on third-party build script warningsRUSTFLAGS="-Dwarnings" cargo build# GOOD — only affects your crateCARGO_ENCODED_RUSTFLAGS="-Dwarnings" cargo build# ALSO GOOD — workspace lints (Cargo.toml)[workspace.lints.rust]warnings = "deny"8. Reproducible Builds with SOURCE_DATE_EPOCH
Problem: Embedding chrono::Utc::now() in build.rs makes builds
non-reproducible — every build produces a different binary hash.
Fix: Honor SOURCE_DATE_EPOCH:
// build.rs
let timestamp = std::env::var("SOURCE_DATE_EPOCH")
.ok()
.and_then(|s| s.parse::<i64>().ok())
.unwrap_or_else(|| chrono::Utc::now().timestamp());
println!("cargo:rustc-env=BUILD_TIMESTAMP={timestamp}");See Build Scripts for the full build.rs patterns.
9. The cargo tree Deduplication Workflow
Problem: cargo tree --duplicates shows 5 versions of syn and 3 of
tokio-util. Compile time is painful.
Fix: Systematic deduplication:
# Step 1: Find duplicatescargo tree --duplicates# Step 2: Find who pulls the old versioncargo tree --invert --package syn@1.0.109# Step 3: Update the culpritcargo update -p serde_derive # Might pull in syn 2.x# Step 4: If no update available, pin in [patch]# [patch.crates-io]# old-crate = { git = "...", branch = "syn2-migration" }# Step 5: Verifycargo tree --duplicates # Should be shorterSee Dependency Management for
cargo-denyand supply chain security.
10. Pre-Push Smoke Test
Problem: You push, CI takes 10 minutes, fails on a formatting issue.
Fix: Run the fast checks locally before push:
# Makefile.toml (cargo-make)[tasks.pre-push]description = "Local smoke test before pushing"script = '''cargo fmt --all -- --checkcargo clippy --workspace --all-targets -- -D warningscargo test --workspace --lib'''cargo make pre-push # < 30 secondsgit pushOr use a git pre-push hook:
#!/bin/sh# .git/hooks/pre-pushcargo fmt --all -- --check && cargo clippy --workspace -- -D warningsSee CI/CD Pipeline for
Makefile.tomlpatterns.
🏋️ Exercises
🟢 Exercise 1: Apply Three Tricks
Pick three tricks from this chapter and apply them to an existing Rust project. Which had the biggest impact?
Solution
Typical high-impact combination:
-
[profile.dev.package."*"] opt-level = 2— Immediate improvement in dev-mode runtime (2-10× faster for parsing-heavy code) -
CARGO_ENCODED_RUSTFLAGS— Eliminates false CI failures from third-party warnings -
cargo-hack --each-feature— Usually finds at least one broken feature combination in any project with 3+ features
# Apply trick 5:echo '[profile.dev.package."*"]' >> Cargo.tomlecho 'opt-level = 2' >> Cargo.toml# Apply trick 7 in CI:# Replace RUSTFLAGS with CARGO_ENCODED_RUSTFLAGS# Apply trick 3:cargo install cargo-hackcargo hack check --each-feature --no-dev-deps🟡 Exercise 2: Deduplicate Your Dependency Tree
Run cargo tree --duplicates on a real project. Eliminate at least one duplicate. Measure compile-time before and after.
Solution
# Beforetime cargo build --release 2>&1 | tail -1cargo tree --duplicates | wc -l # Count duplicate lines# Find and fix one duplicatecargo tree --duplicatescargo tree --invert --package <duplicate-crate>@<old-version>cargo update -p <parent-crate># Aftertime cargo build --release 2>&1 | tail -1cargo tree --duplicates | wc -l # Should be fewer# Typical result: 5-15% compile time reduction per eliminated# duplicate (especially for heavy crates like syn, tokio)Key Takeaways
- Use
CARGO_ENCODED_RUSTFLAGSinstead ofRUSTFLAGSto avoid breaking third-party build scripts [profile.dev.package."*"] opt-level = 2is the single highest-impact dev experience trick- Cache tuning (
save-ifon main only) prevents CI cache bloat on active repositories cargo tree --duplicates+cargo updateis a free compile-time win — do it monthly- Run fast checks locally with
cargo make pre-pushto avoid CI round-trip waste