3 points par GN⁺ 2024-11-27 | 2 commentaires | Partager sur WhatsApp
  • Face aux critiques de Go, les réponses récurrentes comme « les grandes entreprises l’utilisent aussi », « il a aussi des qualités » ou « l’exactitude coûte cher » peuvent masquer le fait que des défauts de conception du langage s’accumulent en coûts à long terme
  • Go est fort sur le runtime asynchrone, le GC, ainsi que les outils de gestion de paquets, de refactoring et de cross-compilation, mais il faut accepter avec cela une toolchain personnalisée, un build system, une convention d’appel, un GC unique et tout l’écosystème
  • L’absence de sum types, le manque de contrôle sur la mutabilité, la zero value, ainsi que les comportements différents des slices, maps et channels nil empêchent le compilateur de garantir de nombreuses invariants
  • Sans cgo, il est difficile d’intégrer Go avec d’autres langages et outils ; avec cgo, on hérite des contraintes du GC, des règles sur les pointeurs, de l’inclusion du runtime et du poids de grosses bibliothèques statiques, si bien que la frontière réseau devient le point d’intégration le plus réaliste
  • L’idée de « faire juste un prototype en Go puis réécrire plus tard » peut en pratique augmenter les coûts d’exploitation, à cause de l’évitement de la réécriture, du couplage avec le code Go existant, et de la charge liée aux linters, à la documentation et aux vérifications manuelles

Les arguments défensifs qui reviennent sans cesse face aux critiques de Go

  • Depuis l’article de 2020 I want off Mr Golang’s Wild Ride, les réactions autour des critiques de Go se répètent continuellement
    • Réaction disant que le cas Windows n’est pas un domaine où Go excelle
    • Réaction disant qu’on ne parle pas des qualités de Go
    • Réaction disant qu’on ne comprend pas les compromis choisis par Go
    • Réaction disant que, puisque de grandes entreprises utilisent Go, il ne peut pas être si mauvais
    • Réaction disant que le coût pour modéliser « correctement » un problème est élevé, donc que discuter d’exactitude n’a pas de sens
    • Réaction disant que Rust a aussi des défauts, donc que la critique de Go est invalide
  • Le point essentiel n’est pas de savoir si Go a des qualités, mais si les arguments défensifs répétés pour continuer à l’utiliser cachent les coûts réels

La méthode qui consiste d’abord à attaquer la légitimité du critique

  • Quand un retour qu’on ne veut pas entendre apparaît, supposer que son auteur est incompétent ou animé par des motifs cachés permet d’éviter d’examiner le fond
  • Repérer un problème n’exige pas forcément une grande expertise, et les développeurs seniors peuvent au contraire ignorer des gênes auxquelles ils se sont habitués
  • Les développeurs juniors, eux, ne se sont pas encore habitués à ce qui est étrange, et peuvent donc ressentir plus vite l’inconfort et poser des questions
  • La valeur d’un retour doit être examinée non pas selon qui le formule, mais selon l’existence réelle du problème

Les limites de l’argument « d’autres entreprises l’utilisent aussi »

  • En matière de choix technologique, regarder ce que d’autres entreprises utilisent est utile pour la découverte, mais insuffisant comme justification d’adoption
  • Les blogs techniques d’entreprise peuvent difficilement exposer tous les coûts d’adoption
    • Ils sont souvent écrits après que la décision a déjà été prise
    • Ils servent à améliorer l’image de l’entreprise et à aider le recrutement
    • Les critiques techniques fortes viennent plus souvent d’individus que d’entreprises
  • L’usage de Go chez Tailscale ressemble davantage à un cas où une équipe d’experts Go connaît les coûts et peut les assumer
  • La souffrance visible dans netaddr.IP est liée à plusieurs contraintes de Go qui se combinent
    • Go n’a pas de sum types, ce qui rend malaisée la représentation d’un type « IPv4 ou IPv6 »
    • Les slices servent de structure de données générique avec un coût de 24 octets sur une machine 64 bits
    • L’absence de surcharge d’opérateurs crée des distinctions comme a == b versus a.equals(b)
    • L’absence de support des données immuables oblige à passer des copies et à surveiller les modifications internes
    • Il est difficile de créer un newtype opaque, ce qui impose des contournements via des packages séparés et des interfaces
  • Une équipe comme Tailscale peut aller très loin dans la compréhension des mécanismes internes et des cas limites de Go, mais toutes les organisations n’ont ni les experts Go ni le budget d’ingénierie de Google ou Tailscale

Ce que Go fait réellement bien

  • Go fournit un runtime asynchrone assez bon, des valeurs par défaut très marquées, un GC moderne avec deux boutons de réglage et une bonne toolchain
  • Comme le runtime asynchrone est au cœur du langage, Go dispose aussi d’outils que des développeurs Rust peuvent lui envier
    • Il est facile de dumper les backtraces de toutes les goroutines en cours d’exécution
    • Il peut détecter les deadlocks
    • Il dispose de son propre profiler
    • Il fait moins sentir le function coloring
  • Les outils de gestion de paquets, de refactoring et de cross-compilation donnent d’abord l’impression d’un progrès par rapport à pkg-config, autotools ou CMake dans l’écosystème C
  • Mais on ne choisit pas uniquement le runtime pratique de Go
    • Une toolchain personnalisée
    • Un build system
    • Une convention d’appel
    • Un GC unique
    • Des composants standards intégrés et un écosystème
    • Et jusqu’aux choix de conception accidentels du langage sont adoptés en bloc

