Eine kurze Einführung in Resilient Software Design

Resilient Software Design: Damit es auch morgen noch läuft

Uwe Friedrichsen

© Shutterstock/Ho Yeow Hui

In letzter Zeit hört man immer häufig den Begriff „Resilience“ und manchmal auch etwas vollständiger „Resilient Software Design“. Irgendwie scheint es etwas mit dem Umgang mit Fehlern zur Laufzeit zusammenzuhängen. Aber was daran ist so neu und anders, dass man dafür einen neuen Begriff prägen muss? Oder hat man wieder nur alten Wein in neue Schläuche gefüllt? Zeit für eine kurze Einführung: Worum geht es? Was ist anders? Und wie fühlt es sich an?

Wo fängt man am besten an, wenn man eine kurze Einführung in Resilient Software Design schreiben will? Wahrscheinlich am besten beim Wert von Software: Der primäre Zweck aller Geschäftsprozesse und der sie implementierenden und unterstützenden IT-Systeme ist es, Geld zu erwirtschaften bzw. Kundenbedürfnisse zu befriedigen – am besten beides. Das funktioniert aber nur, solange die IT-Systeme zuverlässig und – von außen betrachtet – fehlerfrei laufen.

Sind die Systeme nicht verfügbar oder fehlerhaft, sind die Kunden unzufrieden, und man verdient kein Geld mit ihnen – kurzum: Sie sind wertlos. Nur zuverlässig laufende Systeme haben Wert. Die Verfügbarkeit von Systemen in Produktion ist also essenziell für den Wert der Software. (Bei manchen Systemen wie z. B. eingebetteten Systemen geht es häufig sogar um viel mehr als nur einen monetären Wert, aber das wollen wir an dieser Stelle nicht näher betrachten.)

Verfügbarkeit und Fehlertypen

Was aber ist Verfügbarkeit genau? Die Verfügbarkeit A (für Availability, den englischen Begriff für Verfügbarkeit) ist definiert als [1] A := MTTF / (MTTF + MTTR). Darin bedeuten:

  • MTTF (Mean Time To Failure): die durchschnittliche Zeit vom Beginn des ordnungsgemäßen Betriebs eines Systems bis zum Auftreten eines Fehlers
  • MTTR (Mean Time To Recovery): die durchschnittliche Zeit vom Auftreten eines Fehlers bis zur Wiederherstellung des ordnungsgemäßen Betriebs des Systems

Während der Nenner also die gesamte Zeit beschreibt, beschreibt der Zähler den Teil der Zeit, in dem das System ordnungsgemäß funktioniert. Damit kann die Verfügbarkeit Werte zwischen 0 für „gar nicht verfügbar“ und 1 für „immer verfügbar“ annehmen. Wenn man den Wert mit 100 multipliziert, erhält man die vertrautere Darstellung als Prozentwert.

Bevor wir uns der Frage zuwenden, wie man die Verfügbarkeit maximieren kann, ist es sinnvoll, einen kurzen Blick auf die möglichen Arten von Fehlern zu werfen, die die Verfügbarkeit kompromittieren können. Die eingängige Literatur (siehe z. B. [2]) unterscheidet fünf Fehlertypen:

 

  1. Crash Failure (Absturzfehler): Ein System antwortet permanent nicht mehr, hat bis zum Zeitpunkt des Ausfalls aber korrekt gearbeitet.
  2. Omission Failure (Auslassungsfehler): Ein System reagiert auf (einzelne) Anfragen nicht, sei es, dass es die Anfragen nicht erhält oder keine Antwort sendet.
  3. Timing Failure (Antwortzeitfehler): Die Antwortzeit eines Systems liegt außerhalb eines festgelegten Zeitintervalls.
  4. Response Failure (Antwortfehler): Die Antwort, die ein System gibt, ist falsch.
  5. Byzantine Failure (Byzantinischer/zufälliger Fehler): Ein System gibt zu zufälligen Zeiten zufällige Antworten („es läuft Amok“).

 

