nodejs – Sam & Max http://sametmax.com Du code, du cul Wed, 30 Oct 2019 15:34:04 +0000 en-US hourly 1 https://wordpress.org/?v=4.9.7 32490438 Quelle est la différence entre “bloquer” et “en cours d’exécution” ? http://sametmax.com/quelle-est-la-difference-entre-bloquer-et-en-cours-dexecution/ http://sametmax.com/quelle-est-la-difference-entre-bloquer-et-en-cours-dexecution/#comments Tue, 09 Dec 2014 16:57:06 +0000 http://sametmax.com/?p=12766 On vous dit qu’il faut faire attention en utilisant des technologies non bloquantes, car si on bloque dans la boucle d’événement, on bloque tout le programme, et on perd l’intérêt de l’outil.

C’est vrai, mais que veut dire “bloquer” ?

Car si je fais :

for x in range(1000000):
    print(x)

Mon programme va tourner longtemps, et la boucle d’événement va bloquer, n’est-ce pas ?

En fait, “bloquer” est un abus de langage car il y a plusieurs raisons pour bloquer. Dans notre contexte, il faudrait dire “bloquer en attente d’une entrée ou d’une sortie”. D’où l’appellation “Aynschronous non blocking I/O” des technos types NodeJS, Twisted, Tornado, Gevent, etc.

En effet, il faut distinguer deux causes d’attente à votre programme :

  • Attendre que vos instructions se terminent. C’est être “en cours d’exécution”.
  • Attendre qu’un événement extérieur (écrire sur le disque, lire une socket, un clic de souris) arrive à sa conclusion. C’est bloquer sur de l’I/O.

Le premier cas est impossible à éviter. Tout au mieux pouvons-nous répartir la charge du programme sur plusieurs cœurs, processeurs voire machines. Le code devra toujours attendre qu’il se termine, mais ça ira plus vite.

Dans le contexte de la programmation non bloquante telle qu’on vous en a parlé, on est donc dans le deuxième cas.

Il ne s’agit alors pas de s’interdire de faire des boucles ou autre opération longue (ou plutôt, c’est un problème d’optimisation ordinaire qui n’a rien à voir avec le fait de bloquer), il s’agit de ne pas “attendre à ne rien faire” quand une opération extérieure est en cours.

C’est ce que font naturellement NodeJS, Twisted, Tornado, Gevent & Co. Quand on fait un échange HTTP, le bout de données part, puis le reste du code continue de tourner, passant à la tâche suivante, en attendant que le paquet traverse le réseau, atteigne l’autre machine, qui vous répond finalement. C’est ce temps, incompressible, sans contrôle de votre côté, durant lequel il ne faut pas bloquer. Le gain de perf est que votre programme ne se la touche pas pendant les temps d’attente, mais bien entendu que VOTRE, lui, code va prendre du temps et “bloquer” le processeur. Il faut bien qu’il s’exécute.

Ce qu’on entend donc par “il ne faut pas faire d’opération bloquante dans un code qui est déjà non bloquant” c’est “il ne faut pas utiliser un outil à l’API bloquante au milieu d’autres outils non bloquants”.

Par exemple, n’utilisez pas requests avec Twisted, car requests est codé pour attendre sans rien faire jusqu’à obtenir une réponse à chaque requête, bloquant Twisted. Utilisez plutôt treq. C’est pareil pour la lecture d’un fichier, une requête de base de données, etc. Et il existe des boucles d’événements ailleurs que sur le serveur : une page Web possède sa propre boucle (c’est pour cela que tout JS est asynchrone), un toolkit GUI comme QT ou GTK aussi (c’est pour ça qu’ils utilisent la programmation événementielle), etc.

Maintenant vous allez me dire : mais pourquoi bloquer alors ? Pourquoi ne pas toujours éviter de bloquer ?

Et bien parce que si on ne bloque pas, on ne peut pas écrire un programme ligne à ligne. On est obligé d’adopter un style de programmation asynchrone puisqu’on ne sait pas quand le résultat de certaines lignes va arriver. Ça veut dire des callbacks, ou des futures, ou des coroutines, ou du message passing… Bref, un truc plus compliqué. Or, on n’a pas forcément besoin de ce niveau de performance. En fait, la grande majorité des programmes n’ont pas besoin de ce niveau de performance. Donc, on bloque en attendant, non pas Godot, mais l’I/O, parce que c’est plus simple à écrire. Pour pas se faire chier.

