2 points par GN⁺ 2023-08-15 | 1 commentaires | Partager sur WhatsApp
  • Go 1.21 étend la compatibilité fondée sur GODEBUG afin que la nouvelle toolchain reproduise aussi fidèlement que possible le comportement des anciennes versions de Go, avec l’objectif de réduire le coût des mises à niveau
  • Depuis Go 1 en 2012, Go promet la compatibilité source et a limité les ruptures liées aux suppressions ou modifications grâce à la vérification des API publiques et à de vastes tests internes
  • Même des améliorations autorisées par la documentation, comme une meilleure précision de time.Now, des changements d’implémentation de sort, des variations de sortie de compress/flate, l’élargissement des entrées acceptées par strconv.ParseInt ou des changements de parsing dans net.ParseIP, peuvent casser des programmes existants
  • À partir de Go 1.21, les réglages GODEBUG liés à la compatibilité sont conservés au moins 2 ans ou 4 releases de Go, et le comportement antérieur peut rester la valeur par défaut selon la version go du go.mod
  • Go 2 ne sortira pas sous la forme d’une nouvelle spécification qui casserait les programmes Go 1, et Go continue de faire passer la compatibilité avant tout pour que les mises à niveau de la toolchain restent fiables même avec l’ajout de nouvelles fonctionnalités

Principes de base de la compatibilité Go 1

  • En 2012, avec Go 1, Go a défini dans le document « Go 1 and the Future of Go Programs » l’objectif suivant : les programmes conformes à la spécification Go 1 doivent continuer à se compiler sans modification et à s’exécuter correctement pendant toute la durée de vie de cette spécification
  • Le cœur de cette promesse est la compatibilité source
    • Lors d’une mise à jour vers une nouvelle version de Go, le code doit être recompilé
    • De nouvelles API peuvent être ajoutées, mais il faut éviter les ajouts qui casseraient le code existant
  • Il est impossible de garantir qu’aucun changement futur ne cassera absolument tous les programmes
    • Si un programme dépend d’un comportement bogué, il peut casser quand ce bug est corrigé
    • Go cherche à réduire au maximum les ruptures tout en maintenant des mises à niveau stables

Empêcher les ruptures de compatibilité via la vérification des API publiques

  • Dans le développement de Go, la liste des API publiques de chaque package est maintenue dans un fichier séparé du package lui-même
    • Par exemple, go/api/go1.21.txt recense des fonctions, méthodes et types de bytes, cmp, context, etc.
  • Les tests standard vérifient que l’API réelle des packages correspond à ces fichiers
    • Si une nouvelle API est ajoutée, il faut aussi l’ajouter au fichier d’API pour que les tests passent
    • Si une API existante est modifiée ou supprimée, les tests échouent également
  • Au-delà de la suppression d’API, un changement de type peut aussi casser la compatibilité
    • os.Stdout est une variable globale de type *os.File
    • Le remplacer par une interface ayant les mêmes méthodes casserait du code comme greet(f *os.File) qui exige un *os.File
  • La vérification des API est utile pour détecter les modifications ou suppressions d’API, mais elle n’empêche pas tous les changements incompatibles possibles dans Go

Les ruptures subtiles révélées par les tests

  • Les versions de développement des nouvelles releases de Go sont testées en continu sur l’ensemble du code Go interne à Google
    • Si les tests passent, le commit concerné est installé dans la toolchain Go de production de Google
    • Si les tests internes cassent, cela signifie qu’il est probable que du code externe casse aussi, et des moyens sont recherchés pour réduire l’impact
  • Dans la plupart des cas, le changement est annulé ou réécrit pour éviter de casser les programmes
  • Certains changements peuvent rester importants et compatibles au sens de la documentation, même s’ils cassent des programmes
    • Même dans ce cas, l’objectif reste de réduire la portée de l’impact et de signaler les problèmes potentiels dans les notes de release

