L'hypothèse de Sapir-Whorf appliqué à la réinvention de la roue

En linguistique, l'hypothèse de Sapir-Whorf soutient que notre langue influe sur notre pensée. Bien sûr, c’est plus compliqué et controversé que ça, et je vous encourage à lire les pages Wikipédia (en particulier en langue anglaise) si la linguistique vous intéresse.

Cette hypothèse tient-elle en programmation, si on l’applique à la réinvention de la roue ?

Application naïve

Que ce soit en informatique ou en linguistique, on se rend compte rapidement que le langage influe sur les programmes/concepts qu’il est plus ou moins facile d’exprimer.

En effet, les personnes parlant plusieurs langues finissent par se rendre compte que certains mots n’existent pas dans certaines langues. Voici deux exemples :

  • Le mot "bully" en anglais a un sens très précis appliqué aux enfants dans un contexte scolaire. La meilleure traduction en français semble être "tyran", mais "enfant tyran" a déjà un sens dans un contexte familial, donc on ne peut pas vraiment utiliser ce mot sans devoir l’expliquer en français.
  • Avez-vous déjà eu besoin de redémarrer une voiture qui n’avait plus de batterie en la poussant puis en essayant de démarrer en seconde ? En créole réunionnais, on dit simplement qu’on "choque" la voiture.

En programmation, c’est un peu pareil :

  • impossible d’utiliser les génériques avant Go 1.18
  • l’utilisation de la récursion terminale est interdite en Python

Et c’est d’ailleurs pour ça que la question des génériques dans Go a attiré autant de discussions. Selon la loi de Wadler, ce qui est plus facile à comprendre dans un langage de programmation génère le plus de discussions, stériles qui plus est. Quoi de plus facile à comprendre qu’une fonctionnalité manquante ?

Application plus profonde

(Comme dirait @SpaceFox, pas de métaphore filée ici non plus.)

Non, la partie qui m’intéresse le plus, c’est quand un langage de programmation pousse à faire des choix différents. Le sujet est vaste, alors je vais me concentrer sur la question de la gestion des dépendances.

Go

Un point crucial dans le billet de @nohar est que le langage Go encourage à minimiser le nombre de dépendances. Russ Cox, qui a largement influencé la gestion des dépendances dans Go, a écrit sur le problème avec les dépendances logicielles, décrivant un processus très strict avant d’ajouter une dépendance. Je pense que cette réticence fait que la gestion des paquets dans Go a mis un certain temps à évoluer. (Est-elle aussi pratique que dans d’autres langages aujourd’hui ? Je ne sais pas.)

Cette philosophie se voit partout. C’est le cas de @nohar qui supprime testify, ou les conseils de r/golang. Un autre exemple ? Dans le channel Slack #golang au travail, un développeur demandait "quelle alternative à net/http ?" Même s’il existe un certain nombre d’alternatives (cf. par exemple ce qui est supporté par l’agent Go d’Elastic APM), le conseil le plus populaire a été de dire que même si net/http c’est un peu bas niveau, c’est très bien. (Quelqu’un a même dit que s’en servir était un rite de passage.) C’est complètement inenvisageable en Python par exemple, mais c’est aussi parce que le module http souffre de nombreux défauts.

JavaScript et Rust

Du côté complètement opposé, JavaScript et Rust ont des gestionnaires de paquets (npm, yarn, cargo) qui ont toujours visé à rendre aussi simples que possible l’ajout d’une nouvelle dépendance. De nombreux paquets populaires ont de nombreuses dépendances transitives. C’est sûrement pratique pour développer, mais ça alourdit le code et c’est particulièrement pénible quand il faut résoudre une faille de sécurité. create-react-app a presque 100k stars sur GitHub et des dizaines de vulnérabilités. (Ces vulnérabilités n’en sont pas dans le contexte de create-react-app, mais npm audit les remonte par défaut.)

Les autres langages

Les autres langages se situent un peu entre ces extrêmes, dans mon expérience, je ne vais donc pas les évoquer.