Les lacunes du système de types et de la conception du langage

  • Go place au centre un runtime pensé pour implémenter TCP, HTTP, TLS, HTTP/2, DNS et des services web, sous une forme familière pour des développeurs Google formés à Java/C/C++/Python
  • Comme en C, le traitement des erreurs n’est pas fortement pris en charge, et de nombreux états mutables doivent être gérés manuellement par le développeur avec des if et des else
  • Comme en Java, la distinction entre valeur et référence est brouillée, et il est difficile de savoir à l’appel si une valeur sera modifiée
    • Selon que le receiver de méthode soit A ou *A, la variable locale a dans main peut être modifiée ou non
  • Comme il est difficile de limiter clairement mutabilité et immutabilité via les types, passer une référence peut entraîner soit une modification interne de la valeur, soit sa rétention prolongée empêchant sa libération
  • Go ne bloque pas suffisamment les familles d’erreurs suivantes
    • Copier par erreur un mutex et ainsi le neutraliser
    • Ne pas initialiser un champ de struct, ce qui lui donne sa zero value
    • Laisser se propager profondément dans le système un nil, un zéro ou une chaîne vide incorrects

La zero value et les limites du « il suffit d’être prudent »

  • Même si l’ajout d’un nouveau champ à une struct change de fait la forme des entrées d’une fonction, le compilateur n’empêche pas qu’on oublie ce champ dans un composite literal
    • Dans l’exemple, on ajoute b int32 à Params, mais si l’appelant ne passe que a: 47, le code s’exécute avec b=0
    • À l’inverse, si on ajoute directement un argument à une fonction, le compilateur signale une erreur par manque d’arguments
  • Un des principes de Go est que la zero value est utile, mais son sens réel varie selon le type
    • Appeler len sur une slice nil renvoie 0, donc elle se comporte comme une slice vide
    • Écrire dans une map nil déclenche panic: assignment to entry in nil map
  • Les channels ont eux aussi leurs propres règles
    • Envoyer sur un channel nil bloque pour toujours
    • Recevoir depuis un channel nil bloque pour toujours
    • Envoyer sur un channel fermé provoque un panic
    • Recevoir depuis un channel fermé renvoie immédiatement la zero value
  • Go n’a pas de sum types, il est donc difficile d’exprimer dans les types une entrée qui n’autorise que « l’une des options connues », ce qui le rapproche de l’approche du C : « faites juste attention »
  • L’idée que, puisqu’on ne peut pas tout empêcher, il n’est pas nécessaire d’empêcher même une partie des problèmes, est une fausse dichotomie qui renvoie à la prudence manuelle même les problèmes que le système de types pourrait aider à éviter

La comparaison avec Rust n’est pas un débat sur le « remplaçant parfait »

  • Il ne s’agit pas d’affirmer que Rust est parfait ; Go et Rust peuvent être vus comme deux réussites très différentes
    • Le succès de Go dépend fortement de composants inclus et de valeurs par défaut prescriptives
    • Le succès de Rust dépend fortement de son adoption progressive et de sa facilité d’intégration avec d’autres langages
  • Dans les faits, les cas d’adoption de Rust ressemblent davantage à des migrations progressives qu’à des remplacements complets
    • Firefox intègre plusieurs composants Rust essentiels dans une vaste base de code C++
    • Android réimplémente sa stack Bluetooth en Rust
    • Du code cryptographique Rust a été intégré à Python, et du code HTTP Rust est devenu l’un des backends de curl
    • Le patch Rust pour le noyau Linux continue d’être amélioré de manière itérative
  • Les utilisateurs de Rust parlent eux aussi activement des temps de build, des fonctionnalités manquantes du langage et d’autres défauts
  • L’enjeu n’est pas de choisir aveuglément entre Go et Rust, mais de voir quels problèmes peuvent être réduits par des outils capables d’exprimer la mutabilité, la durée de vie et l’abstraction dans les types
  • Même en Rust, on ne modélise pas forcément tous les cas avec une rigueur maximale
    • Par exemple, sauf pour un outil bas niveau comme ls, on peut choisir d’utiliser camino pour ne traiter que des chemins UTF-8
    • Dans le traitement des erreurs, on peut énumérer quelques variants importants et ranger le reste dans Other, Internal ou Unknown
    • Appeler unwrap() sur un Option<T> est une manière d’affirmer explicitement qu’une valeur est présente

Go ressemble davantage à une île séparée des autres écosystèmes

  • Sans cgo, Go reste éloigné de l’assembleur, des linkers, des débogueurs, des outils d’inspection mémoire et des conventions d’appel habituels
  • Go est plus proche d’un langage closed-world que C/C++, et plus hostile au FFI que Node.js, Python ou Ruby
  • Cette différence a aussi des avantages
    • On peut profiler les internals des stacks TLS et HTTP de la même manière que la logique métier
    • Cela contraste avec les langages dynamiques où la stack trace s’arrête dans OpenSSL
    • Le runtime peut prendre en charge les I/O non bloquantes et l’ordonnancement
  • Mais il est difficile de réutiliser tels quels de nombreux outils existants et savoirs institutionnels, et l’intégration entre Go et d’autres langages est elle aussi délicate
  • Chaque mode d’intégration avec Go a son coût
    • Appeler du C depuis Go impose le coût de la frontière FFI ainsi qu’un suivi manuel de descripteurs pour ne pas casser le GC
    • Appeler Go depuis un autre langage oblige à embarquer tout le runtime Go, GC compris, ce qui entraîne de grosses bibliothèques statiques et une charge opérationnelle accrue
  • La meilleure frontière de contact avec Go se situe plutôt du côté de la frontière réseau
    • Des RPC sur TCP comme du HTTP/1 de style REST, JSON-RPC ou gRPC sont relativement moins pénibles si l’on peut absorber le coût de latence
    • Malgré cela, il faut maintenir les invariants des deux côtés, et côté Go le compilateur ne les garantit pas si l’on n’utilise pas constamment des packages de validation

