paint-brush
L'échec est nécessaire, alors acceptez-le : comprendre les stratégies de sécurité et d'échec rapide dans les logicielspar@shai.almog
523 lectures
523 lectures

L'échec est nécessaire, alors acceptez-le : comprendre les stratégies de sécurité et d'échec rapide dans les logiciels

par Shai Almog7m2024/05/07
Read on Terminal Reader

Trop long; Pour lire

Découvrez comment accepter l'échec peut améliorer la qualité de votre application, conduisant à une détection précoce des erreurs, à une gestion robuste des erreurs et à une meilleure stabilité globale.
featured image - L'échec est nécessaire, alors acceptez-le : comprendre les stratégies de sécurité et d'échec rapide dans les logiciels
Shai Almog HackerNoon profile picture
0-item

Les pannes des systèmes logiciels sont inévitables. La manière dont ces pannes sont gérées peut avoir un impact significatif sur les performances, la fiabilité et les résultats de l'entreprise. Dans cet article, je souhaite discuter des avantages de l’échec. Pourquoi devriez-vous rechercher l’échec, pourquoi l’échec est une bonne chose et pourquoi éviter l’échec peut réduire la fiabilité de votre application. Nous commencerons par la discussion entre la sécurité intégrée et la sécurité intégrée ; cela nous amènera à la deuxième discussion sur les échecs en général.

En passant, si vous aimez le contenu de cet article et des autres articles de cette série, consultez mon Livre de débogage , qui couvre son sujet. Si vous avez des amis qui apprennent à coder, j'apprécierais une référence à monLivre sur les bases de Java. Si vous souhaitez revenir à Java après un certain temps, consultez mon Livre Java 8 à 21 .

Échec rapide

Les systèmes Fail-Fast sont conçus pour cesser immédiatement de fonctionner lorsqu’ils rencontrent une condition inattendue. Cet échec immédiat permet de détecter les erreurs plus tôt, rendant le débogage plus simple.


L’approche fail-fast garantit que les erreurs sont détectées immédiatement. Par exemple, dans le monde des langages de programmation, Java incarne cette approche en produisant instantanément une NullPointerException lorsqu'il rencontre une valeur null , arrêtant le système et rendant l'erreur claire. Cette réponse immédiate aide les développeurs à identifier et à résoudre rapidement les problèmes, évitant ainsi qu'ils ne s'aggravent.


En détectant et en stoppant les erreurs plus tôt, les systèmes anti-fail-fast réduisent le risque de pannes en cascade, où une erreur en entraîne d’autres. Cela facilite la maîtrise et la résolution des problèmes avant qu'ils ne se propagent dans le système, préservant ainsi la stabilité globale.


Il est facile d’écrire des tests unitaires et d’intégration pour des systèmes à échec rapide. Cet avantage est encore plus prononcé lorsqu’il s’agit de comprendre l’échec du test. Les systèmes ultra-rapides pointent généralement directement vers le problème dans la trace de la pile d'erreurs.


Cependant, les systèmes infaillibles comportent leurs propres risques, en particulier dans les environnements de production :


  • Perturbations de la production : si un bug atteint la production, il peut provoquer des perturbations immédiates et importantes, affectant potentiellement les performances du système et les opérations de l'entreprise.
  • Appétit pour le risque : les systèmes à défaillance rapide nécessitent un niveau de tolérance au risque de la part des ingénieurs et des dirigeants. Ils doivent être prêts à gérer et à résoudre les défaillances rapidement, en équilibrant souvent cela avec les impacts potentiels sur l'entreprise.

Sécurité intégrée

Les systèmes de sécurité adoptent une approche différente, visant à se rétablir et à continuer même face à des conditions inattendues. Cela les rend particulièrement adaptés aux environnements incertains ou volatils.

Les microservices sont un excellent exemple de systèmes à sécurité intégrée, intégrant la résilience à travers leur architecture. Les disjoncteurs, à la fois physiques et logiciels, déconnectent les fonctionnalités défaillantes pour éviter les pannes en cascade, aidant ainsi le système à continuer de fonctionner.


Les systèmes de sécurité garantissent que les systèmes peuvent survivre même dans des environnements de production difficiles, réduisant ainsi le risque de panne catastrophique. Cela les rend particulièrement adaptés aux applications critiques, telles que les dispositifs matériels ou les systèmes aérospatiaux, où une récupération fluide après les erreurs est cruciale.


Cependant, les systèmes de sécurité présentent des inconvénients :


  • Erreurs cachées : en tentant de récupérer des erreurs, les systèmes de sécurité peuvent retarder la détection des problèmes, les rendant plus difficiles à tracer et potentiellement conduire à des pannes en cascade plus graves.
  • Défis de débogage : cette nature retardée des erreurs peut compliquer le débogage, nécessitant plus de temps et d'efforts pour rechercher et résoudre les problèmes.