Wenn man von Fehlern im Kontext von Verfügbarkeit spricht, denken die meisten Personen nur an Absturzfehler. Es ist aber wichtig zu berücksichtigen, dass alle Fehlerklassen in die Verfügbarkeit einfließen. Es geht also nicht nur darum, die relativ einfach zu handhabenden Absturzfehler zu behandeln, sondern alle Fehlertypen, also z. B. auch zu langsame oder falsche Antworten, zu erkennen und damit umzugehen.

Traditionelle Stabilitätsansätze

Aber wie maximiert man jetzt die Verfügbarkeit? Schaut man sich die zuvor beschriebene Formel für Verfügbarkeit an, gibt es dafür zwei Möglichkeiten: Entweder man versucht, den Wert für MTTF zu maximieren; oder man versucht, den Wert für MTTR zu minimieren. In beiden Fällen entwickelt sich der Wert von A, d. h., die Verfügbarkeit gegen 1, was der gewünschte Effekt ist.

Der traditionelle Ansatz ist, das Eintreten eines Fehlers möglichst lange hinauszuzögern, d. h., den Wert für MTTF so groß zu machen, dass der Wert für MTTR unbedeutend wird (Abb. 1). Bei diesen traditionellen Stabilitätsansätzen treibt man dafür meist einen großen Aufwand auf Infrastrukturebene: Es wird redundante Hardware eingesetzt, man betreibt HA-Cluster (High Availability), es werden mehrfache Netzwerkverbindungen (hoffentlich über verschiedene Switches) verwendet usw.

Abb. 1: Der traditionelle Stabilitätsansatz

Abb. 1: Der traditionelle Stabilitätsansatz

Dazu kommen aufwändige Verfahren zur Vermeidung von Fehlern auf der Softwareebene. Kurzum: Man versucht, die möglichen Fehlerquellen zu antizipieren und ergreift im Vorfeld Maßnahmen, um die Wahrscheinlichkeit des Eintretens solcher Fehler zu minimieren.

Das ist ein durchaus valider Ansatz, solange das betrachtete System relativ isoliert läuft und es nur wenige Faktoren gibt, die die Verfügbarkeit des Systems beeinflussen. Dieser Ansatz spiegelt sich auch in den bekannten Softwarequalitätsstandards wider. So findet sich z. B. im Softwarequalitätsstandard ISO/IEC 25010:2011(en) unter Reliability (Zuverlässigkeit) der Elterncharakteristik zu Availability (Verfügbarkeit) die folgende Definition: „Reliability: degree to which a system, product or component performs specified functions under specified conditions for a specified period of time.“

Wie man in der zweiten Hälfte der Definition lesen kann, ist die zugrunde liegende Annahme, dass es möglich ist, die Rahmenbedingungen für den Betrieb eines Systems deterministisch festzulegen.

Das Ende des Determinismus

Wo ist das Problem bei dieser Annahme und den darauf basierenden Stabilitätsansätzen? Das Problem liegt darin, dass heute so gut wie jedes System (in einem Unternehmen) ein verteiltes System ist. Selbst eine einfache Webanwendung besteht in der Regel aus einem Webserver, einem Application Server und einer Datenbank (Firewalls, Reverse Proxies, Load Balancer, eventuelle Cacheserver, Router und Switches nicht mitgezählt).

Und schaut man sich z. B. ein CRM-System bei einem größeren Unternehmen an, dann ist dieses System in der Regel mit mehreren Dutzend anderen Systemen verbunden – vielfach online. Wir haben es heute also mit hochgradig komplexen, verteilten Systemlandschaften zu tun.

Zu verteilten Systemen hat Leslie Lamport, einer der führenden Köpfe auf diesem Gebiet, einmal etwas salopp, aber treffend gesagt: „A distributed system is one in which the failure of a computer you didn’t even know existed can render your own computer unusable.“

