ADR-0011: Rust as the default language for new systems and infrastructure code¶
- Status: Accepted
- Date: 2026-05-03
- Deciders: Architecture Team
- Tags: systems, infrastructure, rust, c, cpp, language
Context¶
The Systems & Infrastructure category covers network services, embedded firmware, storage engines, and OS components. These systems share constraints that application code does not: no garbage collector, manual memory management, strict latency requirements, and high blast radius for bugs (system crashes, data corruption, security vulnerabilities). The language choice for this category is non-obvious and has long-term implications for security, maintainability, and hiring.
Historically, C and C++ were the only choices. Rust has reached production maturity since 2015, is now accepted in the Linux kernel (6.1+), and is explicitly backed by the NSA, CISA, and the US White House ONCD as the preferred memory-safe alternative to C/C++. The question is no longer whether Rust is viable, but when to prefer it over C/C++ for new projects.
Decision¶
We will default to Rust for all new systems and infrastructure code where the team has or can develop Rust expertise.
C remains acceptable for: - Embedded firmware targeting MCUs with mature vendor HAL/RTOS support in C and limited Rust embedded ecosystem support for that specific target - Extensions to existing C codebases where rewriting is not feasible - Safety-critical systems (automotive, medical) where the industry toolchain and certification infrastructure is still C-centric
C++ remains acceptable for: - Extensions to existing C++ game engine or system codebases - Projects where the team has deep C++ expertise and the codebase predates this ADR
Go remains the default for application-layer backend services (see ADR-0002). This ADR applies only to the Systems & Infrastructure category.
Alternatives considered¶
Option A: Rust (chosen)¶
- Pros: Memory safety guaranteed by the borrow checker at compile time — eliminates entire classes of vulnerability (use-after-free, buffer overflow, data races) that account for ~70% of CVEs in systems code (Microsoft, Google, Android security teams all report similar figures). Performance equivalent to C/C++ — no garbage collector, zero-cost abstractions. Growing ecosystem:
tokio(async networking),embassy(embedded async),prost(Protobuf),serde(serialisation). Accepted in Linux kernel (6.1+). Cargo package manager and build system is significantly better than CMake/Autotools. Active and friendly community. - Cons: Steep learning curve — the borrow checker rejects code patterns that C/C++ engineers consider normal; expect 2-3 months before productivity matches C proficiency. Embedded ecosystem is maturing but less complete than C for some MCU families. Compile times are slower than C. Not yet accepted in safety-critical certification standards (ISO 26262, IEC 62304) at the same level as C — though this is changing.
Option B: C¶
- Pros: Universal — every MCU has a C compiler; every kernel accepts C; every safety standard explicitly supports C (with MISRA). Vast existing codebases and tooling. Maximum hiring pool for embedded.
- Cons: No memory safety — use-after-free, buffer overflows, and data races require external tools (Valgrind, ASAN, Coverity) to detect; many are never found in testing and reach production. MISRA C eliminates some dangerous patterns but is a process control, not a language guarantee. Increasingly difficult to justify for new code given Rust's demonstrated safety properties.
Option C: C++¶
- Pros: More expressive than C; RAII helps with resource management; standard library is richer. Widely used in game engines, HPC, and financial systems.
- Cons: Subset of C's memory safety problems persist even with modern C++ (C++17/20); undefined behaviour is pervasive and subtle. "Modern C++" best practices require significant discipline to apply consistently across a team. Extremely complex language specification. No borrow checker equivalent — shared ownership still requires careful discipline.
Option D: Go¶
- Pros: Easy to learn, excellent standard library, strong concurrency model, fast compilation.
- Cons: Garbage collector introduces latency spikes unsuitable for real-time systems. Runtime overhead rules out most embedded targets. Already the default for application-layer services (ADR-0002) — this ADR explicitly addresses the systems tier.
Consequences¶
Positive
- New systems components have a dramatically lower CVE rate due to memory safety guarantees
- The borrow checker enforces correct concurrency patterns at compile time — data races are impossible in safe Rust
- The ecosystem for async networking (
tokio), embedded (embassy), and systems programming has matured to production quality - CISA/NSA guidance aligns with this decision — reduces compliance/audit risk for government-adjacent projects
Negative / trade-offs
- Teams must invest in Rust learning; expect slower initial velocity on the first project (typically 30–50% slower until the borrow checker becomes intuitive)
- Unsafe Rust (
unsafe {}blocks) can violate the safety guarantees; code review must be especially rigorous for anyunsafeblock - For embedded targets where the Rust HAL is immature, C with MISRA compliance remains more practical
- Safety-critical certification (automotive, medical) is not yet straightforward in Rust; teams targeting those domains should consult the current state of the Ferrocene/Rust safety toolchain
Follow-up actions
- Identify a "first Rust project" in the systems team to build expertise; choose a non-critical component
- Document approved crates for production use (tokio, serde, anyhow, thiserror) — not all crates have the same quality bar
- Review
unsafecode in all Rust PRs as a mandatory policy; never approveunsafewithout explicit justification - Re-evaluate for embedded firmware annually as the embedded Rust ecosystem (Embassy, probe-rs) matures