Fehlerbehandlung - Sollte ein Programm bei Fehlern fehlschlagen oder diese unbemerkt ignorieren

20

Ich schreibe ein einfaches kleines Programm, um MIDI über ein Netzwerk zu übertragen. Ich weiß, dass das Programm Übertragungsprobleme und / oder andere Ausnahmesituationen aufweist, die ich nicht vorhersagen kann.

Für die Ausnahmebehandlung sehe ich zwei Ansätze. Soll ich das Programm so schreiben, dass es:

  • scheitert mit einem Knall, wenn etwas schief geht oder
  • Sollte es den Fehler einfach ignorieren und auf Kosten der Datenintegrität fortfahren?

Welchen Ansatz würde ein Benutzer vernünftigerweise erwarten?
Gibt es eine bessere Möglichkeit, mit Ausnahmen umzugehen?

Sollte meine Entscheidung über die Behandlung von Ausnahmen außerdem davon abhängen, ob ich es mit einer Netzwerkverbindung zu tun habe oder nicht (dh von etwas, bei dem ich vernünftigerweise erwarten kann, dass Probleme auftreten)?

Arlen Beiler
quelle
1
@ GlenH7 Pay it forward ... :)
Mücke
Gibt es eine Möglichkeit, Änderungen um +1 zu erhöhen? ;) Oh, warte, ich folge dir. Sicher, wird es tun :)
Arlen Beiler
2
Msgstr "Welchen Ansatz würde ein Benutzer vernünftigerweise erwarten?" Haben Sie versucht, einen Ihrer Benutzer zu fragen? Flur-Usability-Tests können bemerkenswert effektiv sein. Siehe blog.openhallway.com/?p=146
Bryan Oakley
Ich habe keine Benutzer :)
Arlen Beiler

Antworten:

34

Ignorieren Sie niemals einen Fehler, auf den Ihr Programm stößt. Sie sollten es mindestens in einer Datei oder einem anderen Mechanismus zur Benachrichtigung protokollieren. Es kann vorkommen, dass Sie einen Fehler ignorieren, aber dokumentieren möchten ! Schreiben Sie keinen leeren catchBlock, ohne zu erklären, warum er leer ist.

Ob das Programm scheitern sollte oder nicht, hängt stark vom Kontext ab. Wenn Sie den Fehler ordnungsgemäß behandeln können, versuchen Sie es. Wenn es sich um einen unerwarteten Fehler handelt, stürzt Ihr Programm ab. Das ist so ziemlich das Grundprinzip der Ausnahmebehandlung.

Marco-Fiset
quelle
18
Nicht genug, um eine -1 zu rechtfertigen, aber "nie" ist ein starkes Wort, und es gibt Situationen, in denen das Ignorieren von Fehlern die richtige Vorgehensweise ist. Zum Beispiel Software zum Dekodieren von HDTV-Übertragungen. Wenn Sie auf einige Fehler stoßen, die Sie häufig machen, können Sie sie nur ignorieren und das, was danach kommt, weiter dekodieren.
Whatsisname
1
@whatsisname: true, aber Sie sollten klar dokumentieren, warum Sie es ignorieren. Aktualisierte Antwort, um Ihren Kommentar zu berücksichtigen.
Marco-Fiset
1
@ Marco-Fiset Ich bin nicht einverstanden. Ein Absturz des gesamten Programms ist nur dann die Lösung, wenn das Problem durch einen Neustart behoben werden kann. Dies ist nicht immer der Fall, so dass ein Absturz oft sinnlos ist und das Standardverhalten einfach nur dumm ist. Warum möchten Sie Ihren gesamten Vorgang aufgrund eines lokalisierten Fehlers unterbrechen? Das macht keinen Sinn. Die meisten Anwendungen machen das und aus irgendeinem Grund sind Programmierer damit völlig einverstanden.
MaiaVictor
@Dokkat: Der Punkt ist, nicht mit falschen Werten fortzufahren, was eine falsche Lösung ergibt (Müll rein, Müll raus). Absturz behebt das Problem: Der Benutzer muss entweder verschiedene Eingaben machen oder den Entwickler beim Beheben seines
Problems belästigen
1
@whatsisname Ich denke, Sie möchten in diesem Fall über Ihre Definition von "Fehler" nachdenken. Fehler in den empfangenen Daten sind nicht dasselbe wie Fehler in der Struktur und Ausführung eines Softwareprogramms.
Joshin4colours
18

