Réusinage de code legacy en Go : guide complet de A à Z

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Introduction : pourquoi le code legacy en Go nécessite-t-il un réusinage ?

\n

Go (Golang) est réputé pour sa simplicité, ses performances et ses outils intégrés pour le parallélisme. Cependant, tout projet accumule avec le temps une "dette technique" : variables globales, absence d'interfaces, nombres magiques, fonctions illisibles et absence de tests. Un tel code est appelé legacy. Le réusinage ne consiste pas à tout réécrire de zéro, mais à améliorer progressivement la structure sans modifier le comportement externe. Dans cet article, nous détaillerons un plan étape par étape pour le réusinage de code legacy en Go, de l'analyse à la mise en œuvre.

\n\n

Étape 1 : Diagnostic du code legacy

\n

Avant de modifier quoi que ce soit, il faut comprendre ce que nous avons. Les principaux "drapeaux rouges" dans les projets Go :

\n
    \n
  • Variables globales (var db *sql.DB) — compliquent les tests et le travail parallèle.
  • \n
  • Fonctions monstres (plus de 50-100 lignes) — rendent le code illisible.
  • \n
  • Absence d'interfaces — couplage fort avec des implémentations spécifiques.
  • \n
  • Chaînes/nombres magiques — rendent la recherche et la modification difficiles.
  • \n
  • Absence de tests — réusiner sans tests, c'est marcher sur de la glace mince.
  • \n
\n

Outils d'analyse : utilisez go vet et staticcheck pour trouver des constructions suspectes. gocyclo est également utile pour calculer la complexité cyclomatique.

\n

Exemple de code problématique :

\n
package main\n\nimport "fmt"\n\nvar config map[string]string // variable globale\n\nfunc main() {\n    config = map[string]string{"port": "8080"}\n    startServer()\n}\n\nfunc startServer() {\n    // chaîne magique "port"\n    fmt.Println("Démarrage sur le port :", config["port"])\n    // ... 100 lignes de code\n}
\n\n

Étape 2 : Création d'un filet de sécurité avec des tests

\n

Sans tests, le réusinage devient un jeu de devinettes. Votre tâche est de couvrir la logique clé avec des tests avant les modifications. Utilisez l'approche Golden Files (fichiers de référence) pour les sorties complexes ou les tests d'intégration.

\n

Exemple d'ajout d'un test pour une fonction de code legacy :

\n
// legacy.go (original)\nfunc CalculateDiscount(price float64, isPremium bool) float64 {\n    if isPremium {\n        return price * 0.9\n    }\n    return price * 0.95\n}\n\n// legacy_test.go\npackage main\n\nimport "testing"\n\nfunc TestCalculateDiscount_Premium(t *testing.T) {\n    result := CalculateDiscount(100.0, true)\n    expected := 90.0\n    if result != expected {\n        t.Errorf("Attendu %f, obtenu %f", expected, result)\n    }\n}\n\nfunc TestCalculateDiscount_Regular(t *testing.T) {\n    result := CalculateDiscount(100.0, false)\n    expected := 95.0\n    if result != expected {\n        t.Errorf("Attendu %f, obtenu %f", expected, result)\n    }\n}
\n

Conseil : utilisez go test -cover pour évaluer la couverture. Essayez de couvrir au moins 70 % de la logique métier avant de commencer le réusinage.

\n\n

Étape 3 : Décomposition progressive et extraction d'interfaces

\n

L'objectif principal du réusinage est de rendre le code testable et flexible. Commencez par extraire des interfaces pour les dépendances externes (bases de données, clients HTTP, systèmes de fichiers).

\n

3.1 Extraction d'une interface au lieu d'une utilisation directe de la BDD

\n

Supposons que nous ayons une fonction qui travaille directement avec *sql.DB :

\n
// code legacy\nfunc GetUser(db *sql.DB, id int) (*User, error) {\n    row := db.QueryRow("SELECT id, name FROM users WHERE id = ?", id)\n    // ...\n}
\n

Réusinage : créons une interface UserRepository :

\n
type UserRepository interface {\n    GetByID(id int) (*User, error)\n}\n\ntype userRepo struct {\n    db *sql.DB\n}\n\nfunc (r *userRepo) GetByID(id int) (*User, error) {\n    row := r.db.QueryRow("SELECT id, name FROM users WHERE id = ?", id)\n                

Blogs

Book Recommendations