2 points par GN⁺ 2026-06-12 | 1 commentaires | Partager sur WhatsApp
  • Si la popularité du typage statique a reculé des années 2000 au début des années 2010, puis est remontée au milieu et à la fin des années 2010, c’est non pas à cause d’un effet de mode, mais grâce à l’amélioration de la qualité des systèmes de types statiques
  • De la même façon que, pour creuser un trou, une pelle en papier est moins utile que les mains nues, un système de types dynamique vaut mieux qu’un mauvais système de types statique
  • Les anciens systèmes de types statiques comme les premières versions de Java ou C++98 ne savaient même pas distinguer nullable et non-nullable, n’avaient pas de types somme et obligeaient à écrire les noms de types partout
  • Les systèmes de types modernes comme TypeScript, Haskell, Swift ou Rust fournissent nativement la distinction de null, les types somme / union et l’inférence de types
  • Avec la généralisation dans les IDE de l’autocomplétion des méthodes, les informations fournies au typage statique apportent désormais, au-delà de la détection d’erreurs, un gain de productivité supplémentaire

Hypothèse sur l’évolution de la popularité du typage statique

  • Si le typage statique a perdu en popularité dans les années 2000 jusqu’au début des années 2010, avant de regagner du terrain au milieu et à la fin des années 2010,
    ce n’est pas parce que la programmation est un secteur guidé par les modes, mais parce que la qualité des systèmes de types statiques largement utilisés s’est améliorée

Métaphore de la pelle — la qualité de l’outil détermine le choix

  • Quand on creuse un trou, si la pelle est correcte, on l’utilise évidemment plutôt que les mains nues,
    mais si la seule pelle disponible est faite en papier, elle ne fait que remuer la terre inutilement et il vaut alors mieux creuser à la main
  • Dans un système de types dynamique, il faut suivre soi-même entièrement dans sa tête l’état et le contenu des variables et des champs ;
    l’ordinateur n’aide ni n’entrave, ce qui revient à creuser à mains nues
  • Les systèmes de types statiques rudimentaires fréquents dans les années 1990 et au début des années 2000 étaient comparables à une pelle en papier
    • Ils n’aidaient même pas sur quelque chose d’aussi simple que distinguer les pointeurs nullable et non-nullable
    • Ils n’avaient que des types produit (product type), sans types somme (sum type)
    • Ils imposaient de recopier manuellement les noms de types un peu partout
    • Du code comme BufferedReader bufferedReader = new BufferedReader(new FileReader(filename)); relevait du petit désastre

Ce que les systèmes de types modernes fournissent systématiquement

  • Par contraste avec les systèmes de types modernes comme TypeScript, Haskell, MyPy, Swift ou Rust, on y trouve toujours les éléments suivants
  • Distinction entre nullable et non-nullable

    • Haskell propose Maybe t, TypeScript T | null, Swift T?, Rust Optional<T>
    • Le système de types indique facilement tous les endroits où une vérification de null est nécessaire ainsi que les oublis éventuels
    • En pratique, on voit presque plus d’erreurs de pointeur null à l’exécution
  • Types somme ou types union

    • Ils rendent possible la pratique consistant à rendre les états invalides impossibles à représenter (Make invalid states unrepresentable)
    • On peut créer des objets de machine à états (state machine) dotés de plusieurs champs, chaque champ n’existant que lorsque l’état concerné est actif dans le système
  • Inférence de types

    • Le compilateur peut comprendre que let x = 5; est un nombre, il n’est donc pas nécessaire d’écrire let x: number = 5;

L’apport supplémentaire de la diffusion des fonctionnalités IDE

  • La généralisation de fonctions d’IDE comme l’autocomplétion des noms de méthodes a accru l’utilité des systèmes de types statiques
    • Dans les années 1990, Intellisense de Visual Studio était une fonctionnalité phare ; dans les années 2020, on retrouve un équivalent dans presque tous les IDE et éditeurs
    • Les informations saisies dans le système de types statique se traduisent, en plus de la vérification d’erreurs, par des gains de productivité supplémentaires

Conclusion

  • Un bon système de types dynamique vaut mieux qu’un mauvais système de types statique
  • Mais nous disposons désormais de systèmes de types statiques bien meilleurs qu’autrefois