Sie sollten Fehler niemals stillschweigend ignorieren, da Ihr Programm auf einer Reihe von Aktionen basiert, die implizit von allem abhängen, was passiert, bevor sie richtig ausgeführt werden. Wenn in Schritt 3 ein Fehler auftritt und Sie versuchen, mit Schritt 4 fortzufahren, wird Schritt 4 mit ungültigen Annahmen gestartet, sodass wahrscheinlich auch ein Fehler auftritt. (Und wenn Sie das auch ignorieren, wird in Schritt 5 ein Fehler ausgegeben, und von dort aus beginnen die Dinge zu schneien.)

Die Sache ist, wenn sich die Fehler häufen, werden Sie irgendwann auf einen Fehler stoßen, der so groß ist, dass Sie ihn nicht ignorieren können, weil er darin besteht, dass dem Benutzer etwas gegeben wird und dass etwas völlig falsch ist. Dann beschweren sich Benutzer über das fehlerhafte Funktionieren Ihres Programms und müssen es beheben. Und wenn sich der Teil "Geben Sie dem Benutzer etwas" in Schritt 28 befindet und Sie keine Ahnung haben, dass der ursprüngliche Fehler, der all dieses Durcheinander verursacht hat, in Schritt 3 war, weil Sie den Fehler in Schritt 3 ignoriert haben, wird ein angezeigt verdammt noch mal das Problem zu debuggen!

Wenn andererseits dieser Fehler in Schritt 3 alles in die Luft jagt und eine Fehlermeldung SOMETHING WENT BADLY WRONG IN STEP 3!(oder dessen technisches Äquivalent, eine Stapelverfolgung) erzeugt, ist das Ergebnis dasselbe - der Benutzer beschwert sich über Sie Das Programm funktioniert nicht richtig - aber dieses Mal wissen Sie genau, wo Sie suchen müssen, um es zu beheben .

BEARBEITEN: In Reaktion auf die Kommentare, wenn etwas schief geht, das Sie erwartet haben und wissen, wie man damit umgeht, ist das anders. Wenn beispielsweise eine fehlerhafte Nachricht empfangen wird, handelt es sich nicht um einen Programmfehler. Das ist "Benutzer hat falsche Eingaben gemacht, die die Validierung nicht bestanden haben." Die passende Antwort ist, dem Benutzer mitzuteilen, dass er Ihnen ungültige Eingaben macht. So hört es sich an, als würden Sie das tun. In einem solchen Fall müssen Sie nicht abstürzen und einen Stack-Trace generieren.

Mason Wheeler
quelle
Wie wäre es, zur letzten bekannten guten Position zurückzukehren oder im Falle einer Empfangsschleife einfach diese Nachricht fallen zu lassen und zur nächsten zu gehen.
Arlen Beiler
@Arlen: Auch das kann eine schlechte Idee sein. Fehler treten auf, weil etwas passiert ist, mit dem Sie beim Codieren nicht gerechnet haben . Dies bedeutet, dass alle Ihre Annahmen aus dem Fenster gegangen sind. Diese "zuletzt als gut bekannte Position" ist möglicherweise nicht mehr gut, wenn Sie in der Mitte des Änderungsprozesses einer Datenstruktur waren und sie sich beispielsweise in einem inkonsistenten Zustand befindet.
Mason Wheeler
Was ist, wenn ich es erwarte, wie beschädigte Nachrichten, die eine Deserialisierung verhindern? In einigen Fällen habe ich die Ausnahme serialisiert und über die Leitung zurückgesendet. Dort sehe ich meine Bearbeitung zu der Frage.
Arlen Beiler
@Arlen: Wenn Sie das vorausgesehen haben und sicher sind, dass Sie die Auswirkungen des Fehlers kennen und dass sie richtig enthalten sind, dann ist das anders. Klingt für mich so, als würden Sie den Fehler behandeln und angemessen reagieren, ohne ihn zu ignorieren. Ich habe darüber gesprochen, unerwartete Fehler zu ignorieren , und so klang es, als würden Sie danach fragen.
Mason Wheeler
16

