Votre code Go : de « ça marche » à « approuvé »
\n\nVous 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\nGo 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\nC'est parti pour analyser les erreurs typiques lors d'une revue de code Golang.
\n\n1. Gestion des erreurs : pas seulement if err != nil
\n\nProblème : ignorer les erreurs ou retour « nu »
\n\nLe 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\nLa 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).
// 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\nProblème : répétition de if err != nil
\n\nGo 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
deferpour fermer les ressources et vérifier les erreurs de fermeture (oui,file.Close()retourne aussi une erreur). \n
2. Concurrence : courses de données et mauvais canaux
\n\nProblème : accès à une variable partagée sans synchronisation
\n\nGo 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\nSolution : utiliser sync.Mutex ou des atomiques. Mais le senior dira : « Ne serait-il pas plus simple d'utiliser des canaux ? ».
// 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\nProblème : fuite de goroutines
\n\nVous 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().
// 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\nLe bon modèle est de toujours passer un context.Context et d'écouter le signal d'annulation.
// 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