Il y a bien des moyens de contourner ce problème : les threads, le multiprocessing, les coroutines, etc. Parfois même, on ignore le problème : bloquer quelques ms au milieu d’une boucle d’événements une fois par seconde n’est pas un drame. Une fois que j’ai fini le dossier sur les tests unitaires, je vous ferai un dossier sur la programmation non bloquante, avec aussi une esquisse de la parallélisation.

En attendant, ne stressez pas parce que votre code “bloque” parce qu’il travaille longtemps, assurez-vous juste que les APIs que vous utilisez ne bloquent pas pendant l’I/O, et vous êtes ok.

Et comment savoir ? Et bien si une donnée rentre ou sort de votre programme (ça ne fait pas partie du code source), c’est de l’I/O. Si votre code ressemble à ça :

res = faire_operation_sur_IO()
faire_un_truc_avec_le_res(res)

Alors votre outil est bloquant, puisque qu’il compte sur le fait que la deuxième ligne sera exécutée à coup sûr quand la première sera terminée. Un outil non bloquant exigera quelque chose pour gérer le retour du résultat plus tard: un callback, une promesse, un yield

]]>
http://sametmax.com/quelle-est-la-difference-entre-bloquer-et-en-cours-dexecution/feed/ 9 12766
Crossbar, le futur des applications Web Python ? http://sametmax.com/crossbar-le-futur-des-applications-web-python/ http://sametmax.com/crossbar-le-futur-des-applications-web-python/#comments Sun, 25 May 2014 10:24:36 +0000 http://sametmax.com/?p=10329 crossbar.io depuis quelques temps maintenant, et je suis très, très étonné de ne pas plus en entendre parler dans la communauté Python.]]> Je suis crossbar.io depuis quelques temps maintenant, et je suis très, très étonné de ne pas plus en entendre parler dans la communauté Python.

Bon, en fait, à moitié étonné.

D’un côté, c’est une techno qui, à mon sens, représente ce vers quoi Python doit se diriger pour faire copain-copain avec Go/NodeJs et proposer une “killer feature” dans le monde des applications serveurs complexes.

De l’autre, hum, leur page d’accueil explique à quoi ça sert de cette manière :

Crossbar.io is an application router which implements the Web Application Messaging Protocol (WAMP). WAMP provides asynchronous Remote Procedure Calls and Publish & Subscribe (with WebSocket being one transport option) and allows to connect application components in distributed systems

Moui, moui, moui monseigneur, mais concrètement, là, hein, je peux faire quoi avec ?

C’est toujours le problème avec les gens intelligents (hein Cortex ?) : ils font des trucs super cool, et personne ne comprend à quoi ça sert parce qu’il ne sont pas foutus de l’expliquer.

Moi je suis un peu con, alors je vais profiter qu’on soit tous au même niveau pour vous faire passer le message.

J’étais persuadé d’avoir mis la musique habituelle… Je la remets :

Web Application Message Protocol

Je vous avais parlé d’autobahn dernièrement, un client WAMP qui embarque aussi un routeur basique. Crossbar est la partie serveur, un routeur WAMP plus sérieux.

Crossbar permet à tous les clients d’échanger des messages WAMP à travers lui. Bien entendu, un client WAMP peut parler au serveur Crossbar et inversement comme un client HTTP peut parler à un serveur Apache/Nginx et inversement. Mais plus que ça, les clients peuvent parler entre eux, de manière transparente et simple. Comme un client AMQP peut parler aux autres à travers un serveur RabbitMQ.

Cependant ça ne vous avance pas si vous ne savez pas ce qu’est WAMP ou à quoi ça sert. La charrue avec la peau de l’ours, tout ça.

WAMP est un protocole standardisé pour échanger des messages entre deux systèmes. Ce n’est pas particulièrement lié à Python, on peut parler WAMP dans n’importe quel langage.

Il fonctionne en effet, principalement, au dessus de Websocket, donc on peut l’utiliser directement dans le navigateur, dans Firefox, Chrome, Opera, Safari et même IE10, via une lib Javascript. Qui est en fait juste un gros wrapper autour de l’API websocket standardisant la manière d’envoyer des données. Il n’y a rien de magique derrière, pas de formats compliqués, pas de binaire, c’est vraiment juste des appels websocket contenant des données formatées en JSON avec une certaine convention. En ce sens il fait penser à SockJS et (feu) socket.io.