Choisir entre Fail-Fast et Fail-Safe

Il est difficile de déterminer quelle approche est la meilleure, car les deux ont leurs avantages. Les systèmes Fail-Fast offrent un débogage immédiat, un risque moindre de pannes en cascade et une détection et une résolution plus rapides des bogues. Cela permet de détecter et de résoudre les problèmes rapidement, empêchant ainsi leur propagation.

Les systèmes à sécurité intégrée gèrent les erreurs avec élégance, ce qui les rend mieux adaptés aux systèmes critiques et aux environnements volatils, où des pannes catastrophiques peuvent être dévastatrices.

Équilibrer les deux

Pour tirer parti des atouts de chaque approche, une stratégie équilibrée peut s’avérer efficace :


  • Fail-Fast pour les services locaux : lors de l'appel de services locaux tels que des bases de données, Fail-Fast peut détecter les erreurs plus tôt, évitant ainsi les pannes en cascade.
  • Sécurité intégrée pour les ressources distantes : lorsque vous utilisez des ressources distantes, telles que des services Web externes, la sécurité intégrée peut empêcher les interruptions dues à des pannes externes.

Une approche équilibrée nécessite également une mise en œuvre claire et cohérente tout au long des processus de codage, de révision, d’outillage et de test, garantissant une intégration transparente. Fail-fast peut bien s'intégrer à l'orchestration et à l'observabilité. En effet, cela déplace l'aspect de sécurité vers une couche différente d'OPS plutôt que vers la couche développeur.

Comportement cohérent des couches

C'est là que les choses deviennent intéressantes. Il ne s’agit pas de choisir entre une sécurité intégrée et une sécurité rapide. Il s'agit de choisir la couche qui leur convient. Par exemple, si une erreur est gérée dans une couche profonde en utilisant une approche de sécurité, elle ne sera pas remarquée. Cela peut être OK, mais si cette erreur a un impact négatif (performances, données inutiles, corruption, sécurité, etc.), alors nous aurons un problème plus tard et n'en aurons aucune idée.

La bonne solution consiste à gérer toutes les erreurs dans une seule couche. Dans les systèmes modernes, la couche supérieure est la couche OPS et elle est la plus logique. Il peut signaler l'erreur aux ingénieurs les plus qualifiés pour traiter l'erreur. Mais ils peuvent également fournir une atténuation immédiate, comme le redémarrage d'un service, l'allocation de ressources supplémentaires ou le rétablissement d'une version.

Les nouvelles tentatives ne sont pas sécurisées

Récemment, j'ai assisté à une conférence où les intervenants ont énuméré leur architecture cloud mise à jour. Ils ont choisi de prendre un raccourci vers les microservices en utilisant un framework qui leur permet de réessayer en cas d'échec. Malheureusement, l’échec ne se comporte pas comme nous le souhaiterions. Vous ne pouvez pas l’éliminer complètement par les seuls tests. Réessayer n’est pas une sécurité. En fait, cela peut signifier une catastrophe.


Ils ont testé leur système, et "ça marche", même en production. Mais supposons qu’une situation catastrophique se produise, leur mécanisme de nouvelle tentative peut fonctionner comme une attaque par déni de service contre leurs propres serveurs. Le nombre de façons dont des architectures ad hoc comme celle-ci peuvent échouer est ahurissant.


Ceci est particulièrement important une fois que nous redéfinissons les échecs.

Redéfinir l'échec

Les pannes des systèmes logiciels ne sont pas seulement dues à des pannes. Un crash peut être considéré comme une panne simple et immédiate, mais il existe des problèmes plus complexes à considérer. En fait, les accidents à l’ère des conteneurs sont probablement les meilleurs échecs. Un système redémarre de manière transparente avec à peine une interruption.

Corruption de données

La corruption des données est bien plus grave et insidieuse qu’un crash. Cela entraîne des conséquences à long terme. Les données corrompues peuvent entraîner des problèmes de sécurité et de fiabilité difficiles à résoudre, nécessitant une refonte approfondie et des données potentiellement irrécupérables.


Le cloud computing a donné naissance à des techniques de programmation défensives, telles que des disjoncteurs et des tentatives, mettant l'accent sur des tests et une journalisation complets pour détecter et gérer les pannes avec élégance. D'une certaine manière, cet environnement nous a renvoyé en termes de qualité.