Es gibt andere Optionen zwischen "Sprengen" und "Ignorieren".

Wenn der Fehler vorhersehbar und vermeidbar ist, ändern Sie Ihr Design oder überarbeiten Sie Ihren Code, um ihn zu vermeiden.

Wenn der Fehler vorhersehbar, aber nicht vermeidbar ist, Sie aber wissen, was zu tun ist, wenn er auftritt, müssen Sie den Fehler abfangen und die Situation behandeln. Vermeiden Sie jedoch Ausnahmen als Flusskontrolle. Möglicherweise möchten Sie an dieser Stelle eine Warnung protokollieren und den Benutzer benachrichtigen, wenn Maßnahmen ergriffen werden, um diese Situation in Zukunft zu vermeiden.

Wenn der Fehler vorhersehbar und unvermeidbar ist und Sie in diesem Fall nichts tun können, um die Datenintegrität zu gewährleisten, müssen Sie den Fehler protokollieren und in einen sicheren Zustand zurückkehren (was, wie bereits erwähnt, einen Absturz bedeuten kann).

Wenn der Fehler nicht von Ihnen erwartet wurde, können Sie nicht sicher sein, dass Sie in einen sicheren Zustand zurückkehren können. Daher ist es möglicherweise am besten, nur zu protokollieren und abzustürzen.

Fangen Sie im Allgemeinen keine Ausnahme ab, gegen die Sie nichts unternehmen können, es sei denn, Sie möchten sie nur protokollieren und erneut werfen. Und in den seltenen Fällen, in denen ein Try-Catch-Ignorieren unvermeidbar ist, fügen Sie mindestens einen Kommentar in Ihren Catch-Block ein, um zu erklären, warum.

Weitere Vorschläge zum Kategorisieren und Behandeln von Ausnahmen finden Sie in Eric Lipperts hervorragendem Artikel zur Ausnahmebehandlung.

John M Gant
quelle
1
Die beste Antwort bisher, meiner Meinung nach.
Donnerstag,
6

Das sind meine Ansichten zu der Frage:

Ein gutes Startprinzip ist, schnell zu versagen. Insbesondere sollten Sie niemals Fehlerbehandlungscode für Fehler schreiben, für die Sie die genaue Ursache nicht kennen.

Nachdem Sie dieses Prinzip angewendet haben, können Sie Wiederherstellungscode für bestimmte Fehlerbedingungen hinzufügen, auf die Sie stoßen. Sie können auch mehrere "sichere Zustände" eingeben, zu denen Sie zurückkehren möchten. Das Abbrechen eines Programms ist größtenteils sicher, aber manchmal möchten Sie möglicherweise zu einem anderen bekannten Zustand zurückkehren. Ein Beispiel ist, wie ein modernes Betriebssystem mit einem störenden Programm umgeht. Es wird nur das Programm heruntergefahren, nicht das gesamte Betriebssystem.

Wenn Sie nicht schnell und langsam mehr und mehr spezifische Fehlerbedingungen abdecken, gefährden Sie nie die Datenintegrität und bewegen sich stetig auf ein stabileres Programm zu.

Das Verschlucken von Fehlern, dh der Versuch, Fehler zu planen, für die Sie die genaue Ursache nicht kennen und für die Sie keine spezifische Wiederherstellungsstrategie haben, führt nur dazu, dass in Ihrem Programm immer mehr Code übersprungen und umgangen wird. Da man nicht darauf vertrauen kann, dass frühere Daten korrekt verarbeitet wurden, werden Sie häufig nach falschen oder fehlenden Daten suchen. Ihre zyklomatische Komplexität wird außer Kontrolle geraten und Sie werden einen großen Schlammballen erhalten.

Es ist weniger wichtig, ob Sie die Fehlerfälle kennen oder nicht. Wenn Sie jedoch beispielsweise mit einer Netzwerkverbindung arbeiten, für die Sie eine bestimmte Anzahl von Fehlerzuständen kennen, verschieben Sie das Hinzufügen der Fehlerbehandlung, bis Sie auch den Wiederherstellungscode hinzufügen. Dies steht im Einklang mit den oben dargelegten Grundsätzen.

Alexander Torstling
quelle
6

