La résilience est un concept qui sonne résolument Dev, Ops ou parfois Archi. Et en pratique, on n'y pense bien souvent que lors de la réalisation. Et uniquement à travers le prisme de la technique.
C’est au moment de chiffrer une US (ou pire encore, d’implémenter !) qu’on se souvient (parfois dans l’urgence) qu’il va falloir “gérer les erreurs”.
Alors on ouvre son IDE, et sans sortir de notre zone de confort, on fait ce qu'on sait faire de mieux : un petit retry, un cache, une mécanique de NACK, une DLQ… Et une fois qu'on a remis ces patterns à toutes les sauces sur le tapis, on se dit... "on n'est pas mal là - au moins on est résilients !".
Subtle foreshadowing : lors du prochain passage en production, nos développeurs allaient s'apercevoir que leur solution n'était, en fait, pas si résiliente que ça.
Mais ce raisonnement, qui semble logique sur le moment, aboutit trop souvent à des comportements inattendus (voire dangereux) en production. On vous propose un changement de perspective : concevoir la résilience avant tout comme un choix métier, à prendre conjointement avec votre PO favori.
1. Un voyage inattendu 🧙♂️
Commençons notre aventure en partant de situations purement fictives, rencontrées par nos développeurs au cours de leur implémentation. Toute ressemblance avec un événement, un personnage, un groupe ou une entreprise, passés ou présents, serait bien entendu fortuite et TOTALEMENT involontaire.
- “Hmmm ce service peut nous renvoyer une 404, 409 ou 500... va falloir mettre en place du retry - et même combat pour les timeouts”
- “Ah ! si la transaction échoue, faut qu'on s'assure de ne pas ACK la consommation de l'évènement, pour que ça puisse être retenté... allez, disons 3 fois ? et si c'est toujours pas bon, ça part en DLQ”
- “Si jamais le producteur arrête d'émettre de la donnée fraîche, on peut au moins se protéger avec un p'tit cache, comme ça on peut continuer à répondre aux requêtes”
Ces décisions partent d'une bonne intention, noble et simple :
Conserver le système / la plateforme en fonctionnement, et garantir sa disponibilité.
Grâce au jeu des politiques d'entreprise, des responsabilités floues, des équipes mal organisées, mais aussi des égos surdimensionnés, il arrive qu'une pensée plus "individualiste" se cache derrière ces réflexions :
Empêcherle systèmemon petit scope technique à moi de flancher, quitte à ce que le reste du monde s'écroule
Inutile de dire que le reste des parties prenantes n'a pas voix au chapitre :
- "le métier ? non mais je vais pas leur parler de DLQ, ils vont rien comprendre"
- "les archis ? génial, la feature va prendre 2 sprints de retard - et encore, s'ils acceptent la solution"
- "le produit ? ça va être trop galère, ils vont devoir créer une story technique, mais elle sera jamais priorisée"
- "les autres équipes en adhérence ? bah ils nous préviennent jamais quand ils ont des indisponibilités, nous au moins on sera robustes"
Bilan positif cependant, en évitant d'impliquer ces empêcheurs d'avancer, les développements n'ont pris aucun retard, le sacro-saint planning est respecté. En somme, tous les indicateurs sont au vert.
2. Quand on arrive en vrille... 🎶
Soulagement ! Les tests des QAs sont passés (avec brio qui plus est), let's go to prod ! La livraison est un succès total, la démo une ovation, la feature un carton plein et l'adoption par les utilisateurs finaux grimpe en flèche : bref, le rêve.
Avance rapide de quelques semaines/mois : les utilisateurs utilisent BEAUCOUP (trop ?) la feature, et les soucis commencent à tous émerger en même temps (sinon ce n'est pas drôle) :
- Les retries génèrent des doublons
- Un service amont (auquel on envoie des commandes synchrones), devient lent ou instable, et ne répond plus dans les délais impartis.
- Heureusement, la mécanique de retry est en place.
- Malheureusement, les tentatives successives n'ont pas été traitées de manière idempotente.
- Résultat : Une commande passée deux fois, un paiement doublé, un mail envoyé en trop. Chaque doublon est un bug… causé par un mécanisme censé augmenter la robustesse.
- L'astreinte sonne à tout va
- Un utilisateur se trompe dans un copier-coller, et essaie d'accéder à une ressource qui n'existe pas.
- L'erreur 404 est inattendue, mais le code est robuste !
- Elle se fait attraper au vol, plaquer au sol, étouffer... mais tout est tracé.
- L'outil de supervision est efficace, et remplit son rôle à la perfection : une alerte est levée, l'astreinte est déclenchée.
- Résultat : Les développeurs sont réveillés au milieu de leur premier cycle de sommeil. Ils sont heureux, et effectuent l'analyse avec enthousiasme. Dommage, ce cas d'erreur ne mettait pas en danger le système, n'impactait pas la disponibilité de la plateforme, ne nuisait pas à l'expérience utilisateur, mais il n'avait pas été identifié comme bénin.
- Des données sensibles sont erronées
- Un service amont (dont on consomme les données) renvoie des données inexploitables suite à une montée de version mal planifiée.
- Une US pour pallier au changement non rétrocompatible est ajoutée au backlog fissa, puis elle est subrepticement introduite dans le sprint actif
- Mais pas de pression, les développeurs héros avaient pensé à tout, et le cache en place continue à servir fièrement la dernière donnée connue.
- Malencontreusement, la donnée en question est suivie en temps réel par les utilisateurs - il s'agit d'un moyen de contrôle pour eux, et quelque chose cloche : ils se mettent à douter, la donnée qu'ils considèrent fiable les fait questionner leur utilisation du système.
- Résultat : L'inquiétude des utilisateurs laisse place à la colère puisque la valeur affichée "N'EST PAS COHÉRENTE DU TOUUUUUT !!!" Et puis, c'est le "Pouf !" assourdissant de la confiance accordée par l'utilisateur à la plateforme, qui disparaît.
- Les compensations automatisées génèrent de nouveaux bugs
- Un des systèmes connexes échoue dans le traitement d'une commande qui lui a été envoyée.
- Pas de panique, les développeurs avaient prévu le coup, et ont mis en place une mécanique compensatoire dédiée, déclenchant une nouvelle commande diamétralement opposée, pour garantir un retour à l'équilibre.
- Fâcheusement, le système connexe n'a pas su traiter la commande initiale de manière transactionnelle / atomique, et la mise à jour a été propagée, mais pas partout.
- Résultat : L'état global de la donnée dans le système est encore plus incohérent que suite à l'échec initial. La compensation a engendré l'inverse d'une chose qui n'a pas eu lieu, ou qui n'est pas connue de tous les acteurs. Les contraintes d'intégrité du domaine ne sont plus respectées, et les données deviennent inexploitables.
Pas si robuste que ça, cette plateforme ! Tout cela nous mène à un constat sans appel :
Un mécanisme de résilience dont le comportement n'a pas été spécifiépeutva, en situation de crise, conduire au sur-accident.
3. Au secours ! 😱
On pense souvent que la résilience consiste à "faire en sorte que ça marche toujours”. En réalité, il faut essayer de répondre à la question suivante :
Comment le produit doit-il se comporter vis-à-vis de l’utilisateur, du métier ou des tiers lorsqu'une anomalie survient ?
Vous l'aurez deviné, si la question commence par "Comment le produit...", il est probablement temps de faire appel à un ami : les interlocuteurs produit, qui ont très certainement leur mot à dire, et vous remercieront de penser à eux ne pas attendre l'anomalie en production pour leur demander de définir la cible.
Pour tenter de répondre tous ensemble à cette question, il est tout d'abord nécessaire d'identifier les cas fonctionnels qui ne font pas partie du "happy path". Cependant, les ateliers de cadrage produit sont bien souvent timeboxés et doivent se concentrer sur la production de valeur la plus importante, et ne s'intéressent donc qu'aux cas prépondérants.
C'est le cas notamment des types d'ateliers suivants :
- Journey Mapping
- Story Mapping
- Event Storming
- User Flow
- Example Mapping
Il est possible d'adapter ces ateliers pour réussir à identifier les failure paths, particulièrement le story mapping, où on peut assez facilement chercher les points de friction, les étapes susceptibles d’échouer et les transitions fragiles.

Mais il est également possible de faire cela à l'aide de types d'atelier dédiés, conçus spécifiquement pour adresser ces dark paths :
- Pre Mortem : l'idée est de se placer au niveau du produit dans sa globalité, et d'itérer sur tous les facteurs qui pourraient conduire à son échec. Cela permet (entre autres) de faire émerger des étapes critiques du parcours utilisateur, sur lesquelles il est important de gérer tous les cas de figure (y compris les erreurs, qu'elles soient métier ou techniques)
- Risk Storming : là, il s'agit de produire la matrice de risques du produit, et de les qualifier pleinement (probabilité, impact), de manière à faire ressortir les failure paths les plus impactants, dont l'absence de gestion nuirait gravement au produit.
- Evil User/Abuser Stories : enfin, cette approche consiste à rédiger des user stories comme on sait si bien le faire, avec un léger twist : la persona étudiée est celle d'un utilisateur retors qui cherche à nuire au produit(par exemple, cherche à exploiter une faille de sécurité dans la plateforme, ou à obtenir un avantage injuste dans un système concurrentiel)
Il existe probablement beaucoup d'autres façons d'aborder cela de manière ludique tout en suscitant l'idéation, mais le but du jeu reste le même : obtenir une liste finie et priorisée de cas d'usage "non nominaux", qui doivent ensuite être couverts par la conception fonctionnelle et technique du produit.
4. C'est inconcevable ! 😤
Eh bien non ! Dans notre démarche de conception, on peut s'appuyer sur les axes de réflexion suivants pour chacun des cas métier "non nominaux" - cela permettra de se poser les bonnes questions, et d'avoir des éléments pertinents pour effectuer les prises de décision qui vont impacter notre architecture et nos développements.
En cas d'échec...
- Peut-on continuer à fonctionner ?
- Doit-on stopper net pour éviter un effet domino ?
- Est-ce qu’un fallback est acceptable ?
- Ou peut-on fonctionner en mode dégradé ?
- Quelles sont les limites/conditions acceptables d'une expérience utilisateur diminuée ?
- Doit-on compenser l’erreur ?
- Est-ce requis pour les utilisateurs ?
- Est-ce conforme réglementairement ?
- Est-ce que l'état d'erreur peut être modélisé en tant que tel dans le domaine ?
- A-t-on toutes les informations à disposition pour pouvoir compenser l'erreur, ou a-t-on besoin d'inputs supplémentaires que seuls l'utilisateur détient ?
- L'automatisation en vaut-elle la chandelle ?
- Sous quel délai le service doit-il être rétabli ?
- À quel horizon la donnée doit-elle être à nouveau cohérente ?
- À quelle fréquence l'anomalie est-elle rencontrée ?
- La correction manuelle est-elle source d'erreurs humaines supplémentaires ?
- Est-il possible de garantir le caractère transactionnel/atomique de la mécanique compensatoire automatisée étudiée ?
- Existe-t-il d'autres équipes & processus au sein du SI qui savent adresser cette problématique/ce type de problématique ?
Un indice chez vous : à moins que vos services n'évoluent en autonomie, sans aucune dépendance à qui ou quoi que ce soit, vous n'avez probablement, au sein de votre équipe, pas tous les éléments pour pouvoir répondre à ces questions :
- lancez-vous, faites appel aux autres équipes pour clarifier et contractualiser le comportement attendu au niveau des interfaces que vous partagez
- n'hésitez pas à impliquer votre architecte, il a probablement en visibilité d'autres exigences (dans son registre des non-functional requirements) qu'il est bon de prendre en compte - il pourra également vous aider à mettre par écrit/formaliser les réflexions et les décisions qui en découlent
Cela devrait déboucher sur la production d'un livrable (quelle que soit sa forme), qui récapitule les notions suivantes : Cas d'usage -> Scénario d'échec -> Priorisation associée -> Exigences et contraintes métier à satisfaire -> Solution fonctionnelle à implémenter -> Cas de test respectifs.
Ouf ! On retombe en terrain connu. À partir de ce point, les développeurs devraient être à même d'implémenter correctement la feature, qui ne peut être complète que :
- en réalisant la story nominale
- (si et seulement si la solution fonctionnelle retenue implique une forme d'automatisation) en mettant en place des éléments de solution technique pour répondre aux scénarios d'échec identifiés dans le backlog produit
Les maux de la fin 🥱
En prenant en compte ces "failure paths" au plus tôt lors du cadrage produit, on ne se prémunit pas des incidents de production. On s'assure seulement de ne plus être surpris lorsqu'ils adviennent, et d'avoir une réponse appropriée au contexte, prête à l'emploi.
Cela permet de rassurer les équipes - on évite la panique et les actions malencontreuses effectuées sous le coup de l'émotion (les décisions sont déjà prises en amont).
En ayant choisi les meilleurs candidats à l'automatisation, on évite aussi de perdre un temps précieux en situation de crise : les actions manuelles sont limitées au strict nécessaire (moins d'erreurs humaines), les mécaniques compensatoires sont maîtrisées et laissent le système dans un état stable et cohérent.
Attention donc, si votre backlog est constitué intégralement et uniquement de user stories tirées de happy paths, une prise de recul s'impose.

Nous n'avons fait que survoler l'une des facettes d'un sujet profond et complexe - celui de la conception produit des éléments qui n'apportent pas de valeur métier directe (tout du moins, pas facilement identifiable ou quantifiable). En revanche, pas d'inquiétude, la démarche et les réflexions sont applicables à d'autres types de problématiques rarement matérialisées/priorisées, par exemple un scénario d'exercice du droit à l'oubli (tiré du RGPD) à la demande d'un utilisateur.
En conclusion : en responsabilisant le produit quant au comportement souhaité pour la solution en cas d’échec, vous construirez des systèmes résilients par design, pas par accident.