Revue de code en Go : ce que dirait un senior et pourquoi votre code ne passerait pas

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

Votre code Go : de « ça marche » à « approuvé »

\n\n

Vous avez écrit du code en Go. Il compile. Les tests sont verts. Vous êtes fier. Mais voilà que la revue de code d'un développeur senior arrive, et cinq minutes plus tard, votre pull request est couverte de commentaires rouges. Ça vous dit quelque chose ?

\n\n

Go est un langage où la « simplicité » est trompeuse. Derrière une apparente facilité se cachent des dizaines de pièges qu'un œil expérimenté repère immédiatement. Dans cet article, nous allons passer en revue les remarques les plus fréquentes que les seniors laissent lors d'une revue de code Go. Vous apprendrez non seulement ce qui ne va pas, mais aussi comment le corriger pour que votre code soit approuvé du premier coup.

\n\n

C'est parti pour analyser les erreurs typiques lors d'une revue de code Golang.

\n\n

1. Gestion des erreurs : pas seulement if err != nil

\n\n

Problème : ignorer les erreurs ou retour « nu »

\n\n

Le problème le plus fréquent chez les débutants est une gestion des erreurs insuffisamment informative. Se contenter de remonter l'erreur, c'est 50% du travail. Un senior demandera toujours : « Qu'est-ce qui a mal tourné ? Où ? ».

\n\n
// Mauvais : le senior dira "ajoute du contexte"\nfunc ReadConfig(path string) (*Config, error) {\n    data, err := os.ReadFile(path)\n    if err != nil {\n        return nil, err // ❌ Aucun contexte\n    }\n    // ...\n}\n
\n\n

La bonne approche consiste à encapsuler les erreurs avec fmt.Errorf et le paramètre %w (pour une vérification ultérieure avec errors.Is ou errors.As).

\n\n
// Bien : le senior approuvera\nfunc ReadConfig(path string) (*Config, error) {\n    data, err := os.ReadFile(path)\n    if err != nil {\n        return nil, fmt.Errorf("lire le fichier de configuration %s : %w", path, err) // ✅ Contexte + chaîne\n    }\n    // ...\n}\n
\n\n

Problème : répétition de if err != nil

\n\n

Go est connu pour sa verbosité dans la gestion des erreurs. Mais cela ne signifie pas qu'il faut accepter le copier-coller. Un senior proposera de déplacer les vérifications répétitives dans des helpers ou d'utiliser le modèle « errgroup » pour les goroutines.

\n\n
    \n
  • Ne faites pas ceci : répéter 10 fois if err != nil { return ... } dans une même fonction est normal, mais si le modèle se répète dans différentes fonctions, il est temps de refactoriser.
  • \n
  • Conseil du senior : Utilisez defer pour fermer les ressources et vérifier les erreurs de fermeture (oui, file.Close() retourne aussi une erreur).
  • \n
\n\n

2. Concurrence : courses de données et mauvais canaux

\n\n

Problème : accès à une variable partagée sans synchronisation

\n\n

Go est un langage où les goroutines sont peu coûteuses, mais les courses de données le sont beaucoup. Un senior remarquera immédiatement si vous écrivez dans une map ou un slice depuis plusieurs goroutines sans mutex.

\n\n
// Mauvais : data race\nvar counter int\nfor i := 0; i < 100; i++ {\n    go func() {\n        counter++ // ❌ Course !\n    }()\n}\n
\n\n

Solution : utiliser sync.Mutex ou des atomiques. Mais le senior dira : « Ne serait-il pas plus simple d'utiliser des canaux ? ».

\n\n
// Bien : canaux pour la synchronisation\nch := make(chan int, 100)\nfor i := 0; i < 100; i++ {\n    go func() {\n        ch <- 1\n    }()\n}\nvar counter int\nfor i := 0; i < 100; i++ {\n    counter += <-ch // ✅ Sans courses\n}\n
\n\n

Problème : fuite de goroutines

\n\n

Vous avez oublié de fermer un canal ? La goroutine reste bloquée pour toujours. Lors d'une revue de code Go, un senior vérifiera certainement si une goroutine n'est pas bloquée sans select avec ctx.Done().

\n\n
// Mauvais : la goroutine peut rester bloquée indéfiniment\ngo func() {\n    for msg := range ch {\n        process(msg)\n    }\n}()\n// Si ch n'est jamais fermé — fuite mémoire\n
\n\n

Le bon modèle est de toujours passer un context.Context et d'écouter le signal d'annulation.

\n\n
// Bien : protection contre les fuites\ngo func(ctx context.Context) {\n    for {\n        select {\n        case msg, ok := <-ch:\n            if !ok {\n                return // canal fermé\n            }\n            process(m                

Blogs

Book Recommendations