---
theme: default
#theme: ./themes/rust-vibe
title: Why I Needed Rust
titleTemplate: '%s - Rustikon 2026'
layout: cover
keywords: Rust,programming
download: true
exportFilename: Rustikon_2026_Why_I_Need_Rust
monaco: true
remoteAssets: true
selectable: true
record: true
colorSchema: dark
#colorSchema: white
lineNumbers: false
themeConfig:
primary: '#f74c00'
logoHeader: '/ferris.svg'
fonts:
mono: 'Victor Mono'
drawings:
enabled: true
persist: false
presenterOnly: false
syncAll: true
scripts:
- setup/image-overlay.ts
background: ./images/charles-assuncao-1BbOtIqx21I-unsplash.jpg
class: 'justify-center flex flex-cols photo-bg'
---
Why I Needed Rust
Finally, Infrastructure Automation I Can Sleep On
๐ก โโโโโ
๐ด โโโโโ
๐ฅ โโโโโ
---
# Years. One Problem.
**1987 โ 2025**
| Era | Tool | Lesson |
|-----|------|--------|
| 1990s | Perl | Power without safety is a disaster |
| 2000s | Python | Pragmatism without guarantees is fragile |
| 2010s | Bash ยท Chef ยท Ansible ยท Terraform | More tools don't solve paradigm problems |
| 2020s | Go ยท ??? | |
Each time, I thought I had the answer.
Each time, reality proved me wrong.
---
layout: cover
background: ./mYBMP8pW4uQ.webp
class: 'text-center photo-bg'
---
# The Evolution
How we got here
---
# Stage 1 โ Local (late 80s / early 90s)
**Dumb terminals. Single machine. One state.**
- Local development, long deployment cycles, low urgency
- One state โ easy to observe, easy to control
- IaC: procedural scripts, logic hidden inside the application
> **The Perl Era:** we could do anything.
>
> We could also break anything.
>
> Beautiful, terrifying metaprogramming.
>
> No safety net.
> Silent failures at 3 AM.
>
> *Lesson: power without safety is a disaster.*
๐ก โโโโโ ๐ด โโโโโ ๐ฅ โโโโโ
---
# Stage 2 โ Networks / Internet
**Systems getting farther away. More people. More coordination.**
- Remote access, distributed teams, security becomes relevant
- Cost of downtime rises โ processes become critical
- Harmonizing: package installs, config, updates across multiple machines in parallel
- IaC: reproducible automation, first declarative attempts
> **The Python Era:** rapid development, great community.
>
> But nothing stopped you from being wrong.
>
> Type hints came late โ and optional.
>
> Runtime errors >> compile-time errors.
>
> *Lesson: pragmatism without guarantees is fragile.*
More pieces. More people. Getting interesting.
๐ก โโโโโ ๐ด โโโโโ ๐ฅ โโโโโ
---
# Stage 3 โ Containers / Cloud / CI-CD
**Everything. Everywhere. All at once.**
- Monolith โ distributed, 24ร7ร365, high availability
- Cloud, hybrid, multi-cloud, on-prem โ simultaneously
- Rollback and rollforward: database transactions, but for infrastructure
- Scale horizontally AND vertically โ and *descale*
- CI/CD continuous: new features, new deploys, permanent churn
> **The Cloud/IaC Era:** Ansible, Terraform, Chef, Puppet.
>
> What changed? The syntax.
>
> What didn't? The fundamental problems.
>
> Still fighting type safety. Still discovering errors in production.
>
> *Lesson: more tools don't solve paradigm problems.*
๐ก โโโโโ ๐ด โโโโโ ๐ฅ โโโโโ
---
layout: cover
background: ./3XXSKa4jKaM.webp
---
I could automate infrastructure.
But I couldn't make it reliable.
I couldn't prevent mistakes.
**I couldn't sleep.**
๐ก โโโโโ ๐ด โโโโโ ๐ฅ โโโโโ
---
layout: cover
class: text-center
---
# Why IaC Fails
The restaurant problem
---
layout: two-cols
class: 'restaurant bg-photo'
---
# The Restaurant
Every restaurant has at least three actors.
| Restaurant | Infrastructure |
|---|---|
| Guest declares
what they want | Declarative config
(YAML, HCL) |
| Waiter validates
and transmits | Orchestrator
(K8s, Ansible) |
| Kitchen executes
and delivers | Runtime / provisioning |
| Dish arrives โ
or doesn't | Deployment succeeds โ or not |
::right::
**What makes it work โ or not:**
The guest **declares**. Doesn't implement.
The waiter must know what's possible โ
*before going to the kitchen*.
> "I want X" โ waiter goes to kitchen
>
โ "we don't have X, why is it on the menu?"
>
โ back to the table.
>
>
> Equivalent: I configured a host with port 8443
โ that port isn't allowed
โ reconfigure from zero.
---
layout: two-cols
---
# The Truth That Mutates
**State is not static.
It can change at every step of the chain.**
| Step | **Truth** for this actor |
|---|---|
| Guest speaks | What they want |
| Waiter's notepad | What was written down |
| Kitchen markings | What's done / not done |
| Payment ticket | What was actually served |
> **Fail early = fail cheap. Fail in production = nightmare.**
::right::
**The context problem:**
The waiter knows the regular customer:
*"always no salt."*
The kitchen doesn't. If the waiter changes
โ that context disappears.
**Configuration drift is the same thing:** Implicit state. Not explicit. Not propagated. Lost silently.
**The cost of failure depends on *where* it happens:**
- Fail at the *table* (impossible order):
cheap โ caught before kitchen
- Fail in *kitchen* (ingredient missing):
medium โ renegotiate with guest
- Fail at *delivery* (wrong dish arrives):
expensive โ experience destroyed
---
# "We Don't Have Mushrooms"
**When an actor in the chain can't fulfill part of the order.**
> "Can I substitute vegetables?"
>
> That renegotiation must be **explicit. Traced. Re-authorized.**
Not silent. Not assumed.
**Configuration drift is silent renegotiation:**
The system changes. Nobody notified. State diverges without trace.
**Rust's answer โ `Option`:**
```rust
// The waiter cannot silently skip a missing ingredient
let mushrooms: Option = order.mushrooms;
match mushrooms {
Some(m) => add_to_dish(m),
None => renegotiate_with_guest(&guest)?, // explicit. always.
}
// drift = treating None as Some. Rust makes that impossible.
```
> *The compiler is the waiter who cannot pretend an ingredient exists.*
---
# The Config Evolution
**How we got from code to YAML hell**
1. **Hardcoded** โ everything inside the binary. Full control. Zero flexibility.
2. **External config (JSON)** โ works between machines. Unreadable for humans at scale.
3. **YAML / TOML** โ more readable. Fragile syntax. Implicit types. Silent errors.
4. **YAML + Serde** โ Serde validates the *structure*:
- Does the field exist? Is it the right type?
- Do we accept `"elephant"` as a pet? If the type is `String`... yes.
- **Serde validates shape. Not meaning.**
5. **Helm / Jinja templates** โ YAML generated from variables (in YAML).
- Does it validate the content of the generated YAML? **No. Not at all.**
- Like using an LLM with a markdown reference: the format is there,
but is the content correct?
Nobody guarantees that.
---
layout: cover
background: ./3XXSKa4jKaM.webp
---
Continuous CI/CD.
No semantic validation.
**Continuous hope.**
(crossing our fingers in production)
๐ก โโโโโ ๐ด โโโโโ ๐ฅ โโโโโ
---
class: items-list
---
# Three Questions Without Answers
Question 1 โ
โ Why do we wait for things to break?
- "Works on my machine" โ in production, I don't know
- Fail late = maximum cost. We want: fail fast, fail cheap
Question 2 โ
โ Do we actually know what we want?
- Is the declaration sufficient and consistent with what's *possible*?
- What are the boundaries? Static or dynamic? What is the source of truth โ and when does it mutate?
Question 3 โ
Can we guarantee determinism ?
- CI/CD without semantic validation = continuous hope
- We want certainty, not randomness
- "Works on my machine" cannot be the production standard
> *We're not inventing anything new. Everything already exists.
> The question is whether we're managing it correctly.*
---
layout: cover
background: /jr-korpa-vg0Mph2RmI4-unsplash.jpg
---
The *tools* weren't the problem.
The *languages* weren't the problem.
**The paradigm was the problem.**
---
layout: cover
background: andrew-neel-jtsW--Z6bFw-unsplash.jpg
---
Systems we don't know how to control.
We hope they work.
When they don't โ we fix them.
**Continuous nightmare.**
( alarm state as the new normal)
๐ก โโโโโ ๐ด โโโโโ ๐ฅ โโโโโ
---
layout: section
---
Rust
The answer to all three questions
---
# The Bridge: From Serde to Types
Serde loads structurally valid config.
But `"elephant"` as `pet: String` compiles.
**Rust's answer: don't use `String`. Use a type.**
```rust
// Before: String โ anything goes
pet: String // "elephant" compiles. "unicorn" compiles. ๐คท
// After: closed domain โ impossible values don't exist
enum Pet { Dog, Cat, Rabbit } // "elephant" doesn't compile
```
**This is the shift.**:
Not the config format. The model of what it can contain.
Serde validates shape | Types validate meaning
The compiler validates *before the binary exists*.
---
# What Rust Gives Us
Answer to Question 1: fail early, fail cheap
```rust
// Immutability by default โ invariants are invariants
let config = load_config()?; // cannot change silently
// Option โ no nulls, no assumptions
let mushrooms: Option = order.mushrooms;
match mushrooms {
Some(m) => add_to_dish(m),
None => notify_kitchen_to_skip(), // explicit. always.
}
// Enums as closed domains
enum CloudProvider { Hetzner, UpCloud, AWS, GCP, Azure, OnPrem }
enum Port { Valid(u16) } // not any integer โ a valid port
```
Answer to Question 2: explicit contracts
```rust
// Traits define what every actor in the chain must fulfill
#[async_trait]
pub trait TaskStorage: Send + Sync {
async fn create_task(&self, task: WorkflowTask) -> StorageResult;
async fn update_task(&self, id: &str, status: TaskStatus) -> StorageResult<()>;
// Add a new provider: implement this trait or it doesn't compile
}
```
---
layout: two-cols
---
# The Compiler as Pre-Validator
Answer to Question 3:
guaranteed determinism
```rust
// Closed domain โ you can't forget a case
enum RollbackStrategy {
ConfigDriven,
Conservative, // preserve unless marked deletion
Aggressive, // revert all changes
Custom { operations: Vec },
}
// The compiler enforces exhaustive handling
match strategy {
RollbackStrategy::ConfigDriven => ...,
RollbackStrategy::Conservative => ...,
RollbackStrategy::Aggressive => ...,
RollbackStrategy::Custom { .. } => ...,
// miss one โ compile error
}
```
::right::
**The** *compiler* **validates:**
- Before building the binary
- Not after hours of execution
- Not when a function nobody touched in months finally gets called
- Predictable behavior:
memory, resources, workflows
The compiler is the waiter who validates the order.
Before it reaches the kitchen.
Before the guest waits.
Before any ingredient is missing.
๐ก โโโโโ ๐ด โโโโโ ๐ฅ โโโโโ
---
layout: cover
background: clouds_lpa.jpg
---
# The Human Impact
**When the system is trustworthy:**
โ Sleep comes back
โ Confidence returns
โ The team trusts the automation
โ Stress decreases
โ You can actually rest
> *What you can't measure:
fear.*
>
> *What you can measure:
MTTR.*
>
> *Before: > 30 minutes. Now:
< 5 minutes.*
๐ก โโโโโ ๐ด โโโโโ ๐ฅ โโโโโ
---
layout: cover
background: /jude-infantini-mI-QcAP95Ok-unsplash.jpg
---
Continuous CI/CD.
Types. Compiler. Explicit state.
(to keep sleeping well)
๐ก โโโโโ ๐ด โโโโโ ๐ฅ โโโโโ
---
layout: section
---
In Production
This is not theory
---
layout: center
name: provisioning
---
๐
---
layout: two-cols
---
# Nickel
**YAML rejected. TOML rejected.
Reason: no type safety.**
YAML wrote what we wanted.
It couldn't say what was *possible*.
Nickel closes that gap
โ at config time, not at 3 AM.
```haskell
# Infrastructure schema
# โ validated at config compile time
{
compute | {
region | String,
count | Number & (fun n => n > 0),
scaling | {
min | Number & (fun n => n > 0),
max | Number & (fun n => n >= min),
# -- compiler verifies this relationship
}
}
}
```
::right::
# Typed Source of Truth
Result (ADR-003):
**zero configuration type errors in production.**
Config hierarchy:
defaults โ workspace โ profile โ
environment โ runtime
Each layer merges.
Type system catches conflicts.
At config time โ not deployment time.
The guest wrote an impossible order.
Nickel makes impossible orders unwritable.
> *Serde validates shape.*
>
> *Nickel validates meaning.*
>
> *The compiler validates before deployment.*
---
layout: two-cols
---
# Traits as Provider
**The kitchen can change.
AWS โ UpCloud โ bare metal. Same menu.**
```rust
// Every provider implements the same contract
enum DependencyType { Hard, Soft, Optional }
enum TaskStatus {
Pending, Running, Completed, Failed, Cancelled
}
// Dependency resolution
// โ the orchestrator knows the order
// Installing Kubernetes:
// containerd (Hard) โ etcd (Hard) โ kubernetes
// โ cilium (requires kubernetes)
// โ rook-ceph (requires cilium)
```
**Explicit state โ no drift:**
```rust
pub struct WorkflowExecutionState {
pub task_states: HashMap,
// what happened and when
pub checkpoints: Vec,
pub provider_states: HashMap,
}
```
::right::
# Contracts
- Checkpoint every 5 minutes
- No implicit state.
> No *"the waiter remembers the customer doesn't want salt."*
- It's in the order. Always. Explicit.
---
layout: two-cols
class: -mt-7
---
# Dependency Graph
**Fail_fast: bool is not a config option.
It's a principle encoded as a type.**
Typed DAG โ dependency resolution enforced
at workflow compile time:
The kitchen doesn't serve the main course
before the starter is done.
`DependencyType::Hard` is that rule.
In the type system, not in a runbook.
```rust
pub struct WorkflowConfig {
pub max_parallel_tasks: usize,
pub task_timeout_seconds: u64,
// halt on first failure
pub fail_fast: bool,
// recovery point granularity
pub checkpoint_interval_seconds: u64,
}
containerd (Hard) โ etcd (Hard) โ kubernetes
โ cilium (requires: kubernetes)
โ rook-ceph (requires: kubernetes + cilium)
```
::right::
# Fail Fast, Fail Cheap
- `DependencyType::Hard`
- failure stops the chain. Always.
- `DependencyType::Soft`
- continues, explicitly degraded.
- `DependencyType::Optional`
- missing is expected and fine.
> *The compiler catches the install order.*
> *Not the on-call engineer at 2 AM.*
๐ก โโโโโ ๐ด โโโโโ ๐ฅ โโโโโ
---
layout: two-cols
---
# Real Applications
### Kubernetes
The orchestrator provisions cluster
components as a typed workflow:
```
containerd
โ etcd
โ kubernetes control plane
โ CoreDNS
โ Cilium (CNI)
โ Rook-Ceph (storage)
```
Each dependency is a `DependencyType`.
The compiler catches:
installing Cilium without Kubernetes.
Not the on-call engineer at 2 AM.
>*"In my machine it works" has a price here.*
>
> *This is the highest-stakes infrastructure in the deck.*
::right::
### Blockchain Validators
Validators require brutal uptime.
A validator that fails loses funds โ not your infrastructure's money.
**Your customer's.**
- **Post-quantum cryptography**: CRYSTALS-Kyber + Falcon + AES-256-GCM hybrid. Validator keys protected against quantum computers.
- **SLOs with real error budgets**: 99.99% = 52.6 min downtime/year. Prometheus blocks deploys when burn rate exceeds budget.
- **Deterministic config**: validator parameters are types. A `bond_amount` that isn't a valid `u128` doesn't compile.
---
layout: two-cols
---
# Disaster Recovery
**Rollback as a type, not a procedure**
3 AM. Something broke. You need to rollback.
Without types: you improvise.
With types: you choose a strategy
โ or it doesn't compile.
```rust
// Checkpoint = complete system snapshot
pub struct Checkpoint {
pub workflow_state: Option,
pub resources: Vec,
pub provider_states: HashMap,
}
// Rollback strategy = typed choice, not a runbook
enum RollbackStrategy {
ConfigDriven,
Conservative, // preserve unless marked for deletion
Aggressive, // revert all changes
Custom { operations: Vec },
}
// You cannot do rollback without choosing a strategy.
// The compiler doesn't let you ignore the case.
```
::right::
**Multi-backend backup:** restic, borg, tar, rsync
โ all as enum variants.
Production backup and DR restore use the same type, the same schema.
> *The runbook exists.*
>
> *Nobody reads it clearly at 3 AM under pressure.*
>
> *The type forces the decision before the crisis.*
>
> *The state is the same in prod and in DR. Always.*
---
layout: two-cols
---
# Self-Healing
> **When something breaks at 3 AM**
> โ **the system responds, not you.**
```rust
enum RemediationAction {
ScaleService { service: String, replicas: u32 },
FailoverService { service: String, region: Region },
RestartService { service: String },
ClearCache { service: String, scope: CacheScope },
}
// Typed playbooks. Not shell scripts. Not hope.
// Fails 3 times โ escalates to human.
// Never loops indefinitely.
```
::right::
# โ Typed Remediation
**What happens at 3 AM:**
- Alert fires
โ `RemediationEngine` matches condition
โ runs `RestartService`
- Works: silent. Nobody woken up.
- Fails 3ร: page sent
โ with full state, checkpoint,
and execution history.
> *You wake up to information. Not to chaos.*
๐ก โโโโโ ๐ด โโโโโ ๐ฅ โโโโโ
---
layout: cover
background: /jude-infantini-mI-QcAP95Ok-unsplash.jpg
class: photo-bg
---
Without types. Without compiler. Without explicit state.
**MTTR > 30 minutes.**
โโโโโโโโโโโโโโโโโโโโโโโโ
Rust. Types. Explicit state.
Automated response.
**MTTR < 5 minutes.**
(at 3 AM. without you.)
๐ก โโโโโ ๐ด โโโโโ ๐ฅ โโโโโ
---
layout: two-cols
---
Why This Matters
For everyone in this room
::right::
---
layout: two-cols
---
# For You
If you've been frustrated like me
- Rust solves problems you already have.
- This is not hype. It is operational relief.
Start here:
- Model your infrastructure as types
- Let the compiler pre-validate before deployment
::right::
If you're earlier in your career
- Start with type safety from day one.
- Build for reliability, not only speed.
The shortest path:
- Types for config.
- Traits for providers.
- Determinism for operations.
---
layout: cover
background: ./mYBMP8pW4uQ.webp
class: 'text-center photo-bg'
---
I have perspective from long production experience.
I have seen technologies come and go.
*Rust is not hype. Rust is relief with evidence.*
It solves real operational problems I had for decades.
More years is not a liability.
**It is an advantage.**
---
layout: cover
name: end
background: ./images/cleo-heck-1-l3ds6xcVI-unsplash.jpg
class: 'photo-bg'
---
# Why I Needed Rust
๐ก โโโโโ
๐ด โโโโโ
๐ฅ โโโโโ
Three Closing Lines
I have lived this problem for decades.
Rust gave me deterministic systems and better sleep.
Start small: model infrastructure as types.
Thank you. Questions?
More info:
ยท **jesusperez.pro**
ยท **provisioning.systems** ยท **vapora.dev** ยท **rustelo.dev**