Was er damit ausdrücken will, ist, dass es so viele potenzielle Fehlerquellen in verteilten Systemen gibt, die das korrekte Funktionieren eines Systems kompromittieren können, dass es unmöglich ist, diese alle zu antizipieren und zu vermeiden. Anders formuliert: Fehler sind in verteilten Systemen der Normalfall, nicht die Ausnahme, und es ist nicht möglich, sie vorherzusagen.

Und es wird noch „schlimmer“: Entwicklungen wie Cloud Computing, immer höhere Verfügbarkeitsanforderungen, Mobile und Internet of Things sowie Social Media sorgen für immer komplexere, immer höher vernetzte Systemlandschaften mit immer mehr beteiligten Systemen und immer weniger vorhersehbaren Zugriffs- und Lastmustern.

Der angenommene Determinismus der Stabilitätsansätze lässt sich also nicht mehr aufrechterhalten. Es gibt keine deterministisch definierbaren Rahmenbedingungen in heutigen Systemlandschaften mehr, wie sie in den Softwarequalitätsstandards gefordert werden. Wir haben es mit – teilweise unbekannten – Wahrscheinlichkeiten zu tun. Der Determinismus ist einem Probabilismus gewichen.

Der Resilience-Ansatz

Das führt zum Mantra von Resilient Software Design: Versuche nicht, Fehler zu vermeiden. Akzeptiere, dass sie geschehen und gehe damit um (im Englischen etwas kompakter: Do not try to avoid failures. Embrace them.).

Folgerichtig versucht Resilient Software Design nicht, MTTF zu maximieren, da die Grundannahme ist, dass Fehler im Allgemeinen weder vermeidbar noch vorhersehbar sind, sondern einfach passieren – sprich: MTTF wird als nicht beeinflussbar hingenommen.

Um die Verfügbarkeit trotzdem zu maximieren, muss also versucht werden, MTTR zu minimieren, d. h. die Zeit vom Auftreten eines Fehlers bis zu seiner Behebung (oder akzeptablen Eindämmung) zu minimieren (Abb. 2).

Abb. 2: Der Resilience-Ansatz

Abb. 2: Der Resilience-Ansatz

Damit kann man Resilience folgendermaßen definieren: Resilience: Die Fähigkeit eines Systems, mit unerwarteten (Fehler-)Situationen umzugehen, ohne dass der Anwender davon etwas mitbekommt (bester Fall) oder mit einer definierten Herabsetzung der Servicequalität („graceful degradation of service“ im Englischen, schlechtester Fall).

So weit, so gut. Aber wie designt man eine Anwendung denn konkret, dass sie „resilient“ ist? Das ist ein weites Gebiet, das den Rahmen dieses Artikels bei Weitem sprengen würde. Deshalb möchte ich an dieser Stelle nur einige wenige Resilience-Prinzipien (Abb. 3) kurz vorstellen und beschreiben, welche Fragen man sich dafür beim Design stellen muss bzw. welche Aspekte man berücksichtigen sollte. Da es sich um Prinzipien handelt, mit denen man üblicherweise seine Überlegungen zu Resilient Software Design beginnt, hat auch dieser kleine Einblick durchaus einen praktischen Wert.

Codebeispiele habe ich – wenngleich eher ungewöhnlich für dieses Magazin – aus zwei Gründen weggelassen: Zum einen ist bei Resilience das Erarbeiten eines angemessenen Designs die eigentliche Herausforderung. Zum anderen ist es sehr schwer, Codebeispiele zu zeigen, die nicht nur einen kleinen Spezialfall abdecken. So müsste man für jedes der folgenden Prinzipien eigentlich Unmengen an Codebeispielen zeigen, was dann wieder den Rahmen des Artikels sprengen würde. Also habe ich mich dazu entschlossen, dann lieber gar keine Codebeispiele zu machen und etwas genauer auf die Fragen einzugehen, die man sich beim Design stellen sollte.

Abb. 3: Elementare Grundprinzipien im Resilient Software Design

Abb. 3: Elementare Grundprinzipien im Resilient Software Design

Isolation

