1 points par GN⁺ 2025-06-04 | 1 commentaires | Partager sur WhatsApp
  • La verbosité de la gestion des erreurs en Go figure depuis longtemps parmi les principales sources d’insatisfaction des utilisateurs
  • Diverses propositions d’amélioration syntaxique (par ex. check/handle, try, l’opérateur ?, etc.) ont été discutées et testées, mais toutes ont été rejetées faute d’un consensus suffisant dans la communauté
  • L’impact très large d’un changement de langage sur le code, les outils, la documentation, etc., ainsi que le principe propre à Go de préserver la simplicité, constituent des considérations majeures
  • En raison de la clarté de l’approche actuelle, de sa facilité pour le débogage et de la préférence de certains utilisateurs, les arguments en faveur d’un changement syntaxique restent limités
  • Aucun changement de syntaxe pour la gestion des erreurs n’est prévu dans un avenir prévisible, et les propositions associées seront toutes closes sans étude supplémentaire

Mise en avant du problème de verbosité de la gestion des erreurs en Go

  • L’une des plaintes historiques concernant Go est que la syntaxe de gestion des erreurs est excessivement verbeuse
  • En particulier, des motifs comme if err != nil se répètent partout dans le code
  • Plus un programme nécessite d’appels d’API, plus cette syntaxe devient visible, au point que le code de gestion des erreurs peut dépasser la logique métier elle-même
  • Dans les enquêtes annuelles auprès des utilisateurs, cette plainte revient constamment parmi les premières

Concertation avec la communauté et premières propositions

  • L’équipe Go accorde de l’importance aux retours de la communauté et a poursuivi ses recherches sur des améliorations de la gestion des erreurs
  • Lors des discussions du projet Go 2 en 2018, Russ Cox a formalisé les points clés du problème de la gestion des erreurs
    • La proposition de mécanisme check et handle de Marcel van Lohuizen y figurait
    • Elle incluait une analyse comparative avec des langages similaires et l’examen de diverses alternatives
  • Cette approche rendait effectivement le code plus concis, mais n’a pas été adoptée en raison de la complexité supplémentaire qu’elle introduisait

La proposition try et la suite

  • En 2019, une proposition bien plus simple de fonction intégrée try a été formulée
    • Elle n’implémentait en code que la fonctionnalité de check, en omettant handle
    • La proposition a été critiquée pour le fait qu’elle masquait le flux de contrôle, puis abandonnée face à la réaction négative de la communauté
  • Cette expérience a mis en évidence le risque des propositions trop abouties sans retour suffisant en amont
    • Elle a confirmé que, pour des changements d’ampleur, il est crucial de recueillir des avis plus larges dès les premières étapes de conception

Tentatives supplémentaires et diversité des propositions

  • De nombreuses variantes et approches alternatives de gestion des erreurs ont continué à apparaître dans la communauté
    • Ian Lance Taylor a récapitulé la situation via une umbrella issue, tandis que le Go Wiki, des blogs et d’autres sources ont continué à collecter des exemples
  • En 2024, une proposition visant à appliquer l’opérateur ? emprunté à Rust a été avancée
    • De petits tests d’utilisabilité ont suggéré qu’il était intuitif, mais là encore, aucun consensus n’a émergé malgré la diversité des avis

Blocage des discussions et conclusion

  • Malgré plus de trois propositions officielles ou semi-officielles et des centaines de propositions communautaires, toutes ont été rejetées faute d’un accord ou d’un consensus suffisant
  • Même le groupe des architectes internes de Go ne partage pas de position commune sur l’orientation à suivre
  • Il a donc été décidé de suspendre toute tentative de modification de la syntaxe de gestion des erreurs jusqu’à un éventuel changement de contexte ou à l’émergence d’un consensus particulier