Seulement contrairement aux solutions type SocketJS, il n’est pas limité au navigateur. Il y a des libs pour l’utiliser dans un code serveur Python, C++ ou NodeJS, dans une app Android et même directement depuis les entrailles de Nginx ou d’une base de données Oracle (en SQL) avec certains routeurs.

Schéma général du fonctionnement de WAMP

Comme HTTP, WAMP est juste un moyen de faire parvenir des données d’un point A à un point B. Mais contrairement à HTTP, WAMP permet aux clients de parler entre eux, et pas juste au serveur.

Comprenez bien, ça veut dire qu’on peut envoyer et recevoir des données arbitraires, en temps réel, entre tous ces systèmes, sans se prendre la tête, et de manière transparente.

WAMP c’est donc comme, mais mieux que :

  • des requêtes HTTP, car c’est du push, asynchrone et temps réel;
  • des requêtes websocket via SocketJS car c’est un standard, qui fonctionne SUR et ENTRE plusieurs services côté serveurs malgré les différents langages;
  • des messages AMQP car ça marche dans le navigateur et ça se configure facilement.

Bien utilisé, Crossbar permet d’amener Python dans la cour de frameworks “temps réel” novateurs comme MeteorJS, et potentiellement les dépasser.

Car WAMP permet de faire deux choses. Simplement. Et bien.

1 – Du PUB/SUB

Donc de dire dans son code “appelle cette fonction quand cet événement arrive”. C’est comme les signaux de Django ou QT, mais ça marche à travers le réseau. On le fait souvent avec Redis ces temps-ci. Avec WAMP et Javascript, ça donne :

// connection au routeur WAMP (par exemple, crossbar.io)
ab.connect("ws://localhost:9000", function(session) {
    // je m'inscris à un événement
    session.subscribe('un_evenement_qui_vous_plait', function(topic, evt){
        // faire un truc avec les données reçues
        // à chaque fois que l'événement est envoyé
        // par exemple mettre la page à jour
    });
});
Schéma de fonctionnement du subscribe de WAMP

Des client SUBscribe à un événément. Un événément est un nom arbitrairement choisit par le programmeur, et qu’il va déclencher lui-même quand il pense qu’il se passe quelque chose d’important auquel il faut que le reste du signal réagisse.

Et ailleurs, dans une autre partie du fichier, ou même sur un autre navigateur Web :