Das erste Grundprinzip von Resilience ist Isolation. Auf diesem Prinzip bauen fast alle anderen Resilience-Muster auf. Die Idee hinter Isolation ist, dass ein System niemals als Ganzes kaputtgehen darf. Um das zu vermeiden, teilt man das System in möglichst unabhängige Einheiten auf und isoliert diese gegeneinander. Diese unabhängigen Einheiten werden Bulkheads (als Metapher aus dem Schiffsbau übernommen), Failure Units oder Units of Mitigation genannt. Die Einheiten isolieren sich upstream und downstream gegen Fehler anderer Einheiten, um kaskadierende, d. h. sich über mehrere Einheiten fortpflanzende Fehler zu vermeiden. Dafür stehen eine Menge Muster und Prinzipien zur Verfügung, z. B. lose Kopplung, Latenzüberwachung, Request-Limitierung oder die vollständige Validierung aller Aufrufparameter und Rückgabewerte. Einige der genannten Prinzipien werde ich im weiteren Verlauf dieses Artikels noch vorstellen.

Es ist allerdings nicht hinreichend, nur die Failure Units gegen Fehler zu isolieren. Man benötigt auch eine Fallback-Strategie für den Fall, dass man einen Fehler bemerkt. Auch darauf werde ich im weiteren Verlauf dieses Artikels noch eingehen.

Eine letzte Bemerkung zu Failure Units: Dies ist ein reines Designthema. Es gibt keine fertigen Bibliotheken oder Frameworks, noch irgendwelche vorgefertigten Best Practices, die man einfach verwenden könnte. Man muss sich explizit darüber Gedanken machen, wie man das Gesamtsystem in verschiedene Failure Units aufteilt und das dann entsprechend umsetzen. Hier geht es also, salopp ausgedrückt, um Hirnschmalz und nicht um Tools.

Redundanz

Redundanz ist ein weiteres zentrales Resilience-Muster. Typischerweise sind es Failure Units, die redundant ausgelegt werden. Redundanz ist geeignet, um mit allen Arten von Fehlern umzugehen, nicht nur mit Absturzfehlern, an die die meisten Personen im Zusammenhang mit Redundanz nur denken. Entsprechend muss man sich zunächst einmal Gedanken über das Szenario machen, das man mit Redundanz adressieren möchte:

  • Möchte ich Failover implementieren, d. h. beim Ausfall einer Einheit idealerweise komplett transparent auf eine andere Einheit umschalten?
  • Oder möchte ich die Latenz gering halten, d. h. redundante Einheiten nutzen, um die Wahrscheinlichkeit einer zu langsamen Antwort zu reduzieren?
  • Oder möchte ich Antwortfehler erkennen, d. h. die Antworten mehrerer redundanter (und im Extremfall auch auf unterschiedlicher Hardware laufender und unabhängig entwickelter) Einheiten auswerten, um die Wahrscheinlichkeit fehlerhafter Antworten zu reduzieren?
  • Oder möchte ich Lastverteilung implementieren, d. h. die – für eine Einheit zu zahlreichen – Anfragen auf mehrere Einheiten verteilen?

