This episode discusses package management in Rust: structuring Cargo.toml with features and dependency versions, building multi-crate workspaces for large applications, as well as keeping dependencies secure with cargo audit and cargo tree.

So far we have worked with a single small crate. Real applications are much larger: dozens of dependencies, several binaries and libraries, and the need to verify that all of it is secure. Episode 7 discusses managing all of that with cargo.
You will structure Cargo.toml correctly, understand features and semver, build multi-crate workspaces, and then secure the dependency chain with cargo audit. By the end of the episode, your Rust project will be ready to grow into an organized codebase.
Cargo.toml is the center of package configuration. The [package] section declares metadata, while [dependencies] lists external crates with semver versions.
[package]
name = "aplikasi"
version = "0.1.0"
edition = "2024"
[dependencies]
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1.40", features = ["full"] }Version 1.40 follows semver: cargo allows all compatible versions upward within the same major. Cargo.lock then pins the exact version for reproducible builds. The cargo add command manages this section automatically:
cargo add serde --features derive
cargo add tokio --features fullcargo add serde --features derive adds serde with the derive feature enabled. Cargo.lock is updated, and the whole team gets the same version.
Features are a mechanism for conditional compilation: certain dependencies or code are only enabled when the feature is enabled. This allows a single crate to serve many needs without burdening its users.
[features]
default = ["api"]
api = ["dep:axum"]
cache = ["dep:redis"]
[dependencies]
axum = { version = "0.8", optional = true }
redis = { version = "0.27", optional = true }The api and cache features bring their respective dependencies to life. Users only pay for the features they use. cargo tree --features api shows the dependencies active for a given feature combination.
Cargo uses semantic versioning: major.minor.patch. Patch and minor updates are compatible, major changes can break the API. Cargo also supports ranges like 1.2, >=1.3, <2.0, or an exact version =1.2.3.
cargo tree
cargo tree -i axumcargo tree displays all dependencies and their versions. cargo tree -i axum answers the question "who uses axum and why". When dependencies are pulled in layer by layer, this tree becomes the map for resolving conflicts.
cargo update
cargo outdatedcargo update updates versions within the range allowed by Cargo.lock. cargo outdated (from cargo install cargo-outdated) shows dependencies that are out of date. Both are part of a regular maintenance routine.
For large applications, split into several crates within one workspace: a shared compilation cache, a single Cargo.lock, and unified versioning. A common structure: one workspace with a crates/ directory containing binary and library crates.
[workspace]
members = ["crates/domain", "crates/api", "crates/cli"]
resolver = "2"
[workspace.package]
version = "0.1.0"
edition = "2024"Each crate in members has its own Cargo.toml, and they can depend on one another via path:
cargo add --path crates/domain --package crates/apicargo add --path crates/domain --package crates/api adds a local dependency between crates. cargo build from the root builds the entire workspace. cargo run -p crates/cli runs a specific binary.
The crate ecosystem is open, but it can also contain vulnerabilities. cargo audit scans Cargo.lock against the RustSec vulnerability database:
cargo install cargo-audit
cargo auditcargo audit displays vulnerabilities with severity, package, safe version, and advisory reference. This should be part of CI, not just a local routine.
A few habits that keep the dependency chain safe:
cargo audit in CI, and fail the build when there is a critical advisory.cargo deny to enforce license policy and duplicate dependency rules.--git dependencies unless truly necessary, because there is no stable checksum.Warning
A dependency that pulls in many other crates can drag in duplicate versions. cargo tree -d shows duplicated dependencies — clean them up with cargo update so binaries are smaller and resolution is simpler.
Key takeaways:
Cargo.toml declares the package, dependencies, and features; Cargo.lock pins versions.cargo audit scans for vulnerabilities; cargo deny manages policy.cargo tree -d.In the next episode 8 we will discuss data serialization, persistence, and I/O — serialization with serde for JSON and TOML formats, file I/O and async I/O with tokio, as well as database integration with sqlx, diesel, or sea-orm. You will start storing and loading data.