1 Punkte von GN⁺ 2025-06-04 | 1 Kommentare | Auf WhatsApp teilen
  • Die Ausführlichkeit der Fehlerbehandlung in Go gehört seit Langem zu den häufigsten Klagen der Nutzer
  • Verschiedene Vorschläge zur Syntaxverbesserung (z. B. check/handle, try, der ?-Operator usw.) wurden diskutiert und erprobt, aber ohne ausreichenden Konsens in der Community allesamt verworfen
  • Wichtige Abwägungen sind die weitreichenden Auswirkungen von Sprachänderungen auf Code, Tools, Dokumentation usw. sowie das für Go typische Prinzip, die Sprache einfach zu halten
  • Aufgrund der Klarheit des aktuellen Ansatzes, der einfachen Fehlersuche und der Präferenz mancher Nutzer gibt es nur eine schwache Begründung dafür, überhaupt eine Syntaxänderung einzuführen
  • Auf absehbare Zeit gibt es keine Pläne für Änderungen an der Syntax der Fehlerbehandlung, und die entsprechenden Vorschläge sollen ohne weitere Untersuchung abgeschlossen werden

Das Problem der Ausführlichkeit bei der Fehlerbehandlung in Go

  • Eine der langjährigen Beschwerden über Go ist, dass die Syntax der Fehlerbehandlung übermäßig ausführlich ist
  • Typischerweise tauchen Muster wie if err != nil im Code immer wieder auf
  • Je mehr API-Aufrufe ein Programm benötigt, desto stärker fällt dieses Muster auf, sodass am Ende mehr Code für die Fehlerbehandlung als für die eigentliche Logik entsteht
  • In den jährlichen Nutzerumfragen wird diese Beschwerde weiterhin regelmäßig weit oben genannt

Abstimmung mit der Community und frühe Vorschläge

  • Das Go-Team misst dem Feedback aus der Community große Bedeutung bei und hat deshalb die Erforschung von Verbesserungen der Fehlerbehandlung fortgesetzt
  • In den Diskussionen zum Projekt Go 2 im Jahr 2018 fasste Russ Cox die Kernprobleme der Fehlerbehandlung offiziell zusammen
    • Enthalten war der von Marcel van Lohuizen vorgeschlagene Mechanismus check und handle
    • Dazu kamen Vergleiche mit ähnlichen Sprachen und die Prüfung verschiedener Alternativen
  • Dieser Ansatz machte den Code zwar tatsächlich kompakter, wurde aber wegen der zunehmenden Komplexität nicht übernommen

Der try-Vorschlag und was danach geschah

  • 2019 wurde ein deutlich vereinfachter Vorschlag für die eingebaute Funktion try gemacht
    • Im Code wurde nur die check-Funktionalität übernommen, handle entfiel
    • Der Vorschlag wurde dafür kritisiert, den Kontrollfluss zu verbergen, und nach Gegenwind aus der Community verworfen
  • Diese Erfahrung machte das Risiko ausgereifter Vorschläge ohne ausreichendes Feedback deutlich
    • Sie zeigte, dass bei Vorschlägen für größere Änderungen schon in der frühen Entwurfsphase breiteres Meinungsbild wichtig ist

Weitere Versuche und verschiedene Vorschläge

  • Zahlreiche Varianten und alternative Ansätze zur Fehlerbehandlung wurden in der Community fortlaufend vorgeschlagen
    • Ian Lance Taylor bündelte den Stand in einem Umbrella-Issue, und im Go Wiki sowie in Blogs wurden weiterhin Beispiele gesammelt
  • 2024 gab es einen Vorschlag zur Übernahme des aus Rust bekannten ?-Operators
    • In kleineren Usability-Tests wurde er als intuitiv bewertet, doch auch hier kam trotz vielfältiger Meinungen kein Konsens zustande

Stillstand in der Diskussion und Schlussfolgerung

  • Es gab mehr als drei offizielle oder inoffizielle Vorschläge und Hunderte Community-Vorschläge, doch mangels ausreichender Zustimmung bzw. Konsens wurden sie alle abgelehnt
  • Selbst innerhalb der internen Architektengruppe von Go gibt es keine Einigkeit über die Richtung
  • Bis sich die Lage ändert oder ein besonderer Konsens entsteht, wurde beschlossen, Versuche zur Änderung der Syntax der Fehlerbehandlung selbst einzustellen

Zentrale Argumente für die Beibehaltung des aktuellen Ansatzes

  • Wäre schon beim ursprünglichen Sprachdesign syntaktischer Zucker eingeführt worden, hätte es vielleicht keine Kontroverse gegeben, doch inzwischen gibt es ein Ökosystem, das an einen seit 15 Jahren verwendeten Ansatz gewöhnt ist
  • Die Einführung neuer Syntax würde zwangsläufig die Sorge auslösen, dass zwischen bestehender und neuer Nutzerschaft Stilunterschiede im Code entstehen und die Konsistenz leidet
  • Das passt auch zur Designphilosophie von Go, dieselbe Sache nicht auf mehrere Arten zu tun, und zum Prinzip, Kürze und Konsistenz hoch zu gewichten
    • Auch die erlaubte Neudeklaration bei kurzen Variablendeklarationen (:=) ist eine indirekte Folge der Fehlerbehandlung
  • Die explizite Syntax der Fehlerbehandlung (über if) hat intuitive Vorteile beim Lesen von Code, beim Debugging und beim Setzen von Breakpoints
  • Sprachänderungen sind auch hinsichtlich des tatsächlichen Änderungsumfangs (Code, Dokumentation, Tools usw.) und der Kosten eine große Belastung

Alternative Verbesserungen und künftige Richtung

  • Durch Ausbau der Standardbibliothek (z. B. Einführung von cmp.Or) lässt sich ein Teil des wiederholten Codes reduzieren
  • Mit Code Folding, Autovervollständigung, dem Einsatz von LLMs und anderen IDE- bzw. Entwicklungstools lässt sich die Ausführlichkeit in der Praxis bis zu einem gewissen Grad abfedern
  • In wichtigen Gruppen von Go-Nutzern (z. B. Teilnehmern der Veranstaltung Google Cloud Next) überwiegt die skeptische Haltung gegenüber Sprachänderungen
    • Je länger Go genutzt wird, desto weniger stark wird das Problem der Ausführlichkeit tatsächlich wahrgenommen

Argumente für den Bedarf an Syntaxverbesserungen

  • Auf Basis des Nutzerfeedbacks besteht weiterhin der Wunsch nach Verbesserungen der Syntax der Fehlerbehandlung
  • Eine Syntax für die Fehlerbehandlung, die nicht nur Zeichen spart, sondern die Klarheit erhöht, könnte zur Verbesserung von Codequalität und Sicherheit beitragen
  • Es braucht genauere Forschung zur Fehlerbehandlung, die nicht nur einfache Fehlerprüfungen betrifft, sondern tatsächlich eine funktionale Rolle im Code spielt

Endgültige Schlussfolgerung und künftige Politik

  • In Anerkennung der bisherigen Lage ohne nennenswerten Konsens oder praktische Veränderung wird erklärt, dass die Diskussion und Vorschläge zu syntaktischen Sprachänderungen für die Fehlerbehandlung auf absehbare Zeit vollständig eingestellt werden
  • Die bisherigen Diskussionen und Untersuchungen haben dennoch indirekt zur Verbesserung des Go-Ökosystems und der Prozesse beigetragen
  • Falls künftig eine klarere Problemdefinition und ein tragfähigerer Konsens entstehen, könnte die Diskussion wieder aufgenommen werden
  • Vorerst soll der Schwerpunkt eher darauf liegen, die Robustheit und Einfachheit von Go selbst zu bewahren, statt neue Versuche zu starten