Abhängig vom gewählten Szenario sind dann weitere Aspekte zu berücksichtigen:

  • Welche Routingstrategie passt zu meinem Szenario, wenn ich Load Balancer einsetze? Reicht einfaches Round Robin oder benötige ich eine komplexere Strategie und wird diese von meinem Load Balancer unterstützt?
  • Stellt mein System einen automatischen Masterwechsel sicher, wenn ich einen Master-Slave-Ansatz für Failover verwende und der Master ausfällt?
  • Wenn ich die Latenz gering halten will, kann ich einen Fan out & quickest one wins-Ansatz verwenden, d. h., ich sende meine Anfrage an mehrere redundante Einheiten und nutze die schnellste Antwort. Wie stelle ich bei so einem Ansatz sicher, dass die Einheiten nicht von alten, nicht mehr benötigten Anfragen so belastet werden, dass sie die neuen Anfragen nicht mehr zeitnah bearbeiten können?
  • Welche Aspekte automatisiere ich und welche gestalte ich manuell? Eine allgemeine Resilience-Empfehlung besagt, dass man Fehlerbehebung möglichst vollständig automatisieren sollte, da im Falle eines Fehlers im Produktivsystem eine Stresssituation vorliegt und Menschen unter Stress typischerweise Fehler machen. Die Chance, dass während der Fehlerbehebung neue Fehler entstehen, ist viel zu hoch. Trotzdem muss es für einen Administrator möglich sein, in den Fehlerbehebungsprozess einzugreifen, wenn er es als notwendig erachtet.
  • Wie stelle ich sicher, dass der Administrator stets den aktuellen Zustand des Systems sieht? Welche Einheit ist Master, welche sind Slave? Wie viele Einheiten laufen? Wo laufen sie? Usw.

Redundanz ist also ein mächtiges Muster mit vielfältigen Einsatzmöglichkeiten, das für eine saubere Implementierung aber auch eine Menge konzeptioneller Vorarbeit benötigt.

Lose Kopplung

Lose Kopplung ist ein Grundprinzip zur Vermeidung kaskadierender Fehler und komplementiert so die zuvor beschriebenen Failure Units.

Ein gutes Muster zur Umsetzung loser Kopplung ist die Verwendung asynchroner Event- oder nachrichtenbasierter Kommunikation. Dadurch wird eine maximale Entkopplung der einzelnen Einheiten erreicht, und die Wahrscheinlichkeit sich fortpflanzender Latenzfehler wird minimiert.

Nun gibt es häufig Bedenken gegen den Einsatz asynchroner Kommunikation, weil es so viel komplizierter als synchrone Kommunikation sei. Das stimmt allerdings nur teilweise. Tatsächlich kann das menschliche Gehirn besser mit synchronen Call-Stack-orientierten Aufrufstrukturen umgehen als mit asynchronen Nachrichtennetzwerken. Außerdem benötigt man zusätzliche Visualisierungs- und Überwachungsmöglichkeiten für asynchrone Nachrichtennetzwerke, um den Überblick zur Laufzeit nicht zu verlieren.

Auf der anderen Seite muss man bei synchroner Kommunikation aber Timeout- und Latenzüberwachung ergänzen, um Antwortzeitfehler erkennen und behandeln zu können. Hierbei muss mit dem Fall umgegangen werden, dass man eine Antwort nicht rechtzeitig bekommen hat. Dieser Fall teilt sich in die Frage auf, was ich meinem Aufrufer zurückmelde sowie die Frage, wie ich (bei verändernden Aufrufen) sicherstelle, dass die Information bei der aufgerufenen Einheit angekommen ist. Die Behandlung dieser Aspekte sorgt dafür, dass synchrone Kommunikation in letzter Instanz ähnlich kompliziert ist wie asynchrone Kommunikation.

Setzt man dennoch synchrone Kommunikation ein (wofür es jenseits von Resilience durchaus gute Gründe geben kann), dann sollte man auf jeden Fall Muster wie Timeout und Circuit Breaker einsetzen (für eine gute Beschreibung dieser Muster siehe z. B. [3]), um eine gute Latenzüberwachung zu implementieren. Bibliotheken wie z. B. Hystrix von Netflix können einem bei der Umsetzung gute Dienste leisten).

Ein weiteres hilfreiches Muster in diesem Kontext, das hier kurz erwähnt werden soll, ist Idempotenz. Ein (verändernder) Aufruf ist idempotent, wenn wiederholte Aufrufe keine zusätzlichen Seiteneffekte haben. Ein ganz einfaches Beispiel: Ein Aufruf „Addiere 1 auf Wert“ ist nicht idempotent, weil sich der Wert, auf den dieser Aufruf angewendet wird, als Seiteneffekt mit jedem Aufruf verändert. Ein Aufruf „Setze Wert auf 5“ hingegen ist idempotent. Egal, wie oft ich diesen Aufruf auf einen Wert anwende, das Ergebnis wird immer gleich sein, d. h., es entstehen keine zusätzlichen Seiteneffekte.

