Paris, Bordeaux, Niort, Nantes, Rennes, Toulouse, Aix, Nice

Node ou DENO, TELLE EST LA QUESTION !? đŸ€”

Je vais rĂ©parer le monde que j’ai brisĂ©, et le rendre meilleur qu’il l’Ă©tait auparavant

. . .

Lors de la JS Conf de 2018, ayant eu lieu Ă  Berlin, Ryan Dahl Ă©voqua les 10 erreurs de conception de NodeJS. Quelque temps plus tard (le 13 mai 2020 exactement), la version 1.0.0 de Deno Ă©tait nĂ©e, ainsi que plusieurs nouvelles fonctionnalitĂ©s. La citation prĂ©cĂ©dente (tirĂ©e de l’Ă©pisode 2, de la saison 3 de Mr. Robot), n’aurait pas mieux traduit l’Ă©tat d’esprit de Ryan Dahl Ă  cette Ă©poque, vis-Ă -vis de NodeJS.

Théorie

Si vous vous interrogez… C’est quoi NodeJS ? Qui est Ryan Dahl ? C’est quoi Deno ? Cet article est fait pour vous ! 😉

NodeJS est un environnement d’exĂ©cution pour le langage JavaScript, basĂ© sur le moteur Chrome V8. Si vous ĂȘtes dĂ©jĂ  adepte de ce langage de programmation, vous avez forcĂ©ment NodeJS (et NPM) installĂ© sur votre ordinateur. Historiquement, le moteur Chrome V8 (dĂ©veloppĂ© par l’Ă©quipe Chromium) voit le jour en 2008, et avec lui la possibilitĂ© de compiler directement le code JavaScript en code machine natif, avant de l’exĂ©cuter. De nos jours, il est embarquĂ© dans plusieurs solutions incontournables, telles que Chrome, MongoDB ou encore NodeJS.

Ryan Dahl est, ni plus, ni moins, que le crĂ©ateur de NodeJS. DĂ©veloppĂ© depuis 2008 avec le langage C++ (et basĂ© sur le moteur Chrome V8), NodeJS intĂ©grera quelque temps plus tard, son propre gestionnaire de paquets (NPM), et deviendra trĂšs vite un indispensable de l’Ă©cosystĂšme JavaScript.

NB : Il se peut que je fasse quelques raccourcis lors de mes explications. En effet, l’Ă©cosystĂšme JavaScript est tellement vaste aujourd’hui, que ces quelques lignes / paragraphes ne suffisent pas Ă  dĂ©crire en totalitĂ© ce sujet…

Depuis 2010, les technologies JavaScript ne cessent de croitre. La preuve : il fait partie des langages de programmation les plus utilisĂ©s par les dĂ©veloppeurs, avec Java et Python. Parmi ces technologies, on retrouve les frameworks frontend, tels que Angular, React ou encore VueJS ; mais aussi les frameworks backend, notamment ExpressJS, Polka, Koa, etc… En 2018, alors que tout le monde avait les yeux rivĂ©s sur le concept de JAMStack, Ryan Dahl commença Ă  travailler sur le « successeur » de NodeJS, j’ai nommĂ© : Deno !

Tout comme NodeJS, Deno est Ă©galement basĂ© sur le moteur Chrome V8, mais contrairement Ă  son homologue, celui-ci est dĂ©veloppĂ© avec le langage Rust. De mĂȘme, la gestion de l’asynchronisme diffĂšre, puisque cette fois encore, Deno se rĂ©fĂšre Ă  Tokio pour le traitement des Ă©vĂ©nements.

NB : Pour rappel, JavaScript est un langage synchrone. C’est-Ă -dire qu’il exĂ©cute une seule opĂ©ration Ă  la fois (au sein de ce qu’on appelle, la CallStack). Les opĂ©rations asynchrones, telles que les appels XHR, ou encore les timers, sont pris en charge par l’environnement dans lequel s’exĂ©cute le code (soit le navigateur, soit NodeJS / Deno). En gĂ©nĂ©ral, on parle d’APIs Web.