Principaux arguments en faveur du maintien de l’approche actuelle

  • Si un sucre syntaxique avait été introduit dès la conception initiale du langage, la controverse n’aurait peut-être pas eu lieu, mais aujourd’hui l’écosystème est habitué à une approche utilisée depuis 15 ans
  • L’introduction d’une nouvelle syntaxe ferait inévitablement craindre un écart de style entre anciens et nouveaux utilisateurs ainsi qu’une perte de cohérence
  • Cela correspond aussi à la philosophie de conception de Go (ne pas faire la même chose de plusieurs façons) et à son attachement à la simplicité et à la cohérence
    • L’autorisation de redéclaration avec la déclaration courte de variable (:=) est elle aussi un changement secondaire né des besoins liés à la gestion des erreurs
  • Une syntaxe explicite de gestion des erreurs (via if) présente des avantages intuitifs pour la lecture du code, le débogage et le placement de points d’arrêt
  • Un changement de langage représente également une charge importante en raison de son ampleur et de son coût réels (code, documentation, outils, etc.)

Améliorations alternatives et orientation future

  • Le renforcement de la bibliothèque standard (par ex. l’introduction de cmp.Or) peut réduire une partie du code répétitif
  • Les IDE et outils de développement, avec le pliage de code, l’autocomplétion, l’usage des LLM, etc., permettent en pratique d’atténuer en partie cette verbosité
  • Parmi les principaux groupes d’utilisateurs de Go (par ex. les participants à Google Cloud Next), l’opinion dominante est défavorable à un changement du langage
    • Plus on utilise Go, moins ce problème de verbosité est ressenti dans la pratique

Arguments en faveur de la nécessité d’une amélioration syntaxique

  • D’après les retours des utilisateurs, une demande d’amélioration de la syntaxe de gestion des erreurs subsiste bel et bien
  • Une syntaxe de gestion des erreurs qui n’apporte pas seulement moins de caractères, mais aussi plus de clarté, pourrait contribuer à améliorer la qualité et la sûreté du code
  • Des recherches plus fines sont nécessaires sur la gestion des erreurs qui joue un rôle réel, au-delà de la simple vérification d’erreur

Conclusion finale et politique à venir

  • Constatant l’absence persistante de consensus ou de changement concret à ce jour, il est déclaré que toutes les discussions et propositions de changements syntaxiques du langage pour la gestion des erreurs sont interrompues pour un avenir prévisible
  • Les discussions et recherches menées jusqu’ici ont indirectement contribué à améliorer l’écosystème Go et ses processus
  • Si, à l’avenir, une définition plus claire du problème et un consensus plus net devaient émerger, les discussions pourraient reprendre
  • Pour le moment, la priorité restera de préserver la robustesse et la simplicité de Go plutôt que de tenter de nouvelles approches