Im Falle synchroner Aufrufe mit Latenzüberwachung steht man vor dem Problem, dass man im Falle einer zu langsamen Antwort in der Regel nicht unterscheiden kann, ob der Aufruf gar nicht erst beim Empfänger angekommen ist oder ob der Empfänger nur zu langsam geantwortet hat. Es ist also unklar, ob die Anfrage verarbeitet wurde oder nicht. Verwendet man jetzt nicht idempotente Aufrufe, steht man bei zu langsamen Antworten vor dem Problem, dass man nicht oder nur mit sehr großem Aufwand entscheiden kann, ob man den Aufruf noch einmal senden darf oder nicht.

Verwendet man hingegen idempotente Aufrufe, stellt sich dieses Problem nicht. Man ruft seinen Empfänger einfach so oft auf (natürlich mit sinnvollen Pausen und Fehlerbehebungsmaßnahmen zwischen den Aufrufen, siehe auch Fallback im nächsten Abschnitt), bis man die Rückmeldung erhält, dass der Aufruf erfolgreich verarbeitet worden ist.

Idempotente Aufrufe ermöglichen also, von einer Exactly OnceKommunikation auf eine At Least OnceKommunikation zu wechseln, die wesentlich leichter umsetzbar ist und deutlich geringere Anforderungen an die Kommunikationsinfrastruktur stellt.

Fallback

Das letzte Muster, das an dieser Stelle beschrieben werden soll, ist Fallback. Wie bereits bei Isolation erwähnt, ist es nicht hinreichend, Fehler zu erkennen. Man benötigt auch eine klare Strategie, was man machen will, wenn ein Fehler erkannt worden ist. Diese Strategie sollte sowohl die Bedürfnisse der Nutzer als auch die der Administratoren so gut wie möglich unterstützen.

Die Benutzer sollten im Idealfall überhaupt nicht bemerken, dass ein Fehler aufgetreten ist. Die Administratoren sollten möglichst wenig Stress beim Auftreten eines Fehlers haben, denn Stress während der Fehlerbehebung führt – wie bereits unter Redundanz beschrieben – in der Regel zu neuen Fehlern. Aus diesem Grund sollte die Fehlerbehandlung möglichst vollständig automatisiert erfolgen – einfach per Nagios eine E-Mail an den Administrator zu schicken, ist nicht (mehr) das Mittel der Wahl.

Die Kernfrage, die man sich im Zusammenhang mit Fallbacks stellen muss, ist: Wie soll das System reagieren, wenn ein Fehler entdeckt wird?

Nehmen wir die zuvor beschriebene einfache Webanwendung: Ein Anwender stellt eine Anfrage an die Anwendung. Dies führt zu einer Anfrage an die Datenbank. Die Anfrage an die Datenbank läuft nach – sagen wir – einer Sekunde in einen Timeout, der erkannt wird. Eine Anfragewiederholung (als Maßnahme zum Umgang mit Auslassungsfehlern) läuft ebenfalls in einen Timeout. Jetzt stellt sich die Frage, wie die Anwendung in dieser Situation reagieren soll:

  • Soll dem Anwender eine Fehlerseite mit dem Hinweis gezeigt werden, dass gerade ein Problem besteht und er es später noch einmal versuchen soll?
  • Oder halten wir möglicherweise die Nutzerdaten in Caches und können Leseanfragen weiterhin aus den Caches bedienen und müssen nur Schreibanfragen mit einer „Versuchen Sie es später noch einmal“-Seite quittieren?
  • Oder schreiben wir in dem Fall Schreibanfragen in eine Queue, die von einem asynchronen Job abgearbeitet wird, sobald die Datenbank wieder verfügbar ist und antworten dem Anwender, dass sein Auftrag angenommen wurde und später verarbeitet wird (und sehen ggf. ergänzend dazu für den Anwender eine Möglichkeit vor, den Verarbeitungsstatus seiner Anfrage(n) einzusehen)?
  • Oder … ?

