- Plusieurs choix de conception du langage Go ont été faits de manière inutile ou en ignorant l’expérience accumulée auparavant
- Le problème de gestion de la portée des variables d’erreur rend la lisibilité du code et la recherche de bugs plus difficiles
- La double nature de
nil, l’utilisation mémoire, la portabilité du code et d’autres aspects révèlent une conception peu intuitive et déconnectée de la réalité
- Les limites de l’instruction
defer ainsi que la manière dont la bibliothèque standard gère les situations exceptionnelles compliquent la garantie de la sûreté face aux exceptions
- Des problèmes accumulés comme la gestion mémoire et le traitement insuffisant de l’UTF-8 nuisent à long terme à la qualité des bases de code Go
Critique de long terme du langage Go
- Comme je l’ai expliqué dans de précédents billets (Why Go is not my favourite language, Go programs are not portable), je souligne depuis plus de dix ans plusieurs problèmes du langage Go
- En particulier, les choix de conception inutiles qui ignorent des bonnes pratiques déjà bien connues paraissent de plus en plus regrettables
Le manque d’intuition dans la portée des variables d’erreur
- La syntaxe de Go élargit inutilement la portée de la variable d’erreur (
err), ce qui augmente le risque d’erreurs
- Dans le code d’exemple, la variable
err reste vivante pendant toute la fonction et est réutilisée, ce qui nuit à la lisibilité et à la maintenabilité du code
- Même les développeurs expérimentés peuvent perdre du temps et être induits en erreur lors de la recherche de bugs à cause de ces questions de portée
- La syntaxe n’autorise pas de moyen approprié de limiter localement cette variable
Les deux formes de nil
- Dans Go, il existe une confusion car
nil ne se comporte pas de la même manière selon qu’il s’agit d’un type interface ou d’un type pointeur
- Comme dans l’exemple ci-dessous, même si
s (pointeur) et i (interface) reçoivent tous deux nil, s==i est évalué différemment, ce qui montre un comportement incohérent
- C’est précisément le genre de problème que l’on cherche généralement à éviter dans la gestion de
null, et cela donne l’impression d’une conception insuffisamment réfléchie
Les limites de la portabilité du code
- L’utilisation de commentaires pour la compilation conditionnelle est nettement inefficace du point de vue de la maintenance et de la portabilité
- Quiconque a déjà développé un logiciel réellement portable sait qu’une telle approche est lourde et source d’erreurs
- L’expérience accumulée historiquement en matière de portabilité du code et de cas pratiques a été ignorée
- Voir Go programs are not portable pour plus de détails
Le manque de clarté sur la propriété avec append
- La relation de propriété entre la fonction
append et les slices n’est pas claire, ce qui rend le code difficile à prévoir
- L’exemple montre qu’il est difficile de savoir à l’avance quel effet un
append effectué dans une fonction foo aura réellement sur l’original
- Le langage accumule ainsi des « quirks » qu’il faut connaître, ce qui favorise les erreurs
Une conception inaboutie de defer
- Go ne fournit pas de support clair pour la libération des ressources, contrairement au principe RAII (Resource Acquisition Is Initialization)
- Par rapport aux structures de gestion des ressources de Java et Python, Go ne permet pas de savoir clairement quelles ressources doivent être libérées avec
defer
- Comme le montre l’exemple des fichiers, il faut même gérer soi-même les problèmes de double fermeture, et l’ordre ainsi que la méthode corrects de libération restent flous
La gestion des exceptions dans la bibliothèque standard
- Go ne prend pas en charge les exceptions explicites, mais des situations exceptionnelles comme
panic existent malgré tout
- Dans certains cas,
panic ne provoque pas un arrêt complet du programme et peut au contraire être absorbé
- La bibliothèque standard (
fmt.Print, serveur HTTP, etc.) contient des schémas où les exceptions sont ignorées, ce qui rend impossible toute véritable garantie de sûreté face aux exceptions
- En pratique, écrire du code sûr face aux exceptions reste indispensable, sans qu’il soit possible d’utiliser directement des exceptions
Le traitement de l’UTF-8 et des chaînes
- Même si l’on place des données binaires arbitraires dans le type
string, Go fonctionne sans validation particulière
- Des noms de fichiers créés avant l’ère de l’encodage UTF-8 peuvent ainsi être silencieusement omis
- Lors de sauvegardes, cela peut entraîner la perte de données importantes, ce qui reflète une approche simpliste peu alignée avec la réalité du terrain
Les limites de la gestion mémoire
- Il est difficile de contrôler directement l’utilisation de la RAM, et la fiabilité du GC (garbage collector) a aussi ses limites
- La consommation mémoire de Go augmente, ce qui finit par créer des problèmes de coût et de performance
- Dans des environnements à multiples instances ou conteneurs, des problèmes réels de coût et de passage à l’échelle apparaissent
Conclusion : il existait de meilleures voies
- Alors qu’il existait déjà des conceptions de langage ayant fait leurs preuves, Go les a ignorées sur de nombreux points
- Contrairement aux problèmes des premières versions de Java, de meilleures approches existaient déjà au moment de la sortie de Go
Références
Aucun commentaire pour le moment.