ab.connect("ws://localhost:9000", function(session) {
    // création d'un événement auquel on attache des données
    session.publish('un_evenement_qui_vous_plait', ['des données']);
Schéma de fonctionnement de PUB avec WAMP

Le programmeur décide que quelque chose d’important arrive (création d’un contenu, login d’un utilisateur, notification), et PUBlish l’événement

Et oui, c’est tout. On se connecte à crossbar, et on discute. La fonction du subscribe sera alors appelée avec les données du publish. Même si il y a 3000 km entre les deux codes. Même si le code A est sur un navigateur et le B sur un autre, ou sur un serveur NodeJS, ou une app Android.

Ce qui fait peur au début, c’est qu’il y a TROP de flexibilité :

  • Je dois attendre quoi comme événements ?
  • Qu’est-ce que je passe comme données ?
  • Est-ce que c’est rapide ? Léger ?

Mais en fait c’est super simple : un événement c’est juste une action de votre application comme un élément (un post, un commentaire, un utilisateur…) ajouté, supprimé ou modifié. Finalement c’est le bon vieux CRUD, mais en temps réel, et en push, au lieu du pull. Vous choisissez un nom qui représente cette action, vous attachez des données à ce nom, voilà, c’est un événement que tous les abonnés peuvent recevoir.

Avec un bonus : ça marche sur le serveur aussi ! Votre code Python reçoit “ajouter un commentaire” comme événement ? Il peut ajouter le commentaire en base de données, envoyer un message à un service de cache ou à un autre site en NodeJS pour le mettre à jour, renvoyer un événement pour mettre à jour les pages Web et l’app Android, etc.

On peut passer n’importe quelles données qui peut se JSONiser. En gros n’importe quoi qu’on enverrait via HTTP. Donc des données très structurées, imbriquées et complexes comme des données géographiques, ou très simples comme des notifications

Avec PUB / SUB, WAMP remplace tout ce qu’on ferait normalement avec des appels AJAX dans le browser, et tout ce qu’on ferait avec des files de message côté serveur. Plus puissant encore, il permet de relier ces deux mondes.

Et même si on atteint pas les perfs de ZeroMQ (qui n’a pas de serveur central), c’est très performant et léger.

2 – Du RPC

Appeler une fonction située ailleurs que dans son code. C’est vieux comme le monde (si vous avez des souvenirs douloureux de CORBA et SOAP, levez la main), et c’est extrêmement pratique. Pour faire simple, continuons avec un exemple en Javascript, mais rappelez-vous que ça marche pareil en C++ ou Python :

ab.connect("ws://localhost:9000", function(session) {
   function une_fonction(a, b) {
      return a + b;
   }
   // on déclare que cette fonction est appelable à distance
   session.register('une_fonction', une_fonction);
});
Schéma expliquant register avec WAMP

RPC marche à l’envers de PUB/SUB. Un client expose du code, et un autre demande explicitement qu’il soit exécuté.

Côté appelant :

ab.connect("ws://localhost:9000", function(session) {
    // on appelle la fonction à distance, on récupère une
    // promise qui nous permet de travailler sur le résultat
   session.call('une_fonction', 2, 3).then(
      function (res) {
         console.log(res);
      }
   );
Schéma expliquant CALL en WAMP

Contrairement à PUB/SUB, RPC ne concerne que deux clients à la fois. Mais ça reste asynchrone. Le client demandeur n’attend pas le résultat de l’appel de la fonction. Il est signalé par le serveur quand il est prêt.

Pareil que pour le PUB/SUB, les gens ont du mal à voir l’utilité à cause du trop de flexibilité que ça apporte. Imaginez que votre projet soit maintenant éclaté en de nombreux petits services qui tournent et qui sont indépendants :

  • Un service pour le site Web.
  • Un service d’authentification.
  • Un service pour l’API.
  • Un service pour les tâches longues.
  • Un service de monitoring et administration technique.

Tous ces services peuvent ainsi communiquer entre eux via RPC, mais n’ont pas besoin d’être dans le même processus. On peut profiter pleinement de tous les cœurs de sa machine, on peut même les mettre sur des serveurs séparés.

Mieux, avoir un service bloquant ne pénalise pas tout le système. En effet, un problème avec les systèmes asynchrones en Python est que beaucoup de libs sont encore bloquantes (typiquement les ORMs). Avec ce genre d’architecture, on peut créer un service qui ne fait que les appels bloquant et laisser les autres services non bloquant l’appeler de manière asynchrone. Pendant qu’il bloque, le reste du système peut traiter d’autres requêtes.

Crossbar, plus qu’un routeur WAMP

L’idée des concepteurs de crossbar est de permettre de créer des systèmes avec des services composables qui communiquent entre eux plutôt que tout dans un gros processus central. Ils ne se sont donc pas arrêtés au routing.

Crossar est également un gestionnaire de processus, comme supervisor ou, plus légitimement, circus (Tarek, fait une pause, vient ici !) et sa communication ZeroMQ.

Il se configure avec un simple fichier JSON, et on peut y définir des classes Python qui seront lancées dans un processus séparé et pourront discuter avec les autres clients via WAMP :

{
   "processes": [
      { // premier processus
         "type": "worker",
         "modules": [
            {
               un_worker.Classe
            },
            {
               un_autre_worker.Classe
            }
         ]
      },
      {  // second processus
         "type": "worker",
         "modules": [
            {
               un_autre_worker_dans_un_autre_process.Classe
            }
         ]
      }
   ]
}

Mais si ça ne suffit pas, on peut également lancer des programmes extérieurs non Python dont crossbar va gérer le cycle de vie :

{
   "processes": [
      {
         "type": "guest",
         "executable": "/usr/bin/node",
         "arguments": ["votre_script.js"],
         "stdout": "log"
      }
   ]
}

Vous avez donc ainsi les deux atouts pour avoir une architecture découplée, scalable, exploitant plusieurs cœurs, et compensant en partie les bibliothèques bloquantes :

  • Un protocole flexible, simple, qui permet à tout le monde se parler entre eux (WAMP).
  • Une API qui permet soit de réagir à un changement (PUB/SUB), soit de demander une action (RPC).
  • Un programme qui gère cette communication, et le cycle de vie des composants qui parlent entre eux.

Cas concret

WAMP est typiquement le genre de techno qui ne permet PAS de faire quelque chose qu’on ne faisait pas avant. Ce n’est pas nouveau.

En revanche, WAMP permet de le faire mieux et plus facilement.

Prenez le cas d’un utilisateur qui se connecte sur un forum. Il va sur un formulaire, il poste ses identifiants, ça recharge la page, il est connecté. Si les autres utilisateurs rechargent leurs pages, ils verront un utilisateur de plus connecté.

Si on veut rendre ça plus dynamique, il faut utiliser de l’AJAX, et si on veut avoir une mise à jour presque en temps réel, il faut faire des requêtes Ajax régulières. Ce qui est assez bancal et demande beaucoup de travail manuel.

Certains sites modernes utilisent Websocket, et des serveurs asynchrones comme NodeJS, et un routeur PUB/SUB comme Redis, pour faire cela de manière rapide et plus facile. L’application est très réactive. Mais le système est hétéroclite. Et si on veut envoyer des messages entre des composants serveurs, ça demande encore quelque chose de différent.

WAMP unifie tout ça. Un coup de RPC pour le login pour effectuer l’action:

Schéma d'un exemple concret de RPC WAMP

Notez que le RPC marche de n’importe quel client à n’importe quel client. Il n’y a pas de sens obligatoire. Le login est un exemple simple mais on peut faire des choses bien plus complexes.

Et un coup de PUB/SUB pour prévenir tout le monde que quelque chose s’est passé :

Schéma d'exemple concret de PUB/SUB avec WAMP

Je n’ai mis que des clients légers ici, mais je vous rappelle qu’un client peut être un serveur NodeJS, une base de données, un code C++…

Bien entendu, on pourrait faire ça avec les technos existantes. C’est juste moins pratique.

Notez également que Crossbar encourage à avoir un service qui ne se charge que du login, sans avoir à faire une usine à gaz pour cela. Si demain votre service de login a besoin d’être sur un coeur/serveur/une VM séparé pour des raisons de perfs ou de sécurité, c’est possible. Crossbar encourage ce genre de design.

Voici où est le piège

Car évidement, il y en a toujours un, putain de métier à la con.

Et c’est la jeunesse du projet.

Le projet est stable, le code marche, et les sources sont propres.

Mais la doc, mon dieu la doc… Les exemples sont pas à jour, il y a deux versions qui se battent en duel, on sait pas trop quelle partie sert à quoi.

Et comme tout projet jeune, l’API n’a pas été assez étudiée. Or la partie Python est basée sur Twisted, sans polish. Twisted, c’est puissant, c’est solide, et c’est aussi une API dégueulasse.

Un exemple ? Comment écouter un événement :

# Des imports légers
from twisted.python import log
from twisted.internet.defer import inlineCallbacks

from autobahn.twisted.wamp import ApplicationSession
from autobahn.twisted.wamp import ApplicationRunner

# Une bonne classe bien subtile pour copier Java
class ListenForEvent(ApplicationSession):

    # Deux méthodes de boiler plate obligatoires
    # et parfaitement soulantes pour l'utilisateur
    # final. Cachez moi ça bordel !
    def __init__(self, config):
        ApplicationSession.__init__(self)
        self.config = config

    def onConnect(self):
        self.join(self.config.realm)

    # Bon, on se décide, soit on fait une classe avec des noms
    # de méthode conventionnés, soit on met des décorateurs, 
    # mais pas les deux, pitié !
    @inlineCallbacks
    def onJoin(self, details):
        callback = lambda x: log.msg("Received event %s" % x)
        yield self.subscribe(callback, 'un_evenement')

# Python doit lancer explicitement un event loop.
# Ca pourrait (devrait) aussi être embeded dans une
# sousclasse de ApplicationSession.
# /me prend un colt. BANG !
if __name__ == '__main__':
   runner = ApplicationRunner(endpoint="tcp:127.0.0.1:8080",
                              url="ws://localhost:8080/ws",
                              realm="realm1")
   runner.run(ListenForEvent)

C’est la raison pour laquelle je vous ai montré le code JS et pas Python pour vous vendre le truc. Sur sametmax.com, on aura tout vu :(

Voilà à quoi devrait ressembler le code Python si l’API était mature :

from autobahn.app import App

app = App(url="ws://localhost:8080/ws")

@event("un_evenement")
def handle(details):
    app.log("Received event %s" % x)

if __name__ == '__main__':
   app.run()

Chose que je vais proposer sur la mailing list (ils sont réactifs et sympas, vive les allemands !) dans pas longtemps. Et si ils n’ont pas le temps de le faire, il est possible que je m’y colle. Ca me fait mal aux yeux.

]]>
http://sametmax.com/crossbar-le-futur-des-applications-web-python/feed/ 32 10329
Envie de meurtre http://sametmax.com/envie-de-meurtre/ http://sametmax.com/envie-de-meurtre/#comments Fri, 19 Jul 2013 09:55:57 +0000 http://sametmax.com/?p=6725 Oui, je tape beaucoup sur nodejs et la communauté javascript en général. Mais ils le méritent, bordel !

Je tombe sur un projet OpenSource qui est sorti en janvier dernier. Ils vendent leur popote “As A Service”, donc a priori, on pourrait se dire qu’ils voudraient qu’on les prennent au sérieux.

Ils fournissent un serveur pour se faire la main en local avant de taper dans leur API payante. Cool. Merci.

D’abord, il faut évidement passer par l’installation de node et tout le bordel. Bon, admettons. Même si Python est installé par défaut, il faut bien installer pip.

Mais ensuite, au lancement du serveur, rien. Il reste là, silencieusement. Pas de mode verbose, un mode debug qui ne fait absolument rien.

Mon code client se connecte, et puis nada. Silence radio sur leur lib client et serveur. Vide intersidéral.

La doc est succincte (3 pages) et rien qui ne parle du problème. Question sur la mailling list. Pas de réponse.

A ce stade j’en ai déjà ras le cul. Les libs node qui marchent pas et qui en plus veulent pas mettre un putain de message d’erreur, y en a déjà un paquet. C’est une épidémie dans cette communauté.

Mais je retrousse mes manches, et je vais voir dans le code source.

Et là, je tombe sur 400 lignes de ça :

    var client;
    if (!self._clients[key] || !(client = self._clients[key][id])) {
      if (req.params.retry) {
        res.send(401);
      } else {
        // Retry this request
        req.params.retry = true;
        setTimeout(handle, 25, req, res);
      }
      return;
    }

C’est un appel au génocide, un style de coding comme ça ! Sérieusement, déjà Javascript est un langage moche, avec un scoping de merde, du nesting en pagaille et tout un tas de pièges qui fait que le code est dur à lire et à comprendre.

Mais là, vous imaginez la concentration qu’il faut pour lire ces 10 putains de lignes ? Et les remettre dans le contexte du script complet (qui au passage est commenté comme un diabétique sucre son café).

C’est trop dur de ne pas pondre des trucs innommables comme !(client = self._clients[key][id]) ? Un ligne de plus a taper. Trop difficile de mettre des noms de variables complets ? id, mais id de quoi connard ? Je remonte tout le script pour m’en souvenir ? Et pourquoi tu assignes client = self._clients si c’est pour ne pas t’en servir ? Et puis un double if imbriqué suivi d’un appel récursif asynchrone, c’est tellement fun pour suivre le workflow !

400 lignes comme ça, après le dîner, ça fout la chiasse. Alors oui, c’est de l’open source, faut pas faire le difficile, etc.

Mais bordel, qu’on ne vienne pas me dire que c’est l’avenir. Les ex-script kiddies d’hier, codeurs PHP et VB, commencent à peine à écrire du code propre. Et là grosso modo on va se taper la vague des intégrateurs Web qui se mettent à coder parce que c’est du JS comme dans le navigateur ?

GGGGGGGGGGGGRRRRRRRRRRRRRRRRRRRRRRRR !

Je devrais faire une émission style “joueur du grenier”, mais pour le code pourri.

]]>
http://sametmax.com/envie-de-meurtre/feed/ 42 6725
Non, nodejs n’est pas mature http://sametmax.com/non-nodejs-nest-pas-mature/ http://sametmax.com/non-nodejs-nest-pas-mature/#comments Mon, 14 May 2012 13:34:28 +0000 http://sametmax.com/?p=608 La maturité d’un projet ne se juge pas seulement par le code source lui-même, il se mesure aussi à tout l’écosystème et à la communauté.

Un jour je me suis retrouvé à devoir choisir, sans m’y connaitre vraiment, entre apprendre Python ou Ruby. La raison pour laquelle j’ai choisi Python n’a rien de profondément philosophique: ça marchait.

A l’époque, et c’est encore dans une moindre mesure le cas aujourd’hui, l’écosystème Ruby était bancale:

  • installation de gem qui foire une fois sur deux;
  • projets sans tests (oui, on est mal placé…) ni documentation;
  • environnement isolé très compliqué à mettre en oeuvre.

C’est éxactement ce que je ressens aujourd’hui à propos de NodeJS. Un de mes amis prosélyte quand il s’agit de ce projet m’assurait que c’était le repo le plus suivit sur github, qu’il y avait maintenant moulte ORM, frameworks, et tout ce qu’il fallait pour développer.

Et c’est vrai, Node avance très vite. Trop vite.

Emporté par l’élan de l’enthousiasme, les amateurs de la techno ventent les (réels) mérites de la bête:

  • performances époustouflantes;
  • websocket ou fallback les doigts dans le nez;
  • un seul langage côté serveur et client;
  • une communauté de passionés hyper compétents et productifs;
  • et pleins de vrais projets en prod pour le prouver.

Sur le papier c’est cool, mais en pratique, voici ce qu’on a oublié de vous dire:

  • les packages ont un goût de pas fini: doc fébrile, -h qui fait planter le programme, erreurs de locale impossible à résoudre, etc.
  • il y a 100 initiatives pour tacler le même problème. Pour l’instant pas de clair gagnant pour les libs les plus importantes. La pérénité de vos dépendances se joue à pile ou face.
  • toutes les libs sont en travaux. C’est la seule chose qu’on trouve toujours dans la doc: version beta, l’API va changer, tout va pêter, vous êtes prévenus.
  • un seul langage, mais c’est du JS. Syntaxe de merde, gestion de scope tout pourri, passage des arguments à la walou et nested callbacks en pagaille. Si bien que les projets JS les plus connus (hors jQuery et cie) sont coffeescript et les libs qui inlinent les appels asynchrones. Ça me fait penser à Microsoft qui vente les qualités de son défragmenteur alors que la concurrence ont des FS qui ne fragmentent pas.
  • les debug toolbars, les ORM, les générateurs de formulaires, les libs d’i18n, bref, tous ces trucs qui vous rendent 10 fois plus productifs sont loin d’offrir l’équivalence en terme de features ou de facilité de mise en oeuvre.
  • la seule bonne source d’infos à jour sur NodeJS c’est stackoverflow… Hum… Oh certes, vous avez des docs de références au poil sur plein de sites, mais en 5 minutes, vous allez tomber sur le cas de programmation réel qui n’est décrit nul part.
  • vous avez du mal à trouver un dev Python ou Ruby ? Essayer de trouver un dev NodeJS. Et pour la formation, bonne chance. Ca ne demande pas du tout le même niveau de compétences. La bonne nouvelle : si un mec sait s’en servir, vous êtes à peu prêt sur qu’il est bon.

Ça me rappelle le buzz qu’il y avait eu autour de Python Twisted. Travailler en asynchrone est tellement difficile pour le programmeur lambda que quand j’ai passé un entretien d’embauche chez Jamendo, le mec voulait me recruter rien que parceque je savais à quoi ça servait. Du coup, les alternatives sont encensées, mais vous trouvez vraiment les greenlets si simple ?

Je crois fermement que NodeJS va s’imposer dans le développement Web (merde, encore un truc à apprendre) car, avouons le, ça déchire sa génitrice. Mais ne vous faites pas avoir par les barbus betaddict qui prêchent la dernière update de la bonne parole, si vous mettez les mains dedans, vous allez en chier.

]]>
http://sametmax.com/non-nodejs-nest-pas-mature/feed/ 27 608