Refactoring von Legacy-Code in Go: Schritt-für-Schritt-Anleitung

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Refactoring von Legacy-Code in Go: Eine Schritt-für-Schritt-Anleitung



Die Arbeit mit Legacy-Code in Go ist ein unvermeidlicher Teil des Entwicklerlebens. Oft wurde solcher Code in Eile geschrieben, ohne Tests, mit globalen Variablen und monolithischen Funktionen. Allerdings ist das Refactoring von Legacy-Code in Go nicht einfach nur Umschreiben, sondern ein strategischer Prozess, der Vorsicht und einen systematischen Ansatz erfordert. In diesem Artikel werden wir untersuchen, wie man bestehenden Code sicher und effektiv verbessern kann, ohne das Produkt zu beschädigen.



Warum ist das Refactoring von Legacy-Code in Go schwierig?



Legacy-Code in Go hat oft charakteristische Probleme:

  • Globale Variablen — erschweren das Testen und die parallele Ausführung.
  • Monolithische Funktionen — hunderte Zeilen lange Funktionen, die alles auf einmal erledigen.
  • Fehlende Interfaces — starke Bindung an konkrete Implementierungen.
  • Keine Tests — Refactoring ohne Tests ist wie ein Spaziergang durch ein Minenfeld.


Aber Go bietet leistungsstarke Werkzeuge für die schrittweise Verbesserung von Code. Die Hauptsache ist, in kleinen Schritten vorzugehen und die Funktionsfähigkeit des Systems in jeder Phase zu erhalten.



Schritt 1: Vorbereitung und Testen



Bevor Sie Code ändern, müssen Sie ein „Sicherheitsnetz“ schaffen — Tests. Selbst wenn der Code nicht getestet wurde, können Sie Charakterisierungstests (characterization tests) schreiben. Sie fixieren das aktuelle Verhalten des Systems, damit Sie sicherstellen können, dass nach dem Refactoring nichts kaputt gegangen ist.



package main

import "testing"

func TestCalculateTotal(t *testing.T) { // Angenommen, wir haben eine Legacy-Funktion CalculateTotal result := CalculateTotal(100, 20) expected := 120.0

if result != expected { t.Errorf("CalculateTotal(100, 20) = %f; want %f", result, expected) }}


Führen Sie die Tests aus und stellen Sie sicher, dass sie bestanden werden. Jetzt haben Sie eine Basis für sichere Änderungen.



Schritt 2: Interfaces extrahieren



Eines der Hauptprobleme von Legacy-Code in Go ist die starke Kopplung zwischen Komponenten. Die Lösung ist das Extrahieren von Interfaces. Dies ermöglicht es, reale Abhängigkeiten beim Testen durch Mocks zu ersetzen und vereinfacht zukünftige Implementierungswechsel.



Betrachten wir ein Beispiel. Angenommen, wir haben eine Funktion, die Daten direkt in einer Datenbank speichert:



package main

import "database/sql"

type User struct { ID int Name string}

func SaveUser(db *sql.DB, user User) error { _, err := db.Exec("INSERT INTO users (name) VALUES ($1)", user.Name) return err}


Problem: Wir können diese Funktion ohne eine reale Datenbank nicht testen. Lösung — ein Interface extrahieren:



package main

// UserStorage definiert den Vertrag für die Speicherung von Benutzerntype UserStorage interface { Save(user User) error}

type UserSaver struct { storage UserStorage}

func NewUserSaver(storage UserStorage) *UserSaver { return &UserSaver{storage: storage}}

func (s *UserSaver) SaveUser(user User) error { return s.storage.Save(user)}


Jetzt können wir einen Mock für das Testen erstellen:



package main

import "testing"

type mockStorage struct{}

func (m *mockStorage) Save(user User) error { return nil // immer erfolgreich}

func TestUserSaver_SaveUser(t *testing.T) { saver := NewUserSaver(&mockStorage{}) err := saver.SaveUser(User{ID: 1, Name: "Alice"}) if err != nil { t.Errorf("unexpected error: %v", err) }}


Schritt 3: Dependency Injection



Nach dem Extrahieren von Interfaces ist der nächste Schritt die Abhängigkeitsinjektion (Dependency Injection, DI). Anstatt Abhängigkeiten innerhalb einer Funktion zu erstellen (z. B. eine Datenbankverbindung zu öffnen), übergeben wir sie von außen.



Betrachten wir typischen Legacy-Code:



package                

Blogs

Buchempfehlungen