Deux cas révélés avec Go 1.1

  • Littéraux de structure et nouveaux champs

    • Dans Go 1, net.TCPAddr était une structure avec deux champs, IP et Port, et les littéraux composites sans noms de champs compilaient aussi
    • Quand le champ Zone a été ajouté à net.TCPAddr dans Go 1.1, l’ancien code a cessé de compiler avec l’erreur « too few initializers in struct literal »
    • La façon compatible d’écrire cela consiste à utiliser des littéraux avec noms de champs
var myAddr = &net.TCPAddr{
    IP: net.IPv4(18, 26, 4, 9),
    Port: 80,
}
  • Si Zone n’est pas précisé, ce champ prend sa zero value, c’est-à-dire la chaîne vide
  • L’exigence d’utiliser des littéraux composites avec noms de champs pour les structures de la bibliothèque standard figure dans le document de compatibilité, et go vet signale les littéraux sans noms de champs dont la compatibilité avec les versions futures exige des noms
  • Précision temporelle

    • Après Go 1, time.Now a été modifié pour renvoyer une précision à la nanoseconde au lieu de la microseconde
    • Ce changement pouvait casser des tests qui attendaient l’identité après un aller-retour d’une valeur time.Now via save et load
    • Si la représentation stockée ne conservait qu’une précision à la microseconde, le test pouvait réussir avec Go 1 mais échouer avec Go 1.1
    • Pour aider à corriger ce type de tests, Go a ajouté les méthodes Round et Truncate, et les notes de release ont récapitulé les problèmes potentiels ainsi que ces nouvelles méthodes
    • Comme cette précision améliorée représentait un meilleur comportement et restait dans le cadre documenté de la fonction, le changement a été publié même s’il cassait certains programmes

Trois types de changements pouvant casser la compatibilité

  • Changements de sortie

    • Un changement de sortie se produit lorsqu’une fonction produit un résultat différent d’avant, mais que ce nouveau résultat est au moins aussi correct, voire plus correct, que l’ancien
    • L’ajout de la précision à la nanoseconde dans time.Now en est un exemple typique
    • Dans Go 1.6, l’implémentation de sort a été modifiée pour être environ 10 % plus rapide, ce qui a changé l’ordre des éléments considérés comme égaux
    • Sortie de Go 1.5 : [red blue green white black yellow orange indigo violet]
    • Sortie de Go 1.6 : [red blue white green black orange yellow indigo violet]
    • Le tri autorise plusieurs ordres pour un même résultat, mais un programme qui attendait un ordre précis peut casser
    • Dans Go 1.8, compress/flate a été amélioré pour produire une sortie plus petite avec un surcoût CPU et mémoire comparable
    • À cause de cela, les builds d’archives reproductibles internes à Google ne pouvaient plus reproduire exactement les anciennes archives
    • Le projet concerné a donc forké compress/flate et compress/gzip afin de conserver l’ancien algorithme
    • Pour se prémunir contre les changements de sortie, il est préférable d’écrire les programmes et les tests de façon à accepter toutes les sorties valides
    • Si une sortie strictement reproductible est indispensable, il est possible de forker le code, mais on se coupe alors aussi des corrections de bugs
  • Changements d’entrée

    • Un changement d’entrée survient quand une fonction modifie les entrées qu’elle accepte ou la manière dont elle les traite
    • Go 1.13 a ajouté la syntaxe des underscores pour améliorer la lisibilité des nombres, et strconv.ParseInt a aussi été modifié pour accepter cette nouvelle syntaxe
    • Du code chez un utilisateur externe, qui utilisait les nombres séparés par des underscores comme format de données distinct, s’est retrouvé cassé
    • Ce code essayait d’abord ParseInt, puis ne traitait les underscores qu’en cas d’échec, mais ParseInt n’échouait plus
    • net.ParseIP acceptait les adresses IP décimales avec des zéros initiaux, en suivant les premiers exemples des RFC IP
    • Go interprétait 18.032.4.011 comme 18.32.4.11
    • Les bibliothèques C de type BSD interprétaient au contraire les zéros initiaux comme le début d’un nombre octal, et lisaient cette même chaîne comme 18.26.4.9
    • Dans Go 1.17, net.ParseIP a été modifié pour rejeter complètement les zéros initiaux
    • L’objectif était que lorsque Go et C réussissent tous deux à parser une adresse IP, ils lui donnent la même signification
    • Kubernetes craignait que des configurations stockées de longue date ne puissent plus être parsées avec Go 1.17, et a commencé à utiliser un fork du net.ParseIP d’origine
    • Pour les entrées utilisateur, il est préférable de valider d’abord la syntaxe que l’on veut accepter avant de parser la valeur, mais dans certains cas un fork du code peut être nécessaire
  • Changements de protocole

    • Un changement de protocole survient lorsqu’une modification d’un package devient visible dans le protocole utilisé pour communiquer avec le monde extérieur
    • Go 1.6 a ajouté la prise en charge automatique de HTTP/2
    • Un client Go 1.5 n’utilise que HTTP/1.1 et peut donc fonctionner correctement dans certains environnements avec des équipements réseau intermédiaires spécifiques
    • Après mise à jour vers Go 1.6, HTTP/2 est utilisé, et si cet environnement ne gère pas correctement HTTP/2, le programme peut casser
    • Go cherche à prendre en charge les protocoles modernes par défaut, mais l’activation de HTTP/2 peut casser des programmes sans qu’il y ait de faute du programme ou de Go lui-même
    • Les notes de release de Go 1.6 ont décrit ce changement et fourni des moyens de désactiver HTTP/2
    • Définir explicitement le champ TLSNextProto
    • Définir GODEBUG=http2client=0, GODEBUG=http2server=0, ou les deux
    • La prise en charge des certificats HTTPS fondés sur SHA1 est un autre exemple, plus subtil, de changement de protocole
    • Les autorités de certification ont cessé d’émettre des certificats SHA1 en 2015, et les principaux navigateurs ont cessé de les accepter en 2017
    • Go 1.18 a désactivé par défaut la prise en charge des certificats SHA1, tout en permettant un contournement via GODEBUG
    • Certaines installations de Kubernetes utilisent encore des certificats SHA1 privés, ce qui a conduit Go à conserver ce réglage de contournement plus longtemps que prévu

