Tuez vos dépendances

closeCet article a été publié il y a 7 ans 11 mois 11 jours, il est donc possible qu’il ne soit plus à jour. Les informations proposées sont donc peut-être expirées.

Le titre de la traduction d’aujourd’hui m’a plu tout de suite. Je suis tombé dessus peu de temps après l’affaire NPMGate (toutes ces portes, c’est lassant à force, comme les fenêtres), et donc, j’avais vraiment envie de vous en faire profiter si l’anglais n’est pas votre fort.

D’après la bio de Mike Perham :

Je suis un développeur indépendant de logiciels open source basé à Portland, Oregon, USA, où il vit avec ma ravissante femme, mon énergique petit garçon et un chat « à poils » (ndt: pas trouvé mieux pour furry cat).

J’écris des logiciels depuis 1993 et open-source depuis 1995. En fait je ne connaissais aucun logiciel open-source pour Win32 avant ma petite barre de menus, AppBar, publiée avec le code source complet. A cette époque, tout ce que je pouvais consulter était la documentation MSDN !

Aujourd’hui je suis le fondateur et PDG de Contributed Systems, ma propre entreprise dédiée au développement et au support d’infrastructure logicielle basée sur de l’open-source.


Ce billet parle de Ruby, mais il est valable pour chaque communauté logicielle : Python, JavaScript, Java, etc. Le fléau des dépendances n’épargne personne.

C’est une visualisation des dépendances de chaque application Rails que j’ai déjà pu utiliser. Est-ce que ça vous semble familier :

  • Un Gemfile avec des centaines d’entrées.
  • Des gems de tests qui se chargent en production.
  • Chaque process Rails qui prend des centaines de mégaoctets en RAM.

Le système de gems Ruby est louable pour la façon dont il rend facile le packaging de code Ruby pour que d’autres le réutilise. Mais cette facilité signifie qu’il est aussi assez facile pour ces gems d’appeler d’autres gems de manière transitive, amenant chaque application Rails à « télécharger Internet » et avoir des centaines de dépendances.

Quand vous publiez une gem Ruby, chacune de vos dépendances devient inévitablement une dépendance pour n’importe quelle application utilisant votre gem. Ce qui multiplie l’impact des bugs dans ces gems.

Le cas curieux de mime-types

La gem mime-types a récemment optimisé sa consommation de mémoire vive et économisé des mégaoctets de RAM. Littéralement toute application Rails peut bénéficier de cette optimisation parce que Rails lui-même dépend de mime-types : rails -> actionmailer -> mail -> mime-types

En d’autres mots, cette gem n’était pas utilisée par votre application. Elle n’était pas utilisée par Rails directement. Elle n’était pas utilisée par ActionMailer directement. Elle était utilisée dans les entrailles de l’implémentation  d’ActionMailer et elle utilisait beaucoup trop de mémoire. Chaque application Rails en circulation utilisait 10Mo en trop à cause de ce problème.

Développeurs d’applications, écoutez !

Chaque dépendance de votre application a le potentiel pour la gonfler, la déstabiliser, d’injecter du comportement bizarre via le « monkeypatching » (ndt : il faudrait un expliquer mieux que moi) ou du code natif buggé. Quand vous réfléchissez à ajoutée une dépendance à votre application Rails, c’est une bonne idée de faire un « bilan de santé » avec en ordre de préférence :

  1. Est-ce que j’en ai vraiment besoin ? Tuez-la
  2. Est-ce que je peux implémenter les fonctionnalités minimum requises ? La maitrise

Si vous avez besoin d’une gem :

  1. Est-ce que la gem a une extension native (ndt: dépendante de l’OS) ? Cherchez une alternative pure ruby
  2. Est-ce que la gem tire de nombreuses autres gems ? Cherchez des alternatives plus simples

Les gems avec des extensions natives peuvent déstabiliser votre système, elle peuvent être source de bugs mystérieux et des crashes. Evitez de charger plus de dépendances que ce que vous cherchez en valeur. Exemple d’une mauvaise gem : fog qui charge 39 gems, plus de dépendances que rails lui-même et la plupart inutiles.

Enfin, assurez-vous de ne charger la gem que lorsque c’est nécessaire. Utiliser le support des groupes de Bundler pour désactiver les gens de test quand vous ne testez pas :

Développeurs de Gems, écoutez !