Mais du coup, est-ce que les langages de programmation influent sur les réinventions de la roue ? Tout comme en linguistique, difficile de répondre par oui ou par non. Voici ma réponse :

Le langage en lui-même, non. Il est sûrement faisable en Go d’avoir autant de dépendances qu’en Rust. Mais comme on l’a vu, la communauté et les outils influent certainement, oui. Et, de manière sûrement aussi importante, les personnes attirées par Go sont attirées par ces même idées, et viennent renforcer la communauté : ici encore, corrélation n’est pas causalité.

9 commentaires

Le sujet est vaste, alors je vais me concentrer sur la question de la gestion des dépendances.

J’ai cru observer les mêmes tendances chez les personnes pratiquant le Go et celle pratiquant Rust et JS.

Mais pour moi il s’agit bien là de pratiques culturelles des communautés en question, sans que l’on puisse imputer pour autant quoi que ce soit à ces langages. En effet, je ne trouve rien d’inhérent à Go ou à Rust et JS qui, par essence, encouragerait ou découragerait les arbres de dépendances profonds. Ma conclusion semble d’ailleurs rejoindre la tienne.

Mais alors je ne comprends pas très bien pourquoi le parallèle avec l’hypothèse de Sapir-Whorf a lieu d’être si l’on se concentre sur la question de la gestion des dépendances. Cette question en particulier ne me semble pas propice à une telle analyse.

Toutefois, j’aurais pu largement concevoir qu’un langage de programmation ait bien une influence sur la façon de réfléchir à un problème, de par ses propriétés intrinsèques. Et là, j’aurais vu le parallèle avec Sapir-Whorf.1


  1. Je précise tout de même que je suis relativement septique vis-a-vis de l’hypothèse de Sapir-Whorf en général.

Salut.

En fait je pense que la question est difficile à aborder dans l’absolu. À vrai dire l’argument culturel ne me semble pas suffire à aborder la question.

Récemment je suis tombé sur cet article de blog qui proposait une vue intéressante sur la réinvention de la roue, en posant le postulat que le bénéfice que l’on tire des dépendances tierces est inversement proportionnel à la quantité d’efforts engagés dans la maintenance et l’évolution d’un logiciel.

Ce qui est remarquable, c’est que cela semble parfaitement s’appliquer dans les deux sens :

  • Ajouter une dépendance sert à la base à concéder moins d’effort de développement dans un projet.
  • Quand un projet est parti pour être maintenu en continu, à temps plein, pendant des années, alors le bénéfice de ces dépendances diminue, au point qu’il soit parfois plus rentable de faire le job soi-même et de façon plus adaptée au problème.

Go est un exemple extrême et vivant de ce phénomène. Il était plus rentable pour Google d’embaucher des ex-cadors des Bell Labs pour créer un langage qui compile vite et gère parfaitement ses imports, que d’optimiser leur toolchain C++ pour réduire leurs temps de build qui duraient des heures. BigTable est encore un autre exemple extrême : oui, à une certaine échelle, ça devient même rentable de développer sa propre BDD.

En un sens, cela peut se rapprocher des facteurs culturels : historiquement, les projets faits en Go sont remarquablement souvent de "grosses" solutions. Le genre de projets high-effort où le nombre de lignes de code importe beaucoup moins que la facilité à y naviguer (coucou Kubernetes). Et donc où certaines dépendances externes perdent rapidement leur intérêt.

Depuis Go 1.11 et l’arrivée du tooling standard pour la gestion des dépendances (go mod), la techno s’est vue dotée d’un système de dépendances que je trouve personnellement encore mieux foutu et plus commode à l’utilisation que Cargo. Il est plus facile que jamais d’ajouter ou de mettre à jour une dépendance en Go, y compris de la vendorer pour se prémunir des gags à la npm avec son tristement célèbre module de padding. Avant 1.16, il était même possible de le faire sans jamais quitter le code des yeux (jusqu’à ce que le comportement par défaut change et requière quand même de taper explicitement go get ..., à cause du risque d’erreur, à savoir d’ajouter de nouvelles dépendances au projet sans s’en rendre compte/le vouloir).

Cela fait déjà plusieurs années que c’est en place, et pourtant la culture n’a pas changé : pour moi c’est un signe que ce n’est pas qu’une question de facilités. D’ailleurs, si Go 1.18 fait énormément de bruit avec les generics qui attirent toute l’attention, je pense quand même que cette feature du langage (que j’attends de pied ferme pour améliorer quelques trucs non-critiques, mais quand même un peu chiants dans ma codebase) est beaucoup moins impactante et "révolutionnaire" que les deux autres features majeures à venir en 2022 :

  • Le fuzzing, qui arrive également avec 1.18,
  • L’intégration d’audits de sécurité automatisés dans la toolchain standard qui est prévue pour 1.19.

Je pense qu’avec ces deux ajouts, le comportement des Gophers face aux dépendances externes va encore s’affiner : les problématiques de robustesse et de sécurité du code vont devenir visibles au premier plan, dans n’importe quel projet, quelle que soit sa taille, et je suis persuadé que ce sera l’occasion de tirer de nombreux enseignements sur le fait de "recoder les choses au risque de faire moins robuste et les fuzzer pour dénicher les edge-cases les plus vicieux", ou à l’inverse "rajouter du code non-maitrisé accompagné de ses propres failles de sécurité qui font hurler la CI" (coucou log4j…) dans un projet.

+1 -0

J’avoue ne pas comprendre l’objectif de ce billet ?

Tout d’abord, dire que l’hypothèse de Sapir-Whorf est controversée serait presque une tautologie. C’est avant tout une hypothèse, donc pas une loi ou un théorème. C’est des choses qu’on observe, qu’on ressent, mais qu’il est difficile de démontrer avec rigueur.

Observer qu’un locuteur de toki pona réussit à simplifier sa pensée ne veut pas dire que tous ceux qui parlent toki pona sont des personnes qui promeuvent la simplicité à tout bout de champ.

Maintenant :

  • impossible d’utiliser les génériques avant Go 1.18
  • l’utilisation de la récursion terminale est interdite en Python

Tout comme certains mots et certains concepts n’existaient pas dans certaines langues ? Est-ce que les chasseurs cueilleurs avaient besoin de définir des notions comme « atomes » ? J’ai des doutes.

De même, quand les processeurs ont été conçus, est-ce qu’on imaginait créer des langages de programmation aux paradigmes différents et à entrevoir une approche orientée objet ? J’ai des doutes aussi. Ainsi je pense plutôt que le souci c’est de mettre des mots sur des concepts nouveaux et non pas utiliser la bonne langue qui contient déjà les bons mots pour parler de concepts nouveaux. Selon moi ça n’a pas grand-chose à voir avec l’hypothèse de Sapir-Whorf.

Ensuite, je note que tu parles davantage des écosystèmes liés aux langages qu’aux langages eux-mêmes. Il aurait été intéressant d’aborder autant l’hypothèse que programmer en Go incite à simplifier sa pensée pour gérer les erreurs, que programmer en brainfuck relèverait, au bas mot, d’un semblant de masturbation intellectuelle (et ça, je gage que même Sapir-Whorf serait d’accord là-dessus).

Ensuite, pour finir :

Non, la partie qui m’intéresse le plus, c’est quand un langage de programmation pousse à faire des choix différents

Le problème de fond, pour moi, il se situe là. C’est quelque chose que j’ai commencé à observer quand je me suis limite retrouvé au ban du monde du travail, parce qu’il y avait quelque chose que je n’avais absolument pas compris alors que je m’évertuais à vouloir utiliser les meilleurs outils pour tout un tas de raisons qui me convenaient à moi, mais pas à mes employeurs : beaucoup de gens partent d’un langage de programmation avant d’inventer un problème à résoudre alors qu’il faut, selon moi, se concentrer sur le problème à résoudre avant de partir sur tel ou tel langage.

En refaisant une pirouette sur les langues naturelles puisqu’il est question de Sapir-Whorf et de langues naturelles : qu’est-ce qui justifie aujourd’hui que l’anglais soit la lingua franca de base ? C’est le Commonwealth. C’est grâce à l’ancienne influence de l’Empire britannique. Parce que, non, l’anglais n’est pas et n’a jamais été une langue facile ; le français non plus. À côté il y a des langues construites comme l’espéranto qui requièrent seulement quelques centaines d’heures pour être correctement utilisées.

Et pourtant on utilise l’anglais aujourd’hui parce qu’il résout le problème d’être à peu près compris par le plus grand nombre sur Terre, là où le mandarin connaît paradoxalement le plus de locuteurs au monde, et où d’autres langues plus simples ne jouissent pas de la même popularité.

Donc je veux bien m’abstenir de réinventer la roue, mais je préfère me concentrer sur le fait d’inventer les véhicules de demain, qu’ils requissent des roues carrées ou non, plutôt que de discuter éternellement de maximes type Don’t Reinvent The Wheel qui sont admis par la plupart des gens céans.

Donc je veux bien m’abstenir de réinventer la roue, mais je préfère me concentrer sur le fait d’inventer les véhicules de demain, qu’ils requissent des roues carrées ou non, plutôt que de discuter éternellement de maximes type Don’t Reinvent The Wheel qui sont admis par la plupart des gens céans.

Pourtant, à mes yeux, tout le problème est là : dans le fait d'admettre ces maximes comme universellement bienfondées et ne plus les remettre en question, et d’en faire, par définition, un dogme. Et je pense que c’est ce sujet que @Quentin essaye de creuser.

En parlant d’inventer "les véhicules de demain", tu esquives le plus gros de la problématique, parce que là où le fait d’injecter des dépendances ou non dans un projet peut avoir un impact douloureux, c’est précisément dans le fait de maintenir les projets et de garantir sur le long terme leur robustesse, leur sécurité, leur évolutivité… En somme, c’est le fait de continuer à répondre aux besoins des utilisateurs sans se fatiguer de plus en plus à mesure que le temps passe, et ce n’est ni plus, ni moins important que le fait de développer un logiciel qui réponde efficacement à ces besoins à la base.

+1 -0

Donc je veux bien m’abstenir de réinventer la roue, mais je préfère me concentrer sur le fait d’inventer les véhicules de demain, qu’ils requissent des roues carrées ou non, plutôt que de discuter éternellement de maximes type Don’t Reinvent The Wheel qui sont admis par la plupart des gens céans.

Pourtant, à mes yeux, tout le problème est là : dans le fait d'admettre ces maximes comme universellement bienfondées et ne plus les remettre en question, et d’en faire, par définition, un dogme. Et je pense que c’est ce sujet que @Quentin essaye de creuser.

nohar

De quelle(s) maxime(s) parles-tu ? Celle de réinventer la roue ? Je concède qu’il est important de questionner en permanence ce que l’on sait, et que s’enfermer dans des dogmes n’est pas souvent salvateur.

En parlant d’inventer "les véhicules de demain", tu esquives le plus gros de la problématique, parce que là où le fait d’injecter des dépendances ou non dans un projet peut avoir un impact douloureux, c’est précisément dans le fait de maintenir les projets et de garantir sur le long terme leur robustesse, leur sécurité, leur évolutivité… En somme, c’est le fait de continuer à répondre aux besoins des utilisateurs sans se fatiguer de plus en plus à mesure que le temps passe, et ce n’est ni plus, ni moins important que le fait de développer un logiciel qui réponde efficacement à ces besoins à la base.

nohar

Je pense que ce sont deux problématiques distinctes. À te lire je pense bien évidemment aux nombreux problèmes soulevés par le célèbre billet Software disenchantment et je suis tout aussi préoccupé que vous sur l’aspect maintenabilité sur le long terme, surtout quand je m’aperçois comment les choses évoluent vite… Et périssent très vite aussi.

