Der glückliche Pfad: Happy Path in Go erklärt

Wer sich mit Softwareentwicklung beschäftigt, stößt auf Begriffe und Leitsprüche, die uns helfen sollen, sauberen Code zu schreiben. Ein besonders bekannter und hilfreicher Spruch ist: “Return early, return often”. Eher selten hört man hingegen den Begriff “Happy Path”.
Welchen Zusammenhang die Begriffe haben und wie dir der “Happy Path” helfen kann, besseren Code zu schreiben, erfährst du in diesem Artikel.

In den letzten Monaten habe ich mich vermehrt mit der relativ jungen Programmiersprache Go (golang.org) befasst, deshalb gibt es heute Code-Beispiele in Go. Legen wir doch gleich los mit dem ersten Leitspruch.

Return Early, Return Often

Im Grunde sagt diese Regel, dass wir in unseren Funktionen und Methoden möglichst frühzeitig und oft aussteigen sollen. Bevor ich das näher erkläre, schauen wir uns folgende (vollkommen sinnfreie) Funktion als Negativ-Beispiel an:

// doSomething has lot's of work to do and responds with a string
func doSomething() string {

    var result string
    
    if canDoSomething() {
        // do it
        return "done"
    } 
    
    if trySomethingElse() {

        if hasRights() {    
            // do it
            return "done"
        } else {
            panic("No rights!")
        }

    } else {
        panic("Something went wrong!")
    }

}

Als Programmier-Neuling schreibt man ganz gerne mal in diesem Stiel. Es kommt uns natürlich vor, da wir auch in ähnlichen Prozessstrukturen denken. Für einen Neuling ist das auch absolut valider Code, weil er schlicht funktioniert.
Mit der Zeit kommt man dann darauf, dass man funktional gleichen Code aber auch unterschiedlich schreiben und aufbauen kann. Eine Alternative könnte zum Beispiel folgendermaßen aussehen:

// doSomething has lot's of work to do and responds with a string
func doSomething() string {

    if !canDoSomething {
        panic("Something went wrong!")
    }

    if !trySomethingElse() {
        panic("Something went wrong!")
    }

    if !hasRights() {
        panic("No rights!")
    }

    return "done"
}

Der neue Code wirkt sofort aufgeräumter. Anstatt einer tiefen Verschachtelung, haben wir eine relativ flache Codestruktur.

An dieser Stelle möchte ich zunächst die Möglichkeiten meiner Programmiersprache ausreizen, denn Go hat ein passendes, interessantes Feature: multiple Rückgabewerte. Jede Funktion und Methode kann mehrere Werte zurückgeben. Dieses Feature hat in der Go-Welt relativ schnell ein praktisches Paradigma erschaffen. Man erwartet für gewöhnlich, dass jede Funktion, welche einen nicht-erfolgreichen Ausgang haben kann, als letzten Rückgabewert den entstandenen Fehler berichtet. Da in Go Fehler auch nur Werte eines bestimmten Typs - konkret vom Typ error - sind, ist das in der Praxis sehr einfach.

// doSomething has lot's of work to do and responds with a string and a possible error
func doSomething() (string, error) {

    var result string

    if !canDoSomething {
        return result, fmt.Error("Something went wrong!")
    }

    if !trySomethingElse() {
        return result, fmt.Error("Something went wrong!")
    }

    if !hasRights() {
        return result, fmt.Error("No rights!")
    }

    result = "done"
    return result, nil
}

Happy Path

Happy Path ist ein feststehender Begriff in der Softwareentwicklung. Wikipedia beschreibt den Begriff so:

In the context of software or information modeling, a happy path is a default scenario featuring no exceptional or error conditions, and comprises the sequence of activities executed if everything goes as expected.

In den Codebeispielen oben haben wir den Verlauf dieses besonderen Pfads geändert. Ich versuche das mal mit folgenden Bildern zu verdeutlichen:

Der fehlerfreie Programmpfad ist jetzt eine gerade Linie, die sich bis zum Ende der Funktion zieht. Das steigert die Lesbarkeit des Programms enorm, da ich automatisch weiß, dass jeglicher eingerückter Code nur Businesslogik ist, oder eben einen Exception Path (das Gegenstück zum Happy Path) darstellt.

Wie immer sind solche Regeln natürlich nicht stur und zwingend immer einsetzbar. Manchmal brauchen wir auch einfach mehr als einen fehlerfreien Programmpfad. Es ist allerdings nicht von der Hand zu weisen, das eine Minimierung der Pfade und eine strukturelle Anpassung zu einem aufgeräumteren Code führen kann, der leichter verständlich ist.

Auch interessant