1 Kommentare

 
GN⁺ 2025-06-04
Hacker-News-Kommentare
  • Wer leichtfertig vorschlagen möchte, das Go-Team hätte auch andere Alternativen wählen können, sollte sich unbedingt den Go2ErrorHandlingFeedback-Wiki oder die GitHub-Issuesuche ansehen. Fast jede vorgeschlagene Idee wurde bereits ernsthaft diskutiert, und als Nutzer, der den transparenten Ansatz des Go-Teams schätzt, macht es mir große Freude, Go jeden Tag zu verwenden.

    • Im Entwurfsdokument werden C++, Rust und Swift erwähnt, aber do-Notation/for-comprehensions/monadic-let aus funktionalen Sprachen wie Haskell, Scala oder OCaml, nach denen ich suche, sind kaum zu finden. Das Go-Team wirkt zwar wie ein Meister der Sprachgestaltung, stößt aber beim Thema Fehlerbehandlung offenbar an die Grenzen statischer Typen ohne parametrischen Polymorphismus, ähnlich wie Java. Ich halte das für ein Problem, das aus dem grundlegenden Sprachdesign stammt.

    • Obwohl das Dokument von klugen und erfahrenen Leuten verfasst wurde, ist es sehr merkwürdig, dass Lösungen wie Haskells Maybe/Either-Monaden und der bind-Operator (do-Notation) nirgends erwähnt werden. Dabei sind sie in der Praxis weder schwierig noch akademisch, sondern ein sehr eleganter und bewährter Weg, Fehler sicher weiterzugeben. Ich verstehe nicht, warum die Go-Community das nicht aufgegriffen hat. Ich bin dankbar, dass diese Seite existiert, aber dass eine so bekannte Lösung übergangen wird, ist schwer nachzuvollziehen.

    • Fast alle Sprachen bieten verschiedene bessere Ansätze, daher frage ich mich, warum dieses Problem gerade in Go so stark hervorsticht. Liegt es einfach daran, dass es keinen Konsens gibt, oder gibt es eine Besonderheit der Sprache Go, wegen der Lösungen aus anderen Sprachen nicht passen?

    • In Go-Kritik sieht man oft das Phänomen, dass vergleichsweise weniger fachkundige Leute voraussetzen, Go-Entwickler verstünden weniger von Sprachen als sie selbst. Tatsächlich sind Go-Entwickler in den meisten Fällen deutlich erfahrener und wissen viel mehr. Nicht-Experten glauben oft, eine Sprache mit mehr Features sei automatisch besser, übersehen dabei aber, dass es in Wirklichkeit darauf ankommt, die Gesamtbalance gut hinzubekommen.

  • Ich denke, die Nutzer profitieren von Gos konservativer Haltung, neue Sprachfeatures nur mit Vorsicht hinzuzufügen. Bei Swift gibt es so viele Feature-Änderungen, dass das Lernen schwierig wird, und selbst auf aktuellen Macs erlebt man mitunter, dass nicht einmal ein einfaches Projekt gebaut werden kann. Weil ständig neue Keywords hinzukommen und sich ändern, fehlt Swift etwas an Beständigkeit, während genau diese Beständigkeit eine Stärke von Go ist.

  • Einmal hatte ich in Go einen ungewöhnlichen Fall, in dem eine Funktion erwartete, dass in einer internen Funktion ein Fehler auftritt, und wenn die interne Funktion keinen Fehler lieferte, musste die Funktion selbst einen Fehler zurückgeben. In dieser seltenen Struktur musste ich mit if err == nil verzweigen, schrieb aber aus Gewohnheit if err != nil, und weil ich zu sehr an das übliche Muster gewöhnt war, brauchte ich lange, um den Fehler zu finden. Ich dachte mir damals, dass eine sprachliche Unterscheidung zwischen dem häufigen if err != nil und dem seltenen if err == nil solche Fehler reduzieren könnte.

    • Jedes Mal, wenn ich if err == nil schreibe, füge ich den Kommentar // inverted hinzu, um das Muster hervorzuheben. Es wäre gut, wenn die Sprache das automatisch behandeln würde, aber derzeit lässt sich so die Unterscheidung zumindest deutlicher machen.
    • Eigentlich ist das eher ein Argument gegen Syntaxänderungen. Das häufige Muster if err == nil { return ... } könnte im Code sogar noch merkwürdiger aussehen. Es gibt die Ansicht, dass die aktuelle Go-Fehlerbehandlung klar und gut lesbar ist und deshalb von vielen bevorzugt wird.
    • Dasselbe Durcheinander kann auch bei Mustern wie if fruit != "Apple" entstehen, daher sollte man das grundsätzlich nicht nur als Problem der Fehlerbehandlung sehen, sondern als allgemeines Problem von Zustandsverzweigungen. Fehler werden letztlich auch nur wie andere Zustandswerte behandelt.
    • In IDE oder Schriftart-Einstellungen könnte man if err != nil wie ein Sonderzeichen rendern, damit es sich natürlicher in den Hintergrund einfügt und weniger auffällt, während ein anders geschriebenes if err == nil hervorgehoben wird. So ließen sich Fehler möglicherweise auf Editor-Ebene vermeiden.
    • Es wurde auch vorgeschlagen, die Lesbarkeit im Editor zu verbessern, indem Muster wie if err … { verkürzt dargestellt werden.
  • Mir gefällt Gos explizite Fehlerbehandlung. Ich verstehe eine Funktion einfach so, dass sie entweder immer erfolgreich ist (minimal error) oder fehlschlagen kann. Funktionen, die scheitern können, müssen zwingend behandelt werden, bevor man zum nächsten Schritt übergehen kann. In vielen Sprachen werden Fehler über Exceptions die Stack-Ebene hinaufgeworfen, bis sie irgendwo gecatcht werden, was oft nur zeigt, wo der Fehler aufgetreten ist, aber wenig praktische Hinweise liefert. In Go hat man klar folgende Optionen: 1) Fehler ignorieren 2) bei Fehler sofort zurückkehren 3) Fehler wrappen und nützliche Informationen ergänzen 4) einen bestimmten Fehler interpretieren und verzweigt behandeln (z. B. in 404 umwandeln). In Go2 würde ich gern einen Result<Value, Failure>-Typ oder spezifischere und aufzählbare Fehlertypen sehen. Wegen der Kompatibilität zu Go 1 wäre eine Einführung in Go 2 passender.

    • Meiner Erfahrung nach muss die Fehlerbehandlungspolitik immer vom Aufrufer entschieden werden, und es ist nicht wünschenswert, sie tief im Call Stack zu behandeln. Am Ende wird es leicht zu einer simplen Wiederholungsarbeit, Fehler nur zu wrappen und nach oben weiterzugeben.
    • „Gos Fehlerbehandlung“ bieten tatsächlich schon die meisten Sprachen wie funktionale Sprachen, Rust oder Java, nicht nur JavaScript oder Python. Letztlich lässt sich Gos Fehlerbehandlung in jeder Sprache mit Generics umsetzen. Wenn der Vergleich bei JS oder Python stehen bleibt, ist das nur ein übliches Muster.
    • Gerade der Punkt „Wenn eine Funktion fehlschlägt, muss sie zwingend behandelt werden“ ist laut Kritik die Schwachstelle von Go. In Go kann man Fehler faktisch vollständig ignorieren, daher könnte Gos Ansatz gerade dann ein Nachteil sein, wenn man wirklich robuste Software bauen will.
    • Die bittere Einschätzung lautet, Go2 werde am Ende nur ein „Labor bleiben, das nie veröffentlicht wird“.
  • Gos Art der Fehlerbehandlung gefiel mir anfangs nicht, aber nachdem ich den Blogpost errors-are-values gelesen und begonnen hatte, panic(err) an passenden Stellen einzusetzen, war ich im Gegenteil sehr zufrieden. Für anomale Zustände, die der aufrufende Code nicht direkt behandeln sollte, konnte ich mit panic viele kleinteilige Fehlerverzweigungen stark reduzieren. Diese Art des Fehlermanagements hilft mir in der Praxis sehr.

    • Dagegen gibt es den Einwand, dass diese Logik Gos schwache Fehlerbehandlung nicht wirklich verteidigt und ihre Vorteile auch bei Verbesserungen nicht verloren gingen.
    • Es wird erwähnt, dass auch PHP Fehlerbehandlung nach Leveln oder Fehlerunterdrückung am Call Site per @-Operator kennt und dass es in bash Fehlermanagement-Techniken wie -e gibt.
    • Als ich in C# zum ersten Mal den Ablauf mit try/catch/finally sah, fand ich das neuartig, heute bevorzuge ich aber eher die schlichte Logik wie in Go. Auch ein hoher Codeumfang (Loc) hat den Vorteil, dass der Kontrollfluss tatsächlich klar ist.
    • Es wird angemerkt, dass auch Rusts auf Sum Types basierende Fehlerbehandlung zum Paradigma „errors are values“ gehört.
  • Wenn behauptet wird, dass die Ausführlichkeit schnell verschwindet, sobald man Fehler tatsächlich behandelt, stellt sich die Frage, ob das manuelle Erzeugen von Stack Traces wirklich „Behandlung“ ist. Nach Gos Definition wären dann nicht auch Exceptions eine Form der Behandlung? Das ist ein amüsanter Gegenpunkt.

    • Ich frage mich, ob Dutzende Zeilen Stack Trace wirklich klare Informationen liefern. Persönlich halte ich einen einzigen Wrap Error für viel effizienter, und auch beim Bereinigen von Logs hilft das. In über zehn Jahren mit Go habe ich nie ausführliche Stack-Informationen gebraucht, die bis zu Runtime-Funktionen hinunterreichen.
  • Mir gefällt nicht, dass dieser Artikel das Problem von Gos Fehlerbehandlung einfach als „die Syntax ist zu ausführlich“ behandelt. Die eigentlichen Probleme sind meiner Meinung nach: 1) Fehler gehen leicht stillschweigend verloren oder werden versehentlich ignoriert 2) Funktionsergebnisse lassen sich nicht so leicht wie Werte weiterreichen oder speichern 3) verschachtelte Fehler wie bei errors.Is greifen ungeschickt in das Typsystem ein 4) Fehler-Switching ist schwierig 5) Sentinel Values werden in der Standardbibliothek häufig verwendet 6) die Kombination mit Generics ist unglücklich, sodass zusätzlicher Paketbedarf entsteht.

    • 90 % der professionellen Go-Programmierer schreiben Testfälle für jeden einzelnen Fehler-Rückgabepfad, um ihre Coverage zu erfüllen; in Sprachen mit Exceptions wäre das unnötige Arbeit.
    • Ich halte die Behauptung für unzutreffend, dass in diesem Artikel „It’s too verbose“ das Hauptproblem gewesen sei. Selbst wenn man die Syntax ändert, verbessert das die eigentlichen Punkte nicht wesentlich.
    • Es gibt auch die Sicht, dass Gos sehr langsames Veränderungstempo — Generics haben ebenfalls lange gedauert — gerade ein Vorteil ist.
    • Als Googler bin ich erneut enttäuscht von den Entscheidungen des Go-Teams.
  • In Elixir (und Erlang) geben Funktionen normalerweise Tupel wie {:ok, result} oder {:error, description} zurück. Dank der with-Syntax in Elixir lässt sich Fehlerbehandlung am unteren Ende des Blocks bündeln, was die Lesbarkeit deutlich verbessert. Würde man etwas Ähnliches wie with in Go einführen, könnte man den Code lesbarer machen, indem man nur dann fortfährt, wenn der Fehler nil ist, und ganz unten einen Error-Handler-Block platziert.

    • Wegen des Community-Konsenses führt Go selbst grundlegende und wertvolle Features wie Sum Types, Fehlerbehandlung und Paketmanagement nur sehr langsam ein. Generics brauchten 13 Jahre, Fehlerbehandlung 16 Jahre, Paketmanagement 9 Jahre — das Veränderungstempo ist extrem langsam. Vorsicht ist wichtig, aber wenn man zu sehr der Perfektion nachjagt, werden Entscheidungen immer weiter vertagt.
    • Gos Muster mehrfacher Rückgabewerte wird je nach Perspektive auch als unnatürlich angesehen. Kritikpunkt ist, dass mit Funktionen, die mehrere Typen zurückgeben, letztlich nur Variablenzuweisung möglich ist.
  • Ich verstehe nicht, warum man nicht einfach dem Rust-Stil folgt. Gerade jetzt, wo es Generics gibt, sollte sich etwas Ähnliches schnell umsetzen lassen. Ich kann auch die Kritik nicht nachvollziehen, dass der ?-Operator in Rust das Ignorieren von Fehlern fördere. Tatsächlich gibt es in Go viele Fälle, in denen man Fehler-Rückgabewerte ignoriert, ohne dass ein Compilerfehler entsteht. Nur wenn wie in Rust die Rückgabe eines Result-Typs erzwungen wird, lassen sich solche Fehler vermeiden. Wenn das aus Bequemlichkeitsgründen umstritten ist, müsste man konsequenterweise auch panic verbieten — so das starke Argument.

    • Eine Meinung dazu ist, dass Go Result nicht einführen kann, weil es keine Sum Types gibt und das ungewöhnliche Design verlangt, dass jeder Typ einen Zero Value haben muss.
    • Auf die Behauptung, ein Komfort-Feature wie der „?-Operator“ bedeute, dass man „wrapped errors nicht mehr verwenden wird“, lautet die Gegenrede, dass sich solche Features im Gegenteil so gestalten lassen, dass sie Wrapping fördern.
    • Als Nachteil von Komfort-Features im Rust-Stil wird angeführt, dass Verzweigungsfluss in einer Zeile verborgen wird, Debugging-Breakpoints schwerer zu setzen sind und die Syntax extrem auf Bubbling statt auf Enrichment/Handling ausgerichtet ist; Go habe aus genau solchen Gründen bestimmte Syntaxformen verworfen (z. B. den ternären Operator).
    • Selbst wenn man Rust-Stil direkt als Vergleich nimmt, wird technisch hinterfragt, was in Go überhaupt das Äquivalent dazu wäre.
    • Es kam das Feedback, man wolle Codebeispiele sehen, was denn seit Einführung der Generics konkret im Rust-Stil implementiert worden sei.
  • Anders als bei einer Checkbox-Diskussion über Feature-Übernahme wie bei Rust sollte eine Sprache als Teil einer gesamten Konsistenz entworfen werden. Nur weil man eine Featureliste vollständig abhaken kann, heißt das nicht, dass sie auch wirklich zum Wesen der Sprache passt.

    • Es gibt das Bild, dass Rust durch Design by Committee eine schwer lesbare und weniger konsistente Syntax bekommen habe.
    • Es gibt die Meinung, dass es eine „perfekte Lösung“ ohnehin nicht gibt.
    • Laut Umfrageergebnissen nannten nur 13 % die Fehlerbehandlung als Gos einziges gravierendes Problem, und es gibt nicht wenige Nutzer, die den aktuellen Zustand bevorzugen. Siehe Umfrageergebnisse