La maintenance coûte du temps aussi bien que l’utilisation de la mauvaise technologie en fonction du besoin à satisfaire. Et même si la loi de Moore commence à connaître ses limites, l’histoire a montré au moins deux choses essentielles à prendre en compte, surtout dans un métier comme le nôtre :

  1. Si le problème est ancien à résoudre, alors la solution l’est aussi : il est préférable de se tourner vers des outils éprouvés ;
  2. une technologie peut tout bouleverser du jour au lendemain et rendre caduque un bon nombre de technologies existantes, indépendamment de leur fiabilité au travers du temps. On redoute notamment la capacité des ordinateurs quantiques à casser toute la cryptographie utilisée sur Internet.

Dans les deux cas, qu’on le veuille ou non, il y a une course effrénée à l’innovation (« les véhicules de demain »), elle ne plaît pas à tout le monde (notamment à cause de ces histoires de gaspillage, de problème de maintenance…), et peut-être que le plus intéressant serait d’envisager l’utilisation ou la création d’outils qui réussiraient à s’abstraire de cet effet de bord qui rend tout obsolète en 18 mois en moyenne. Sans vouloir verser dans le « yakafokon » bien sûr.

Peut-être que Go paraît être un excellent compromis si j’en crois ton expérience et les échos que j’en reçois, d’ailleurs. Mais de mon point de vue, je n’arrive pas à faire le lien avec l’hypothèse de Sapir Whorf.

Pour commencer, je ne sais pas s’il y a vraiment un intérêt à tirer des raisonnements sur quelque chose d’aussi flou et peu démontré que l’hypothèse de Sapir-Whorf – pour laquelle le consensus scientifique semble être qu’elle est fausse, ou alors vraie mais faiblement sur certains éléments précis.

Pour moi le lien entre un langage et son fonctionnement tel qu’il est pratiqué est plus du domaine de la culture que d’une mécanique d’induction de pensée. Quand tu développes dans un langage, généralement tu essaies de faire propre « selon les normes du langage », donc tu vas regarder ce qui se fait d’habitude et suivre ce fonctionnement. C’est plus ou moins facilité par les outils mais pas forcément (importer des tonnes de libs en JS n’a pas attendu d’avoir NPM, à une époque on avait des sites avec des dizaines d’imports de libs dans le <head>). S’il y a un besoin mais pas les outils, les outils apparaissent et deviennent plus ou moins standards (ant puis maven puis gradle en Java) ; inversement, il peut y avoir des outils créés mais peu utilisés si la culture ne les juge pas utiles (cf les messages précédents).

Le « code de chercheur » est souvent un bon exemple de ce qu’on peut faire avec un langage sans avoir la culture qui va avec ; et c’est souvent très éloigné des standards habituels.

D’autre part il y a des phénomènes de rétroaction qui font que, dans tous les langages plus ou moins adaptés pour résoudre le problème, les développeurs vont se tourner vers ceux qui correspondent déjà le plus à leur culture, à leur façon de faire, ce qui va renforcer cet aspect de la culture.


D’autre part, c’est en grande partie hors sujet, mais bon…

Personnellement je trouve que le postulat de ce billet est faux, ou au moins oublie une bonne partie des projets de l’industrie : les projets longs et complexes, mais qui ne peuvent pas se permettre de faire grossir leur équipe au point d’internaliser beaucoup de dépendances (notamment pour des questions de rentabilité, à cause de la taille de la clientèle potentielle). Je pense notamment à tous les progiciels et logiciels « orienté métier », qui sont souvent complexes, assez spécifiques et/ou paramétrables, et avec une clientèle réduite même si elle est prête à payer relativement cher pour ces produits. On trouve aussi là dedans beaucoup de logiciels de production, comme des sites e-commerce.

C’est une part aussi importante qu’elle est invisible de l’industrie ; et elle touche de la petite PME aux grandes entreprises multinationales.

Et c’est un pan de l’industrie dans lequel, d’expérience (directe ou indirecte), il y a plus de problèmes avec du code interne là où il aurait pu y avoir une dépendance, qu’avec des dépendances problématiques, et de loin. Je la problématique est d’autant plus prégnante que :

  1. La complexité et la charge de maintien (légale, notamment) du cœur du métier est importante par rapport à ce qu’il est raisonnablement possible de mettre comme effort ;
  2. Il y a du renouvellement d’équipe et de la perte de connaissance sur le logiciel (qu’elle soit dû à des problèmes internes, ou à une mauvaise gestion via des SSII/ESN, les deux existent).

Par contre je suis assez d’accord avec l’article tant qu’on reste sur du « code jetable » (sites de présentation, évènementiels, une bonne partie des applications mobile – hélas…) ou, de l’autre côté, des très gros projets sur lesquels l’éditeur peut se permettre de mettre une grosse équipe.

Je ne vois pas en quoi cela contredit le postulat, en fait. Il parle bien de l’utilité des dépendances comme une fonction de l’effort de développement, et non de la taille/complexité du projet.

Tu le précises toi-même :

les projets longs et complexes, mais qui ne peuvent pas se permettre de faire grossir leur équipe

… Donc où l’effort concédé reste tiré vers le bas et ne grossit pas alors qu’il devrait.

J’ai moi-même bossé pendant des années sur un projet de ce genre (6 devs max, 2–3 la plupart du temps, à maintenir des centaines de milliers de lignes de Python et de C++), et honnêtement dans ce contexte, que l’on utilise des dépendances ou non, c’est de toute façon la merde : tu te retrouves de toute façon la tête sous l’eau avec un logiciel qui pourrit sans rien pouvoir y faire. On est très, très loin d’une situation de développement saine, parce que dans ce contexte s’ajoutent également les problèmes de management, d’éclatement des responsabilités et de communication qui font que de toute manière, les forces de frottement ne peuvent que s’accumuler dans le temps, le turnover rester haut, et l’expertise de l’équipe sur le code et ses problématiques, plafonnée par une courbe descendante.

Autrement dit, dans le contexte de ces projets, il y a généralement des problèmes bien plus urgents à résoudre que de savoir s’il faut ou non réinventer la roue, et des facteurs bien plus importants pour expliquer la difficulté à maintenir une qualité logicielle/de service correcte.

+0 -0

Tu fais des suppositions quant à l’état du projet que je ne fais pas (ça peut se passer comme tu le décris comme ça peut bien se passer si l’éditeur a compris l’effort qu’il a les capacités de mettre et s’y tient, j’ai vu les deux).

SpaceFox

Certes, mais dans les deux cas, l’effort de développement et de maintenance reste borné, pour une raison valable ou non. Et donc on peut déduire (selon le postulat en question) que l’utilité des dépendances externes reste importante dans ce contexte, ce qui va exactement dans le même sens que ce que tu dis.

Un autre angle de vue (qui est d’ailleurs cité par le premier billet) indique que quoiqu’il arrive toutes les fonctionnalités qui font partie du "coeur de métier" (core) gagnent à être développées "maison". Et l'outsourcing (à traduire ici plutôt comme "l’utilisation de dépendances") devrait rester cantonné, quand il est pratiqué, à ce qui n’est pas la spécialité de l’équipe, autrement dit à ce qui ne relève pas de la valeur ajoutée directe que l’équipe développe dans le produit. Ce avec quoi je suis on ne peut plus d’accord, en ce qui me concerne.

Il est tout à fait normal de déléguer la maximum de choses au départ, mais ce que j’observe, c’est qu’à mesure que l’effort de maintenance croît (c’est-à-dire "que le temps passe" si on reste à équipe fixe, sans turnover), les premières dépendances externes à dégager du projet sont les plus proches du véritable coeur de métier du code. Dans mon exemple personnel actuel, l’ORM que j’ai dégagé était vraiment la brique externe la plus proche de l’intelligence que fournit mon backend à ses utilisateurs, contrairement à beaucoup d’autres dépendances que j’utilise comme de la crypto, le protocole gRPC, ou le protocole OIDC, ou le parsing et la validation de JWT, ou un client Discord… qu’il ne me viendrait même pas à l’esprit de remplacer par du code maison.

+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte