Back to flin
flin

Appels panic en production : suivi et élimination

Comment nous avons traqué et catégorisé chaque appel panic dans le codebase Rust de 186K lignes de FLIN -- 5 panics en production, 120 panics de test, et la stratégie pour les éliminer.

Juste A. Gnimavo (Thales) & Claude | March 26, 2026 7 min flin
EN/ FR/ ES
flinauditpanicserror-handlingproduction

En Rust, panic! est un crash délibéré. Le programme affiche une trace de pile et se termine. Dans le code de test, les panics sont le mécanisme standard pour les assertions -- chaque assert!, assert_eq! et unwrap() est un panic potentiel, et c'est par conception. Dans le code de production, les panics sont presque toujours des bugs. Ils transforment ce qui devrait être une erreur gérée en crash de processus, et dans un runtime de langage comme FLIN, un crash signifie que toutes les applications tournant sur ce runtime tombent.

L'audit de FLIN a catalogué chaque appel panic dans 186 252 lignes de code Rust. Le nombre brut était alarmant : plus de 800 sites de panic dans le codebase. Le nombre raffiné était rassurant : seuls 5 étaient dans des chemins de code de production. Les restants étaient des assertions de test, ce qui est exactement là où les panics ont leur place.

Cet article retrace ces cinq panics de production -- où ils vivent, pourquoi ils existent, s'ils devraient être éliminés, et à quoi ressemble la stratégie d'élimination.

Le recensement des panics

L'audit a compté les appels panic à trois niveaux : les invocations explicites de la macro panic!(), les appels .unwrap() sur les types Result et Option, et les appels .expect() qui fournissent un message avant de paniquer. Ensemble, ceux-ci forment l'ensemble complet des sites de panic dans le codebase.

