Onboarding and Team Conventions for Rust Codebases
Onboarding to any codebase is a ramp from "I have access" to "I can ship a trustworthy change," but a Rust codebase adds a step most dynamically typed codebases skip: nothing runs, and no review can meaningfully happen, until the toolchain and workspace actually compile. That single fact reshapes onboarding order, and it is also why this section leans so heavily on written conventions rather than "just read the code and match the style you see."
The deeper reason conventions carry extra weight in Rust is the type system itself. A newtype, an enum with data, a trait bound, or a lifetime annotation encodes a decision about the domain that in a looser language might live only in a comment, a test, or a senior engineer's memory. That expressiveness is a genuine strength, but it only pays off for a new hire if the team has written down what those patterns mean here, because the compiler will happily accept a technically-correct type that is not the idiomatic one the team actually intends.
Summary
- Rust onboarding layers a hard compilation gate, then workspace orientation, then convention internalization, before code review becomes productive rather than adversarial.
- Insight: Skipping straight to "read the code" fails in Rust because the type system encodes intent that is invisible without the team's shared vocabulary for it.
- Key Concepts: compilation gate, workspace orientation, dependency direction, type-as-documentation, idiomatic vs. correct.
- When to Use: Structuring a new-hire's first days, deciding what belongs in a conventions doc versus what CI should just enforce, and diagnosing why a technically-passing PR still needs heavy review.
- Limitations/Trade-offs: Conventions and CI enforcement reduce review friction but do not replace it; they narrow disagreement to genuinely novel judgment calls instead of eliminating judgment altogether.
- Related Topics: Toolchain pinning, code review norms, workspace crate boundaries, API design guidelines.
Foundations
Onboarding to most codebases follows a familiar arc: get access, get the environment running, read some code, make a small change, get it reviewed. Rust onboarding follows the same arc but with a gate inserted before "read some code" can even start: the workspace has to compile with the exact toolchain the team uses, or nothing else is verifiable. A missing rustup component, a mismatched edition, or a stale Cargo.lock does not produce a slightly-off result the way a missing npm dependency might in a looser ecosystem, it produces a hard stop.
This is why onboarding checklists for Rust teams put toolchain setup (rustup, pinned MSRV, rustfmt, clippy) before almost everything else, and why "time to first green cargo test" is a meaningful onboarding metric in a way "time to first npm install" often is not. Only after that gate clears does workspace orientation become possible: understanding not just what files exist, but how a Cargo workspace's member crates depend on each other, since a multi-crate workspace has an explicit dependency direction (a binary crate depending on a domain library, which depends on nothing framework-specific) that a new hire has to internalize before a change "in the right place" versus "the expedient place" becomes obvious.
Conventions are the third layer: the team's agreed answers to the questions Rust's type system raises but does not force, like when to model a value as a newtype versus a raw primitive, when an enum should carry data versus be a plain tag, and where the boundary sits between a library's thiserror types and a binary's anyhow error handling.
Mechanics & Interactions
These three layers are not independent, they gate each other. A new hire cannot meaningfully do workspace orientation before the compilation gate clears, because reading code you cannot build or test invites false assumptions about what actually works. And a new hire cannot internalize conventions by pattern-matching on file structure alone, because a Cargo workspace's Cargo.toml boundaries reveal crate membership but not the domain rules encoded inside each crate's types, which is exactly the information a conventions doc has to supply explicitly.
This is where Rust's expressive type system becomes a double-edged tool for onboarding specifically. A function signature like fn calculate_tax(amount: Money, rate: TaxRate) -> Result<Money, TaxError> tells a reader far more than the equivalent in a dynamically typed language would, because Money and TaxRate are presumably newtypes preventing unit confusion, and TaxError is presumably an exhaustive enum a caller must handle. But that extra information only helps a new hire if they already know the team's convention that "wrap primitives in domain newtypes at API boundaries," because otherwise the signature reads as unremarkable, and a well-intentioned new hire might "simplify" it back to raw f64 and String without understanding what was lost.
This is the practical reason "if it compiles it's idiomatic" is a trap for new Rust engineers specifically. The compiler enforces memory safety, type correctness, and borrow rules, all real and valuable, but it does not enforce that a String should have been a domain newtype, that a Vec<Result<T, E>> should have been collected into Result<Vec<T>, E>, or that a public function should return &str instead of an owned String. Those are idiom and API-design judgments the compiler is silent on, which is precisely the gap code review and a written conventions doc have to fill, and precisely why review on a Rust PR that compiles cleanly can still be substantial: the review is not chasing correctness bugs the compiler already caught, it is chasing idiom and intent.
// Compiles fine either way - the compiler has no opinion on which is idiomatic here.
fn price(amount: f64, currency: String) -> f64 { amount } // "correct," not idiomatic
fn price(amount: Money) -> Money { amount } // idiomatic per team conventionAdvanced Considerations & Applications
At workspace scale, orientation and conventions both get harder to hold in one person's head, which is why teams externalize them rather than relying on tribal knowledge passed peer to peer. A codebase-orientation doc that lists "read root Cargo.toml, then the binary crate's main.rs, then the domain crate's lib.rs" turns a scavenger hunt into a five-minute checklist, and a conventions doc that states "errors: thiserror in libraries, anyhow in binaries" turns a recurring, low-value review comment into a fact a new hire can look up once.
The trade-off worth naming honestly is where a convention belongs versus where CI enforcement should replace it entirely. Naming and formatting (snake_case modules, rustfmt line width) are cheap to enforce mechanically and should never consume human review attention; deciding whether a new domain concept deserves a newtype is a judgment call formatting cannot make, and belongs in a written convention a reviewer can point to rather than relitigate from scratch on every PR. Teams that push too much into "the reviewer will catch it" burn senior engineers' time on the same recurring comments; teams that try to encode everything into CI lint rules end up fighting the compiler over decisions that genuinely require human judgment about the domain.
Onboarding compresses for senior transfers from other Rust codebases, but it does not skip layers, since the compilation gate and workspace orientation are specific to this repository even for an engineer who already knows the language deeply; what compresses is convention internalization, because an experienced Rust engineer already recognizes the shape of the questions (newtype or primitive, thiserror or anyhow) even before learning this team's specific answers.
| Approach | Strength | Weakness | Best Fit |
|---|---|---|---|
| Tribal knowledge (ask a senior) | Zero upfront documentation cost | Does not scale past a handful of engineers; inconsistent answers over time | Very small, co-located teams |
| Written conventions doc | Consistent, referenceable, reduces repeated review comments | Goes stale if not updated when the team's judgment shifts | Any team past a few engineers, especially with remote or async review |
| CI-enforced lint policy | Zero review cost once configured; always applied | Only covers mechanically checkable rules, not domain judgment | Formatting, naming, and clippy-detectable patterns specifically |
Common Misconceptions
- "If the PR compiles and tests pass, it's ready to merge" - compiling proves type and memory correctness, not that the domain modeling or API shape matches team idiom, which is what most substantive Rust review actually evaluates.
- "An experienced engineer from another language ramps at the same speed in Rust" - the borrow-checker and ownership mental model genuinely takes longer to internalize than syntax, independent of general engineering seniority.
- "A conventions doc replaces code review" - it removes repeated low-value comments about settled questions, but genuinely novel design decisions still need a reviewer's judgment.
- "Onboarding is done once the first PR merges" - the checklist's later phases (owning a small feature, contributing back to the conventions doc) are where workspace-scale intuition actually forms.
- "Workspace orientation is just learning the folder structure" - the folder structure shows crate boundaries, but the dependency direction between crates (which one is allowed to depend on which) is the actual architectural fact a new hire needs.
FAQs
Why does Rust onboarding put toolchain setup before reading any code?
Because nothing in the workspace is verifiable, not even a simple read-through, until it compiles with the pinned toolchain; a mismatched edition or missing component is a hard stop, not a minor inconvenience.
What's the difference between "workspace orientation" and just browsing files?
Orientation specifically means understanding the dependency direction between crates (which crate a binary depends on, which a domain library must stay independent of), not just knowing which files exist.
Why isn't "the code compiles" a sufficient bar for review approval?
Compiling proves type and memory correctness; it says nothing about whether the chosen types, error handling, or API shape match the team's idiom, which is what most Rust code review time is actually spent on.
How does Rust's type system act as documentation, concretely?
A signature using domain newtypes and an exhaustive error enum tells a reader what values are valid and what can go wrong, information that in a loosely typed language would only live in comments or tests.
Why does that type-as-documentation benefit require a conventions doc to actually land?
Because a new hire who does not yet know the team's convention (for example, "wrap primitives in newtypes at API boundaries") reads an expressive signature as unremarkable and may simplify it back to raw primitives, losing the safety it encoded.
Do senior engineers from other languages skip Rust onboarding steps?
They compress convention internalization since they already recognize the shape of the questions, but they still go through the compilation gate and workspace orientation, since those are specific to this repository regardless of language seniority.
What should live in a written conventions doc versus be left to review?
Settled, recurring judgment calls (error-handling split, newtype policy, naming) belong in a written doc reviewers can point to; genuinely novel design decisions still need live review discussion.
What should CI enforce instead of a human reviewer?
Anything mechanically checkable, like formatting and many clippy lints, should never consume review attention; CI enforcing those frees reviewers to focus on domain modeling and idiom questions the compiler cannot check.
Why is "time to first green `cargo test`" a meaningful onboarding metric?
Because it marks the point the compilation gate has cleared and orientation and review can meaningfully start; a long delay there usually signals a tooling or documentation gap worth fixing for the next hire.
Does onboarding really end when the first PR merges?
No, later checklist phases like owning a small feature or contributing a clarification back to the conventions doc are where a new hire actually builds workspace-scale intuition, not just repo familiarity.
Why do multi-crate workspaces need more onboarding structure than a single-crate repo?
Because the dependency direction between crates is an architectural constraint a new hire can violate without any compiler error, if a binary crate's framework-specific code leaks into a domain library that is supposed to stay framework-agnostic.
How do conventions stay from becoming out of date?
By treating the conventions doc as a living artifact updated through the same PR process as code, often explicitly by having onboarding retros or "I had to ask" moments turn into doc PRs the same week.
Related
- Onboarding Devs Checklist - the phased, chronological version of this model.
- Codebase Orientation - the workspace-reading practice this page's second layer describes.
- Conventions & Style Guide - the written answers this page argues matter more in Rust.
- Code Review Guidelines - where idiom judgment gets applied in practice.
Stack versions: This page is conceptual and not tied to a specific stack version.