Le piège de l’idée de Go comme langage de prototype

  • Comme Go est facile à apprendre et simple à recruter, on peut être tenté de faire d’abord un prototype en Go, puis de réécrire plus tard ou de faire venir des experts si la situation se complique
  • Mais en pratique, le « code jetable » existe presque jamais
    • La réécriture prend du temps
    • Coordonner une transition fluide est difficile
    • Des détails peuvent se perdre
    • Le développement de nouvelles fonctionnalités s’arrête
    • Il faut reformer les équipes à une nouvelle technologie
  • Plus les composants Go se multiplient, plus il devient rationnel de continuer à choisir Go pour interagir avec le code Go déjà en place
  • Les linters aident dans une certaine mesure, mais ils ne peuvent pas égaler un compilateur d’un langage qui traite sérieusement ces problèmes dès l’origine, et ils ralentissent aussi le développement
  • La complexité absente du langage se déplace vers la base de code
    • Les invariants qui ne sont pas exprimés dans les types doivent être écrits directement dans le code
    • La seule signature d’une fonction ne permet pas facilement de savoir si les données seront modifiées, conservées, si la zero value est acceptée, si une goroutine sera lancée, si un channel nil est possible, ou quels types peuvent réellement être passés dans un interface{}
    • On finit par dépendre de la documentation, et la documentation a à la fois un coût de mise à jour et un coût quand elle n’est pas mise à jour

En résumé, les « mensonges » qui reviennent

  • Les phrases qu’on se répète pour continuer à utiliser Go peuvent se résumer ainsi
    • Puisque d’autres l’utilisent, ce sera bon pour nous aussi
    • Ceux qui expriment des inquiétudes sont des élitistes ou des gens impolis
    • Le runtime asynchrone séduisant et le GC compensent tous les autres défauts
    • Chaque défaut de conception du langage, pris isolément comme ensemble, reste acceptable
    • On peut compenser avec de la prudence, des linters et davantage de relecteurs
    • Puisque le langage est facile à écrire, développer du logiciel de production l’est aussi
    • Puisque le langage est simple, tout le reste l’est aussi
    • On n’en utilisera qu’un peu ou seulement au début, et il sera facile d’en sortir
    • On pourra toujours réécrire plus tard
  • Si l’outil choisi ne supprime pas la complexité, cette complexité est déplacée vers d’autres développeurs, les opérateurs ou les clients
  • Se retrouver réveillé la nuit en production parce qu’une valeur nil est arrivée là où elle n’aurait jamais dû se trouver rejoue encore le Billion Dollar Mistake

2 commentaires

 
tsboard 2024-11-28

Je me demande si un amateur qui n’a passé qu’un temps très bref mais intense avec le langage Go peut vraiment se permettre d’écrire là-dessus... Go a des avantages et des inconvénients très nets, donc ceux qui le choisissent comme ceux qui l’évitent semblent avoir des raisons bien précises. Personnellement, je ne pense pas qu’il faille le comparer à Rust, mais plutôt à Kotlin (Java).

Les goroutines de Go sont vraiment excellentes, mais ce n’est pas de la magie. Surtout sur le backend, dans un petit projet qui n’utilise qu’un seul MySQL, cette concurrence est vraiment difficile à gérer. Des problèmes comme l’épuisement des ressources MySQL ou la gestion du pool, auxquels on ne fait pas particulièrement attention dans un runtime JS/TS, sont plus compliqués qu’on ne l’imagine. Au final, dans cette situation, la base de données devient le goulot d’étranglement, ce qui atténue en partie les avantages de la concurrence en Go. (L’I/O asynchrone ou la boucle d’événements d’un runtime JS/TS peut même être plus adaptée.) Il suffit de lancer un outil comme hey avec -c 100 pour s’en rendre compte.

Et même si le GC est excellent, cela ne veut pas dire qu’on peut se contenter de passer des objets par pointeur à tout-va et ignorer complètement le nettoyage derrière. Tout est affaire de compromis, mais avec Go aussi, il vaut mieux, si possible, passer les petits objets par copie de valeur afin qu’ils soient traités immédiatement à la fin de la fonction. Je suis peut-être enfermé dans une manière de penser un peu datée, mais il ne fallait pas aborder les pointeurs trop facilement sous l’angle de l’efficacité comme en C/C++.

Le fait de devoir presque toujours renvoyer error dans les retours de fonction et de devoir le vérifier à chaque fois avec if err != nil {} est vraiment pénible, mais c’est aussi un avantage. Parce que le coût est plus faible qu’avec try catch. Et le mot-clé defer, qui joue un rôle similaire à finally {}, est lui aussi excellent. C’est appréciable de ne pas avoir à se demander à quel moment libérer les ressources. J’aime aussi le fait qu’il soit possible de mettre en place immédiatement un excellent serveur backend avec la seule bibliothèque standard (1.23 et plus). Et surtout, le meilleur point reste qu’une fois compilé pour l’OS cible, il n’y a besoin d’aucun autre runtime ni d’aucune préinstallation.