1 commentaires

 
GN⁺ 2026-06-12
Avis sur Lobste.rs
  • Cet article est bon, mais je ne suis pas totalement d’accord. Même si les systèmes de types statiques du début des années 2000 n’étaient pas excellents, ils étaient selon moi bien préférables à l’absence totale de typage statique
    Il n’y avait pas de types somme fermés, mais on pouvait modéliser une grande partie avec le sous-typage, et il n’y avait pas de types non-null, mais les références et types non pointeurs de C++, ainsi que les types primitifs de Java, en couvraient une partie. En Ruby ou JavaScript, non seulement tous les types pouvaient être null, mais ils pouvaient aussi être traités comme des chaînes, comme des entiers, ou comme n’importe quel autre type du programme, ce qui était pire
    Je pense qu’un grand facteur du changement de tendance autour du typage statique est que, pendant le boom des réseaux sociaux du Web 2.0, l’avantage du premier arrivé comptait plus que tout. Mieux valait lancer vite et itérer, quitte à accumuler de la dette technique en Ruby ou Python, que de se faire dépasser comme Friendster ou Digg, et si c’était lent, on pouvait simplement acheter plus de serveurs avec l’argent à bas taux facilement disponible à l’époque
    Ensuite, avec le boom mobile, les logiciels se sont mis à tourner sur des appareils utilisateurs limités et hors de contrôle, et les applications à typage dynamique lentes étaient tout simplement lentes en pratique, tandis qu’en cas d’erreur de type on ne pouvait pas se rétablir proprement via un gestionnaire global de réponse comme sur un serveur. Dans ce contexte, la sécurité et les performances du typage statique sont devenues bien plus convaincantes

    • Il existe pas mal d’articles comparant Java et le C++ des années 90 à des bases de code en langages à typage dynamique, concluant à des taux de bugs similaires, et les partisans des langages dynamiques s’en servent souvent pour affirmer que le typage statique n’est pas utile
      Au début des années 2000, j’étais d’accord aussi, car les systèmes de types de l’époque imposaient souvent des contraintes qui n’aidaient pas à structurer le code, tout en ne garantissant que des propriétés presque jamais erronées. En particulier, la combinaison du sous-typage et de l’héritage d’implémentation manquait de souplesse
      J’ai changé d’avis en utilisant des systèmes de types plus modernes. Dans snmalloc, le système de types C++ impose une machine à états de propriété mémoire, et dans d’autres bases de code il vérifie le bon comportement d’overflow de compteurs de buffers circulaires. Dans les deux cas, quand on se trompe, le débogage est pénible et c’est une source d’erreurs fréquente, mais le compilateur a réellement refusé de compiler du code que je pensais correct, empêchant ainsi des bugs d’entrer dans la branche principale
    • Je pense que développer dans un langage à typage dynamique est plus lent que dans un langage à typage statique. Je vois sans cesse l’argument inverse, mais je ne le comprends pas
      Dans un IDE, appuyer sur . puis taper quelques lettres du nom de la méthode avant de valider la bonne suggestion permet d’économiser 2 secondes toutes les quelques secondes, et fait aussi gagner les 30 secondes qu’il faudrait autrement pour aller chercher la définition d’une classe quand on ne sait pas quelles méthodes existent. Ce principe est aussi bien décrit sur https://grugbrain.dev/#grug-on-type-systems
      On écrit bien plus souvent des lignes qui appellent des méthodes que des annotations de type de paramètres de fonctions, donc le compromis est massivement défavorable au typage dynamique. Ce qui avait de la valeur, ce n’était pas d’autoriser du code absurde qui échoue à l’exécution, mais de pouvoir omettre le type des variables locales, et les langages à typage statique n’ont jamais eu besoin de l’interdire
    • Les systèmes de types populaires du début des années 2000 n’étaient pas juste “pas incroyables” : ils étaient mauvais, et très verbeux
      Dans les rares bases de code qui prenaient le système de types au sérieux, on accumulait des pages de code qui ne disaient rien, tout en gardant des montagnes de conditions d’exécution, et dans le cas de Java, plus la hiérarchie de types grossissait, plus le programme devenait concrètement lent. La plupart des bases de code utilisaient les types de manière incomplète tout en ajoutant beaucoup de conditions à l’exécution, sans réduire énormément la couverture de tests nécessaire par rapport à un système dynamique
      Les langages dynamiques n’apportaient aucun bénéfice statique, mais ils étaient concis, donc plus faciles à lire, à relire et à tester. C’était particulièrement vrai dans des environnements comme les frameworks d’injection de dépendances de la fin des années 90 et du début des années 2000, où l’ajout d’un nouveau service imposait de modifier plusieurs fichiers XML. On pouvait aussi travailler sans IDE consommant la moitié de la RAM
      C’est exactement à ça qu’a ressemblé le début de ma carrière, donc je suis entièrement d’accord avec l’article. Le rapport coût/bénéfice de Java 1.4 à Java 6 était tellement mauvais que j’ai presque abandonné les langages à typage statique, et ce n’est que quelques années plus tard, en touchant à Haskell comme hobby, que j’ai compris que le typage statique pouvait lui aussi avoir un rapport coût/bénéfice raisonnable, et que le problème, c’était Java. L’essai “python is not java” illustre aussi très bien cette période sombre
    • Le sous-typage fondé sur l’héritage était encore plus pauvre. On n’obtenait pas l’ergonomie du pattern matching avec vérification d’exhaustivité, et l’implémentation se retrouvait éparpillée à plusieurs endroits
    • L’explication selon laquelle il était crucial de mettre son site en ligne avant ses concurrents, de le placer devant les utilisateurs et de verrouiller les économies d’échelle paraît aussi très familière dans la situation actuelle
  • Je doute qu’on ait réellement constaté dans nos logiciels des gains de fiabilité depuis que le typage statique est devenu l’air du temps
    J’ai toujours pensé que les avantages du typage statique tenaient bien davantage au feedback immédiat pendant le développement et à la réduction des échecs critiques à l’exécution, mais même si de tels échecs restent théoriquement possibles, j’ai l’impression qu’en pratique ils ne surviennent pas si souvent

    • Si. À partir du moment où nous avons commencé à viser zéro erreur TypeScript dans une base de code TypeScript non triviale, les tentatives d’appel de méthodes sur undefined et null ont chuté brutalement
      Des juniors et même certains seniors étaient sceptiques au début, pensant qu’on allait se retrouver avec des @ts-ignore partout, mais en pratique il n’y en a eu qu’environ trois, y compris ceux dus à des types cassés dans des dépendances. Avant, il arrivait environ une fois par semaine qu’un conflit de types fasse planter l’app sur la branche de développement et bloque mon travail ; maintenant, je ne me souviens même plus de la dernière fois que c’est arrivé
      Le simple fait de satisfaire tsc réduit les bugs liés aux types, y compris quand le code n’a pas été écrit par moi. À l’inverse, les linters sont devenus trop zélés ces temps-ci, et à force de vouloir satisfaire des outils comme Sonar, j’ai vu de vraies régressions de refactoring. 95 % des alertes étaient fausses, 3 % venaient de bugs de l’outil, et les 2 % utiles ne pointaient même pas vers la vraie cause des bugs. Au lieu de passer une semaine à aligner la base de code pour corriger un bug, j’en ai ajouté deux autres au passage
      Le travail pour satisfaire tsc produisait en gros deux corrections de bugs purs et une régression par jour, mais les régressions étaient en général moins graves, plutôt du mauvais comportement qu’un crash complet
      En y ajoutant des tests basés sur les propriétés, cela prenait en moyenne 2 à 4 heures et révélait toujours au moins un bug. Si votre code se prête aux tests basés sur les propriétés, il faut en faire
      En augmentant la couverture de test avec le modèle bon marché DeepSeek V4 Flash tout en faisant attention à ne pas générer de tests poubelle, j’ai corrigé environ 2 à 3 bugs logiques par jour, sans crash. En revanche, le lot de tests reste tout juste maintenable
      Quand on a laissé des juniors bricoler des tests avec des modèles de la famille Sonnet et Opus 4.5, 4.6, les modèles n’ont produit que des tests qui « documentaient le comportement actuel », donc l’effet des corrections a été faible, et le lot de tests était impossible à maintenir, au point qu’il a fallu le jeter
      Les tests basés sur des modèles sont très bons pour attraper des bugs, mais la configuration est complexe, et il est très pénible de les pousser à explorer les recoins plutôt que de brûler des cycles sur des fonctionnalités de surface. Un genre de fuzzer basé sur des modèles et piloté par profil serait intéressant
      En résumé, les vérificateurs de types attrapent bien les défaillances critiques et nombre de confusions, et les tests basés sur les propriétés sont excellents. Les tests classiques demandent beaucoup de discipline pour offrir un bénéfice régulier
    • Personnellement, oui. Dans le JavaScript que j’utilise, les bugs de pointeur nul sont devenus presque négligeables après le passage à TypeScript, et mes collègues ont eu la même expérience
  • C’est surtout l’assimilation de TypeScript à un bon système de types avec laquelle j’ai du mal à être d’accord

    • Oui. TypeScript n’est pas sain, et en particulier sa manière de restreindre les types à travers await m’a mordu plusieurs fois. Cela dit, il a bel et bien amélioré la situation de façon spectaculaire
      Honnêtement, j’ai fini par accepter le typage structurel aussi, et je pense que cela aura une influence positive sur la conception des langages à venir
  • Cet argument n’est pas très convaincant. Des langages de programmation corrects avec types de données algébriques et inférence de types existaient déjà depuis le milieu des années 1990
    Les systèmes de types de Java et C++ étaient très pauvres, mais SML, OCaml et Haskell existaient déjà, avec un ressenti assez proche d’aujourd’hui. Si les gens n’utilisaient pas ces langages, c’est un problème de culture, d’adoption et d’exigences implicites, pas quelque chose qu’on peut expliquer uniquement par « les systèmes de types utilisables n’étaient pas assez bons »
    Ou bien, si l’argument est « les systèmes de types des langages populaires de l’époque étaient mauvais, et ceux des langages populaires d’aujourd’hui sont meilleurs, donc les systèmes de types sont devenus plus populaires », cela ressemble à un raisonnement circulaire
    Il y a aussi énormément de nuances dans la différence entre les langages conçus avec un système de types dès le départ et ceux conçus sans types à l’origine, puis dotés d’un système de types plus tard

  • Même en ayant toujours eu une préférence pour le typage dynamique, je trouve cet article assez juste. Aujourd’hui je travaille en C#, j’utilise Lisp pour mes loisirs, et j’ai aussi utilisé Python auparavant
    Quand j’étais obligé d’utiliser Java 5, je me battais en permanence contre le système de types, généralement à cause de mauvaises décisions prises par les auteurs de bibliothèques. Après être passé à C# vers 2010, le système de types n’était plus activement nuisible, mais restait pour l’essentiel redondant, et il n’empêchait même pas les exceptions de pointeur nul, qui étaient la confusion de types la plus fréquente en Python
    Le système de types de C# n’a vraiment commencé à m’aider qu’autour de 2020, avec l’arrivée des types de référence non nullables. Cette année, il accueille aussi des types union natifs, mais des bibliothèques de types union imposant l’exhaustivité étaient possibles au moins depuis 2016, et j’ai commencé à en utiliser à partir de 2020
    Je pense que l’effet de mode joue encore un rôle, mais qu’une partie de cet effet n’est pas mauvaise. Des langages à la mode dotés de systèmes de types plus expressifs ont aussi apporté des améliorations aux langages ordinaires que nous utilisons au travail contre rémunération

  • Haskell et son système de types existaient déjà dans les années 2000. Ce n’était pas aussi largement utilisé qu’aujourd’hui, mais cela existait clairement, donc cette affirmation devrait être nuancée sur ce point
    Personnellement, je pense que TypeScript a été un facteur majeur pour familiariser les utilisateurs de langages grand public avec de meilleurs systèmes de types. Au-delà de sa qualité et du soutien de Microsoft, il avait l’avantage de s’appliquer à JavaScript, et JavaScript avait plus urgemment besoin de types que Python. À cause de « Undefined is not a function. » et de « The good parts. »

    • J’aimerais bien qu’un livre « good parts » adapté aux versions récentes de JavaScript sorte tout en restant concis
      « Real World Haskell » est sorti en 2008 et visait à rendre Haskell plus attractif pour les programmeurs grand public. Je ne sais pas dans quelle mesure cela a aidé à diffuser la bonne parole
      Dans l’univers Java, Scala a apporté des types sophistiqués en 2004, et .NET a eu F# en 2005. Scala a peut-être obtenu les utilisateurs les plus visibles, comme Twitter, mais il n’était pas dans une position lui permettant d’absorber une grande part des utilisateurs de sa plateforme comme TypeScript, ni assez attractif pour attirer massivement des utilisateurs d’autres langages comme Rust ou Go
    • Le texte traite déjà de cette question. Il compare les systèmes de types statiques pauvres des débuts de Java, ou de C++98, populaires dans les années 1990 et au début des années 2000, à des pelles en papier
      Dans le paragraphe suivant, il mentionne Haskell comme un « système de types moderne », mais à la fin des années 1990 et au début des années 2000, les personnes ayant une expérience de Haskell — même simplement pour y avoir touché un peu — représentaient en pratique presque 0 %. Le texte parle de la manière dont l’immense majorité des développeurs faisait alors l’expérience des langages à typage statique, et de la raison pour laquelle cette majorité les évitait collectivement
    • Je pense que Haskell et OCaml souffrent dans une certaine mesure d’un écosystème d’outils faible. Les langages eux-mêmes sont excellents, mais ils perdent en adoption à cause d’une multitude de petites coupures de papier côté outils
      Par exemple, pour utiliser dune en OCaml, il faut comprendre les fichiers opam, les fichiers dune, la syntaxe des ocaml module et la syntaxe d’ocaml. Les extensions de compilateur optionnelles de Haskell me paraissent tout aussi intimidantes
      À comparer avec cargo, où il suffit de connaître le toml et Rust