Sie sollten Fehler niemals unbemerkt ignorieren. Und vor allem nicht auf Kosten der Datenintegrität .

Das Programm versucht etwas zu tun. Wenn es fehlschlägt, müssen Sie sich der Tatsache stellen und etwas dagegen unternehmen . Was das sein wird, hängt von vielen Dingen ab.

Am Ende forderte der Benutzer das Programm auf, etwas zu tun, und das Programm sollte ihm mitteilen, dass es nicht erfolgreich war. Es gibt viele Möglichkeiten, wie es geht. Es kann sofort beendet werden, es kann sogar bereits abgeschlossene Schritte rückgängig machen oder es kann andererseits alle Schritte fortsetzen und ausführen, die es kann, und dann dem Benutzer mitteilen, dass diese Schritte erfolgreich waren und diese anderen fehlgeschlagen sind.

Welchen Weg Sie wählen, hängt davon ab, wie eng die Schritte zusammenhängen und ob der Fehler wahrscheinlich bei allen zukünftigen Schritten erneut auftritt, was wiederum vom genauen Fehler abhängen kann. Wenn eine starke Datenintegrität erforderlich ist, müssen Sie ein Rollback auf den letzten konsistenten Status durchführen. Wenn Sie nur einige Dateien kopieren, können Sie einige überspringen und dem Benutzer am Ende mitteilen, dass diese Dateien nicht kopiert werden konnten. Sie sollten keine Dateien stillschweigend überspringen und dem Benutzer nichts mitteilen.

Der einzige Unterschied bei der Anzeigenbearbeitung besteht darin, dass Sie in Betracht ziehen sollten, den Vorgang einige Male zu wiederholen, bevor Sie aufgeben und dem Benutzer mitteilen, dass er nicht funktioniert hat, da es im Netzwerk wahrscheinlich vorübergehende Fehler gibt, die bei einem erneuten Versuch nicht wieder auftreten.

Jan Hudec
quelle
Wollten Sie wirklich ein Fragezeichen am Ende der ersten Zeile einfügen?
ein Lebenslauf vom
@ MichaelKjörling: Nein. Copy-Paste-Fehler (habe die Formulierung aus der Frage kopiert und das Fragezeichen versehentlich eingefügt).
Jan Hudec
4

Es gibt eine Klasse von Fällen, in denen das Ignorieren von Fehlern das Richtige ist: Wenn nichts gegen die Situation unternommen werden kann und schlechte und möglicherweise falsche Ergebnisse besser sind als keine Ergebnisse.

Der Fall des Decodierens des HDMI-Streams zu Anzeigezwecken ist ein solcher Fall. Wenn der Stream schlecht ist, ist es schlecht, wenn Sie darüber schreien, wird dies nicht auf magische Weise behoben. Sie tun, was Sie können, um es anzuzeigen, und lassen den Betrachter entscheiden, ob es erträglich ist oder nicht.

Loren Pechtel
quelle
1

Ich glaube nicht, dass ein Programm unbemerkt ignorieren oder Chaos anrichten sollte, wenn es auf ein Problem stößt.

Was ich mit interner Software mache, schreibe ich für mein Unternehmen ...

Dies hängt vom Fehler ab. Wenn es sich beispielsweise um eine wichtige Funktion handelt, die Daten in MySQL eingibt, muss der Benutzer darüber informiert werden, dass ein Fehler aufgetreten ist. Der Fehlerbehandler sollte versuchen, möglichst viele Informationen zu sammeln und dem Benutzer eine Vorstellung davon zu vermitteln, wie er den Fehler selbst korrigieren kann, damit er die Daten speichern kann. Ich biete auch gerne eine Möglichkeit an, uns die Informationen, die sie zu speichern versuchen, im Stillen zu senden. Wenn sich dies verschlimmert, können wir sie manuell eingeben, nachdem der Fehler behoben wurde.

Wenn es sich nicht um eine kritische Funktion handelt, die fehlerhaft sein kann und das Endergebnis des von ihnen angestrebten Ziels nicht beeinflusst, wird möglicherweise keine Fehlermeldung angezeigt, aber es wird eine E-Mail gesendet, die diese automatisch in unsere Fehlerverfolgungssoftware einfügt oder eine E-Mail-Verteilergruppe, die alle Programmierer im Unternehmen benachrichtigt, damit wir über den Fehler informiert sind, auch wenn dies nicht der Fall ist. Auf diese Weise können wir das hintere Ende reparieren, während niemand weiß, was vor sich geht.

Eines der größten Dinge, die ich zu vermeiden versuche, ist, dass das Programm nach dem Fehler abstürzt - es kann nicht wiederhergestellt werden. Ich versuche immer, dem Benutzer die Möglichkeit zu geben, fortzufahren, ohne die Anwendung zu schließen.

Ich glaube, wenn niemand etwas über den Fehler weiß, wird er niemals behoben. Ich bin auch fest davon überzeugt, dass die Fehlerbehandlung der Anwendung auch dann funktioniert, wenn ein Fehler entdeckt wird.

Wenn der Fehler netzwerkbezogen ist - warum lassen Sie die Funktionen nicht einen einfachen Test der Netzwerkkommunikation durchführen, bevor Sie die Funktion ausführen, um den Fehler an erster Stelle zu vermeiden? Wenn Sie den Benutzer dann nur darauf hinweisen, dass keine Verbindung verfügbar ist, überprüfen Sie bitte Ihr Internet usw. usw. und versuchen Sie es erneut.

Jeff
quelle
1
Ich persönlich führe eine Menge Überprüfungen durch, bevor ich wichtige Funktionen ausführe, um Fehler zu begrenzen, die ich nicht vollständig verstehe und die keine nützliche Fehlerbehandlung bieten, die detaillierte Informationen zurückgibt. Es funktioniert nicht 100% der Zeit, aber ich kann mich nicht erinnern, wann ich das letzte Mal einen Bug hatte, der mich stundenlang verloren hatte.
Jeff
1

Meine eigene Strategie besteht darin , zwischen Codierungsfehlern (Bugs) und Laufzeitfehlern zu unterscheiden und es so schwierig wie möglich zu machen, Codierungsfehler zu erzeugen.

Fehler müssen so schnell wie möglich behoben werden, daher ist ein Design by Contract- Ansatz angemessen. In C ++ überprüfe ich gerne alle meine Vorbedingungen (Eingaben) mit Behauptungen oben in der Funktion, um den Fehler so schnell wie möglich zu erkennen und das Anhängen eines Debuggers und das Beheben des Fehlers zu vereinfachen. Wenn der Entwickler oder Tester stattdessen versucht, das Programm weiter auszuführen, wird der Verlust der Datenintegrität zum Problem.

Und finden Sie Wege, um den Fehler zu verhindern. Konstante Korrektheit und die Auswahl von Datentypen, die für die Daten geeignet sind, die sie enthalten, sind zwei Möglichkeiten, um das Erstellen von Fehlern zu erschweren. Fail-Fast eignet sich auch außerhalb von sicherheitskritischem Code, der eine Möglichkeit zur Wiederherstellung benötigt.

Für Laufzeitfehler , die möglicherweise mit fehlerfreiem Code auftreten können, wie z. B. Netzwerk- oder serielle Kommunikationsfehler oder fehlende oder beschädigte Dateien:

  1. Den Fehler protokollieren.
  2. (Optional) Versuchen Sie, den Vorgang unbeaufsichtigt zu wiederholen oder auf andere Weise wiederherzustellen.
  3. Wenn der Vorgang weiterhin fehlschlägt oder nicht behebbar ist, melden Sie den Fehler dem Benutzer. Dann kann der Benutzer wie oben entscheiden, was zu tun ist. Denken Sie an das Prinzip des geringsten Erstaunens , denn ein Verlust der Datenintegrität überrascht den Benutzer, wenn Sie ihn nicht rechtzeitig gewarnt haben.
Snips-n-Schnecken
quelle
0

Ein Fehlschlag ist die richtige Option, wenn Sie Grund zur Annahme haben, dass der Gesamtzustand des Programms instabil ist und etwas Schlimmes passieren kann, wenn Sie es von nun an laufen lassen. Etwas "Ignorieren" ist in Ordnung, wenn Sie wissen, dass der aktuelle Vorgang nicht ausgeführt werden kann, das Programm jedoch lauf weiter.

Fabien
quelle