Revenons-en au sujet : nous sommes le 13 mai 2020, la version 1.0.0 de Deno voit le jour. Parmi son lot de nouveautĂ©s, on retrouve en premier lieu l’exĂ©cution native du code TypeScript. Contrairement Ă  NodeJS qui prend « seulement » en charge la syntaxe CommonJS (ou ES Modules via l’extension .mjs), Deno supporte complĂštement le sur-ensemble typĂ© de Microsoft, Ă  savoir TypeScript.

Seconde nouveautĂ© : la gestion des dĂ©pendances. La trop forte relation avec NPM (et le package.json) figure parmi les erreurs de conception de NodeJS, d’aprĂšs Ryan Dahl. Pour pallier Ă  cela, Deno rĂ©cupĂšre ce dont il a besoin directement Ă  partir du Web. Il suffit alors d’importer les modules depuis une URL au sein du code (plutĂŽt que faire rĂ©fĂ©rence aux node_modules). Cette fonctionnalitĂ© donnera lieu Ă  la convention « deps.ts Â» , qui (comme son homologue, le package.json) permet de regrouper toutes les dĂ©pendances externes au sein d’un seul et mĂȘme fichier.

deps.ts

L’autre changement notable qui vient avec Rust, est l’omniprĂ©sence de la sĂ©curitĂ© lors de l’exĂ©cution des scripts. En effet, Deno ne vous permettra pas de lire et / ou d’Ă©crire un fichier sans en ĂȘtre prĂ©alablement autorisĂ©. Pour cela, il faut spĂ©cifier les autorisations lors de l’interprĂ©tation du code. Il en va de mĂȘme avec les appels externes. Par exemple, si vous souhaitez rĂ©aliser une API qui ira Ă©crire dans une base de donnĂ©es distante, vous devrez autoriser les accĂšs rĂ©seaux. Cela se traduit simplement par l’ajout de « flags » lors de l’utilisation de l’outil de ligne de commande :

deno run --allow-net main.ts 

Aujourd’hui encore, NodeJS ne se soucie pas de cette dimension, ce qui lui vaut quelques critiques


Concernant le coĂ»t de mise en oeuvre de Deno, comme pour NodeJS, tout a Ă©tĂ© pensĂ©. Que vous soyez sur Linux, Windows ou Mac OS ; que ce soit via Curl, le PowerShell ou encore HomeBrew ; les moyens pour installer l’outil de ligne de commande ne manquent pas. Celle-ci se veut d’ailleurs trĂšs pratique, puisqu’elle propose un mode REPL, la possibilitĂ© de linter et / ou de formater le code, ainsi que de mettre Deno Ă  jour, tout simplement.

Les fonctionnalitĂ©s de Deno sont nombreuses ! Je pourrais aussi mentionner sa capacitĂ© Ă  compiler le WebAssembly nativement, mais ne l’ayant pas encore testĂ©, je vous invite Ă  aller faire un tour sur la documentation officielle.

En pratique…

TrĂȘve de thĂ©orie, place Ă  la pratique. Il paraĂźt que Deno est plus performant que NodeJS (puisque codĂ© en Rust), voyons si c’est vraiment le cas… Ici, j’ai choisi de confronter ces deux environnements JavaScript avec trois cas d’usage :

  • L’exĂ©cution d’un script simple
  • L’exĂ©cution d’un script avec interactions au systĂšme de fichiers
  • L’exĂ©cution d’un script avec accĂšs rĂ©seaux

NB : Les versions de NodeJS et de Deno utilisées sont respectivement 14.8.0 et 1.3.0.

#1 – Fibonacci

Vous l’aurez reconnu, ce premier script permet de rĂ©cupĂ©rer le n-iĂšme nombre de la suite de Fibonacci. J’ai volontairement rĂ©alisĂ© deux fonctions, une itĂ©rative (pour un parcours linĂ©aire) et une rĂ©cursive (pour un parcours d’arbre), afin de rĂ©vĂ©ler s’il existe une diffĂ©rence de traitement de ces fonctions, entre NodeJS et Deno. En ajoutant un wrapper de temps (ici showTime()), j’obtiens les rĂ©sultats suivants :

x 1000 2000 3000 4000 5000
Node 3ms 6ms 12ms 24ms 36ms
Deno 3ms 8ms 14ms 27ms 39ms

iterativeFibonacci

x 10 20 30 40 50
Node < 1ms 2ms 9ms 1050ms 141.680s
Deno 1ms 3ms 13ms 1320ms 183.450s

recursiveFibonacci

On remarque trĂšs vite que le parcours linĂ©aire (itĂ©ratif) est drastiquement plus performant que le parcours d’arbre (rĂ©cursif). Plus intĂ©ressant encore, les chiffres sont rĂ©guliers ! Peu importe l’environnement, les comportements sont similaires : 

  • Temps d’exĂ©cution linĂ©aire avec iterativeFibonacci()
  • Temps d’exĂ©cution exponentiel avec recursiveFibonacci()

Malheureusement, les chiffres ne mentent pas. On est forcĂ© de constater que Deno est un peu en retard vis-Ă -vis de NodeJS. De maniĂšre rĂ©cursive, ce dernier rĂ©cupĂšre la 5000Ăšme occurrence de la suite de Fibonacci en 2 minutes et 20 secondes, alors qu’il faut environ 40 secondes supplĂ©mentaires Ă  Deno pour arriver au mĂȘme rĂ©sultat. MalgrĂ© ce lĂ©ger retard, je me suis aperçu lors de mes tests, que la CallStack se remplissait plus vite avec NodeJS (une diffĂ©rence d’environ 150 Ă  200 opĂ©rations), pour une mĂȘme allocation de ressources.

Fait intéressant :

En parlant de « tests Â», j’en profite pour signaler que Deno est livrĂ© avec une API de test unitaire intĂ©grĂ©e. Il est donc trĂšs simple de tester rapidement son code, lĂ  oĂč avec NodeJS, j’aurais eu besoin de NPM pour rĂ©cupĂ©rer Karma / Mocha (ou mieux Jest), afin de lancer mes tests unitaires. Voici un exemple concret, avec les fonctions de Fibonacci :

#2 – Files Renamer

Passons maintenant à un cas d’usage plus pratique, avec un script de renommage massif de fichiers.

filesRenamer.js
filesRenamer.ts

Vous l’aurez remarquĂ©, je suis passĂ© Ă  TypeScript dans ce second script. De plus, si vous tentez de l’exĂ©cuter, vous allez trĂšs vite dĂ©chanter
 Ă€ partir de maintenant la sĂ©curitĂ© entre en jeu ! En effet, lorsqu’on va vouloir interagir avec les fichiers (en lecture ou Ă©criture), il va falloir autoriser Deno Ă  le faire, en passant par la commande suivante :

deno run -–allow-read –-allow-write filesRenamer.ts 

PlutĂŽt simple, non !? 😏 Il suffit d’y penser


Ce qui est intĂ©ressant ici (outre les performances) ce sont les diffĂ©rences et similitudes qui existent entre l’API de Deno et celle de NodeJS. Dans l’ensemble, mĂȘme si les scripts sont construits de la mĂȘme façon (lancement avec les arguments, lecture du dossier, lecture du fichier, Ă©criture du fichier), on s’aperçoit que l’on gagne quelques lignes de code avec Deno. En faisant un focus sur les fonctions readDir(), on remarque qu’elles ne retournent pas la mĂȘme structure de donnĂ©es. L’une retourne seulement les noms des fichiers contenus dans le dossier parcouru, alors que l’autre retourne une liste d’objets, qui disposent notamment du nom du fichier, mais surtout de la nature du fichier. Cela m’évite donc l’appel Ă  la fonction stat() pour savoir s’il s’agit d’un dossier (ou non), puisque la donnĂ©e est directement accessible.

Je trouve que Ryan Dahl a su tirer parti des avantages et inconvĂ©nients de NodeJS, et a ainsi comblĂ© les manques avec Deno. L’exemple le plus concret de cette conjecture est l’utilisation native des promesses Ă  l’instar de l’usage des fonctions callback. En outre, Deno a su conserver les versions synchrones et asynchrones pour certaines fonctions : chmod / chmodSync, mkdir / mkdirSync, remove / removeSync, etc
 Ce qui est plutĂŽt une bonne approche si on souhaite sĂ©duire un plus large public.

NB : La version 10 de NodeJS signe l’arrivĂ©e des fonctions promesses du module « fs Â». Avant cela, il fallait « promisifier » toutes les fonctions grĂące au module « util Â» de NodeJS.

Fichiers 1000 2000 3000 4000 5000
Node 220ms 425ms 625ms 980ms 1025ms
Deno 455ms 885ms 1340ms 2050ms 2255ms

Pour ce qui est des performances, encore une fois, les donnĂ©es ci-dessus corroborent les temps d’exĂ©cution obtenue sur les fonctions de Fibonacci. NodeJS reste plus rapide que Deno Ă  l’heure actuelle. D’ailleurs, d’aprĂšs ce test, ce dernier est au moins 2 fois plus lent Ă  exĂ©cuter le code JavaScript / TypeScript que son homologue.

#3 – Web Server

Le dernier aspect que je souhaite mettre en lumiĂšre, est la mise en oeuvre d’un serveur HTTP. Dans ces deux derniers scripts, que ce soit pour NodeJS ou Deno, la mise en place d’un serveur Web se fait trĂšs simplement (comme le veut la philosophie du JavaScript). Tous deux utilisent leur module « http » : NodeJS l’importe depuis les node_modules, alors que Deno le rĂ©cupĂšre depuis ses librairies standard. 

NB : La rĂ©cupĂ©ration de modules Ă  partir d’URLs ne signifie pas que le Web est constamment sollicitĂ©. Au premier appel, Deno met en cache la version du module spĂ©cifiĂ© lors de l’import, pour les prochains usages.

Pour ce qui est de leur temps de rĂ©ponse, j’ai constatĂ© qu’ils mettent 2ms Ă  rĂ©pondre Ă  la requĂȘte « /whoami » en GET. Évidemment, l’exemple ci-dessous est trivial, et si l’on souhaite mettre en oeuvre un service backend performant, on ira tout de suite chercher un framework adaptĂ© qui offre plus de fonctionnalitĂ©s. Cependant, ces deux bouts de code reprĂ©sentent la base de certains frameworks Web (notamment ExpressJS pour NodeJS, ou Alosaur pour Deno).

webServer.js
webServer.ts

Autre fait intéressant :

Deno implĂ©mente la plupart des APIs Web. C’est-Ă -dire que les fonctions telles que setTimeout, clearTimeout, setInterval, clearInterval sont accessibles, mais Ă©galement fetch ! Donc, si vous souhaitez rĂ©cupĂ©rer une ressource Ă  partir d’une URL, c’est possible nativement sans avoir besoin d’utiliser un Ă©ventuel Axios (bien qu’il existe dĂ©jĂ  en tant que librairie tierce), ou tout autre librairie similaire. Puisqu’une dĂ©mo vaut mieux que des mots, voici ce que je vous propose :

deno run —allow-net getArticles.ts mrdoomy
getArticles.ts

Contre toute attente, ces deux environnements d’exĂ©cution pour le langage JavaScript ne sont pas si diffĂ©rents l’un de l’autre. Ce qui m’a heurtĂ© en premier lieu avec Deno, c’est l’utilisation de dĂ©pendances au travers d’import qui font directement rĂ©fĂ©rence au Web. Le fait de se passer de NPM (et du package.json) est assez dĂ©routant, mais on s’y fait vite grĂące Ă  la convention « deps.ts » .

Ensuite, l’utilisation native du TypeScript est fortement apprĂ©ciĂ©e. J’insiste sur le mot « native » , car avec NodeJS il aurait fallu configurer son environnement et transpiler le code pour finalement l’exĂ©cuter. Bien sĂ»r, ces tĂąches sont gĂ©nĂ©ralement prises en charge par un bundler (Webpack / RollupJS), mais malgrĂ© tout, il s’agit d’une couche supplĂ©mentaire dont on pourrait se dĂ©lester.

Enfin, le concept de permissions m’a tout de suite plu. En effet, le fait d’autoriser (ou non) la lecture, l’Ă©criture, les accĂšs rĂ©seaux, etc…  Permet d’ĂȘtre pleinement maitre du code que l’on joue. Les Ă©ventuels risques liĂ©s Ă  la sĂ©curitĂ© sont ainsi gĂ©rĂ©s, lĂ  oĂč NodeJS est pour l’instant incapable de s’en prĂ©munir


NB : Je suis ravi de devoir spĂ©cifier la lecture et l’écriture (distinctement) lorsque je travaille sur le systĂšme de fichiers avec un chemin absolu. Une erreur est vite arrivĂ©e
 Mais bien sĂ»r, personne ne fait ça. đŸ˜…

À l’heure oĂč j’Ă©cris ces quelques lignes / paragraphes, Deno a le vent en poupe ! ComparĂ© Ă  NodeJS, il est plus sĂ©curisĂ© et plus lĂ©ger. Bien qu’il n’arrive pas (encore) Ă  Ă©galiser ce dernier en matiĂšre de rapiditĂ© d’exĂ©cution, il reprĂ©sente un fort (et unique) concurrent en tant qu’environnement JavaScript.

De par son mode fonctionnement, ainsi que ses nombreuses fonctionnalitĂ©s, Ryan Dahl a clairement su combler les lacunes de sa prĂ©cĂ©dente crĂ©ation en dĂ©veloppant cette nouvelle technologie. Deno s’inscrit aujourd’hui dans un contexte de Web moderne (notamment au regard des appels de dĂ©pendances). Le support du TypeScript, « corrige » l’aspect faiblement typĂ© du JavaScript, et fait ainsi de Deno une solution complĂšte. De plus, la prĂ©sence de Rust au sein de son code, promet bien des choses en termes de performances.

La communautĂ© est forte ! À tel point qu’on voit apparaitre chaque jour de plus en plus de librairies tierces, je veux notamment parler de MongoDB, Prettier, GraphQL, Moment, etc… Certains incontournables de NPM sont dĂ©jĂ  prĂȘt pour Deno. De mĂȘme, si vous souhaitez jouer avec de l’authentification / du chiffrement au sein de tes APIs ; BCrypt, JWT et OAuth2 (pour ne citer qu’eux) rĂ©pondent aussi prĂ©sent Ă  l’appel ! D’ailleurs, je tiens Ă  signaler qu’il existe une multitude de frameworks backend avec Deno, le choix vous appartient (mais je vous conseille d’aller faire un tour chez Alosaur).

Le mot de la fin

Pour l’instant, je n’abandonnerais pas NodeJS. Il s’agit d’une solution mature dans l’Ă©cosystĂšme Web, qui commence Ă  se rĂ©pandre dans le monde de l’entreprise. En France, les petites / moyennes entreprises ont dĂ©jĂ  optĂ© pour cette solution, et les grandes s’y mettent davantage (au dĂ©triment de Spring / Django). Cependant, je suis trĂšs enthousiaste Ă  propos de Deno. Au mĂȘme titre que GraphQL vis-Ă -vis de REST, je le considĂšre actuellement comme une alternative, mais je pense qu’il va faire changer les moeurs. L’aspect sĂ©curitaire devrait inciter les professionnels Ă  migrer certaines de leurs applications vers l’environnement JavaScript. Bien que les dĂ©pendances standard de Deno soient stables, elles ne sont (pour la plupart) pas encore disponibles en version « finale » / 1.0.0, mais lorsque ce sera le cas, nous devrions assister Ă  un changement majeur / une migration au sein de la communautĂ© des dĂ©veloppeurs… Se laisseront-ils tenter par le cĂŽtĂ© obscur !? 🙄

CET ARTICLE A ÉTÉ RÉDIGÉ PAR :

Damien Chazoule
INGÉNIEUR INFORMATIQUE
ALLTECH Niort