Si vous avez déjà vu un déploiement propre se transformer en incident en chaîne, vous comprenez déjà la physique émotionnelle d’Ariane 5. Tout paraît nominal jusqu’à ce que ça ne le soit plus. Ensuite vous recevez des télémétries qui ressemblent à de l’art abstrait, des alarmes qui s’enchaînent et un tableau de bord qui affirme que le ciel tombe parce que, au sens propre, il tombe.
Ce n’était pas une mystérieuse défaillance du type « la science des fusées, c’est difficile ». C’était une défaillance d’ingénierie logicielle avec une odeur familière : code réutilisé, hypothèses non concordantes, exception non gérée et une « redondance » qui a dupliqué le même bug deux fois. En termes SRE : une défaillance en mode commun avec un excellent temps de disponibilité jusqu’à l’instant précis où tout a basculé.
Ce qui s’est passé dans les 40 premières secondes
Le 4 juin 1996, le premier vol d’Ariane 5 s’est élevé depuis Kourou. Ariane 5 était un lanceur neuf, conçu pour emporter des charges utiles plus lourdes qu’Ariane 4. Nouveau véhicule, nouveau profil de vol, nouvelles dynamiques. Mais des parties du logiciel — plus précisément dans le système de référence inertielle (IRS) — avaient été réutilisées depuis Ariane 4.
Environ 37 secondes après le décollage, le logiciel IRS a atteint un dépassement numérique lors d’une conversion d’une valeur en virgule flottante vers un type entier incapable de représenter la valeur plus grande. Ce dépassement a déclenché une exception. L’exception n’a pas été gérée de la manière requise pour un véhicule en vol. L’ordinateur IRS s’est arrêté. Presque immédiatement, l’IRS redondant a aussi échoué de la même façon, car il exécutait le même code avec les mêmes hypothèses. La redondance a fait ce pour quoi elle avait été conçue : basculer vers exactement la même défaillance.
Le système de guidage, privé de données d’attitude valides, a interprété des données de diagnostic comme si elles étaient des données de vol réelles. La fusée a dévié fortement de sa trajectoire prévue. Les charges structurelles ont explosé. La sécurité de zone — voyant un véhicule hors de contrôle — a déclenché l’autodestruction. Ce n’est pas du mélodrame ; c’est une limite de sécurité. Quand on lance au-dessus d’un territoire habité, on ne peut pas « attendre et voir ».
Les fusées n’explosent pas parce qu’elles sont en colère. Elles explosent parce que nous leur avons ordonné de le faire, volontairement, pour empêcher le rayon d’explosion de se propager là où il ne devrait pas.
Faits intéressants et contexte historique
- Le vol 501 était le vol inaugural d’Ariane 5, ce qui signifie qu’il n’y avait pas un long historique opérationnel pour masquer les problèmes — seulement des simulations et des hypothèses.
- Ariane 5 utilisait deux systèmes de référence inertielle destinés à la redondance, mais les deux exécutaient le même logiciel et étaient exposés aux mêmes conditions d’entrée.
- La variable en échec représentait une valeur liée à la vitesse horizontale issue du traitement d’alignement/attitude — valable pour l’enveloppe de vol d’Ariane 4, pas pour celle d’Ariane 5 en phase initiale.
- L’exception est survenue lors d’une conversion float→int où le type entier était trop petit pour représenter la valeur, causant un overflow.
- Certaines fonctions IRS restaient actives après le décollage alors qu’elles n’étaient pas nécessaires pour le vol, parce que les désactiver était jugé risqué ou pas worth le changement.
- Le calculateur de guidage a traité certains mots de diagnostic comme des données après la défaillance de l’IRS, un classique « garbage in, hard turn out ».
- L’autodestruction de la zone de sécurité est une fonctionnalité conçue, pas un accident — quand le guidage est perdu et que la trajectoire est dangereuse, la terminaison protège les personnes au sol.
- L’incident est devenu une étude de cas incontournable en ingénierie logicielle car la défaillance était déterministe, bien documentée et douloureusement évitable.
La faute technique : une conversion, un dépassement, deux calculateurs
Voici le cœur du problème, débarrassé du mythe : le logiciel IRS a tenté de convertir un nombre en virgule flottante en un entier signé 16 bits (ou un type entier de taille similaire dans l’implémentation). La valeur float dépassait la plage maximale d’un entier représentable. Le runtime a levé une exception d’overflow. Cette exception a déclenché l’arrêt de l’ordinateur IRS. Une fois hors service, il ne fournissait plus de références d’attitude et de vitesse. Le guidage est alors devenu aveugle.
Pourquoi la conversion existait
Dans beaucoup de systèmes embarqués de guidage, on trouve des représentations numériques mixtes. Le flottant sert pour les calculs intermédiaires ; les entiers servent pour des messages à format fixe, le stockage ou la performance. Quand ces systèmes ont été conçus, la mémoire et les cycles CPU étaient rares. Même aujourd’hui, la déterminisme compte : des champs d’entiers de taille fixe dans les messages bus ou les trames de télémétrie sont stables, testables et faciles à parser.
Donc la conversion elle-même n’est pas suspecte. La partie suspecte est l’hypothèse qu’elle contenait : « cette valeur tiendra toujours ». C’était vrai dans les conditions de vol d’Ariane 4. Ce ne l’était pas pour Ariane 5. L’enveloppe initiale d’Ariane 5 produisait une composante de vitesse horizontale plus élevée que ce qu’Ariane 4 avait connu au même instant. Même code, nouvelle physique.
Pourquoi la gestion d’exception a été fatale
Dans les systèmes critiques pour la sécurité, les exceptions ne sont pas des « bugs », ce sont des signaux. Un overflow est l’équivalent logiciel d’une soupape indiquant une surpression. Soit vous le gérez d’une façon qui préserve un comportement sûr, soit vous laissez le composant crasher et espérez que la redondance vous rattrape.
Ils ont protégé certaines conversions, parce qu’elles étaient connues comme susceptibles de déborder. Mais cette conversion-là n’était pas protégée — parce qu’on n’attendait pas qu’elle déborde. Là est le piège : les défaillances les plus dangereuses sont celles que vous avez « prouvé » impossibles en vous basant sur le système d’hier.
Ce qui aurait dû se produire à la place
Dans un monde idéal, la conversion aurait été protégée et saturante : limiter la valeur à min/max et définir un drapeau d’état, ou utiliser un type entier plus large, ou conserver la valeur en float, ou désactiver ce calcul après le décollage s’il n’est pas nécessaire. Choisissez-en une. N’importe laquelle, correctement implémentée, coûte infiniment moins que de reconstruire une fusée et d’expliquer le cratère aux parties prenantes.
Voici la vérité sèche et ironique : les ordinateurs sont des employés très littéraux. Ils ne « débordent pas un peu » ; ils débordent précisément et puis s’arrêtent sans préavis.
Une citation pour rester honnête : « L’espoir n’est pas une stratégie. » — General Gordon R. Sullivan
Pourquoi la redondance a échoué : le piège du mode commun
La redondance n’est pas magique ; c’est des maths. Deux systèmes n’améliorent la fiabilité que si leurs modes de défaillance sont suffisamment indépendants. Si les deux systèmes exécutent le même logiciel, la même configuration, les mêmes types numériques et voient les mêmes entrées au même moment, vous n’avez pas construit de redondance — vous avez construit une défaillance synchronisée.
Cela s’appelle une défaillance en mode commun. On la retrouve partout : deux alimentations provenant du même tableau, deux clusters Kubernetes partageant le même fournisseur DNS, bases de données « active-active » qui se bloquent toutes les deux sous le même motif de requête.
Ariane 5 avait deux unités IRS. Les deux ont subi le même overflow à peu près au même instant. Il n’y avait pas de dégradation gracieuse, pas « une unité passe en mode réduit », pas d’implémentation indépendante, pas de représentation numérique hétérogène, pas de diversité qui compte.
À noter aussi : la temporisation de bascule compte. Si l’unité redondante meurt des millisecondes après la primaire, vous n’avez pas basculé — vous avez juste ajouté un délai si court qu’il ressemble à un bug de logs.
La redondance sans diversité est une couverture réconfortante. Elle tient chaud jusqu’au moment où le feu l’atteint.
Vue système : exigences, validation et code « non essentiel »
La défaillance n’était pas « un mauvais cast ». C’était une chaîne de décisions qui a rendu le cast fatal. Diagnostiquons les points de décision.
1) Réutiliser sans revalidation
La réutilisation logicielle est une bonne pratique d’ingénierie. La réutilisation aveugle est du jeu. Le logiciel d’Ariane 4 a été réutilisé dans Ariane 5, mais la validation n’a pas complètement couvert la nouvelle enveloppe de vol. C’est le genre de raccourci qui paraît responsable dans un plan de projet : moins de changements, moins de régressions, moins de risques.
Réalité : moins de changements peut signifier moins d’attention. Le code devient « fiable ». Le code « fiable » est l’endroit où les bugs vont se retirer pour revenir ensuite sous forme de fantômes.
2) Fonctions non essentielles en cours d’exécution pendant le vol
Une partie du calcul en échec appartenait à la logique d’alignement qui n’était pas nécessaire après le décollage. Pourtant elle continuait de tourner. Pourquoi ? Parce que la désactiver demandait des modifications et des retests, et la laisser active semblait sûre parce que « ça marchait avant ».
C’est un anti-pattern opérationnel : laisser des travaux d’arrière-plan non critiques s’exécuter dans une fenêtre critique parce que les désactiver paraît risqué. Dans les systèmes de production, on appelle ça des « cron jobs inoffensifs » qui deviennent les plus gros consommateurs pendant une panne. Dans les fusées, vous ne pouvez pas SSHer et tuer le processus.
3) Politique de gestion d’exception qui priorisait l’arrêt
Il y a des cas où arrêter est plus sûr que continuer. Mais l’arrêt doit être conçu comme un état sûr. Pour un système de référence inertielle alimentant le guidage, l’arrêt n’est pas sûr à moins que le guidage ait une source alternative vérifiée et que le protocole empêche l’interprétation des diagnostics comme des vérités.
En exploitation, on dit : échouer vite est excellent quand on peut réessayer. Dans une fusée à T+37 secondes, vous ne pouvez pas réessayer. Votre bouton « redémarrer » est la police d’assurance.
4) Vérification qui a manqué la vraie enveloppe opérationnelle
Voilà la partie que les ingénieurs détestent parce que ce n’est pas un bug unique. C’est un décalage entre ce qui a été testé et ce qui comptait. Vous pouvez avoir des milliers de cas de test et quand même rater la limite qui définit la réalité : « Cette variable peut-elle dépasser 32767 ? »
Et oui, c’est ennuyeux. C’est pour ça que ça tue les systèmes.
Trois mini-récits d’entreprise que vous reconnaîtrez
Mini-récit 1 : L’incident causé par une mauvaise hypothèse
Une entreprise de paiements exploitait une flotte de services calculant des scores de risque fraude. Le score était stocké dans un entier signé 16 bits parce qu’il « n’avait besoin de représenter que 0–10 000 » au lancement du système des années plus tôt. Le modèle initial plafonnait le score. Tout le monde a oublié que le plafond était un simple plafond et non une loi de la nature.
Un nouveau modèle a été déployé. Il était meilleur — rappel plus élevé, moins de faux négatifs. Il a aussi émis des scores au-dessus de l’ancien plafond pendant certains pics de trafic saisonniers. Une conversion profonde dans une bibliothèque partagée tronquait ou overflowait les valeurs, qui se sont alors mappées en « risque ultra-faible » à cause d’un wraparound. Le système anti-fraude ne s’est pas mis à crier. Il est devenu silencieux. C’est la pire des défaillances : celle qui ressemble à du succès.
L’incident a duré des heures parce que les tableaux de bord de monitoring suivaient la moyenne du score et le nombre de paiements bloqués. Les moyennes n’ont pas beaucoup bougé. La queue a explosé. Il a fallu qu’un ingénieur regarde un histogramme brut pour voir qu’un paquet de transactions avait des scores négatifs absurdes.
La correction a été simple : stocker le risque en 32 bits, ajouter des contrôles explicites de bornes et valider les plages de sortie du modèle lors du déploiement. La correction culturelle fut plus dure : arrêter de traiter « nous n’avons jamais vu cette valeur » comme une garantie. Dans un système vivant, le futur est l’endroit où vos hypothèses meurent.
Mini-récit 2 : L’optimisation qui a eu l’effet inverse
Une équipe de stockage a « optimisé » un pipeline d’ingestion en passant des timestamps 64 bits à des secondes-since-epoch 32 bits dans un index, car cela réduisait la mémoire et améliorait le cache. Les benchmarks étaient excellents. Les graphiques montaient. Les promotions sont arrivées.
Puis ils se sont étendus dans une région où certains appareils envoyaient des timestamps lointains dans le futur à cause d’un bug d’horloge firmware. Ces timestamps ont overflowé la représentation 32 bits. Les enregistrements ont été indexés dans des buckets temporels absurdes. Les requêtes des « 15 dernières minutes » récupéraient parfois des données du futur, que l’application considérait comme les plus récentes et donc « les plus valides ». Les utilisateurs ont vu des données se téléporter dans le temps.
Il a fallu des jours pour démêler cela parce que les données n’étaient pas corrompues en stockage ; elles l’étaient dans l’index. Reconstruire l’index a nécessité de retransformer des milliards de lignes. Cela signifiait brider l’ingestion, prendre du retard et forcer les dirigeants à regarder un SLA de « fraîcheur des données » qui devenait soudain réel.
Ils sont revenus au 64 bits, ont ajouté une validation d’entrée et implanté une voie de quarantaine : les enregistrements avec des timestamps hors plage arrivaient toujours, mais étaient marqués, isolés et exclus des requêtes par défaut. La leçon était directe : une optimisation qui change les bornes numériques n’est pas une optimisation ; c’est une refonte.
Mini-récit 3 : La pratique ennuyeuse mais correcte qui a sauvé la situation
Un fournisseur SaaS de taille moyenne exploitait une base multi-tenant avec de lourdes tâches batch. Rien d’excitant — jusqu’à ce qu’une mise à jour d’une librairie en amont change un format de sérialisation. La plupart des équipes l’ont découvert en production avec fracas.
Cette équipe ne l’a pas fait. Ils avaient une pratique douloureusement ennuyeuse : chaque mise à jour de dépendance déclenchait un test de rejouement contre un échantillon de trafic de production capturé, et ils stockaient des artefacts « golden » pour les vérifications de compatibilité. L’environnement de rejouement n’était pas parfait, mais il était fidèle assez pour attraper des divergences de bornes.
Le test a signalé un problème subtil : un champ numérique auparavant sérialisé en entier 64 bits était désormais décodé en 32 bits chez un consommateur en raison d’une ambiguïté de schéma. Sous des plages normales tout fonctionnait. Sous des valeurs rares et élevées, cela overflowait et déclenchait une boucle de retry. Cette boucle de retries serait devenue un thundering herd contre la base de données.
Ils ont corrigé le schéma, figé les versions et livré. Personne en dehors de l’équipe n’a remarqué. C’est le but. Le travail de fiabilité est souvent invisible. Si votre pratique de fiabilité est glamour, vous faites probablement de la réponse aux incidents, pas de l’ingénierie.
Tâches pratiques : commandes, sorties et décisions
La défaillance d’Ariane 5 est un problème de bornes numériques enveloppé dans un problème de validation et de discipline opérationnelle. Voici des tâches concrètes que vous pouvez exécuter sur des systèmes réels pour prévenir cette même classe d’erreur. Chaque tâche inclut : une commande, une sortie d’exemple, ce que cela signifie et la décision à prendre.
1) Trouver les conversions qui réduisent la précision dans les builds C/C++ (avertissements du compilateur)
cr0x@server:~$ make CFLAGS="-O2 -Wall -Wextra -Wconversion -Wsign-conversion" 2>&1 | head -n 8
src/nav.c:214:23: warning: conversion from ‘double’ to ‘int16_t’ may change value [-Wfloat-conversion]
src/nav.c:215:18: warning: conversion to ‘int16_t’ from ‘int’ may change the sign of the result [-Wsign-conversion]
...
Signification de la sortie : Le compilateur vous indique exactement où vous pourriez overflower, tronquer ou inverser le signe.
Décision : Traitez ces avertissements comme des blocages de release pour le code critique de sécurité ou monétaire. Ajoutez des vérifications de bornes explicites ou élargissez les types. Si vous devez réduire, documentez la borne et faites-la respecter.
2) Chasser les casts risqués dans un dépôt (grep rapide)
cr0x@server:~$ rg -n "(\(int16_t\)|\(short\)|\(int\))\s*\(" src include
src/irs/align.c:88:(int16_t)(h_velocity)
src/irs/align.c:131:(short)(bias_estimate)
Signification de la sortie : Les casts explicites sont l’endroit où l’intention et le danger se rencontrent.
Décision : Examinez chaque cast : quelle plage est attendue, que se passe-t-il si elle est dépassée et s’exécute-t-il dans une fenêtre critique.
3) Identifier les exceptions/crashes non gérés via les logs systemd
cr0x@server:~$ journalctl -u navd --since "1 hour ago" | tail -n 12
Jan 22 10:11:04 stage navd[1827]: converting float to int16: value=40211.7
Jan 22 10:11:04 stage navd[1827]: FATAL: SIGABRT after overflow trap
Jan 22 10:11:04 stage systemd[1]: navd.service: Main process exited, code=killed, status=6/ABRT
Jan 22 10:11:04 stage systemd[1]: navd.service: Failed with result 'signal'.
Signification de la sortie : Une conversion numérique a déclenché un trap, mettant le service hors service.
Décision : Décidez si le service doit s’arrêter brutalement ou se dégrader. Pour les boucles de contrôle critiques, concevez un mode sûr ; pour les services sans état, implémentez retries et coupe-circuits.
4) Vérifier les traps noyau/CPU pour les crashes ressemblant à un overflow (core dump activé)
cr0x@server:~$ coredumpctl list navd | tail -n 3
TIME PID UID GID SIG COREFILE EXE
Jan 22 10:11:04 1827 1001 1001 6 present /usr/local/bin/navd
Signification de la sortie : Il y a un core dump que vous pouvez inspecter au lieu de deviner.
Décision : Récupérez le core, identifiez la conversion exacte et la plage d’entrée, et ajoutez un test de régression pour cette frontière.
5) Mesurer si vos services « redondants » échouent ensemble (défaillance corrélée)
cr0x@server:~$ awk '$3=="ERROR" {print $1,$2,$6}' /var/log/irs-a.log | tail -n 5
2026-01-22 10:11:04 overflow value=40211.7
2026-01-22 10:11:04 shutdown reason=exception
cr0x@server:~$ awk '$3=="ERROR" {print $1,$2,$6}' /var/log/irs-b.log | tail -n 5
2026-01-22 10:11:04 overflow value=40212.1
2026-01-22 10:11:04 shutdown reason=exception
Signification de la sortie : Même timestamp, même raison : défaillance en mode commun.
Décision : Introduisez de la diversité : implémentations différentes, validations différentes, seuils différents, ou au moins des comportements décalés (l’un clamp, l’autre alerte).
6) Valider les plages numériques à la frontière (garde d’exécution)
cr0x@server:~$ python3 - <<'PY'
import math
MAX_I16=32767
vals=[120.0, 32766.9, 40000.1]
for v in vals:
ok = -MAX_I16-1 <= v <= MAX_I16
print(f"value={v} fits_int16={ok}")
PY
value=120.0 fits_int16=True
value=32766.9 fits_int16=True
value=40000.1 fits_int16=False
Signification de la sortie : Vous pouvez détecter des conditions d’overflow avant de convertir.
Décision : Faites respecter des gardes aux interfaces : si hors plage, clamp, rejetez ou routez vers un chemin en mode sûr avec alarmes.
7) Confirmer que votre télémétrie n’est pas « des diagnostics interprétés comme vérité » (vérifications de schéma)
cr0x@server:~$ jq -r '.frame_type, .attitude.status, .attitude.roll' telemetry/latest.json
DIAGNOSTIC
FAIL
-1.7976931348623157e+308
Signification de la sortie : Une trame de diagnostic est parsée comme une trame d’attitude, et une valeur sentinelle fuit à travers.
Décision : Rendez les types de message explicites et validés. Refusez de consommer des trames qui ne correspondent pas au schéma et à l’état.
8) Repérer les événements de saturation/clamping (vous voulez les voir)
cr0x@server:~$ grep -R "SATURAT" -n /var/log/navd.log | tail -n 5
41298:WARN SATURATION h_velocity=40211.7 clamped_to=32767
41302:WARN SATURATION h_velocity=39880.2 clamped_to=32767
Signification de la sortie : Le système a rencontré des valeurs hors plage mais est resté vivant.
Décision : Investiguer pourquoi la plage est dépassée. Décidez si le clamp est acceptable ou s’il masque un vrai changement de modélisation/physique.
9) Valider que les jobs « non essentiels » ne tournent pas dans la fenêtre critique
cr0x@server:~$ systemctl list-timers --all | head -n 12
NEXT LEFT LAST PASSED UNIT ACTIVATES
Wed 2026-01-22 10:12:00 UTC 32s Wed 2026-01-22 10:07:00 UTC 4min ago rotate-alignment.timer rotate-alignment.service
Wed 2026-01-22 10:15:00 UTC 3min 32s Wed 2026-01-22 10:00:00 UTC 11min ago logrotate.timer logrotate.service
Signification de la sortie : Des timers se déclenchent pendant votre période critique.
Décision : Désactivez ou replanifiez les tâches non critiques pendant les fenêtres de lancement/pointe. Le travail « d’arrière-plan » n’est arrière-plan que jusqu’à ce qu’il ne le soit plus.
10) Confirmer que la bascule fonctionne réellement sous charge (health et readiness)
cr0x@server:~$ kubectl get pods -n guidance -o wide
NAME READY STATUS RESTARTS AGE IP NODE
irs-a-7c6b9f9c7b-2m8qk 1/1 Running 0 3d 10.42.1.12 node-1
irs-b-7c6b9f9c7b-q9d2p 1/1 Running 0 3d 10.42.2.19 node-2
cr0x@server:~$ kubectl describe svc irs -n guidance | sed -n '1,30p'
Name: irs
Namespace: guidance
Selector: app=irs
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.43.128.10
Endpoints: 10.42.1.12:9000,10.42.2.19:9000
Signification de la sortie : Vous avez deux endpoints, mais cela ne prouve pas qu’ils échouent de manière indépendante.
Décision : Lancez des tests de chaos qui injectent la défaillance spécifique (chemin overflow) et vérifiez que le consommateur rejette les données invalides et continue en sécurité.
11) Détecter les redémarrages corrélés (signe de mode commun)
cr0x@server:~$ kubectl get events -n guidance --sort-by=.lastTimestamp | tail -n 10
10m Warning BackOff pod/irs-a-7c6b9f9c7b-2m8qk Back-off restarting failed container
10m Warning BackOff pod/irs-b-7c6b9f9c7b-q9d2p Back-off restarting failed container
10m Normal Pulled pod/irs-a-7c6b9f9c7b-2m8qk Container image pulled
10m Normal Pulled pod/irs-b-7c6b9f9c7b-q9d2p Container image pulled
Signification de la sortie : Les deux réplicas plantent pour la même raison au même moment.
Décision : Cessez de supposer que des réplicas égalent résilience. Introduisez des versions indépendantes, des feature flags ou des déploiements échelonnés avec canaris.
12) Inspecter les bornes numériques dans les schémas de messages (exemple protobuf)
cr0x@server:~$ rg -n "int32|int64|sint32|sint64|fixed32|fixed64" schemas/attitude.proto | head -n 20
12: int32 roll_millirad = 1;
13: int32 pitch_millirad = 2;
14: int32 yaw_millirad = 3;
18: int32 h_velocity_cm_s = 7;
Signification de la sortie : Vous encodez la vitesse en int32 centimètres/seconde. Bien. Mais vous devez confirmer les valeurs maximales avec une marge.
Décision : Écrivez la valeur physique maximale possible (plus facteur de sécurité), puis confirmez que le type peut la représenter sur tous les modes de mission.
13) Exiger des tests basés sur les propriétés pour les plages numériques (fuzzing des frontières)
cr0x@server:~$ pytest -q tests/test_numeric_bounds.py -k "int16_guard"
1 passed, 0 failed, 0 skipped
Signification de la sortie : Vous avez une couverture automatisée qui essaye des valeurs près et au-delà des frontières.
Décision : Exigez ces tests pour tout code qui convertit des types ou sérialise des mots de télémétrie/contrôle.
14) Vérifier que le comportement en mode sûr est atteignable (feature flag / commutation de mode)
cr0x@server:~$ curl -s localhost:9000/status | jq
{
"mode": "SAFE_DEGRADED",
"reason": "overflow_guard_triggered",
"attitude_valid": true,
"velocity_valid": false
}
Signification de la sortie : Le composant n’a pas crashé ; il est passé en mode dégradé et a clairement marqué ce qui est valide.
Décision : Préférez la dégradation explicite à la défaillance implicite. Faites en sorte que les consommateurs avalent les flags de validité ou refusent les champs invalides.
Seconde blague courte (et dernière) : Si vos vérifications de plage sont « à ajouter plus tard », félicitations — vous venez d’inventer une liste TODO propulsée par une fusée.
Mode opératoire de diagnostic rapide
Quand un système part soudainement dans un comportement absurde — virages brutaux dans les boucles de contrôle, télémétrie incohérente, redémarrages en chaîne — ne commencez pas par réécrire le code. Commencez par trouver le goulot et la frontière. Voici un ordre de triage pratique qui fonctionne pour les fusées et les services web.
1) Confirmez le mode de défaillance : crash, sortie incorrecte ou mauvaise interprétation
- Crash : processus qui quittent, pods qui redémarrent, systemd qui rapporte des signaux. Cherchez des traps, aborts, exceptions.
- Sortie incorrecte : le service reste en ligne mais émet des valeurs impossibles (négatives là où c’est impossible, NaN, floats extrêmes).
- Mauvaise interprétation : le consommateur malparse des trames ou traite des diagnostics comme des données.
2) Vérifiez une défaillance en mode commun à travers les redondances
Si primaire et secours échouent en quelques secondes l’un de l’autre, supposez une cause partagée : binaire partagé, config partagée, dépendance partagée, motif d’entrée partagé. La redondance n’est pas la cause racine ; c’est un indice.
3) Identifiez la frontière qui a changé
Demandez : quelle valeur est devenue plus grande, plus petite ou a changé d’unités ? Nouvelle trajectoire, nouvelle charge, nouveau tiers client, nouveau modèle, nouveau matériel. C’est là où Ariane 5 a vécu : une nouvelle enveloppe de vol alimentait des hypothèses numériques anciennes.
4) Validez le contrat de données aux interfaces
La plupart des catastrophes surviennent entre composants : sérialisation, dérive de schéma, mismatch d’unités, endian, ou troncation silencieuse. Confirmez le type de message et les plages numériques à la frontière.
5) Ensuite seulement, optimisez ou refactorez
L’optimisation tend à réduire la marge de sécurité (types plus petits, moins de vérifications, chemins rapides). Pendant le diagnostic, vous voulez plus d’observabilité et plus de vérifications, pas moins. Corrigez d’abord ; accélérez ensuite ; réduisez le coût en dernier.
Erreurs courantes : symptôme → cause racine → correctif
Symptôme : « Ça marchait en simulation, ça plante en production »
Cause racine : L’enveloppe simulée n’incluait pas les plages pires cas (ou utilisait des valeurs à la Ariane 4).
Correctif : Élargir les vecteurs de test pour inclure des valeurs extrêmes mais physiquement possibles ; ajouter des tests basés sur les propriétés ; exiger des preuves de bornes pour toute conversion de type réductrice.
Symptôme : La primaire et la sauvegarde échouent presque simultanément
Cause racine : Défaillance en mode commun : logiciel/config identiques exposés aux mêmes entrées.
Correctif : Ajouter de la diversité (implémentation ou version différente), échelonner les comportements (l’un clamp, l’autre s’arrête), et valider la bascule sous injection de défaillance spécifique.
Symptôme : L’amont provoque des décisions erratiques en aval après sa défaillance
Cause racine : Données de diagnostic ou invalides interprétées comme valides à cause de contrats faibles.
Correctif : Ajouter des types de trame explicites et des flags de validité ; rejeter les données invalides ; échouer fermé pour les décisions de contrôle ; éviter le parsing « best effort » dans les chemins de sécurité.
Symptôme : Une tâche « non essentielle » déclenche des incidents pendant des fenêtres critiques
Cause racine : Des calculs inutiles tournent encore pendant la phase critique, consommant CPU ou atteignant des chemins de code rares.
Correctif : Désactiver ou protéger les tâches non essentielles après des transitions d’état ; utiliser une planification aware-mode ; prouver que les chemins post-décollage (ou de pointe) sont minimaux.
Symptôme : Un petit changement de code cause une instabilité massive
Cause racine : Vous avez changé la représentation numérique, les unités ou le comportement de saturation ; le système reposait sur des hypothèses non documentées.
Correctif : Traitez les changements de type numérique comme des changements d’interface. Versionnez le contrat. Ajoutez des tests de compatibilité et des replays de trafic.
Symptôme : Le monitoring montre des « moyennes normales » alors que la réalité est cassée
Cause racine : Échec dans la queue (overflow rare) masqué par des moyennes ; absence d’histogrammes/percentiles.
Correctif : Surveillez les distributions, pas seulement les moyennes. Suivez min/max, percentiles et le compte d’événements clampés/invalides.
Listes de contrôle / plan pas à pas
Checklist : Prévenir les défaillances par dépassement numérique dans le code critique de sécurité ou monétaire
- Inventaire des conversions numériques (float→int, int→int plus petit, conversions d’unités). Si vous ne pouvez pas les lister, vous ne pouvez pas les contrôler.
- Définir des plages attendues avec marge pour chaque valeur, y compris les « nouveaux modes de mission » (nouveau véhicule, nouvelle région, nouveau modèle, nouvelle classe de client).
- Rendre les conversions explicites et protégées : vérification de plage, clamp/saturation, et journaliser un événement structuré.
- Standardiser les unités dans les interfaces. Si vous devez convertir, faites-le une fois à la frontière et enregistrez l’unité dans le schéma.
- Décider d’une politique de défaillance : plantage, dégradation, clamp ou rejet. Pour les systèmes de contrôle, favoriser la dégradation sûre avec flags de validité clairs.
- Tester au-delà de l’enveloppe : pas seulement le « max attendu », mais le « max plausible », plus des valeurs adversariales (NaN, infini, négatif, immense).
- Vérifier l’indépendance de la redondance : version différente, flags du compilateur différents, chemin de code différent, ou au moins comportement différent sur overflow.
- Exiger des tests de contrat entre producteur et consommateur, incluant schéma, unités et bornes de valeurs.
- Observer le clamp : tableaux de bord pour « événements de saturation » et « données invalides rejetées ». Ils devraient être proches de zéro et investigués.
- Organiser un game day qui injecte le scénario d’overflow exact et prouve que le système demeure sûr (pas seulement « up »).
Checklist : Réutiliser du code sans importer d’anciennes hypothèses
- Lister chaque module réutilisé et son enveloppe opérationnelle d’origine.
- Pour chaque module, noter ce qui a changé dans le nouveau système (entrées, plages, timing, unités, performance).
- Relancer la vérification contre les nouvelles enveloppes ; n’acceptez pas « déjà qualifié » comme argument.
- Supprimer ou désactiver la logique non nécessaire dans les phases critiques. Moins de code actif signifie moins de surprises.
- Documenter la justification de toute conversion non protégée : pourquoi elle ne peut pas déborder et ce qui garantit cela.
FAQ
1) L’échec d’Ariane 5 était-il « juste un dépassement entier » ?
Non. L’overflow a été le déclencheur. La défaillance était systémique : réutilisation sans revalidation, code non essentiel en vol, gestion d’exception qui a arrêté un composant critique, et consommateurs réinterprétant des sorties invalides.
2) Pourquoi le système de référence inertielle de secours n’a-t-il pas sauvé la fusée ?
Parce qu’il a échoué de la même manière, pour la même raison, au même moment. C’est une défaillance en mode commun. La redondance n’aide que quand les défaillances sont suffisamment indépendantes.
3) Pourquoi une conversion float→int a-t-elle été utilisée ?
Les champs d’entiers de taille fixe sont courants pour les messages, les formats de télémétrie et la performance déterministe. La conversion est normale. L’absence de garde contre les valeurs hors plage est ce qui l’a rendue dangereuse.
4) N’auraient-ils pas pu « attraper l’exception » et continuer ?
Ils auraient pu la gérer, mais « continuer » doit signifier « continuer en sécurité ». Les options incluent : clamp avec flag de validité, passer en mode dégradé, ou désactiver le calcul après le décollage.
5) Pourquoi le guidage a-t-il réagi si violemment après la défaillance de l’IRS ?
Le guidage a besoin d’informations d’attitude et de taux. Lorsqu’il a reçu des données invalides (ou des diagnostics traités comme des données), il a calculé des commandes de contrôle incorrectes. En boucle fermée, de mauvaises entrées peuvent produire des sorties agressives très rapidement.
6) Quelle est la leçon SRE ici ?
Les hypothèses sont des dépendances de production. Si vous réutilisez des composants, vous devez revalider les hypothèses sous de nouveaux schémas de charge. Aussi : surveillez les événements de frontière, pas seulement le temps de disponibilité.
7) « Fail fast » est-il mauvais ?
Fail fast est excellent quand vous pouvez réessayer ou contourner la défaillance. Dans les systèmes où on ne peut pas réessayer (systèmes de contrôle, systèmes de sécurité, actions irréversibles), il faut une dégradation sûre et des règles strictes de validité des données.
8) Comment éviter que « diagnostics soient interprétés comme données » dans les systèmes distribués ?
Utilisez des types de message explicites, validation de schéma, contrats versionnés et consommateurs défensifs qui refusent d’agir sur des trames invalides ou inattendues. Traitez les erreurs de parsing comme des événements de niveau sécurité.
9) Ajouter plus de tests résout-il cette classe de problème ?
Seulement si les tests incluent les bonnes frontières. Dix mille tests happy-path ne rattraperont pas un garde d’overflow manquant. Concentrez-vous sur les tests d’enveloppe, le fuzzing près des limites numériques et les tests de contrat entre interfaces.
10) Quelle est la bonne forme de redondance ?
La redondance avec indépendance : implémentations différentes, versions différentes, compilateurs différents, modalités de capteurs différentes, ou au moins un comportement de défaillance différencié. Si les deux côtés partagent le même bug, vous avez acheté deux places pour le même crash.
Prochaines étapes à réellement entreprendre
La leçon d’Ariane 5 n’est pas « ne faites pas d’erreurs ». C’est « arrêtez de faire confiance à des hypothèses qui ne sont pas appliquées ». Une fusée n’est qu’un déploiement de production très coûteux avec moins d’options de rollback.
- Inventoriez les conversions numériques dans vos systèmes critiques cette semaine. Si vous ne savez pas où elles sont, vous ne pouvez pas les défendre.
- Activez des avertissements stricts du compilateur et faites des conversions réductrices une porte d’entrée en revue.
- Ajoutez des gardes d’exécution aux interfaces : vérifications de plage, clamp avec flags, ou rejet avec alarmes.
- Prouvez l’indépendance de la redondance par injection de défaillance. Si les deux répliques meurent ensemble, appelez ça comme il se doit : un seul système dupliqué.
- Tuez le travail non essentiel dans les fenêtres critiques. Si ce n’est pas nécessaire après le décollage, ne le faites pas tourner après le décollage.
- Surveillez les distributions et les événements de frontière (comptes de saturation, trames invalides rejetées), pas seulement les moyennes et le temps de disponibilité.
Faites cela, et vous n’éviterez pas seulement un moment à la Ariane 5. Vous livrerez aussi plus rapidement, dormirez mieux et passerez moins de matins à expliquer à la direction pourquoi les graphiques semblaient corrects alors que la réalité brûlait.