Prise en charge étendue de GODEBUG dans Go 1.21

  • Go 1.21 étend et formalise l’usage de GODEBUG afin de réduire même les problèmes de compatibilité subtils
  • Pour les changements autorisés par les règles de compatibilité de Go 1 mais susceptibles de casser des programmes existants, un réglage GODEBUG est défini afin que chaque programme puisse refuser le nouveau comportement
    • Il peut exister des cas où l’ajout d’un réglage est impossible, mais ils sont considérés comme très rares
  • Les réglages GODEBUG destinés à la compatibilité sont conservés au moins 2 ans, soit 4 releases de Go
    • Des réglages comme http2client et http2server peuvent rester bien plus longtemps, voire indéfiniment selon les cas
  • Quand c’est possible, chaque réglage GODEBUG est associé à un compteur runtime/metrics
    • Le nom du compteur suit le format /godebug/non-default-behavior/<name>:events
    • Par exemple, si GODEBUG=http2client=0 est défini, /godebug/non-default-behavior/http2client:events compte le nombre de transports HTTP configurés sans HTTP/2
  • La valeur par défaut de GODEBUG dans un programme est alignée sur la version de Go indiquée dans le go.mod du package principal
    • Si le go.mod contient go 1.20 et que l’on met à jour vers la toolchain Go 1.21, les comportements contrôlés par GODEBUG qui changent en Go 1.21 conservent le comportement de Go 1.20 tant que le go.mod n’est pas passé à go 1.21
  • Chaque réglage GODEBUG peut être modifié avec une directive //go:debug dans package main
  • Tous les réglages GODEBUG sont répertoriés dans une liste centrale