Module                   Explicit panic!  .unwrap()  .expect()  Total
codegen/bytecode.rs              2            0          0         2
vm/vm.rs                        48            0          0        48
vm/renderer.rs                   9            0          0         9
parser/parser.rs               ~600           0          0       ~600
tests/**                         0          ~120        ~20      ~140
                                                                -----
TOTAL                          ~659         ~120        ~20      ~799

Les 600 panics du parser étaient tous dans le code de test (ligne 8866 et au-delà). Les 140 panics du répertoire tests étaient par définition du code de test. Il restait les modules codegen, VM et renderer avec 59 sites de panic de production à évaluer.

Les cinq panics de production

Après avoir filtré les panics qui n'étaient accessibles que par des chemins de code internes (jamais déclenchés par du code FLIN écrit par l'utilisateur), l'audit a identifié cinq panics qu'un développeur FLIN pourrait théoriquement déclencher :

PANIC-001 : dépassement du pool de constantes

rust// codegen/bytecode.rs line 1711
pub fn add_constant(&mut self, value: Value) -> u16 {
    if self.constants.len() >= u16::MAX as usize {
        panic!("Constant pool overflow: too many constants (max {})", u16::MAX);
    }
    self.constants.push(value);
    (self.constants.len() - 1) as u16
}

Un utilisateur peut-il le déclencher ? Oui, mais uniquement avec un programme FLIN extraordinairement grand -- un qui définit plus de 65 535 constantes uniques. Une application FLIN typique utilise quelques centaines de constantes. Atteindre 65 535 nécessiterait un effort délibéré.

Devrait-ce être un panic ? Possiblement oui. C'est une limite dure du format bytecode. La réponse appropriée est de refuser la compilation, ce qu'un panic fait effectivement (bien qu'un message d'erreur approprié serait mieux).

Verdict : Convertir de panic! à un CompileError::ConstantPoolOverflow qui produit un message d'erreur convivial indiquant au développeur de découper son programme en modules.

PANIC-002 : assertion de type float

rust// codegen/bytecode.rs line 2709
fn read_float_constant(&self, index: u16) -> f64 {
    match &self.constants[index as usize] {
        Value::Float(f) => *f,
        other => panic!("Expected Float constant at index {}, got {:?}", index, other),
    }
}

Un utilisateur peut-il le déclencher ? Non, dans des circonstances normales. Si cette assertion se déclenche, cela signifie que le compilateur a un bug.

Verdict : Convertir en Result pour un rapport d'erreur plus propre, mais marquer comme priorité basse puisque la condition devrait être inatteignable.

PANIC-003 à PANIC-050 : assertions de types de la VM

La VM contient 48 appels panic, presque tous suivant le même pattern :

rust// vm/vm.rs -- typical type assertion pattern
fn builtin_string_upper(&mut self) -> Result<Value, VmError> {
    let value = self.pop()?;
    let s = match value {
        Value::Object(id) => self.get_string(id)?.to_uppercase(),
        Value::Text(s) => s.to_uppercase(),
        _ => panic!("upper() requires a string argument, got {:?}", value),
    };
    Ok(Value::text(s))
}

Un utilisateur peut-il les déclencher ? Oui. Si un développeur FLIN écrit upper(42), la VM recevra une valeur entière là où elle attend une chaîne, et le panic se déclenchera.

Devrait-ce être des panics ? Absolument pas. Ce sont les panics les plus importants à éliminer. Un développeur FLIN passant le mauvais type à une fonction intégrée devrait recevoir une erreur d'exécution claire, pas un crash de processus.

Verdict : Convertir tous les 48 en retours VmError::TypeError. C'est la tâche d'élimination de panics de plus haute priorité.

rust// AFTER: proper error on type mismatch
fn builtin_string_upper(&mut self) -> Result<Value, VmError> {
    let value = self.pop()?;
    let s = self.as_string(&value)
        .map_err(|_| VmError::TypeError {
            function: "upper",
            expected: "text",
            got: value.type_name(),
        })?;
    Ok(Value::text(s.to_uppercase()))
}

PANIC-051 à PANIC-059 : assertions du moteur de rendu

Le moteur de rendu contient 9 appels panic, principalement dans l'évaluation d'expressions et la génération HTML.

Un utilisateur peut-il les déclencher ? Oui. Les expressions de templates qui s'évaluent à des types inattendus peuvent atteindre ces sites de panic.

Verdict : Convertir en dégradation gracieuse -- rendre un marqueur d'erreur visible dans la sortie HTML et journaliser un avertissement, mais ne jamais crasher le processus serveur.

La stratégie d'élimination

L'audit a proposé une stratégie à trois niveaux pour l'élimination des panics :

Niveau 1 : convertir en VmError (48 panics de la VM). Chaque assertion de type dans les gestionnaires de fonctions natives de la VM devrait retourner Result<Value, VmError> au lieu de paniquer.

Niveau 2 : convertir en dégradation gracieuse (9 panics du renderer). Les panics du renderer devraient être remplacés par un rendu de secours qui produit une sortie visible mais sans crash.

rust// Tier 2: graceful degradation in renderer
fn extract_text_content(&self, value: &Value) -> String {
    match value {
        Value::Text(s) => s.clone(),
        Value::Object(id) => {
            self.vm.get_string(*id)
                .unwrap_or_else(|_| format!("[render error: invalid object {}]", id))
        }
        Value::Int(n) => n.to_string(),
        Value::Float(f) => f.to_string(),
        Value::Bool(b) => b.to_string(),
        Value::None => String::new(),
        other => format!("[render error: unexpected {:?}]", other.type_name()),
    }
}

Niveau 3 : convertir en CompileError (2 panics du codegen). Le dépassement du pool de constantes et l'assertion de type float devraient produire des erreurs de compilation plutôt que des crashs à l'exécution.

Les 120 panics de test

Les 120 panics dans le code de test ne sont pas des bugs. Ce sont le mécanisme standard de test de Rust. Chaque .unwrap() est un site de panic. Chaque assert_eq! est un site de panic. Ce sont des utilisations correctes de panic -- ils terminent le test avec un message d'échec, ce qui est exactement ce que les assertions de test doivent faire.

Mesurer les progrès

Production panic sites:
  Start of audit:     59 (48 VM + 9 renderer + 2 codegen)
  After Tier 1:       11 (0 VM + 9 renderer + 2 codegen)
  After Tier 2:        2 (0 VM + 0 renderer + 2 codegen)
  After Tier 3:        0 (0 VM + 0 renderer + 0 codegen)

Target: zero production panics before beta release

Pourquoi zéro panic compte

Un runtime de langage est une infrastructure. Quand un développeur FLIN écrit une application web et la déploie en production, il fait confiance au fait que le runtime ne crashera sous aucune entrée que ses utilisateurs fournissent. Un panic dans le runtime n'est pas juste un bug de FLIN -- c'est une indisponibilité pour chaque application construite sur FLIN.

Zéro panic en production n'est pas un objectif théorique. C'est une propriété concrète et mesurable du binaire. Soit les appels panic! existent dans les chemins de code de production, soit ils n'existent pas. L'audit nous a donné la liste exacte. Les sessions de correction les élimineraient. Et le pipeline CI garantirait qu'ils ne reviennent jamais.


Ceci est la partie 154 de la série « Comment nous avons construit FLIN », documentant comment un CEO à Abidjan et un CTO IA ont conçu et construit un langage de programmation à partir de zéro.

Navigation de la série : - [153] Ce que l'audit nous a appris sur la construction d'un langage - [154] Appels panic en production : suivi et élimination (vous êtes ici) - [155] 93 sessions auditées en un seul passage

Share this article:

Responses

Write a response
0/2000
Loading responses...

Related Articles

Thales & Claude thales

Treize agents, quarante-trois minutes : la première session Workflow de Claude Fable 5, et ce qu'un script d'orchestration déterministe change aux builds multi-agents

Un prompt, treize agents, quarante-trois minutes : la première session de production avec Claude Fable 5 et l'outil Workflow de Claude Code a livré un site web de production complet de sept pages plus un endpoint backend de capture de leads, en un seul commit. Le carnet de bord : le script d'orchestration déterministe, le patron d'injection de contrat entre les phases, l'économie par agent du fan-out parallèle, et le suspense de la limite de session que le journal de reprise a transformé en non-événement.

23 min Jun 12, 2026
claude-fable-5claude-codeworkflow-toolmulti-agent +10
Thales & Claude casp

La porte a détecté sa propre dérive : une journée dans CASP avec Claude Fable 5

Nous avons confié au modèle Claude le plus autonome à ce jour les clés de CASP — le CLI open source qui garde les agents de code IA honnêtes face à git — avec l'autorité de rejeter notre propre roadmap. Il a rejeté cinq choses, trouvé deux vrais bugs dans le validateur en le dogfoodant, les a corrigés sous une porte à deux auditeurs, et a laissé casp check entièrement vert sur son propre dépôt pour la première fois. CASP 0.3.0 en est le résultat.

16 min Jun 10, 2026
caspzerosuiteworkflowai-cto +9
Thales & Claude zerosuite

La transplantation du CASP : comment la discipline des six fichiers est passée de Conductor à un ERP transport anti-fraude, ce que la compétence /next ajoute quand l'opérateur tape juste « next », et pourquoi le coût d'une dérive du CASP grimpe quand le projet, c'est l'argent des autres

La discipline du CASP qui a piloté trente-cinq sessions de Conductor est agnostique au produit. Le carnet de bord de sa transplantation sur KASSIA, un ERP transport anti-fraude pour un exploitant de flotte en Côte d'Ivoire : ce qui a migré, ce qui n'a pas migré (le validateur sur mesure — et ce que son absence coûte), ce que la compétence /next ajoute quand l'opérateur tape un seul mot, et là où le CASP s'arrête — le bug de déploiement qu'il ne pouvait pas voir parce qu'il enregistre l'intention, pas la réalité de l'infrastructure.

23 min Jun 8, 2026
kassiaerp-kassia-transport-logistiquezerosuiteCASP +15