Je n’utilise pas Go depuis très longtemps, donc j’ai l’impression de m’étendre un peu trop avec des opinions très personnelles, alors je vais m’arrêter là. Haha, moi j’aime bien Go, mais j’aime aussi les autres langages !

 
GN⁺ 2024-11-27
Commentaires sur Hacker News
  • Cet article pointe bien de nombreux défauts de Go, mais je ne pense pas que la gestion explicite des erreurs en fasse partie
    J’ai toujours détesté la gestion par exceptions, car elle ajoute une couche de « magie » qu’il est trop facile de casser, et même si ce n’est pas un argumentaire techniquement parfait, après des décennies d’expérience je préfère une approche où l’erreur est traitée exactement à l’endroit où elle se produit
    On peut dire que Rust le fait de manière plus élégante, et je préfère Rust pour mes projets personnels, mais dans les grands projets où passent des développeurs de niveaux très variés, je trouve que la philosophie de Go est assez raisonnable parmi les approches modernes de gestion des erreurs
    Dans ma région, Go est bien plus adopté que d’autres langages « récents » grâce à sa simplicité. Ce n’est pas le meilleur langage, mais il intègre beaucoup de garde-fous qui évitent de se tirer une balle dans le pied, donc pour un langage généraliste c’est souvent le meilleur choix

    • La gestion des erreurs en Go a plusieurs défauts. Comme l’auteur s’appuie fortement sur Rust, l’alternative n’est pas l’exception mais le type somme Result
      Il n’y a pas de trace de pile, et comme il faut envelopper les erreurs, il faut créer non seulement des messages d’erreur uniques, mais aussi des messages d’enrobage uniques à chaque point d’appel, afin de pouvoir grep les messages et reconstituer une sorte de trace de pile
      Le « tuple de retour » est un dispositif étrange qui n’existe qu’à cause des erreurs, alors qu’on ne peut même pas utiliser de tuples ailleurs dans le langage. À cause de règles d’initialisation de variables maladroites, on finit un jour ou l’autre par utiliser la mauvaise variable err
      Go aurait dû avoir des énumérations, ou au moins introduire un meilleur sucre syntaxique comme le ? de Rust au lieu de cet étrange « tuple de retour » réservé aux erreurs. Dans son état actuel, c’est fastidieux et propice aux erreurs
      Go a de l’obstination, mais trop souvent cela ressemble à une obstination destinée à protéger le langage de la critique. Malheureusement, pour qu’un langage de programmation devienne populaire, le marketing — donc aussi les mensonges — compte, et cela fonctionne
    • Je programme depuis plus de 30 ans et j’ai aussi utilisé C# pendant près de 10 ans, mais je considère les exceptions comme l’une des pires idées de l’histoire de la programmation
      Une exception est un horrible goto que n’importe quelle bibliothèque peut lancer dans mon code, et elle est presque toujours mal gérée. Même après qu’une exception a été « traitée », l’application se retrouve généralement dans un état inconnu, ce qui la rend impossible à raisonner correctement
      Les erreurs Go sont en général faciles à déboguer, même pour des ingénieurs juniors, alors que la vraie cause des exceptions Java est souvent difficile à identifier, même pour des principal engineers expérimentés
    • J’ai toujours vu les gens qui louent la gestion des erreurs de Go ne la comparer qu’aux exceptions, comme si les exceptions étaient la seule alternative
      En revanche, je n’ai encore jamais vu quelqu’un habitué aux types somme comme Maybe, Option ou Result considérer l’approche de Go comme ne serait-ce qu’un peu acceptable
    • Cette partie sur la gestion des erreurs relève presque de la religion pure, et va même à l’encontre de l’un des principes les plus fondamentaux de Go : « le code doit être plus facile à lire qu’à écrire »
      Si je lis la logique d’une méthode donnée et que 75 % du code n’est que du bruit lié aux erreurs, tandis que seuls 25 % des lignes sont nécessaires pour comprendre ce que fait réellement le programme, c’est difficile à lire
      Quand je découvre une base de code, je veux d’abord comprendre ce qu’elle essaie d’accomplir avant d’examiner les cas limites liés aux erreurs, puis voir ensuite comment elle garantit la correction
      Sur ce point, la gestion des erreurs de Go est un échec majeur. Ce n’est pas que la gestion explicite des erreurs soit mauvaise, c’est l’obstination à mélanger chemin de succès et traitement d’erreur ligne par ligne qui pose problème. On peut aussi traiter les erreurs explicitement dans une zone dédiée
    • Je ne suis pas d’accord avec l’idée que la gestion explicite des erreurs soit excessive
      Le point avec lequel je suis d’accord, c’est qu’elle force à réfléchir à toutes les possibilités que le code peut produire. Mais c’est davantage une question proche du C que des autres langages
      Quand on abstrait du code, il n’est pas toujours nécessaire de traiter l’erreur immédiatement, ou bien on peut vouloir la traiter plus bas dans la pile d’appels
      Le langage sacrifie beaucoup de lisibilité pour imposer une manière particulière de faire
  • Rust et Go sont très différents, et les gens semblent vouloir un entre-deux qui n’existe pas aujourd’hui
    C’est-à-dire un langage avec ramasse-miettes, relativement simple, compilé en binaire à liaison statique, mais avec un système de types proche de Rust et des choses comme les types résiduels
    Syntaxiquement, Gleam et Kotlin s’en rapprochent dans une certaine mesure, sans y correspondre complètement. J’aime Rust, mais je le trouve trop complexe pour beaucoup de gens qui, même s’ils sont capables de créer des choses, n’ont pas fait d’études d’informatique et ne travaillent pas comme programmeurs
    Si on n’utilise le langage qu’occasionnellement, on ne se souvient pas de ce qu’est une vtable, ni quand et comment une valeur est libérée. Il n’existe pas de « langage parfait », mais Go et Rust ont tous deux apporté d’excellentes choses, et j’aimerais que quelqu’un s’inspire des deux pour créer un langage simple, largement utilisable

    • Kotlin est intéressant comme entre-deux, mais pour la plupart des tâches il est bien moins productif que Go, et à mon avis il n’est pas adapté aux travaux pour lesquels on sortirait Rust
      En pratique, Kotlin est très complexe, et on finit par perdre du temps à vouloir faire trop intelligemment. Il y a mille façons de faire les choses, avec surcharge d’opérateurs, proxies, propriétés, companion objects, exceptions et types résultat
      En supposant qu’on utilise Gradle, le système de build est proche de la torture pour quelqu’un habitué à go build
      L’API des coroutines semble à la fois plus complexe et plus limitée que les goroutines. C’est plus structuré, mais moins flexible et plus coûteux en effort à utiliser
      Le contrôle d’accès est aussi maladroit. Il n’y a pas de moyen de rendre un type visible uniquement dans le package : c’est soit limité au fichier, soit public au module entier. Cela agrandit involontairement la surface de l’API, ou empêche de répartir la complexité sur plusieurs fichiers
      Kotlin/Native et Kotlin/JVM sont en pratique deux choses différentes. Kotlin/JVM est mature, mais comme il tourne sur la JVM, cela élimine une partie des cas d’usage pour lesquels on envisagerait Rust
      L’écosystème Kotlin/Native est très faible et la documentation manque aussi. Le suivi des tickets contient même des bugs effrayants
      Kotlin/Native ne permet même pas de distribuer des bibliothèques sous forme de seul code source, ce qui impose une CI complexe pour construire des binaires pour chaque système d’exploitation et architecture. Sinon, on finit par ne pas distribuer la bibliothèque du tout, ce qui semble encore aggraver la faiblesse de l’écosystème
    • Personnellement, je pense que la plupart des fonctionnalités de Rust que les gens aimeraient avoir dans Go peuvent encore rester dans les « concepts » de Go
      Comme pour l’ajout des génériques, il suffirait d’en ajouter trois exactement : Result, un conteneur générique pour les erreurs ; Optional, pour une meilleure gestion de nil ; et un peu de sucre syntaxique, comme l’opérateur Elvis
      Le grand avantage qui explique presque à lui seul le succès de Go, c’est qu’il a été créé par une très grande entreprise qui l’utilisait elle-même, avec les moyens de produire d’excellents outils et des bibliothèques open source. Le fait qu’un langage soit déjà utilisé et qu’il ait des bibliothèques semble être le principal facteur déterminant de son succès
    • Mon avis sur le sujet, reformulé, est le suivant : partir de JavaScript
      La syntaxe de base est très directe, l’adoption est déjà énorme, l’écosystème est vaste, les runtimes ne cessent de s’améliorer, et il peut aussi être compilé en WASM
      Puis enlever les parties bizarres, par exemple une trop grande flexibilité dans les redéfinitions, l’abus de la portée globale, les bizarreries numériques, etc., et ajouter des types, des types somme/produit, Result, Maybe, decimal, etc.
      Et si on y ajoute un style où tout est expression, du pattern matching, des fonctionnalités fonctionnelles comme le currying, ainsi qu’une bibliothèque standard complète, on pourrait obtenir le langage le plus proche de ce que beaucoup de gens veulent
      Une autre grande avancée que j’aimerais voir dans les outils de développement serait de refondre HTML pour y intégrer de vrais contrôles avancés et permettre un style facile avec du CSS inline. Cela réduirait la quantité de JS nécessaire côté client
      Je pense que ces deux choses augmenteraient fortement la productivité de programmation pour le développement lié au web
    • Je suis l’auteur : en quoi Gleam n’atteint-il pas ce point, selon vous ? Moi aussi, je cherche cet entre-deux, donc Gleam m’intrigue beaucoup
    • C# et F# sont de loin ce qui s’en approche le plus. Les autres options n’ont pas un système de types ou des outils suffisamment bons pour rivaliser avec eux
      On peut compiler en binaire natif statique avec dotnet publish -p:PublishAot=true. En F#, il faut utiliser les méthodes Console.* au lieu de print*, car print repose en interne sur une réflexion non bornée à cause de la sortie structurelle du spécificateur de format %A, ce qui nuit à la taille du binaire et fait râler le compilateur
      Je recommanderais en particulier F# comme « alternative à Rust plus simple et plus orientée métier ». Le langage est orienté expression, avec unions discriminées, inférence de types HM complète, et application graduelle des types qui fonctionne bien. L’analyse de données et la modélisation de domaine y sont aussi très agréables
      Pour la programmation système, C# devient une option. Il offre d’excellents primitifs de concurrence, une interopérabilité native rapide et parfois presque sans coût, des binaires natifs plus petits que ceux de Go, et de nombreuses API bas niveau, y compris du SIMD portable
      Go est souvent mal adapté à ce genre de travail, voire incapable de le faire sans contournements. .NET gagne aussi en popularité dans la communauté non jeu vidéo de ce domaine, et beaucoup de bibliothèques haute performance apparaissent. De plus, contrairement aux langages de niche proposés dans les commentaires voisins, on bénéficie d’un immense écosystème existant, sans devoir tout faire soi-même
  • Go : « Je suis un langage simple ! »
    L’utilisateur utilise Go pendant un moment
    L’utilisateur : « Je te déteste, tu es censé être un langage simple ! »
    C’est peut-être parce que j’ai plus de 50 ans, mais moi, j’aime les langages simples
    Cette « critique » ne me paraît pas très équilibrée, et comme toute technologie implique des compromis, un jugement sans équilibre me semble faible
    Cela dit, ma conclusion est différente : https://www.inkmi.com/blog/why-we-chose-go-over-rust-for-our...

    • C’est une déformation qu’on voit souvent accolée aux critiques de Go. Du genre : « Tu ne comprends pas la simplicité, ton cerveau est infecté par Java. »
      L’article donne beaucoup d’exemples d’incohérences agaçantes du langage, et ça, c’est l’inverse de la simplicité
      J’aime la présentation de Go par Rob Pike, et certains points m’ont vraiment ouvert les yeux. J’aimerais simplement que le Go que je vois en pratique ressemble beaucoup plus au Go que les fans de Go décrivent de façon abstraite
    • J’aimerais que le discours selon lequel Go est un langage « simple » disparaisse
      Contrairement aux apparences, dès qu’on commence à utiliser Go, on se rend vite compte que ce n’est pas simple. Il y a beaucoup de complexité cachée et de pièges. Par exemple : https://archive.ph/WcyF4
      Cela reste un langage utile et assez largement utilisé, mais ce n’est pas un langage « simple »
    • Le problème de Go, c’est qu’il n’a pas été conçu comme un langage simple pour ses utilisateurs, mais comme un langage facile à implémenter pour ses mainteneurs
      À mon sens, un langage simple devrait avoir une forte cohérence. Peu de règles, mais des règles universelles et cohérentes partout. Or Go est rempli d’étrangetés, d’incohérences et de contournements
      Un bon exemple est l’omission de type. C’est possible pour les déclarations de tableaux, de slices et de maps, mais pas pour les structs. Si c’était aussi autorisé pour les structs, cela aurait plusieurs avantages en termes de lisibilité, et permettrait aussi des paramètres nommés via des arguments de struct anonymes, ce qui donnerait une conception bien meilleure que les contournements laids qu’on utilise aujourd’hui
    • Scheme est un langage simple, mais Go cache sa complexité jusqu’au moment où elle explose dans le pire des cas
      Bien sûr, la plupart des alternatives raisonnables à Go sont encore pires sous cet angle. Python, Ruby, JS, ce genre de choses
    • Go n’est « simple » que quand on fait des choses simples. Si vous essayez d’écrire vous-même un KV store ou une base de données, vous ressentez très vite le besoin de fonctionnalités de langage un peu plus modernes
  • Je me demande ce qui pousse quelqu’un à dénigrer un langage aussi longuement. Même les quelques points valables de l’article ne sont pas écrits sur un ton constructif, donc j’appellerais plutôt ça du blâme
    Quel langage échappe à la critique ?
    Est-ce un texte écrit pour se sentir mieux en attribuant l’échec de ce projet au langage ? Ou bien l’auteur n’arrive-t-il pas à comprendre que tout le monde ne pense pas et ne travaille pas de la même façon, et que ce qui est insupportablement irritant pour certains passe presque inaperçu pour d’autres ?
    Les langages qui ne répondent pas à de vrais besoins finissent généralement par disparaître d’eux-mêmes, sans qu’on ait vraiment besoin de les y aider
    Go a très bien fonctionné sur les projets que j’ai rencontrés, même s’il est différent de langages « plus raffinés ». J’espère que l’auteur n’est pas forcé de travailler avec Go. Le reste de la communauté continuera à construire des services, à faire des retours à l’équipe Go, et à regarder le langage évoluer lentement sans casser la prod

    • La réponse à « qu’est-ce qui pousse quelqu’un à le blâmer aussi longuement ? » se trouve dans l’article
      La complexité intrinsèque ne disparaît pas juste parce qu’on choisit de fermer les yeux
      Si vous choisissez de ne pas vous préoccuper de la complexité, vous ne faites que la repousser sur d’autres développeurs de l’organisation, les ops, les clients, quelqu’un d’autre. Et maintenant, ce sont eux qui doivent contourner vos hypothèses pour que tout fonctionne correctement
      Ces derniers temps, je suis souvent ce « quelqu’un d’autre », et j’en ai assez
      Bien sûr que Go a très bien fonctionné sur les projets qu’il a rencontrés. C, C++, PHP et JavaScript fonctionnent aussi. Dans notre monde, énormément de choses « fonctionnent ». Ajouter une voie de circulation de plus, ça fonctionne aussi, et un État policier, ça fonctionne aussi
      Mais peut-être que quelque chose d’autre pourrait fonctionner bien mieux ?
    • Ce n’est qu’un blog personnel, un simple coup de gueule. C’est très bien comme ça, pas besoin d’y voir plus profond que ça
      S’il l’a écrit, c’est probablement parce que l’auteur du billet original avait l’impression que son point de vue n’était pas suffisamment représenté « dans le monde ». Moi non plus, je ne suis pas fan de Go, et la popularité de Go en 2022 me laissait perplexe. Parce qu’on avait l’impression que des défauts assez évidents étaient ignorés
      On ne voit plus tellement de longs billets de blog expliquant que PHP est un mauvais langage, simplement parce que le sujet a déjà été traité mille fois
    • Tout le monde n’aime pas uniquement les reviews ennuyeuses et cliniques ; certaines personnes aiment aussi les textes avec un peu de passion et d’humour
      Ce n’est pas parce que ce billet de blog n’est pas écrit d’une manière qui vous plaît qu’il n’a aucune valeur pour d’autres
    • L’un des facteurs très subjectifs et irrationnels qui m’ont amené à presque détester Go, c’est que pendant des années, les fanatiques de Go ont fait du gaslighting à tout le monde sur absolument tous les aspects de Go
      Tout ce que Go faisait, que ce soit l’implémentation la plus élémentaire ou quelque chose que d’autres langages faisaient déjà mieux, était présenté comme indispensable, supérieur, et comme la seule façon de résoudre ce problème
      Tout ce que Go ne faisait pas était présenté comme inutile, ou traité comme un complot de Big Complexity visant à rendre les pauvres utilisateurs non-Go accros au sucre syntaxique, qu’il s’agisse d’une vraie gestion de null, d’une meilleure gestion des erreurs, de moins de boilerplate, des génériques, ou de la création de solutions cohérentes sans inventer des cas particuliers pour tout
      Même dans ce fil, on continue à combattre des hommes de paille. Dès qu’on parle de gestion des erreurs, au lieu de reconnaître l’évidence que la gestion des erreurs en Go pourrait être bien meilleure, on sort des exemples comme JavaScript, comme si les gens qui critiquent Go considéraient JavaScript comme l’apogée de la gestion des erreurs
    • Bjarne l’a mieux dit que personne. Il n’existe que deux sortes de langages : ceux dont les gens se plaignent, et ceux que personne n’utilise
  • Chaque fois que je travaille dans un autre langage, j’ai envie de revenir à Go, même si ce n’est pas un langage parfait
    J’aime le fait que, littéralement, ça marche, tout simplement. On installe Go, on récupère le code, on écrit du code, et c’est fini
    Pas besoin de découvrir quelle version, quel runtime, quelle configuration, quel outil de build ou quel gestionnaire de paquets il faut utiliser. Il suffit d’installer Go et de s’en servir
    Le seul autre langage qui offre probablement une expérience comparable, c’est Rust
    C’est peut-être un mensonge qu’on se raconte à soi-même, mais chaque fois que j’utilise Python, TypeScript ou Java, j’ai juste envie d’écrire du code, et au lieu de ça je finis par déboguer ou essayer de comprendre des problèmes de configuration, de gestionnaire de paquets, d’outils de build ou de versions, au point d’en redouter la programmation

    • La force de Go, c’est sa chaîne d’outils intégrée. Comme le dit l’article, cela rend l’interopérabilité plus difficile, mais d’après mon expérience personnelle et limitée, ce n’est pas un cas d’usage si fréquent
    • C’est l’aspect de Go qui mérite le plus d’éloges, et Rust a lui aussi adopté cela avec ses canaux
  • Chaque fois que je lis des critiques de Go, je me dis la même chose. Et pourtant je continuerai à utiliser Go
    Je comprends qu’il y ait beaucoup de problèmes qui sont faciles à documenter en théorie et auxquels les gens se heurtent souvent en pratique, mais malgré cela, j’ai toujours l’impression qu’en conditions réelles c’est l’un des meilleurs langages de programmation
    Parmi les défauts souvent listés, il y en a certains avec lesquels je ne suis pas d’accord, par exemple j’aime la manière de gérer les erreurs explicitement partout. Sur d’autres points, je suis d’accord, mais ils ne me gênent pas beaucoup plus que les défauts d’autres langages de programmation
    Je trouve presque triste que les gens particulièrement sensibles aux domaines où Go n’a pas réussi semblent condamnés à s’en plaindre toute leur vie
    Mais quand il s’agit de choisir quel langage utiliser pour un projet, je n’applique pas une énorme logique rationnelle sur « quel est le meilleur langage ». Je choisis un langage qui me convient bien, avec lequel j’ai le sentiment de pouvoir écrire régulièrement de bons logiciels, et dans lequel il est agréable de travailler
    En tant que personne attachée à la rigueur, j’aimerais que ce langage soit plus proche de Rust, mais ce n’est pas le cas aujourd’hui. Je ne déteste pas Rust, mais ce n’est pas le langage vers lequel je me tourne dans l’urgence
    On a déjà écrit largement assez de textes sur à quel point Go serait affreux. Au moins, je sais maintenant ce que ça devait faire d’être fan de PHP il y a quelques années. C’est exagéré, mais à mon avis pas tant que ça

    • La phrase « les gens sensibles aux domaines où Go n’a pas réussi s’en plaindront toute leur vie » ressemble à une excellente caution pour cet article. Parce que c’est littéralement le cœur du texte
      Vous allez utiliser Go
      Et vous allez le regretter
      Mais à ce moment-là, il sera déjà trop tard et vous serez coincé avec
      L’auteur avance des arguments assez convaincants, et ils sont très difficiles à réfuter. Dans beaucoup des cas explicitement énumérés, ainsi que dans de nombreuses situations professionnelles où des entreprises techniquement peu compétentes utilisent des technologies inadaptées, Go est un mauvais choix et vous regretterez de l’avoir mis en production
      Les entreprises devraient s’en tenir à des outils ennuyeux. Pour un projet personnel, bien sûr, faites ce que vous voulez
    • J’aime assez Go. La gestion explicite des erreurs est parfois un peu pénible, mais c’est simplement une manière de faire le travail
      À part ça, je considère que sa simplicité est un point fort. C’est agréable à utiliser, facile à apprendre, et les performances sont généralement plutôt bonnes. Je ne le choisirais pas pour toutes les situations, mais il y en a beaucoup où c’est un bon outil
      De la même manière, j’aime aussi assez Java et Python, et je maîtrise suffisamment bien les deux. Ce sont de bons outils pour ce pour quoi ils ont été conçus
      Je ne comprends pas pourquoi les gens s’attachent avec autant de passion aux langages de programmation. Un langage est un outil. On peut préférer certains outils, mais ce n’est pas parce qu’on en déteste un qu’il devient invalide
    • « Je choisis un langage qui me convient bien » est probablement le choix le plus sage pour tout le monde. Golang ne devient un problème que quand un responsable oblige les autres à l’utiliser
    • J’aime Go, mais après avoir trop écrit et lu de code Go, je fais des cauchemars où des démons if err != nil me giflent au visage
      Il y a eu beaucoup de bonnes propositions pour corriger ce problème, mais une partie extrêmement conservatrice et très bruyante de la communauté a torpillé chaque proposition avec des retours presque venimeux
      Maintenant, l’équipe Go a psychologiquement peur de ces gens et a abandonné le combat pour améliorer la gestion des erreurs
      Dans tous les sondages auprès des développeurs Go, la communauté vote massivement pour une amélioration de la gestion des erreurs, mais une petite minorité d’extrémistes a fait dérailler toute avancée
    • Si beaucoup de gens détestent vraiment Go, c’est parce qu’ils ont déjà fait l’expérience d’autres fonctionnalités de langage
      Go a adopté une attitude arrogante consistant à décider lui-même de quelles fonctionnalités on pourrait avoir besoin, et n’a fourni qu’un ensemble très limité d’outils utilisables pour le travail
  • Discussion à l’époque :
    (130 points, 148 commentaires) https://news.ycombinator.com/item?id=34188528
    (748 points, 544 commentaires) https://news.ycombinator.com/item?id=31205072

  • Il y a ce passage : « J’ai commencé à beaucoup plus m’intéresser à la sémantique qu’à la syntaxe, donc je n’ai même pas regardé Zig, Nim, Odin, etc. Je ne m’intéresse plus aux “meilleurs C” »
    Je trouve l’article assez verbeux. La partie où il dit que Go est « accidentel » est absurde, car Rust l’est tout autant par ses origines et sa conception
    Une chose qui m’a frappé, c’est que Nim n’est absolument pas un « meilleur C ». Il a un garbage collector, et on peut aussi utiliser le comptage de références. Si on le veut, on peut s’en servir comme d’un meilleur C
    Le système de types de Nim évite beaucoup des problèmes de Go, sans être aussi strict que Rust
    Au final, énormément de logiciels ont été écrits et déployés en Go, ils tournent vite, ont peu de temps d’arrêt, sont généralement efficaces, et semblent avoir relativement peu de problèmes de sécurité. Je pense qu’il y a bien moins de projets logiciels déployés en Rust, et Firefox reste encore à plus de 95 % en C++

    • La phrase « il y a bien moins de projets logiciels déployés en Rust, et Firefox reste à 95 % en C++ » est amusante, parce qu’elle me rappelle ce tweet de 2022[0]
      Je n’ai aucun intérêt particulier dans cette querelle, et au travail j’utilise Python et C++ avec satisfaction. Même s’il y a beaucoup moins de logiciels écrits en Rust, on peut quand même dire que beaucoup de logiciels ont été écrits et déployés en Rust. Sans oublier les investissements des grandes entreprises
      0: https://x.com/m_ou_se/status/1599173341117435905
  • Je ne comprends pas pourquoi Go est toujours comparé à Rust. Le point de comparaison le plus pertinent, c’est Java.

    • Je pense que c’est exactement comme ça qu’il faut comprendre Go. Go est un langage pour créer des serveurs dans des environnements où la forte cohérence du code et le temps d’adaptation très court des ingénieurs juniors sont importants. Autrement dit, il est parfait pour les scénarios de grandes entreprises où l’on utilisait Java.
      Si la comparaison entre Go et Rust est plus fréquente mais moins utile, c’est probablement parce que les deux font partie de la nouvelle vague de langages, et que tous deux produisent fondamentalement des binaires à liaison statique.
    • Go n’est pas une réinvention de Java, mais une réinvention de C.
      Dans les situations où Java est un bon choix, Go est un très mauvais choix. Non seulement le langage est limité, mais son écosystème est aussi très limité par rapport à Java.
      Je continue à utiliser principalement Go, C# et TypeScript, et cette combinaison offre une excellente couverture. Si j’ai six mois pendant lesquels je peux absorber une baisse de productivité, ou si un projet aux exigences de qualité plus faibles se présente, j’ajouterai Rust.
      Apprendre le langage prend une à deux semaines, mais apprendre à faire de l’ingénierie dans son écosystème demande du temps.
    • En réalité, Go a été créé pendant qu’on attendait la compilation de C++. L’objectif était de faire quelque chose de meilleur que C++ pour les services réseau, et ça a marché. C’est juste que la barre était assez basse.
      La plupart des entreprises qui n’ont pas de fortes préoccupations de performance n’utilisent pas C++ pour les services réseau. Si Rust avait déjà été mature, cela aurait été un bon choix, mais l’un des premiers problèmes clés, le temps de compilation, peut être encore pire sur des bases de code Rust.
      J’ai effectivement vu des cas où Go était choisi à la place de Rust, et la raison principale était que Go compile beaucoup plus vite.
    • Les personnes qui doivent décider dans quel langage écrire quelque chose de nouveau sont très susceptibles d’envisager Go et Rust ensemble. Il est très peu probable qu’elles envisagent Java.
      Si l’on demande si Rust et Go sont suffisamment différents pour devoir être choisis dans des cas distincts, la réponse est oui. C’est précisément pour cela qu’on les considère ensemble avant d’en choisir un.
  • À un moment, je cherchais un Python « meilleur ». Je voulais un langage plus simple et plus sûr que C/C++, mais plus rapide que Python, et surtout capable de produire un binaire unique.
    J’ai donc tout regardé, de Rust à des langages moins connus comme Hare.
    Go aurait dû être le choix évident, mais pour une raison que je n’arrive pas à expliquer, je détestais sa syntaxe. Pour Rust, je comprends : il utilise beaucoup de caractères spéciaux difficiles à mémoriser et à taper sur un clavier non QWERTY, en plus d’autres points pénibles. Pour plusieurs Lisp, le problème venait des parenthèses et de la notation polonaise inversée. Mais pour Go, je n’arrive pas à rationaliser pourquoi je ne l’aime pas.
    J’ai fini par décider de compiler le code Python avec Nuitka. Je ne sais pas pour le gain de vitesse, mais maintenant je peux fournir un binaire.
    Je regarde aussi de plus en plus C#, parce que la compilation AOT progresse. En revanche, je n’aime pas sa verbosité en mode par défaut ni son attachement trop fort à Windows.
    J’aimais beaucoup Nim et Crystal, mais leurs petites communautés constituaient un frein. Cela dit, j’ai vraiment été impressionné par tout ce que Nim accomplit avec une communauté aussi réduite, au point de le considérer comme un excellent langage.
    Je vais essayer de me motiver à apprendre l’un des langages mentionnés.

    • .NET Core n’est pas lié à Windows, sauf pour certains frameworks spécifiques comme WPF et le stockage des identifiants. Il existe aussi des alternatives à WPF qui fonctionnent partout.
      De nos jours, c’est en fait assez agréable à utiliser.
    • Nuitka offre une bonne compatibilité, mais hérite aussi des inconvénients de l’API C de Python.
      Il existe une autre approche qui consiste à abandonner l’API C et à construire un pont vers des langages comme Go ou Rust.
      J’ai travaillé sur py2many pendant plusieurs années. J’aimerais avoir des retours sur cette approche.