Le cas de panic(nil)

  • Dans Go 1.21, panic(nil) déclenche désormais une panic d’exécution non nulle
  • Ce changement permet au résultat de recover d’indiquer de façon fiable si la goroutine courante est en train de paniquer
  • Le nouveau comportement est contrôlé par un réglage GODEBUG et varie selon la ligne go du go.mod du package principal
    • Avec go 1.20 ou inférieur, panic(nil) reste autorisé
    • Avec go 1.21 ou supérieur, panic(nil) devient une panic contenant runtime.PanicNilError
  • Cette valeur par défaut fondée sur la version peut être explicitement remplacée en ajoutant la ligne suivante à package main
//go:debug panicnil=1
  • Cette combinaison permet de mettre à jour vers une nouvelle toolchain tout en conservant le comportement de l’ancienne, de contrôler finement uniquement les réglages nécessaires, et de vérifier via la supervision de production si des comportements non par défaut sont utilisés
  • Plus de détails sont disponibles dans « Go, Backwards Compatibility, and GODEBUG »

Go 2 ne cassera pas Go 1

  • Le document « Go 1 and the Future of Go Programs » contenait à l’origine une réserve indiquant qu’une spécification Go 2 pourrait un jour voir le jour
  • Go 2 au sens d’une version qui ne compilerait plus les programmes Go 1 n’arrivera pas
  • Go 2, au sens d’une grande révision de Go 1 lancée en 2017, a déjà eu lieu
  • Go considère que la compatibilité a bien plus de valeur qu’une rupture avec le passé, et a choisi de renforcer encore cette compatibilité
  • De nouveaux travaux intéressants continueront d’arriver, mais de manière prudente et compatible afin que les mises à niveau entre toolchains restent aussi stables que possible

