ontoref/assets/presentation/talk-structure.md

629 lines
28 KiB
Markdown
Raw Permalink Normal View History

# Talk Structure: Why I Needed Rust
> Orientado a: `abstract_en.md`
> Fuente: `2026-02-17-notas_voz.md`
---
## Sistema de Medidores *(elemento visual persistente)*
Tres barras que aparecen en esquina de cada slide de transición.
Escala 05. Se actualizan en cada etapa clave.
```
🛡 Confianza ●●●●● = máxima certeza sobre el sistema
😴 Descanso ●●●●● = puedes dormir tranquilo
🔥 Estrés ●●●●● = alarma permanente, pesadillas
```
**Estado inicial (apertura)**
```
🛡 ●●●○○ 😴 ●●●○○ 🔥 ●○○○○
```
*Se presenta como "¿Reconoces estos números? Sigamos..."*
**Progresión a lo largo de la charla:**
| Momento | 🛡 Confianza | 😴 Descanso | 🔥 Estrés |
|---|:---:|:---:|:---:|
| Etapa 1 — Local | ●●●●○ | ●●●●○ | ●○○○○ |
| Etapa 2 — Redes | ●●●○○ | ●●●○○ | ●●○○○ |
| Etapa 3 — Cloud/CI-CD | ●●○○○ | ●○○○○ | ●●●●○ |
| YAML hell / peak pain | ●○○○○ | ○○○○○ | ●●●●● |
| Rust — compilador | ●●●○○ | ●●○○○ | ●●●○○ |
| Rust — tipos + traits | ●●●●○ | ●●●●○ | ●●○○○ |
| Cierre | ●●●●● | ●●●●● | ●○○○○ |
> El título de la charla ES el último estado del medidor de sueño.
---
## 0. Hook de apertura
**Pregunta de apertura al público** *(manos arriba)*:
> "¿Quién ha sido despertado a las 3am por un fallo de infraestructura?"
*Pausa. Mirar la sala. Asentir.*
> "Esto es para vosotros."
---
**Imagen concreta**: definir un host, un nodo, un puerto.
- Eso es todo. Queremos que algo corra en algún sitio y que algo pueda hablar con él.
- ¿Cómo de difícil puede ser?
**38 años de infraestructura. 19872025.**
- Perl (90s) → Python (2000s) → Bash → Chef → Ansible → Terraform → Go → ???
- Cada vez pensé que tenía la respuesta.
- Cada vez la realidad me demostró que no.
**Promesa**: vamos a ver por qué eso, durante décadas, nos ha dado pesadillas. Y por qué Rust cambió eso.
*→ Mostrar medidores en interrogante o en estado inicial neutro*
---
## 1. Evolución de los desafíos en infraestructura (2013-2025)
*Abstract: "The evolution of infrastructure challenges (2013-2025)"*
### Etapa 1 — Local (finales 80s / primeros 90s)
- Terminales tontos, desarrollo local
- Ciclos de despliegue largos, baja urgencia
- Un solo estado, fácil de observar y controlar
- IaC: scripts procedurales, lógica oculta en comandos o en la aplicación
- **La era Perl**: podíamos hacer cualquier cosa. También podíamos romper cualquier cosa.
- Metaprogramming bello y aterrador. Sin red de seguridad. Fallos silenciosos a las 3am.
- *Lección: poder sin seguridad es un desastre.*
> *Medidores fin Etapa 1:* `🛡 ●●●●○ 😴 ●●●●○ 🔥 ●○○○○`
> *"Tienes un servidor. Sabes lo que tiene. Puedes dormir."*
### Etapa 2 — Conectividad en red / Internet
- Los sistemas están cada vez más lejos — acceso remoto, redes distantes
- Seguridad empieza a importar; procesos críticos, coste de caída sube
- Más agentes participando (dev, ops, seguridad) — hay que armonizar
- Armonizar: instalación de paquetes, configuración de recursos, actualizaciones en paralelo
- IaC: procesos automatizables y reproducibles, primeros intentos declarativos, config-driven model
- **La era Python**: desarrollo rápido, gran comunidad. Pero nada te impedía estar equivocado.
- Type hints llegaron tarde, y opcionales. Errores en runtime > errores en compilación.
- *Lección: pragmatismo sin garantías es frágil.*
> *Medidores fin Etapa 2:* `🛡 ●●●○○ 😴 ●●●○○ 🔥 ●●○○○`
> *"Más piezas. Más personas. Empieza a ponerse interesante."*
### Etapa 3 — Contenedores / Cloud / CI-CD
- Monolítico → distribuido, 24×7×365, alta disponibilidad
- Cloud, híbrido, multi-cloud, on-prem — todo a la vez
- Rollback y rollforward: como transacciones de BD, pero en infraestructura
- Escalado horizontal **y** vertical, también **desescalado** — en ambas direcciones
- CI/CD continuo, ciclos cortos — nuevas funcionalidades, nuevos despliegues, permanente
- IaC: Helm, Ansible, Terraform — más herramientas, más complejidad
- Múltiples versiones de "lo que creemos que es válido" — la fuente de verdad se fragmenta
- **La era Cloud/IaC**: Ansible, Terraform, Chef, Puppet. ¿Qué cambió? La sintaxis.
- Seguimos peleando con type safety. Seguimos descubriendo errores en producción.
- *Lección: más herramientas no resuelven problemas de paradigma.*
- **Resultado**: estados de alarma como norma, no como excepción
> *Medidores fin Etapa 3:* `🛡 ●●○○○ 😴 ●○○○○ 🔥 ●●●●○`
> *"¿Hemos aumentado la productividad? Sí.<br> ¿Hemos aumentado el estrés? Sí.<br> ¿Hemos aumentado las posibilidades de tener problemas? También.<br> ¿Tenemos más control y seguridad? No."*
---
```
┌─────────────────────────────────────────────┐
│ SLIDE STANDALONE — The Breaking Point │
│ │
│ Podía automatizar infraestructura. │
│ │
│ No podía hacerla fiable. │
│ │
│ No podía prevenir los errores. │
│ │
│ No podía dormir. │
└─────────────────────────────────────────────┘
```
> *Medidores:* `🛡 ●○○○○ 😴 ○○○○○ 🔥 ●●●●●`
> *Tono: sin ironía. Personal. Pausa larga. Dejar que aterrice.*
> *Lo que no se puede medir: el miedo.*
---
## 2. Por qué el IaC tradicional falla a escala
*Abstract: "Why traditional approaches to IaC fall short at scale"*
### La analogía del restaurante *(hilo conductor de toda la charla)*
Un restaurante tiene tres actores: **comensal** (mesa), **camarero** (servicio), **cocina**.
| Restaurante | Infraestructura |
|---|---|
| Comensal declara lo que quiere | Config declarativa (YAML, HCL, Helm) |
| Camarero valida y transmite | Orchestrator (K8s, Ansible, Terraform) |
| Cocina ejecuta y entrega | Runtime / provisioning |
| El plato llega — o no | Deployment exitoso — o no |
**Lo que hace que funcione** — o no:
- El comensal declara, no implementa: no dice *cómo* cocinar, dice *qué* quiere
- El camarero debe saber qué es posible y validar *antes* de ir a cocina
- "Quiero X" → camarero va a cocina → "X no hay, ¿por qué está en la carta?" → vuelve a mesa
- Equivalente: configuré un host con el puerto 8443 → ese puerto no está permitido → hay que reconfigurar desde cero
**La verdad que muta — y el problema de estado**
- La "verdad" no es estática. Muta en cada paso de la cadena:
- Comensal: su petición oral
- Camarero: la nota que toma
- Cocina: las marcas sobre lo que ya está hecho o no
- Caja: el ticket de pago
- Cada actor ve una *parte* de la verdad, relevante para él en ese momento
- Como en un sistema neuronal: hay eventos que no activan ciertas neuronas porque no son relevantes para ellas
- **El camarero conoce al cliente habitual** ("siempre sin sal") — la cocina, no. ¿Está ese contexto explícito en el pedido, o asumido?
- Si el camarero cambia, o si interviene otra estación de cocina → el estado implícito se pierde
**El coste del fallo depende de dónde ocurre**
- Fallo en mesa al pedir (pedido imposible): barato — se corrige antes de llegar a cocina
- Fallo en cocina al elaborar (ingrediente no disponible): medio — hay que volver a mesa y renegociar
- Fallo al entregar (plato se cae): caro — hay que rehacer desde cero
- Fallo cuando llega el plato y no es lo que se pidió: muy caro — experiencia destruida + tiempo perdido
- **Fail early = fail cheap. Fail in production = nightmare.**
**La renegociación: "no tengo champiñón"**
- Un actor en la cadena descubre que no puede cumplir parte del pedido
- Debe volver atrás y validar con el comensal: "¿te pongo verduras?"
- Esto requiere que el cambio sea explícito, trazado, y re-autorizado — no silencioso
- Equivalente en infra: configuration drift, estado divergente sin notificación
---
### El ciclo de la config: cómo llegamos hasta aquí
1. **Hardcoded** — dependencias, rutas, puertos dentro del binario. Control total, cero flexibilidad
2. **Config externa** (JSON) — funciona bien entre máquinas, ilegible para humanos a escala
3. **YAML / TOML** — más legible, pero sintaxis frágil: indentación, tipos implícitos, errores silenciosos
4. **YAML + Serde** — cargamos la config con Serde, que valida la *estructura*:
- ¿Existe el campo? ¿Es del tipo correcto?
- ¿Aceptamos "elefante" como animal de compañía? Depende del tipo. Pero si el tipo es `String`... sí.
- **Serde valida forma. No valida significado.**
5. **Helm / Jinja templates** — escribimos YAML a partir de variables (en YAML o JSON)
- El template genera el YAML final que consume la aplicación
- ¿Valida el contenido del YAML generado? **No. En absoluto.**
- Como usar un LLM con un markdown de referencia: el formato está, pero ¿el contenido es correcto? Eso no lo garantiza nadie.
6. **Resultado**: CI/CD continuo + config sin validación semántica = **esperanza continua**
> *"Lo que hacemos es escribir lo que queremos, como una carta a los Reyes Magos. Y cruzamos los dedos."*
---
```
┌─────────────────────────────────────────────┐
│ SLIDE STANDALONE — Esperanza continua │
│ │
│ CI/CD continuo. │
│ Sin validación semántica. │
│ │
│ Esperanza continua. │
│ │
│ (cruzamos los dedos en producción) │
└─────────────────────────────────────────────┘
```
> *Medidores:* `🛡 ●○○○○ 😴 ○○○○○ 🔥 ●●●●●`
> *Tono: irónico, de reconocimiento. La audiencia asiente.*
---
### Las tres preguntas sin respuesta
**Pregunta 1 — ¿Por qué esperamos a que se rompa?**
- "In my machine it works" — en producción, no lo sé
- Fail late = coste máximo
- Queremos: fail fast, fail cheap — cuanto antes, menor el coste
**Pregunta 2 — ¿Tenemos claro lo que queremos?**
- ¿La declaración es suficiente y consistente con lo que es posible?
- ¿Respeta los límites del sistema? ¿Cuáles son esos límites — estáticos o dinámicos?
- ¿Cuál es la fuente de verdad? ¿Y cuándo y cómo muta?
**Pregunta 3 — ¿Podemos garantizar determinismo?**
- ¿Tenemos herramientas que usen nuestras definiciones para operar automáticamente en el ciclo de vida?
- ¿Podemos tener certeza de que obtendremos lo que formulamos — no de forma aleatoria?
- No "en mi máquina funciona". Siempre. En cualquier máquina. En cualquier momento.
> *En realidad no estamos inventando nada. Todo ya existe. El problema es si lo estamos gestionando adecuadamente.*
---
```
┌─────────────────────────────────────────────┐
│ SLIDE STANDALONE — El paradigma │
│ │
│ Las herramientas no eran el problema. │
│ │
│ Los lenguajes no eran el problema. │
│ │
│ El paradigma era el problema. │
└─────────────────────────────────────────────┘
```
> *Tono: claridad repentina. El momento en que todo encaja.*
> *Necesitaba algo que forzara claridad — no que habilitara el caos.*
> *Que previniera errores — antes de producción.*
> *Que hiciera las asunciones explícitas — no ocultas.*
---
```
┌─────────────────────────────────────────────┐
│ SLIDE STANDALONE — Pesadilla continua │
│ │
│ Sistemas que no sabemos cómo controlar. │
│ Esperamos que funcionen. │
│ Cuando no funcionan, los arreglamos. │
│ │
│ Pesadilla continua. │
│ │
│ (el estado de alarma es lo normal) │
└─────────────────────────────────────────────┘
```
> *Medidores:* `🛡 ●○○○○ 😴 ○○○○○ 🔥 ●●●●●`
> *Tono: oscuro, sin ironía. El suelo emocional de la charla. Pausa aquí.*
---
## 3. Type safety y memory safety como fiabilidad en producción
*Abstract: "Type safety and memory safety as production reliability"*
### El puente: de YAML+Serde a tipos Rust
Serde carga config estructuralmente válida. Pero "elefante" como valor de `animal_companía: String` compila.
La respuesta de Rust: **no uses `String`. Usa un tipo.**
```rust
enum Animal { Perro, Gato, Conejo } // "elefante" no compila
```
Eso es lo que cambia. No el formato de la config. El modelo de qué puede contener.
---
### Lo que Rust introduce — respuesta a las tres preguntas
**Tipado estático estricto** *(responde: "¿tenemos claro lo que queremos?")*
- Un número no es un string. Un puerto no es cualquier entero.
- Los tipos son la declaración de intenciones — sin ambigüedad, sin equívocos
- Las conversiones (cast) son explícitas, no implícitas
- Tipos primarios ricos + structs, enums, tuplas, newtypes
**Inmutabilidad por defecto** *(responde: "¿qué puede cambiar?")*
- "Quiero tomate. Puedes inventarte lo que quieras, pero sin tomate no es el plato que pedí."
- `let` es inmutable. `let mut` es explícito y deliberado.
- Los invariantes son invariantes — no silenciosamente mutados en runtime
**`Option<T>`, no nulos** *(responde: "¿qué es opcional y qué es obligatorio?")*
- No `null`, no "asume que está si no se especifica"
- `Option<T>`: o está (`Some`) o no está (`None`) — y ambos casos se manejan explícitamente
- Restaurante: "champiñón opcional. Si no está, lo sé. Lo decido. Lo gestiono."
- El camarero no puede ignorar que no hay champiñón — el compilador no le deja
**Enums como dominio cerrado** *(responde: "¿cuáles son los valores válidos?")*
- Un cloud provider es `enum Provider { AWS, GCP, Azure, OnPrem }` — no un `String` libre
- Un puerto es un `u16` con rango validado — no cualquier entero
- Las restricciones están en el tipo, no en comentarios, no en documentación, no en la memoria del equipo
**Traits como contratos entre actores** *(responde: "¿pueden los actores de la cadena cambiar?")*
- Composición en vez de herencia rígida
- Cada "actor" de la cadena (AWS provider, K8s, on-prem) implementa el mismo trait
- La cadena funciona con cualquier implementación que cumpla el contrato
- Como el restaurante: el camarero puede cambiar — si conoce el protocolo, el proceso es el mismo
**El compilador como el camarero que valida** *(responde: "¿cuándo detectamos el fallo?")*
- Valida **antes** de construir el binario — no en runtime, no en producción
- No cuando lleva horas ejecutándose. No cuando se llama a una función que nadie tocaba hace meses.
- Fail early. Fail cheap. En compilación, no en el 2am del domingo.
- Binarios predecibles: comportamiento de memoria, recursos y flujos deterministas
> *"El compilador es el camarero que valida el pedido antes de que llegue a cocina. Antes de que el cliente espere. Antes de que el ingrediente no esté."*
---
### El impacto humano — cuando el sistema es fiable
Esto no es solo técnico. Cuando la infraestructura es predecible:
- El sueño vuelve
- La confianza vuelve
- El equipo confía en la automatización
- El estrés baja
- Puedes descansar de verdad
> *"Lo que no se puede medir: el miedo. Lo que sí se mide: el MTTR. Antes > 30min. Ahora < 5min."*
> *Medidores — compilador activo:* `🛡 ●●●○○ 😴 ●●○○○ 🔥 ●●●○○`
> *Medidores — tipos + traits + options:* `🛡 ●●●●○ 😴 ●●●●○ 🔥 ●●○○○`
> *Medidores — Rust completo en producción:* `🛡 ●●●●● 😴 ●●●●● 🔥 ●○○○○`
---
```
┌─────────────────────────────────────────────┐
│ SLIDE STANDALONE — Certeza continua │
│ │
│ El compilador valida antes del binario. │
│ Los tipos definen lo que es posible. │
│ El contrato se cumple — o no compila. │
│ │
│ Certeza continua. │
│ │
│ (para continuar durmiendo bien) │
└─────────────────────────────────────────────┘
```
> *Medidores:* `🛡 ●●●●● 😴 ●●●●● 🔥 ●○○○○`
> *Tono: resolución. El arco cierra. El título de la charla, demostrado.*
---
## 4. Orquestación segura multi-cloud y on-prem
*Abstract: "Building safe orchestration across multi-cloud and on-prem environments"*
### De la teoría al sistema real
Todo lo anterior no es hipotético. Es la arquitectura de **Provisioning** — un sistema de orquestación de infraestructura en producción, escrito en Rust, que gestiona despliegues en AWS, UpCloud y on-prem desde una sola base de código.
La pregunta es: ¿cómo se traduce "tipos y traits" a un sistema que maneja multi-cloud real?
---
### Nickel como source of truth tipado (ADR-003)
YAML fue rechazado explícitamente. TOML fue rechazado. La razón: sin type safety.
Nickel — un lenguaje de configuración funcional con sistema de tipos — reemplaza YAML:
```nickel
{
infrastructure = {
compute | {
region | String,
count | Number & (> 0),
auto_scaling | {
min | Number & (> 0),
max | Number & (>= min), -- el compilador verifica esto
}
}
}
}
```
- Validación en tiempo de compilación de la config — no en runtime, no en producción
- Merging jerárquico tipado: defaults → workspace → profile → environment → runtime
- Resultado (ADR-003): **zero configuration type errors en producción**
- Alternativas rechazadas explícitamente: TOML (no type safety), KCL, YAML+validación híbrida
> *"Serde valida forma. Nickel valida significado. El compilador valida antes del despliegue."*
---
### Traits como contratos de provider (el restaurante, resuelto)
El problema del restaurante: la cocina puede cambiar. AWS no es UpCloud no es bare metal.
La solución Rust: todos implementan el mismo trait.
```rust
// El trait es el menú. Los providers son las cocinas.
#[async_trait]
pub trait TaskStorage: Send + Sync {
async fn create_task(&self, task: WorkflowTask) -> StorageResult<WorkflowTask>;
async fn update_task(&self, id: &str, status: TaskStatus) -> StorageResult<()>;
async fn list_tasks(&self, filter: TaskFilter) -> StorageResult<Vec<WorkflowTask>>;
async fn audit_operation(&self, op: AuditEntry) -> StorageResult<()>;
}
// Implementaciones: FilesystemStorage, SurrealDbStorage, MemoryStorage
// El orchestrator no sabe cuál usa. El compilador garantiza que todas cumplen.
```
```rust
// Dominios cerrados — no strings libres
enum RollbackStrategy { ConfigDriven, Conservative, Aggressive, Custom { operations: Vec<String> } }
enum DependencyType { Hard, Soft, Optional }
enum TaskStatus { Pending, Running, Completed, Failed, Cancelled }
enum ProviderType { UpCloud, AWS, Local }
```
Cuando se añade un nuevo provider: implementa el trait o no compila. No hay forma de olvidarse de un caso.
---
### Orquestación con grafo de dependencias
El orchestrator construye un DAG (grafo acíclico dirigido) de tareas con detección de ciclos en tiempo de compilación del workflow.
```rust
pub struct WorkflowConfig {
pub max_parallel_tasks: usize,
pub task_timeout_seconds: u64,
pub fail_fast: bool, // falla rápido, falla barato
pub checkpoint_interval_seconds: u64,
}
```
Instalación de Kubernetes como ejemplo — el orchestrator resuelve:
```
containerd (Hard) → etcd (Hard) → kubernetes → cilium (requires kubernetes) → rook-ceph
```
No se puede instalar kubernetes si containerd falla. El tipo `DependencyType::Hard` lo garantiza.
El compilador detecta dependencias circulares. No el operador de guardia a las 3am.
---
### El estado explícito elimina el drift
El problema de configuration drift: el sistema real diverge silenciosamente del estado deseado.
Rust obliga a que toda mutación sea explícita (`let mut`). El sistema de estado del orchestrator hace lo mismo a nivel de infraestructura:
```rust
pub struct WorkflowExecutionState {
pub status: WorkflowStatus,
pub task_states: HashMap<String, TaskExecutionState>,
pub checkpoints: Vec<WorkflowCheckpoint>, // qué se hizo y cuándo
pub statistics: WorkflowStatistics,
}
```
- Checkpoints cada 5 minutos (configurable)
- Hasta 50 checkpoints retenidos
- Cada estado de provider capturado en `HashMap<String, ProviderState>`
- Sin estado implícito. Sin "el camarero recuerda que el cliente no quiere sal". Está en el pedido.
> *Medidores — sección 4:* `🛡 ●●●●● 😴 ●●●●● 🔥 ●○○○○`
---
## 5. Aplicaciones reales
*Abstract: "Real applications: Kubernetes, blockchain validators, disaster recovery"*
### Kubernetes — despliegue con tipos
El orchestrator se despliega en Kubernetes. 3 réplicas, rolling update, anti-affinity entre nodos.
Pero lo relevante no es que *corre* en Kubernetes — es cómo se *gestiona* Kubernetes como target.
El orchestrator provisiona los componentes del cluster como workflow tipado:
```
containerd → etcd → kubernetes control plane
→ kubelet (workers)
→ CoreDNS
→ Cilium (CNI, requires kubernetes)
→ Rook-Ceph (storage, requires kubernetes + Cilium)
```
Cada componente es un tipo. Cada dependencia es un `DependencyType`. El compiler detecta si intentas instalar Cilium sin Kubernetes. No el CI a las 2am.
Los health probes del orchestrator en K8s — `/health` cada 10 segundos — no son decoración. Son parte del contrato: el sistema sabe cuándo está sano. Lo sabe antes de que falle el cliente.
---
### Blockchain validators — disponibilidad como tipo
Los validators de blockchain (Polkadot, Ethereum) tienen un requisito brutal: uptime o slashing. Un validator que falla pierde fondos. No hay margen para "en mi máquina funciona".
Lo que Rust + este sistema aporta:
**Secretos con post-quantum cryptography (ADR-006)**
- CRYSTALS-Kyber (KEM) + Falcon (signatures) + AES-256-GCM (hybrid)
- Las claves del validator no pueden ser comprometidas por computación cuántica futura
- La rotación de claves es automática, trazada, con audit log
**SLOs con error budgets reales (ADR-009)**
```
Tier 1 — Critical: 99.99% uptime = 52.6 minutos de downtime/año
```
Esto no es un número de marketing. Es un Prometheus rule que dispara alertas y bloquea deploys cuando el burn rate supera el presupuesto.
**Configuración determinista**
- Los parámetros del validator son tipos, no strings: no puede entrar un valor de `bond_amount` que no sea un `u128` validado
- La config del validator es reproducible bit-a-bit en cualquier nodo — mismo Nickel schema, mismo resultado
---
### Disaster recovery — rollback como tipo, no como procedimiento
El problema clásico de DR: el runbook existe, pero no se ejecutó correctamente, o el estado en DR no coincide con el de producción.
**Checkpoints como snapshots de estado completo**
```rust
pub struct Checkpoint {
pub workflow_state: Option<WorkflowExecutionState>,
pub resources: Vec<ResourceSnapshot>,
pub provider_states: HashMap<String, ProviderState>,
}
```
No es "vuelve al commit anterior". Es el estado completo del sistema: qué estaba corriendo, en qué provider, con qué configuración, en qué momento.
**Rollback como estrategia tipada**
```rust
enum RollbackStrategy {
ConfigDriven, // lo que diga la config
Conservative, // preserva todo salvo lo marcado
Aggressive, // revierte todo
Custom { operations: Vec<String> }, // playbook explícito
}
```
No hay forma de hacer rollback sin elegir una estrategia. El compilador no te deja ignorar el caso.
**Self-healing automatizado (ADR-010)**
```
┌─────────────────────────────────────────────┐
│ SLIDE STANDALONE — MTTR │
│ │
│ Sin tipos. Sin compilador. Sin estado. │
│ │
│ MTTR > 30 minutos. │
│ │
│ (a las ??, apagar el fuego) Alarma pesadilla│
│ ──────────────────────────────── │
│ │
│ Rust. Tipos. Estado explícito. │
│ Automated response. │
│ │
│ MTTR < 5 minutos.
│ │
│ (a las 3am, sin ti) Descanso en paz mental │
└─────────────────────────────────────────────┘
```
> *Medidores:* `🛡 ●●●●● 😴 ●●●●● 🔥 ●○○○○`
> *Tono: cifras reales, no promesas. Pausa después de "sin ti".*
El `RemediationEngine` ejecuta playbooks tipados: `ScaleService`, `FailoverService`, `RestartService`, `ClearCache`. Si el remedio falla 3 veces, escala a humano. No itera indefinidamente.
**Multi-backend backup tipado**
- restic, borg, tar, rsync — todos como variantes de un enum
- El backup de producción y el restore en DR usan el mismo tipo, el mismo schema
- "Funciona en prod pero no en DR" no puede ocurrir si el estado es el mismo tipo
> *"No estamos cruzando los dedos. Tenemos el estado. Tenemos el tipo. Tenemos el rollback. Dormimos."*
> *Medidores — sección 5:* `🛡 ●●●●● 😴 ●●●●● 🔥 ●○○○○`
---
## Cierre
### Para los veterans de infraestructura
Si has vivido lo que yo he vivido:
- Rust resuelve los problemas que *ya conoces* — no problemas hipotéticos
- No es hype. Llevo décadas viendo tecnologías venir y irse.
- Dale una oportunidad real. Tu sueño te lo agradecerá.
### Para los engineers más jóvenes
Aprende de alguien que lo intentó todo:
- No malgastes décadas en infraestructura frágil
- Empieza con type safety desde el principio
- Constrúyelo fiable desde el día uno
### La perspectiva
> *"A mi edad, tengo perspectiva. He visto tecnologías venir y irse. Rust no es hype. Resuelve problemas reales que he tenido durante décadas. Tener más años no es una limitación. Es una ventaja."*
---
- No hemos inventado nada nuevo. Los problemas siempre han existido.
- Rust los reúne y los resuelve: tipos, traits, compiler, memory safety.
- El resultado: infraestructura predecible. Deployments sin pesadillas.
- **Duermes bien.**
> *Medidores — cierre:* `🛡 ●●●●● 😴 ●●●●● 🔥 ●○○○○`
>
> *Slide final: los tres medidores en verde. Título de la charla. Fin.*