1 commentaires

 
GN⁺ 2025-06-04
Commentaire sur Hacker News
  • Si vous voulez facilement suggérer que l’équipe Go aurait pu choisir d’autres alternatives, j’aimerais vraiment que vous consultiez d’abord le wiki Go2ErrorHandlingFeedback ou la recherche d’issues GitHub. Presque toutes les idées proposées ont déjà été discutées sérieusement, et en tant qu’utilisateur reconnaissant de l’approche transparente de l’équipe Go, j’apprécie chaque jour d’utiliser Go

    • Le brouillon de conception mentionne C++, Rust et Swift, mais j’ai du mal à y trouver des choses comme la do-notation / for-comprehensions / monadic-let des langages fonctionnels tels que Haskell, Scala ou OCaml. L’équipe Go donne l’impression d’être passée maître dans la conception de langages, mais se retrouve en réalité bloquée sur le problème de la gestion des erreurs à cause des limites d’un typage statique sans polymorphisme paramétrique, comme en Java. À mon avis, c’est un problème qui vient de la conception fondamentale du langage

    • Bien que le document ait été rédigé par des personnes intelligentes et expérimentées, je trouve très surprenant qu’aucune solution comme les monades Maybe/Either de Haskell et l’opérateur bind (do-notation) ne soit mentionnée nulle part. En pratique, ce n’est ni difficile ni pédant, et c’est une manière très élégante et éprouvée de propager les erreurs en toute sécurité. Je ne comprends pas pourquoi la communauté Go n’a pas essayé d’intégrer cela. Je suis reconnaissant que cette page existe, mais ignorer une solution aussi connue est difficile à comprendre

    • Presque tous les langages proposent diverses approches meilleures, alors je me demande pourquoi le problème ressort autant uniquement avec Go. Est-ce simplement qu’aucun consensus ne se dégage, ou bien y a-t-il une caractéristique propre à Go qui fait que les solutions des autres langages ne conviennent pas ?

    • On voit souvent, dans les critiques de Go, une tendance de personnes relativement peu expertes à supposer que les développeurs Go comprennent moins bien les langages qu’eux. En réalité, dans la plupart des cas, les développeurs Go ont au contraire bien plus d’expérience et en savent bien davantage. Les non-spécialistes ont tendance à penser qu’un langage avec plus de fonctionnalités est forcément meilleur, en oubliant que ce qui compte vraiment, c’est l’équilibre global

  • Je pense que les utilisateurs bénéficient du conservatisme de Go quand il s’agit d’ajouter de nouvelles fonctionnalités au langage. Dans le cas de Swift, les changements de fonctionnalités sont si nombreux qu’il est difficile à apprendre, et même sur un Mac récent il arrive souvent qu’un projet pourtant simple ne compile pas. Comme les mots-clés continuent d’augmenter et de changer, Swift perd en continuité d’usage, tandis que Go a pour force sa constance

  • Une fois, dans une fonction Go, je me suis trouvé dans une situation exceptionnelle où une fonction interne était censée produire une erreur, et si elle n’en produisait pas, la fonction appelante devait au contraire considérer cela comme une erreur. Dans cette structure peu courante, il fallait brancher sur if err == nil, et par habitude j’ai écrit if err != nil, ce qui m’a fait perdre beaucoup de temps avant de repérer l’erreur tant j’étais conditionné par le schéma habituel. Je me suis dit qu’un support syntaxique au niveau du langage pour distinguer le fréquent if err != nil du rare if err == nil aurait peut-être permis de réduire ce genre d’erreur

    • Chaque fois que j’écris if err == nil, j’ajoute un commentaire // inverted pour mettre en évidence le motif. Ce serait bien que le langage s’en charge automatiquement, mais pour l’instant cela permet au moins de rendre la différence plus visible
    • En fait, c’est plutôt un argument contre un changement de syntaxe. Le motif fréquent if err == nil { return ... } pourrait au contraire paraître plus maladroit dans le code. Beaucoup de gens préfèrent la manière actuelle de gérer les erreurs en Go parce qu’elle est claire et facile à lire
    • On peut rencontrer la même confusion avec un motif comme if fruit != "Apple", donc au fond ce n’est pas uniquement un problème propre à la gestion des erreurs, mais plus généralement un problème de branchement sur l’état. Une erreur est finalement traitée comme n’importe quelle autre valeur d’état
    • Il serait possible, via l’IDE ou la configuration de police, de rendre if err != nil comme un symbole spécial pour qu’il se fonde naturellement dans l’arrière-plan et attire moins l’attention, et de faire ressortir uniquement le motif différent if err == nil, afin de réduire les erreurs au niveau de l’éditeur
    • On pourrait aussi améliorer la lisibilité dans l’éditeur en affichant de façon abrégée des motifs comme if err … {
  • J’aime bien la gestion explicite des erreurs de Go. Je comprends simplement qu’une fonction réussit toujours (minimal error) ou peut échouer. Une fonction susceptible d’échouer doit impérativement être traitée avant de passer à l’étape suivante. Dans beaucoup de langages, les erreurs sont levées sous forme d’exceptions et remontent la pile jusqu’à être capturées, ce qui, selon moi, indique seulement où l’erreur s’est produite sans donner d’indice réellement utile. En Go, on dispose clairement des options suivantes : 1) ignorer l’erreur 2) retourner immédiatement en cas d’erreur 3) envelopper l’erreur pour ajouter des informations utiles 4) interpréter une erreur précise pour effectuer un traitement conditionnel (par exemple la convertir en 404). Dans Go2, j’aimerais essayer d’ajouter un type Result<Value, Failure> ou des types d’erreurs plus spécifiques et énumérables. Je pense qu’il serait plus approprié de l’introduire dans Go 2 pour préserver la compatibilité avec Go 1

    • D’après mon expérience, la politique de gestion des erreurs doit impérativement être décidée par l’appelant, et il est peu souhaitable de la traiter plus bas dans la pile. Au final, les erreurs deviennent facilement un simple travail répétitif d’enrobage et de remontée vers le haut
    • En réalité, la « gestion des erreurs de Go », la plupart des langages — langages fonctionnels, Rust, Java, etc., et non JavaScript ou Python — la proposent déjà. L’idée est qu’avec seulement les génériques, on peut implémenter la manière de Go dans n’importe quel langage. Si l’on compare seulement à JS ou Python, cela ne reste qu’un motif très courant
    • Le fait qu’« une fonction qui échoue doit forcément être traitée » est précisément, selon certains, le point faible de Go. En pratique, Go permet tout à fait d’ignorer complètement les erreurs, ce qui peut devenir une faiblesse si l’on veut produire un logiciel réellement robuste
    • Go2 risque au final de rester un « laboratoire qui ne sortira jamais », selon une opinion amère
  • Au début, je n’aimais pas beaucoup la manière dont Go gère les erreurs, mais après avoir lu le billet de blog errors-are-values et commencé à utiliser panic(err) aux endroits appropriés, j’en suis au contraire venu à en être très satisfait. Pour les états anormaux que le code parent ne doit pas traiter directement, l’utilisation de panic m’a permis de réduire fortement toutes les branches de gestion d’erreur parasites. Cette manière de gérer les erreurs m’aide beaucoup dans le travail réel

    • Certains rétorquent que ce raisonnement ne défend en rien la faiblesse de la gestion des erreurs de Go, et que même en l’améliorant, ses avantages ne disparaîtraient pas
    • Mention aussi que PHP permet une gestion des erreurs par niveau ou la suppression d’erreur au site d’appel avec l’opérateur @, et que bash dispose aussi de techniques comme -e pour gérer les erreurs
    • Quand j’ai vu pour la première fois le flux try/catch/finally en C#, je l’ai trouvé original, mais aujourd’hui je préfère au contraire une logique simple comme celle de Go. Même avec un volume de code plus élevé (Loc), je considère que la clarté du flux de code est un avantage réel
    • Il est mentionné que le modèle d’erreur basé sur les sum types de Rust relève lui aussi du paradigme « errors are values »
  • À l’affirmation selon laquelle, dès qu’on traite réellement les erreurs, la verbosité disparaît vite, on peut répondre de façon amusée en se demandant si générer manuellement une stack trace constitue vraiment un « traitement ». Selon cette définition de Go, une exception ne serait-elle pas elle aussi une forme de traitement ?

    • Je doute qu’une stack trace de plusieurs dizaines de lignes soit réellement une information claire. Personnellement, je trouve qu’une simple erreur enrichie sur une ligne est bien plus efficace, et cela aide aussi à garder des logs propres. En plus de dix ans d’utilisation de Go, je n’ai jamais eu besoin d’informations de pile aussi verbeuses incluant jusqu’aux fonctions du runtime
  • Je n’aime pas que cet article réduise le problème de la gestion des erreurs en Go à la simple idée que « la syntaxe est verbeuse ». À mon avis, les vrais problèmes sont plutôt : 1) les erreurs peuvent facilement être omises silencieusement ou ignorées par inadvertance 2) on ne peut pas facilement transmettre ou stocker le résultat d’une fonction comme une valeur 3) des erreurs imbriquées comme avec errors.Is s’articulent maladroitement avec le système de types 4) il est difficile de faire du switching sur les erreurs 5) les sentinel values sont très présentes dans la bibliothèque standard 6) l’articulation avec les génériques est mauvaise, au point de créer un besoin de packages dédiés

    • 90 % des programmeurs professionnels Go écrivent des cas de test pour chaque branche de retour d’erreur afin d’atteindre la couverture, alors que dans les langages à exceptions ce travail est inutile
    • Je pense qu’il est faux de dire que cet article présente « It’s too verbose » comme problème principal. Même en changeant la syntaxe, les améliorations essentielles resteraient limitées
    • Il existe aussi un point de vue selon lequel la très grande lenteur d’évolution de Go (les génériques ont aussi pris longtemps) est précisément un avantage
    • En tant que Googler, me voilà encore une fois déçu par les décisions de l’équipe Go
  • En Elixir (et Erlang), une fonction renvoie généralement un tuple {:ok, result} ou {:error, description}. Grâce à la syntaxe with d’Elixir, on peut regrouper la gestion des erreurs en bas du bloc, ce qui améliore beaucoup la lisibilité. Si Go adoptait quelque chose de proche d’un with, on pourrait n’enchaîner l’exécution que lorsque l’erreur est nil, avec un bloc de gestion placé tout en bas, ce qui rendrait le code plus lisible

    • En raison des problèmes de consensus communautaire, Go est extrêmement lent même à introduire des fonctionnalités pourtant précieuses comme les sum types de base, la gestion des erreurs ou le package management. Il a fallu 13 ans pour les génériques, 16 ans pour la gestion des erreurs, 9 ans pour la gestion des packages. La prudence est importante, mais à force de poursuivre la perfection, les décisions sont sans cesse repoussées
    • Le motif des retours multiples en Go peut aussi paraître anormal selon le point de vue adopté. La critique est qu’avec une fonction qui renvoie plusieurs types, la seule chose possible est en pratique l’affectation à des variables
  • Je ne comprends pas pourquoi ils ne reprennent pas simplement le style de Rust. Surtout maintenant qu’il y a les génériques, quelque chose de proche pourrait être implémenté rapidement. Je ne trouve pas convaincant l’argument selon lequel l’opérateur ? de Rust serait pratique mais encouragerait à ignorer les erreurs. En pratique, Go laisse très souvent passer des valeurs de retour d’erreur ignorées sans produire d’erreur de compilation. Pour vraiment éviter les erreurs humaines, il faudrait imposer un retour de type Result comme en Rust. Si cela devient controversé au nom de la commodité, alors il faudrait aussi interdire panic, non ?

    • Certains estiment que Go ne peut pas introduire Result parce qu’il n’a pas de sum types et qu’il repose sur une conception particulière où tous les types doivent avoir une zero value
    • À l’affirmation selon laquelle une fonctionnalité de confort comme « l’opérateur ? » conduirait à « ne plus utiliser d’erreurs enrichies », d’autres répondent qu’au contraire on peut concevoir une telle fonctionnalité de manière à encourager le wrapping
    • Parmi les inconvénients des fonctionnalités orientées commodité (style Rust), il est expliqué que le flux conditionnel se retrouve caché sur une seule ligne, qu’il devient plus difficile d’y placer des breakpoints de débogage, et que cela se concentre de façon excessive sur la remontée de l’erreur plutôt que sur son enrichissement ou son traitement, ce qui rapproche cela de syntaxes que Go a rejetées (par ex. l’opérateur ternaire)
    • Même si l’on voulait appliquer directement le style Rust en comparaison, une question technique se pose : qu’est-ce qui serait réellement équivalent en Go ?
    • Depuis l’introduction des génériques, certains demandent des exemples de code montrant ce qui aurait été implémenté « à la manière de Rust »
  • Je pense qu’il ne faut pas discuter de l’adoption des fonctionnalités comme on coche des cases à la manière de Rust ; un langage doit être conçu dans une cohérence d’ensemble. Le fait d’avoir coché toute une liste de fonctionnalités ne signifie pas automatiquement qu’elles correspondent à la nature profonde du langage

    • Rust a aussi l’image d’un langage conçu de manière trop collégiale, ce qui rendrait sa syntaxe difficile à lire et moins cohérente
    • Certains estiment qu’il n’existe pas de « solution parfaite »
    • Les résultats du sondage montrent que seulement 13 % des répondants ont identifié la gestion des erreurs comme le problème critique unique de Go, et il y a aussi pas mal d’utilisateurs qui préfèrent l’état actuel. Voir les résultats du sondage