1 commentaires

 
GN⁺ 2023-08-15
Avis de Hacker News
  • La question importante en matière de compatibilité n’est pas « faut-il le faire », mais « comment le faire ». En réalité, il ne s’agit pas tant de compatibilité avec le passé que du souhait que, à partir de maintenant, mon code continue simplement de fonctionner
    Go 1.21 apporte deux éléments clés qu’il est difficile de voir réunis dans d’autres écosystèmes de langages : chaque changement dispose d’un réglage GODEBUG, peut être annulé individuellement, et il existe aussi des métriques permettant de détecter l’utilisation ou non de l’ancienne implémentation. Il existe également une version de toolchain par module, et les toolchains Go plus anciennes ou plus récentes peuvent être récupérées automatiquement et en toute sécurité, comme des modules
    En bonus, si l’on précise une version spécifique comme go 1.21.2, alors même en l’exécutant avec un Go plus récent, les configurations d’opt-out concernées sont appliquées automatiquement jusqu’à ce que le nouveau comportement soit explicitement demandé. On peut le déclarer dans le code, dans go.mod ou dans des variables d’environnement : une approche simple et élégante qui couvre presque tous les cas d’usage de compatibilité, des développeurs aux personnes chargées du déploiement

    • Perl permet quelque chose de similaire : écrire use v5.24 en tête de fichier permet de le faire se comporter comme Perl 5.24, avec une application au niveau du fichier plutôt qu’au niveau du module
    • Désolé, mais avec suffisamment d’équipes et une base de code de 1 à 10 millions de lignes ou plus, cette approche ressemble à un cauchemar
      Rien que le fait de savoir si l’on prend en charge ou non une nouvelle version crée déjà un problème absurdement complexe, et cela ouvre une zone grise profonde où « nous pensons prendre en charge la nouvelle version et la nouvelle version pense nous prendre en charge, mais chacun rate quelque chose »
    • Ce genre de fonctionnalité existe clairement aussi dans d’autres langages grand public. Haskell va même plus loin en permettant d’activer et désactiver des fonctionnalités du langage fichier par fichier
    • Dire qu’il s’agit de « fonctionnalités clés absentes des autres écosystèmes de langages », puis énumérer des fonctionnalités courantes dans d’autres langages, donne l’impression d’être l’utilisateur de Go le moins enthousiaste qui soit
  • J’aime vraiment cette direction. Il n’y a rien de mieux que de pouvoir arriver dans une base de code Go et s’attendre à ce que tout fonctionne simplement en augmentant la version de Go
    Ce qui m’inquiète, en revanche, c’est qu’il est difficile d’améliorer fortement le système de types sans changements cassants du genre « ce code était incorrect, il ne compile donc plus ». Je ne sais pas si l’équipe Go s’intéresse à ce sujet, mais il y a beaucoup de gains faciles qui pourraient fortement améliorer la robustesse à la compilation sans ajouter de fonctionnalités au langage
    Par exemple : signaler les nil non vérifiés, vérifier les accès aux tableaux, inférer les types dans les littéraux de structures imbriquées, ou encore effectuer des vérifications d’exhaustivité sur les énumérations. C’est particulièrement pénible en Go lors de l’écriture d’appels gRPC imbriqués : il suffirait d’un niveau d’inférence de type, puisque le type est déjà présent dans la signature de la fonction appelée

    • Pour répéter ce que l’on entend hors de la communauté Go, les énumérations paramétrées à la Rust ou Swift sont un don du ciel. Elles rendent d’innombrables programmes beaucoup plus faciles à écrire
      Si le système de types de Go devait s’améliorer, j’aimerais qu’il ajoute ces types de données algébriques simples et élégants. Ils conviendraient vraiment très bien à Go ; faites-vous ce cadeau
  • Tout à fait d’accord. Si les versions de Go sont attendues, c’est parce qu’elles ajoutent souvent des choses utiles sans rien casser
    Aucune partie du langage ne semble irrémédiablement cassée au point de ne pas pouvoir être corrigée par un changement rétrocompatible. Même les quelques pièges, comme l’affectation des variables de boucle, font généralement l’objet de propositions qui préservent la compatibilité

    • C# maintient lui aussi une compatibilité totale avec le passé, mais j’observe l’ambiance exactement inverse chez les développeurs .NET : « le langage devient boursouflé », « il devient difficile à apprendre »
      Personnellement, je partage l’enthousiasme vis-à-vis des versions de Go, mais la différence d’attitude entre les deux écosystèmes est intéressante
    • Pour apporter ma petite contribution, j’ai souvent rencontré des ruptures assez importantes lors des mises à niveau de Go. J’ai eu beaucoup moins de problèmes avec les mises à niveau de Rust ou de gcc, et les mises à jour des compilateurs C et Rust m’ont réellement semblé rétrocompatibles
      J’avais autrefois dressé une liste de certaines ruptures rencontrées : https://news.ycombinator.com/item?id=29763324
      Avec Rust ou gcc, on met à jour le compilateur et on obtient de nouvelles fonctionnalités du langage, mais la plupart des bibliothèques complexes restent inchangées et peuvent être mises à jour séparément. Par exemple, HTTP en Rust est séparé via hyper, et en C via libcurl
      À l’inverse, quand on met à jour le compilateur Go, on récupère à la fois les génériques, embed, des fonctionnalités de toolchain, mais aussi des changements dans tls, les bibliothèques de sécurité et le client/serveur HTTP. Les gros blocs de bibliothèque standard comme http, tls et crypto auraient largement gagné à être des bibliothèques séparées. On aurait ainsi pu mettre à jour le compilateur sans crainte, et faire évoluer les bibliothèques à son propre rythme
    • Je suis globalement d’accord, mais nul ne connaît l’avenir. L’édition Rust 2018 a introduit le mot-clé async, alors que dans l’édition 2015 on pouvait créer une variable ou une fonction nommée async : c’était donc un changement cassant
      La rétrocompatibilité est une bonne chose, mais il est difficile d’être certain qu’un avenir où Go renoncerait à telle innovation X parce qu’elle casserait la compatibilité soit acceptable
    • J’aimerais qu’il existe de meilleurs moyens de réduire la verbosité autour des types somme et de la gestion des erreurs
  • Je soutiens fortement la position selon laquelle un futur Go 2 ne rompra jamais la compatibilité avec Go 1. Si le langage devait changer à ce point, il vaudrait mieux le forker et lui donner un autre nom
    Cela dit, je me demande pourquoi ne pas aller plus loin et dire clairement qu’« il n’y aura pas de Go 2 », afin de lever toute ambiguïté. Si le Go 2 théorique exécute tous les programmes Go 1, en quoi serait-il différent d’une release Go 1.xx ? Le texte dit qu’« il n’y aura pas de Go 2 qui casse les programmes Go 1 », mais il ne semble pas aller au-delà

    • Il y a déjà cinq ans, il était dit que « ce serait le cas ». Mais avec un « si » en préambule
      https://github.com/golang/proposal/blob/d661ed19a203000b7c54...
      Le document explique que, si ce processus fonctionne comme prévu, il n’y aura pas de Go 2 au sens important du terme, et que la transition se fera progressivement via de nouvelles fonctionnalités du langage et des bibliothèques. À un moment donné, pour des raisons marketing, on pourra appeler ça « Go 2 », mais on pourra aussi simplement sauter cette étape. L’argument est : il n’y a pas eu de C 2.0, alors pourquoi faudrait-il un Go 2.0 ?
      Les langages populaires comme C, C++ ou Java sont en pratique toujours en version 1.N, et je pense que Go ferait bien de suivre cette voie. Un vrai Go 2, au sens d’un nouveau langage ou de bibliothèques centrales incompatibles, ne serait pas une bonne option pour les utilisateurs et pourrait être nuisible
    • Java a déjà emprunté ce chemin. Il y a eu Java 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, puis le discours a changé : « puisqu’on ne casse de toute façon pas la compatibilité ascendante, appelons-les Java 8, 9, 10… 21 plutôt que 1.x »
      Au final, cette approche me semble logique
    • On surestime peut-être l’ampleur des changements nécessaires pour casser la compatibilité source. Un exemple bénin est l’ajout de mots-clés
      Si la communauté veut clairement une nouvelle fonctionnalité du langage, et que la bonne façon — ou la seule — de l’ajouter passe par un nouveau mot-clé, alors une règle interdisant absolument de casser la compatibilité source empêche cette fonctionnalité d’être ajoutée pour toujours. Je suis d’accord pour dire que les grands changements touchant à la sémantique du langage sont importants, mais les changements incompatibles avec le passé ne sont pas toujours gigantesques
    • Les éditions de Rust sont, à mon avis, un bon contre-exemple. Les différences entre éditions sont des breaking changes, mais en pratique elles ne sont pas énormes
      En théorie, l’idée d’une compatibilité ascendante qui ne change jamais est séduisante, mais dans la réalité il existe aussi des breaking changes qui ont du sens. Devoir porter indéfiniment une fonctionnalité du langage qui n’avait pas pris en compte X ou Y lors de sa conception initiale ne me semble pas forcément avantageux
    • La fin de l’article le dit déjà. Un Go 2 au sens d’une rupture avec le passé, où les anciens programmes ne compileraient plus, n’arrivera jamais ; et un Go 2 au sens d’une révision majeure de Go 1, amorcée en 2017, est déjà arrivé
  • J’utilise beaucoup Go, et cette orientation fait vraiment chaud au cœur
    La compatibilité n’est peut-être pas très amusante du point de vue de l’équipe du langage. Il faut toujours garder un pied solidement ancré dans un « passé lointain ». Mais pour quelqu’un qui doit maintenir de grands systèmes Go, c’est un immense cadeau

    • J’ai travaillé pendant plusieurs années sur le compilateur Go, et ce n’était pas un si gros problème. Nous réfléchissions prudemment et rejetions beaucoup d’idées
      Si quelque chose ne s’intégrait pas correctement, c’est que ce n’était pas encore la bonne solution, donc on réessayait ; et si ça ne s’intégrait toujours pas, c’était probablement que nous ne comprenions pas encore assez bien le problème, donc il fallait le laisser mûrir davantage
      J’ai vraiment apprécié de travailler avec des gens qui réfléchissaient prudemment et cherchaient à faire entrer les bonnes idées. Je suis reconnaissant à Russ d’avoir joué le rôle de BDFL, et d’avoir travaillé avec Ian, Rob et Rob ; grâce à cela, je suis devenu un bien meilleur ingénieur
  • Article connexe : Forward Compatibility and Toolchain Management in Go 1.21 - https://news.ycombinator.com/item?id=37122932

  • La phrase « l’ennui, c’est bien. L’ennui, c’est stable. Être ennuyeux signifie que l’on peut se concentrer sur son travail sans se soucier de ce qui a changé dans Go » me parle vraiment
    Dans mon travail principal, j’utilise NodeJS et l’écosystème JS en général, et c’est vraiment pénible. L’écosystème est fragmenté, chacun fait à sa façon, et il est difficile de rendre les choses stables. J’aime toujours ce travail, mais j’aimerais que l’écosystème JS dispose lui aussi d’une base moderne et stable sur laquelle on puisse compter

    • Pour l’écosystème JS, par exemple npm ou React, c’est possible. Mais il faut aussi reconnaître que JavaScript, en tant que langage, privilégie la compatibilité ascendante depuis avant même l’existence de Go
    • Je me demande pourquoi Go n’est pas encore le nouveau Java/.NET
      Beaucoup d’outils et d’API sont écrits en Go, et le fait de ne pas nécessiter de runtime séparé sur le système cible est souvent cité comme un gros avantage. Le langage semble aussi simple à apprendre et à utiliser, la prise en charge dans VSC et GoLand est bonne, et les reproches courants comme la gestion des erreurs ne ressemblent pas à des défauts rédhibitoires
      Je me demande ce qui manque encore à Go pour devenir, dans les décennies à venir, un pilier du développement, ou au moins une grande part du marché de l’emploi. Dans certains endroits, il est encore considéré comme un langage de niche
    • JavaScript est célèbre pour sa compatibilité ascendante. C’est précisément pour cela qu’il est devenu l’état chaotique que tu décris
    • Je pense qu’il existe désormais une base réellement stable. Les modules ES et le code ES2020 sont pris en charge à la fois par Node et par les principaux navigateurs. Node dispose aussi d’un exécuteur de tests intégré
      Le problème est de faire en sorte que tout le monde atteigne cette baseline. Une fois là, les choses devraient aller beaucoup mieux
      Par ailleurs, l’écosystème JS inclut aussi le travail d’UI frontend, et ses domaines d’application sont si nombreux qu’il est impossible d’éviter l’apparition de multiples implémentations. C’est même plutôt souhaitable
  • Le fait d’avoir déplacé une partie du code de Python vers Golang nous a beaucoup aidés à scaler. Je suis vraiment ravi de voir que Go continuera de respecter son engagement fondamental : la compatibilité ascendante

  • Une discussion sur le parsing d’IP, donc. Je me demande comment le inet_ntoa de BSD était écrit à l’origine
    atoi/atol et sscanf avec %d/%u parsant toujours exactement des entiers décimaux, il aurait fallu utiliser %i ou un strtou en base 0 pour produire ce genre d’effet étrange

    • Pas besoin de deviner. La page de manuel de mon inet_aton indique qu’il vient de 4.3BSD : https://github.com/dank101/4.3BSD-Reno/blob/master/lib/libc/...
      Le inet_addr de 4.2BSD, encore antérieur, utilise aussi la même logique : https://github.com/dank101/4.2BSD/blob/master/lib/libc/inet/...
      inet_aton et inet_addr parsant les adresses de la manière la plus naturelle. Utiliser quelque chose comme strtoul, ou surtout sscanf, aurait été maladroit. La beauté des pointeurs en C, c’est qu’ils rendent les petites tâches de parsing très faciles — peut-être même trop faciles
    • J’ai ri en lisant ce passage. Il y a longtemps, en voulant « nettoyer » /etc/hosts, j’avais complété chaque octet avec des zéros
      Au final, j’ai dû revenir en arrière et supprimer les zéros
  • En tant que concepteur de langage, je respecte les choix faits ici, y compris celui de ne pas créer un vrai Go 2
    Je compte moi aussi reprendre certaines des techniques permettant de garantir la compatibilité