Une partie de votre travail en tant qu’auteur de bibliothèque est de traiter vos utilisateurs et leur application avec respect. Vous devriez faire un effeort pour minimiser vos propres dépendances pour qu’elles ne chargent pas de code inutile ou qui causera des problèmes dans l’application de l’utilisateur. Vous contrôlez votre propre code mais vous ne contrôlez pas vous dépendances. Chaque bug dans une de vos dépendances devient un bug qui cause du stress à vos utilisateurs et leur application.

En tant que développeur de gem, pour chaque gem en dépendance est-ce que vous savez :

  • quelle mémoire chacune consomme ?
  • combien de temps ça prend à charger ?
  • si ça fait du monkeypatching en dehors de son propre module ?

Sidekiq, avec toutes ses fonctionnalités, n’a que 3 dépendances à l’exécution : concurrent-ruby, connection_pool, et redis.

Die json, die (allemand pour « The json, the »)

Tellement de gems déclare une dépendance à json, oj, multi_json, ou yajl-ruby. Il y a tellement de couches ossifiées autour du traitement de JSON qu’il n’y a qu’une seul chose sensée à faire : toute les supprimer. JSON est dans la stdlib (ndt: le coeur du langage) depuis la version 1.9, vous n’avez vraiment plus du tout besoin de déclarer une dépendance. Simplement require ‘json’ et laissez Ruby s’en occuper.

Rails l’a fait, vous le pouvez aussi !

Pourquoi choisir un client HTTP quand vous pouvez tous les avoir ?

Chaque application Rails embarque une demi douzaine de clients HTTP : faraday, rest-client, httparty, excon, typhoeus, curb, etc. C’est parce que plusieurs gems les utilisent de manière interne. Un gem ruby ne devrait jamais utiliser autre chose que Net::HTTP en interne ! Apprenez l’API Net:HTTP, tuez ces dépendances et arrêtez de forcer vos utilisateurs à charger des gems HTTP.

Disons que vous voulez proposer une version optimisée de curb : ok, mais rendez la optionnelle. Laissez le choix au développeur d’utiliser curb mais net/http devrait toujours être le choix par défaut.

Optimiser Rails 5.0

Ces dernières semaines, j’ai travaillé (en tandem avec plusieurs autres développeurs, coucou @_matthiewd, @applerebel !) sur la réduction des dépendances des gems dans Rails 5.0. Rails 4.2.5 demande 34 gems. Rails 5.0b1 demandait 55 gems. Rails 5.0b2 demande 39 gems. Je m’attend à ce que Rails 5.0 demande 37 gems ou moins. Jusqu’ici j’ai supprimé Celluloid, EventMachine, thread_safe, et json.

Malheureusement il n’y a plus de « fruit facile à cueillir ». J’adorerai supprimer Nokogiri, c’est une telle dépendance énorme avec composant massif sous forme d’extension native, mails il y a d’autres dépendances non triviales autour. Oga est une belle alternative, plus simple. Si vous livrez une gem qui repose sur Nokogiri, envisagez de la rendre optionnelle et proposer par défaut REXML (je sais, mais au moins c’est dans la stdlib), ou Oga instead.

Faites partie de la solution

Je peux aider sur Rails 5.0 mais je ne peux pas corriger chaque gem. Si vous êtes un développeur de gem, auditez vos propres dépendances et supprimez-en autant que vous pouvez, Si vous êtes un développeur d’application, jetez un oeil à votre gemfile et regardez si vous pouvez supprimer une gem ou deux. Simplifiez, simplifiez, simplifiez.

Par exemple, je pense qu’il est possible pour la gem Stripe de supprimer ses deux dépendances.

Règles à retenir

Quelques règles d’ingéniérie logicielle :

  • Aucun code ne s’exécute plus rapidement qu’aucun code
  • Aucun code n’a moins de bugs qu’aucun code
  • Aucun code ne consomme moins de mémoire qu’aucun code
  • Aucun code n’est plus facile à comprendre qu’aucun code

Tuez ces dépendances. Vos gems et applications seront meilleures comme ça.

Article original publié sur le site perso de Mike Perham.

1 Commentaire
Le plus ancien
Le plus récent
Commentaires en ligne
Afficher tous les commentaires
Serge
17/05/2016 11:10

Le même phénomène se retrouve avec le développement de site internet. Avant de commencer la page HTML, le stagiaire avait déjà ajouté jquery, Bootstrap, les fonts, … et cie pour afficher une bête page statique.
A 100% d’accord avec tes arguments, ‘coder à l’ancienne’ et utiliser un code minimaliste ne prend pas forcément plus de temps et présente de nombreux avantages en terme de maintenance et de sécurité. Je vais bookmarké ta page pour faire un tuto pour nos prochains stagiaires.