Un système infaillible au niveau des données pourrait empêcher que cela se produise. La résolution d’un bug va au-delà d’une simple correction. Cela nécessite de comprendre sa cause profonde et d’éviter sa réapparition, en passant par une journalisation complète, des tests et des améliorations de processus. Cela garantit que le bug est entièrement résolu, réduisant ainsi les risques qu'il se reproduise.

Ne corrigez pas le bug

S'il s'agit d'un bug de production, vous devriez probablement revenir en arrière si vous ne pouvez pas annuler instantanément la production. Cela devrait toujours être possible, et si ce n’est pas le cas, c’est quelque chose sur lequel vous devriez travailler.


Les échecs doivent être parfaitement compris avant qu’un correctif ne soit entrepris. Dans mes propres entreprises, j'ai souvent sauté cette étape à cause de la pression, dans une petite startup, c'est pardonnable. Dans les grandes entreprises, nous devons comprendre la cause profonde. Une culture de débriefing des bugs et des problèmes de production est essentielle. Le correctif doit également inclure une atténuation des processus qui empêche des problèmes similaires d’atteindre la production.

Échec du débogage

Les systèmes à échec rapide sont beaucoup plus faciles à déboguer. Leur architecture est intrinsèquement plus simple et il est plus facile d’identifier un problème dans un domaine spécifique. Il est crucial de lever des exceptions même pour des violations mineures (par exemple, des validations). Cela évite les types de bogues en cascade qui prédominent dans les systèmes lâches.

Cela devrait être renforcé par des tests unitaires qui vérifient les limites que nous définissons et vérifient que les exceptions appropriées sont levées. Les nouvelles tentatives doivent être évitées dans le code car elles rendent le débogage exceptionnellement difficile et leur place appropriée est dans la couche OPS. Pour faciliter davantage cela, les délais d'attente doivent être courts par défaut.

Éviter les pannes en cascade

L’échec n’est pas quelque chose que nous pouvons éviter, prédire ou contre lequel nous pouvons pleinement tester. La seule chose que nous pouvons faire est d’atténuer le coup en cas de panne. Souvent, cet « adoucissement » est obtenu en utilisant des tests de longue durée destinés à reproduire autant que possible des conditions extrêmes dans le but de trouver les points faibles de nos applications. C'est rarement suffisant. Les systèmes robustes doivent souvent réviser ces tests en fonction de véritables échecs de production.

Un bon exemple de sécurité serait un cache de réponses REST qui nous permet de continuer à travailler même lorsqu'un service est en panne. Malheureusement, cela peut conduire à des problèmes de niche complexes tels qu'un empoisonnement du cache ou une situation dans laquelle un utilisateur banni avait toujours accès en raison du cache.

Hybride en production

La sécurité intégrée est mieux appliquée uniquement en production/mise en scène et dans la couche OPS. Cela réduit le nombre de changements entre la production et le développement, nous voulons qu'ils soient aussi similaires que possible, mais cela reste un changement qui peut avoir un impact négatif sur la production. Cependant, les avantages sont énormes, car l’observabilité permet d’obtenir une image claire des pannes du système.


La discussion ici est un peu colorée par mon expérience plus récente de création d'architectures cloud observables. Cependant, le même principe s’applique à tout type de logiciel, qu’il soit embarqué ou dans le cloud. Dans de tels cas, nous choisissons souvent d'implémenter la sécurité intégrée dans le code. Dans ce cas, je suggérerais de l'implémenter de manière cohérente et consciente dans une couche spécifique.


Il existe également un cas particulier de bibliothèques/frameworks qui fournissent souvent des comportements incohérents et mal documentés dans ces situations. Je suis moi-même coupable d'une telle incohérence dans certains de mes travaux. C'est une erreur facile à commettre.

Dernier mot

Ceci est mon dernier article sur la théorie du débogage qui fait partie de mon livre/cours sur le débogage. Nous pensons souvent au débogage comme à l’action que nous entreprenons en cas de problème. Ce n'est pas le cas. Le débogage commence au moment où nous écrivons la première ligne de code. Nous prenons des décisions qui auront un impact sur le processus de débogage au fur et à mesure que nous codons, souvent nous ignorons tout simplement ces décisions jusqu'à ce que nous obtenions un échec.


J'espère que cet article et cette série vous aideront à écrire du code préparé pour l'inconnu. Le débogage, de par sa nature, traite de l’inattendu. Les tests ne peuvent pas aider. Mais comme je l’ai illustré dans mes articles précédents, il existe de nombreuses pratiques simples que nous pouvons adopter pour faciliter la préparation. Il ne s’agit pas d’un processus ponctuel, c’est un processus itératif qui nécessite une réévaluation des décisions prises lorsque nous rencontrons un échec.