More about Cargo and crates.io
Release profiles, workspaces, custom subcommands, and the gates an orchestrator standardizes
By now cargo build, cargo test, and cargo run are reflex. This chapter is about everything else Cargo does. Most of it matters for orchestrators because Cargo is where you anchor the rules an agent has to follow.
Release profiles
Cargo ships two profiles by default: dev (the default when you run cargo build) and release (when you pass --release). They differ on optimization, debug info, and a few other knobs.
You override them in Cargo.toml:
[profile.dev]
opt-level = 0
debug = true
[profile.release]
opt-level = 3
debug = false
lto = "thin"
codegen-units = 1The knobs worth knowing:
| Setting | What it controls | Trade |
|---|---|---|
opt-level | 0-3 plus "s", "z" | Higher is faster runtime, slower compile |
debug | Debug info embedded in binary | Bigger binary, better stack traces |
lto | Link-time optimization ("thin", "fat", false) | Slower link, faster runtime |
codegen-units | Parallelism during codegen | Lower is slower compile, faster runtime |
strip | Strip symbols from binary | Smaller binary, no symbol-level debugging |
For benchmarking, use cargo build --release. Numbers from a dev build are not real.
Workspaces: shared target, shared deps
A workspace is a directory with one root Cargo.toml and many member crates. One target/ directory, one Cargo.lock, one shared dependency graph.
[workspace]
resolver = "2"
members = ["crates/*"]
[workspace.dependencies]
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
thiserror = "2"In each member crate:
[dependencies]
tokio = { workspace = true }
serde = { workspace = true }workspace.dependencies is the antidote to version drift across crates. One bump in the root, every member follows.
Workspace lints: the policy gate
[workspace.lints] lets the root crate dictate lint levels for every member. This is where you bake "no unwrap in production code" into the build.
[workspace.lints.clippy]
unwrap_used = "deny"
expect_used = "deny"
panic = "deny"
todo = "deny"
unimplemented = "deny"
dbg_macro = "deny"Each member crate opts in:
[lints]
workspace = trueNow cargo clippy --workspace fails the build if any member crate uses .unwrap(), .expect("..."), or panic!(). Agents will try. The gate stops them at CI time.
Test modules need an escape hatch. Use #[expect(clippy::unwrap_used)] on the test module so that cargo clippy still flags new unwraps that drift outside tests.
#[cfg(test)]
#[expect(clippy::unwrap_used, clippy::expect_used)]
mod tests {
use super::*;
// tests freely use .unwrap() here
}Publishing to crates.io
Most readers will not publish a crate. The short version: cargo login <token>, add description, license, repository, and readme fields to Cargo.toml, then cargo publish. Once a version is published it cannot be deleted, only yanked. Use cargo publish --dry-run first.
Installing binaries
Cargo doubles as a binary installer:
cargo install ripgrep
cargo install cargo-nextest --locked
cargo install --git https://github.com/foo/barBinaries go in ~/.cargo/bin/, which should be on your PATH. --locked uses the upstream Cargo.lock rather than resolving fresh, which makes installs reproducible. Use it.
Extending Cargo
Any binary on your PATH named cargo-foo becomes the subcommand cargo foo. This is how cargo-watch, cargo-edit, cargo-nextest, and friends work.
Tools worth having installed by default:
| Tool | What it does |
|---|---|
cargo-watch | Re-runs a command on file changes |
cargo-edit | cargo add, cargo rm, cargo upgrade (modern Cargo ships add and rm natively) |
cargo-deny | License + vuln + duplicate dependency policy |
cargo-nextest | Faster, more informative test runner |
cargo-flamegraph | Profile-guided flamegraphs |
cargo-bloat | What is making your binary big |
cargo-machete | Find unused dependencies |
cargo-audit | RustSec vulnerability scan |
cargo-deny and cargo-audit belong in CI for any production crate.
The orchestrator's standard gate
A workspace where agents push code should standardize on these CI checks, in this order:
cargo fmt --all --check
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo test --workspace --all-features
cargo deny checkEvery gate is non-negotiable. -D warnings is what makes clippy actually block; without it, warnings are visible but landable. The agent learns very quickly that lazy code does not pass.