Code review in Go: Was prüfen und wie man Feedback gibt
Code review (Code-Review) ist nicht nur eine Formalität, sondern eines der effektivsten Werkzeuge zur Verbesserung der Codequalität, zum Wissensaustausch im Team und zur Vermeidung von Bugs. In der Go-Community (Golang) hat Code review eine besondere Bedeutung, da die Sprache Einfachheit, Lesbarkeit und Idiomatik fördert. In diesem Artikel schauen wir uns an, worauf man bei der Überprüfung von Go-Code achten sollte und wie man Feedback gibt, das hilfreich und nicht verletzend ist.
1. Allgemeine Prinzipien des Code review in Go
Bevor wir zu den technischen Details kommen, ist es wichtig, an die Review-Kultur zu denken. Ihr Ziel ist es, den Code zu verbessern und einem Kollegen zu helfen, nicht Ihre eigene Richtigkeit zu beweisen. Hier sind einige goldene Regeln:
- Seien Sie konstruktiv. Schreiben Sie statt „das ist schlecht" lieber „hier könnte es eine Goroutine-Leck geben, lass uns einen Kontext hinzufügen".
- Fragen Sie, statt zu behaupten. „Warum hast du
sync.Mutexstatt eines Channels gewählt?" ist eine gute Frage. „Du bist dumm, du hättest einen Channel nehmen sollen" ist schlecht. - Verzögern Sie Reviews nicht. Versuchen Sie, innerhalb von 24 Stunden zu antworten (oder nach Absprache im Team).
- Konzentrieren Sie sich auf Logik und Architektur. Stilistische Korrekturen (Leerzeichen, Einrückungen) sollten Sie besser
gofmtüberlassen.
2. Was zuerst prüfen: Eine Checkliste für Go
Hier ist eine strukturierte Liste, worauf Sie bei der Überprüfung von Go-Code achten sollten. Unterteilen wir sie in logische Blöcke.
2.1. Fehlerbehandlung
Go hat keine Exceptions, Fehler sind Werte. Dies ist eine der Schlüsselidiome der Sprache.
- Überprüfen Sie alle zurückgegebenen Fehler. Das Ignorieren eines Fehlers mit
_ist nur in sehr seltenen Fällen zulässig (z. B. beim Schreiben inio.Discard). - Verwenden Sie
panicnicht für normale Fehler.panicist für nicht wiederherstellbare Zustände gedacht (z. B. Initialisierungsfehler des Servers). - Wickeln Sie Fehler ein. Verwenden Sie
fmt.Errorf("etwas ist schiefgelaufen: %w", err), um Kontext hinzuzufügen. Dies ermöglicht die Verwendung vonerrors.Is()underrors.As().
// Schlecht: Kontextverlustif err != nil { return err}
// Gut: Fehler einwickelnif err != nil { return fmt.Errorf("konnte Datei %s nicht öffnen: %w", filename, err)}2.2. Goroutinen und Nebenläufigkeit
Go ist bekannt für seine leichtgewichtige Nebenläufigkeit, aber sie ist auch die Quelle vieler Bugs.
- Goroutine-Leaks. Stellen Sie sicher, dass jede gestartete Goroutine irgendwann beendet wird. Verwenden Sie Kontext (
context.Context) zum Abbrechen. - Korrekte Verwendung von Channels. Wer einen Channel erstellt, schließt ihn auch. Das Schließen eines Channels liegt in der Verantwortung des Senders.
- Data Race. Vermeiden Sie gleichzeitiges Schreiben in dieselbe Variable ohne Synchronisation. Verwenden Sie Mutexe (
sync.Mutex) oder Atomiks (sync/atomic).
// Beispiel mit Kontext zum Abbrechen einer Goroutinefunc process(ctx context.Context, dataCh chan int) { for { select { case <-ctx.Done(): log.Println("Beende Arbeit") return case val, ok := <-dataCh: if !ok { return // Channel geschlossen } fmt.Println(val) } }}2.3. Go-Idiome und -Stil
Go ist eine Sprache, in der die Lesbarkeit des Codes am höchsten geschätzt wird.
- Variablennamen. Kurze Namen für kurze Gültigkeitsbereiche.
ifür eine Schleife,rfür einen Reader. Für globale Variablen längere Namen. - Vermeiden Sie „magische Zahlen". Verwenden Sie Konstanten.