Le Go idiomatique moderne, génériques compris : penser en Go au lieu de traduire depuis un autre langage.
Pourquoi ce livre
La préface raconte que Bodner voulait appeler son livre « Boring Go » : « bien écrit, Go est ennuyeux ». Et il précise aussitôt : « ennuyeux ne veut pas dire trivial ». Go n'a pas d'héritage, pas d'exceptions, pas de surcharge de fonctions ni d'opérateurs : la liste de ce qui manque est le produit. Un langage petit, prévisible, qui ne vous réveille pas à 3h du matin.
Le piège, c'est qu'on peut écrire du Go qui ressemble à du Java ou du Python. Bodner prévient : « vous serez malheureux du résultat, et vous vous demanderez pourquoi tout ce bruit autour de Go ». Tout le livre tient dans cette promesse : apprendre à écrire du Go qui ressemble à du Go. La 2e édition (janvier 2024) couvre les génériques, le fuzzing, et le premier changement cassant de l'histoire du langage (les variables de boucle de Go 1.22).
Les idées qui restent
1Chaque déclaration dit quelque chose au lecteur
En Go, le choix entre var et := n'est pas une affaire de goût. var x int dit « je veux la valeur zéro, c'est voulu ». x := 10 est la forme normale dans une fonction. Toute variable non utilisée est une erreur de compilation, toute conversion de type est explicite : « les conversions de types sont un des endroits où Go choisit d'ajouter de la verbosité en échange de la clarté » (p. 27). La valeur zéro garantie (0, "", nil) supprime toute une famille de bugs hérités de C. Le principe qui revient à chaque page : « écrivez vos programmes d'une façon qui rend vos intentions claires » (p. 17).
2Une slice est une petite structure {pointeur, longueur, capacité}, et tous les pièges en découlent
Le chapitre 3 donne LE modèle mental de Go. Une slice n'est pas un tableau : c'est trois champs, un pointeur vers un tableau de fond, une longueur, une capacité. Tout s'explique alors mécaniquement. append réalloue quand la capacité est pleine (d'où le x = append(x, ...) obligatoire). Et surtout, découper une slice ne copie rien :
x := []string{"a", "b", "c", "d"}
y := x[:2] // len 2, cap 4 : même tableau de fond
y = append(y, "z")
fmt.Println(x) // [a b z d] : x a changé sans qu'on le touche
« Prudence quand vous découpez une slice : les deux slices partagent la même mémoire » (p. 49). La protection existe, méconnue : la « full slice expression » y := x[:2:2], dont le troisième index borne la capacité et force un nouveau tableau au prochain append.
3Tout est passé par copie, même ce qui n'en a pas l'air
Go est « call by value » partout : chaque paramètre de fonction est une copie. Les maps semblent faire exception (les modifications sont visibles depuis l'appelant), les slices à moitié (on peut modifier les éléments, pas la longueur). L'explication tient en une phrase de Bodner : « chaque type en Go est un type valeur. Simplement, parfois, la valeur est un pointeur » (p. 116). Même logique pour defer : les arguments sont évalués au moment du defer, pas à la sortie de la fonction. Une fois ce modèle en tête, plus aucun comportement de passage de paramètre ne surprend.
4Les pointeurs sont un dernier recours, et la mutabilité se lit dans la signature
Là où Java et Python cachent des références partout, Go rend la mutabilité visible : un paramètre pointeur annonce « cette fonction peut modifier votre donnée ». Bodner en fait une règle : préférez retourner une nouvelle valeur plutôt que muter via un pointeur, parce que « les valeurs rendent plus facile de comprendre comment et quand vos données sont modifiées » (p. 124). L'argument performance est contre-intuitif : des données accessibles via des pointeurs dispersés en RAM sont environ cent fois plus lentes à lire que des valeurs contiguës (étude de Forrest Smith citée p. 138). Le ramasse-miettes de Go est conçu pour la latence (pauses sous la demi-milliseconde), et moins on alloue sur le tas, moins il travaille.
5Les interfaces sont implicites, et c'est du découplage gratuit
Le chapitre 7 est le cœur du livre. En Go, un type implémente une interface sans le déclarer : il suffit d'avoir les bonnes méthodes. Bodner appelle ça du duck typing vérifié par le compilateur : c'est le consommateur qui définit l'interface dont il a besoin, le fournisseur n'en sait rien. D'où la maxime « Accept interfaces, return structs » (acceptez des interfaces, retournez des structs), que Bodner attribue à Jack Lindamood (p. 162). Le chapitre assume jusqu'à son titre de section : « Go n'est pas particulièrement orienté objet (et c'est très bien) » (p. 178). Et il démine LE piège du langage : une variable d'interface qui contient un pointeur nil n'est PAS égale à nil, parce qu'une interface n'est nil que si son type ET sa valeur le sont.
6Les erreurs sont des valeurs, et le compilateur vous force à les regarder
« Pour ceux qui ont l'habitude des exceptions, l'approche de Go semble anachronique. Mais des principes solides d'ingénierie logicielle la sous-tendent » (p. 203). Le raisonnement de Bodner tient en deux points : les exceptions créent des chemins d'exécution invisibles dans le code ; et comme Go exige que toute variable déclarée soit lue, retourner l'erreur force l'appelant à la traiter, ou à l'ignorer explicitement avec _. La devise du chapitre : « le Go idiomatique préfère le code qui détaille explicitement les conditions d'échec au code plus court qui gère tout en ne disant rien » (p. 220). panic existe, mais pour les bugs irrécupérables, pas comme mécanisme d'exceptions déguisé.
7Les génériques, enfin, mais sans zèle
Go a attendu plus de dix ans avant d'ajouter les génériques, et le chapitre 8 (nouveau dans cette édition) explique le compromis : compilation rapide, binaires raisonnables, lisibilité. Les contraintes sont des interfaces, en cohérence totale avec le reste du langage. Et Bodner tempère l'enthousiasme : « ne vous sentez pas obligé de tout convertir aux paramètres de type immédiatement » (p. 199). Sa mise en garde la plus utile : ne remplacez pas un paramètre d'interface par un générique en espérant gagner en performance. Sur une fonction triviale, l'appel devient environ 30 % plus lent en Go 1.20, parce que le compilateur ajoute des résolutions à l'exécution au lieu de générer une fonction par type comme C++ (p. 200-201).
8La concurrence structure un problème, elle ne l'accélère pas
Le chapitre 12 s'ouvre sur une douche froide : « plus de concurrence ne rend pas les choses automatiquement plus rapides, et peut rendre le code plus dur à comprendre » (p. 288). La concurrence n'est pas le parallélisme : c'est « un outil pour mieux structurer le problème qu'on essaie de résoudre ». Le reste du chapitre est un manuel d'hygiène : la goroutine qui ne se termine jamais garde sa mémoire pour toujours (la « fuite de goroutine », p. 299), le done channel l'arrête proprement, les channels coordonnent, les mutex protègent un champ de struct, et la devise de la communauté tranche : « partagez la mémoire en communiquant, ne communiquez pas en partageant la mémoire » (p. 313).
Trois choses que je ne savais pas avant de le lire
- Go 1.22 a opéré le premier changement cassant de l'histoire du langage : chaque itération de boucle crée désormais une nouvelle variable, ce qui tue le bug classique des goroutines qui capturent toutes la dernière valeur. Le mécanisme qui le permet : la directive
godu go.mod, qui fige le comportement par module (préface + ch. 4). - 100 % de couverture de tests ne prouve rien : Bodner le démontre en direct avec une fonction buggée qui passe une suite de tests à couverture totale (« la couverture de code est nécessaire mais pas suffisante », p. 386).
- Nommer son receiver
thisouselfest explicitement non idiomatique (p. 144) : Go refuse jusqu'au vocabulaire de l'orienté objet classique.
Mon avis, honnêtement
C'est le livre que je recommande pour apprendre Go, et la raison est simple : il explique pourquoi. La plupart des livres d'introduction listent la syntaxe ; celui-là justifie chaque choix du langage, et c'est exactement ce qu'il faut pour Go, un langage dont les choix dérangent (pas d'exceptions, pas d'héritage, un seul mot-clé de boucle). Bodner écrit clair, court, progressif, et chaque piège est montré en code qui tient en cinq lignes.
Les réserves. Il n'y a pas d'exemple fil rouge mémorable : on retient des règles, pas des histoires. Les chapitres sur l'outillage et les modules (10 et 11) sont utiles mais vieilliront plus vite que le reste. Et le livre assume d'être un livre de deuxième langage : si vous n'avez jamais programmé, commencez ailleurs.
Ce qui m'a le plus marqué, c'est la cohérence entre le langage et le livre. Go dit : la clarté vaut mieux que l'astuce. Le livre applique la consigne à chaque page, jusqu'à déconseiller ses propres nouveautés quand elles n'apportent rien (les génériques « sans zèle »). En 2026, où l'IA génère du Go qui ressemble à du Java traduit, un livre qui définit noir sur blanc ce que « idiomatique » veut dire est devenu un outil de relecture autant qu'un manuel.
Odilon
Toujours valable en 2026 ?
Oui : la 2e édition (janvier 2024) couvre Go 1.20 à 1.22, génériques, fuzzing et nouvelles règles de boucle comprises. Il lui manque seulement les itérateurs arrivés avec Go 1.23 (range sur une fonction). Tout le reste est le Go d'aujourd'hui.
Pour qui ?
Lisez-le si
- Vous venez de PHP, Java, Python ou JS et voulez apprendre Go sans le traduire depuis votre ancien langage
- Vous écrivez déjà du Go mais les slices, les interfaces nil ou les fuites de goroutines vous ont déjà mordu
- Vous relisez du Go généré par IA et voulez savoir ce que « idiomatique » veut dire, précisément
- Vous avez suivi un tutoriel Go et cherchez le livre qui explique le pourquoi derrière la syntaxe
Passez si
- Vous n'avez jamais programmé : c'est un livre de deuxième langage, il suppose les bases acquises
- Vous cherchez un livre d'architecture ou de microservices : ici on apprend le langage, pas le système
- Vous voulez du Go avancé (runtime, scheduler, assembleur) : ce n'est pas le sujet
Pour aller plus loin
Le cours Go de ce site pratique exactement ces fondations, goroutines et channels compris. La fiche Designing Data-Intensive Applications de cette bibliothèque complète le versant systèmes (ce qui casse quand c'est distribué). Et le cours Tests prolonge le chapitre 15.
Commentaires (0)