Das sind alles Varianten einer Graceful Degradation Of Service, der kontrollierten Herabsetzung der Servicequalität. Jede dieser Optionen kann je nach Anwendungsfall valide sein. Nur eine Variante ist nicht valide, nämlich dass der Anwender die Sanduhr sieht, bis sein Browser nach fünf Minuten einen Netzwerk-Timeout meldet.

Wichtig ist hierbei, dass das keine Entscheidungen sind, die ein Entwickler „on the fly“ treffen kann, während er gerade die entsprechende Fehlerbehandlung implementiert, also z. B. im Fall von Java oder C# den Code für den zugehörigen Ausnahmebehandlungsblock schreibt.

Das sind letztlich Fachanforderungen, nämlich die Frage, welche Reaktion der Anwender im Falle eines technischen Fehlers zu Gesicht bekommen soll. Diese Fragen müssen wie die User Stories und die Basisarchitektur geklärt sein, bevor ein Entwickler den zugehörigen Code schreibt. Das sind Fragen, die beispielsweise im agilen Umfeld ein Product Owner beantworten muss.

Hat man die grundsätzliche Fallback-Strategie festgelegt, kann man die Strategie bei der Umsetzung noch durch Resilience-Muster wie Escalation Strategy oder Error Handler unterstützen, die das Implementieren einer automatischen Fehlerbehebung unterstützen (für eine detaillierte Beschreibung dieser Muster siehe z. B. [1]).

Zusammenfassung

Das war ein kurzer Ausflug in das Design von Resilience anhand einiger elementarer Prinzipien. Natürlich gäbe es noch sehr viel mehr zu schreiben, da Resilience eine reichhaltige und umfangreiche Mustersprache bietet, aber das würde den Rahmen dieses Artikels bei Weitem sprengen.

Was bleibt unterm Strich? Bei Resilient Software Design geht es darum, die Verfügbarkeit von Systemen und Systemlandschaften zu maximieren. Im Gegensatz zu traditionellen Stabilitätsansätzen versucht Resilience nicht, die Eintrittswahrscheinlichkeit von Fehlern zu reduzieren, sondern nimmt Fehler als unvermeidbar und unvorhersehbar hin und versucht stattdessen, möglichst schnell auf auftretende Fehler zu reagieren.

Dieser Ansatz passt sehr gut zu den heutigen komplexen, verteilten und hochgradig vernetzten Systemlandschaften, in denen es unmöglich ist, die vielfältigen möglichen Fehlerquellen zu antizipieren und durch vorbeugende Maßnahmen auszuschließen – und der immer höher werdende Grad an Verteilung und Vernetzung, sei es wegen Cloud, Mobile oder Internet of Things sowie die immer schwerer vorhersagbaren Lastmuster, ebenfalls wegen Mobile und Internet of Things, aber auch wegen Social Media, werden dafür sorgen, dass Resilient Software Design in Zukunft noch viel wichtiger wird, als es jetzt bereits ist.

Aufmacherbild: concept on determination and resilience von Shutterstock / Urheberrecht: Ho Yeow Hui

Verwandte Themen:

Geschrieben von
Uwe Friedrichsen
Uwe Friedrichsen
  Uwe Friedrichsen ist ein langjähriger Reisender in der IT-Welt. Als CTO der codecentric AG darf er seine Neugierde auf neue Ansätze und Konzepte sowie seine Lust am Andersdenken ausleben. Seine aktuellen Schwerpunktthemen sind agile Architektur und verteilte, hochskalierbare Systeme. Er ist außerdem Autor diverser Artikel und diskutiert seine Ideen regelmäßig auf Konferenzen.
Kommentare

Hinterlasse einen Kommentar

Hinterlasse den ersten Kommentar!

avatar
400
  Subscribe  
Benachrichtige mich zu: