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.
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 :
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.
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.
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).
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 dmnchzl
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 !? đ