🚨 Cette édition est relativement longue, votre boîte mail risque de la tronquer. Je vous conseille de cliquer sur le titre ci-dessus pour l’ouvrir dans votre navigateur web. ☝️
Au programme aujourd’hui :
Une surprise en introduction !
Réduis la portée de ton JavaScript pour faciliter sa maintenance par Tim
L’agence de recrutement pour les leads dévs RoR !
Temps de lecture : 7 minutes
Hello les petits Biscuits !
Bienvenue sur la 15ème édition de Ruby Biscuit.
Vous êtes maintenant 314 abonnés 🥳
J’espère que vous êtes en forme ! Je profite de cette introduction pour vous annoncer que nous publierons notre première édition hors série mercredi prochain !
Et ce n’est pas n’importe qui qui prendra la parole. Une chercheuse en traitement automatique du langage (TAL), sous-domaine de l’intelligence artificielle.
Elle s’appelle Chyrine et a rejoint Capsens il y a quelques semaines ! Elle a écrit ses premières lignes de codes à 12 ans, puis suite à un master en recherche, elle s’est prise de passion pour le TAL. Cette passion l’a poussée jusqu’en doctorat pour devenir chercheuse. 🎓
Son rôle est de nous aider à utiliser au mieux l’IA au quotidien pour performer davantage dans notre travail et notre organisation. Elle s’est proposée pour vous partager ses connaissances sur différents sujets : comment fonctionne l’IA, que peut-on attendre d’elle en tant que dév, ce qu’elle n'est pas encore capable de faire à notre place, comment choisir les bons outils, comment les utiliser, quels sont les risques de partager notre code à d’autres sociétés, etc.
J’ai déjà pu lire ce qu’elle vous a préparé pour la semaine prochaine et j’ai été bluffée ! Alors soyez patients et croyez-moi, vous n’allez pas être déçus !
Avant de vous laisser entre les mains de Tim pour l’article du mois, je tenais à vous rappeler que vous êtes tous les bienvenus pour donner votre avis en commentaire et partager vos expériences sur les sujets que nous abordons. Vous pouvez aussi mettre un petit like ❤️ et/ou partager la newsletter à un copain ou une copine ! 😉
Bonne lecture.
Réduis la portée de ton JavaScript pour faciliter sa maintenance
C'est en travaillant sur de vieilles applications que j'ai pu constater à quel point l'architecture des fichiers JavaScript est trop souvent laissée de côté. Nous, devs Ruby, sommes très forts pour ordonner, refactoriser, optimiser nos applications mais lorsqu'il s'agit du JavaScript... il n'y a plus grand monde. J'ai pu travailler sur des apps géantes, l'une d'entre elles dépassant même les 10 ans 😱, d'autres étaient plus récentes. On y trouvait beauuuuucoup d'AJAX, des composants Vue.js, des "templates" Handlebars/Mustache, des animations et transitions à tout va, des formulaires améliorés et bien sûr, jamais de test.
Sur une de ces applications en particulier, cela m'a posé beaucoup de problèmes. Je ne pouvais plus assurer sa maintenance, c'était devenu trop fastidieux d'y ajouter de nouvelles fonctionnalités et Sprocket n'arrivait même plus à compiler les assets à la volée en local. Ce qui m'a le plus dérangé et ce que cette application avait en commun avec beaucoup d'autres que j'avais vues, c'est que l'entièreté de ses fichiers JavaScript étaient importés dans application.js. Beurk 🤢.
⚠️ ATTENTION, certaines images peuvent heurter la sensibilité des plus jeunes.
Je n'avais plus le choix, il fallait tout reprendre. Je vous partage aujourd'hui les leçons tirées de cette expérience en espérant pouvoir vous épargner ce casse tête.
Commençons par le commencement :
Pourquoi importer tout son JavaScript dans l'application.js
est un problème ?
Ce n’est pas 1 problème, c’en est 3 : la performance, la maintenance et le débugage.
1. Performance : Cette pratique affecte avant tout la performance de votre app en obligeant le navigateur à charger constamment l'intégralité du JavaScript, même si une seule partie est nécessaire pour une page spécifique. Il faut savoir que cette méthode génère un seul fichier de cache. Ainsi, la moindre modification du code requiert le téléchargement complet du fichier, annulant les avantages du cache et ralentissant le chargement des pages.
2. Maintenance : En l'absence de séparation claire des responsabilités, il devient complexe de cerner rapidement les fichiers spécifiques où il faut fouiller en cas de bug, modification ou ajout de fonctionnalités.
3. Débugage : Il est possible que cela occasionne des conflits de namespace dont nous avons parlé dans la précédente newsletter. Les bugs structurels peuvent survenir sur des pages avec lesquelles ils n'ont pas de rapport direct. Et si de la métaprogrammation est utilisée, la situation devient encore plus complexe, car l'identification des erreurs est un véritable casse-tête. De plus, si un bug survient, c'est l'ensemble du JavaScript de l'application qui sera cassé.
Pour vous donner une métaphore de Rubyistes, il ne nous viendrait jamais à l'idée d'inclure toutes les méthodes de tous nos modèles dans l'ApplicationRecord
! Pourtant c'est bien ce qu'on fait en JavaScript 🫠.
Si cela cause autant de problèmes, pourquoi est-il d'usage d'importer tous nos fichiers JavaScript dans application.js
, alors ?
Simplement parce que c'est un ancien pattern du web.
L'histoire d'HTTP
HTTP 1 fonctionnait de manière séquentielle pour récupérer les fichiers, y compris les fichiers JavaScript. Lorsqu'un navigateur demandait des fichiers au serveur, une connexion TCP (Transmission Control Protocol) distincte était nécessaire pour chaque fichier.
Autrement dit, 1 nouveau fichier = 1 nouvelle connexion TCP. Chaque nouvelle connexion initiait un processus de requête et de réponse entre le navigateur et le serveur, ce qui était, comme vous pouvez l'imaginer, relativement long. C'est pour cela qu'on privilégiait l'import de notre Javascript dans un seul fichier, ce qui demandait une seule et unique connexion TCP.
Ce pattern était encore acceptable en 2010. Le JavaScript n'était pas autant utilisé et complexe qu'aujourd'hui. Cependant, cela a changé avec la multiplication de nouvelles librairies et frameworks JavaScript.
HTTP 2 a donc été introduit en 2015, pour pallier ce problème de connexion TCP trop nombreuses.
Cette nouvelle version a grandement amélioré l'efficacité de la communication entre les navigateurs et les serveurs grâce à 3 fonctionnalités :
Le multiplexage qui permet de traiter plusieurs demandes et réponses en même temps sur une seule connexion,
La compression des en-têtes qui réduit la quantité de données échangées entre le client et le serveur,
La "priorisation" qui permet au serveur de prioriser le contenu en lui indiquant quels fichiers sont les plus importants, ce qui a permis de charger en premier les éléments cruciaux pour l'affichage de la page.
En 2022, HTTP 3 a pointé le bout de son nez pour encore améliorer la vitesse, réduire la latence et la fiabilité des connexions web avec un nouveau protocole QUIC (Quick UDP Internet Connections), qui a remplacé le TCP.
Pour en savoir plus, je vous recommande cette vidéo de Lucian Oprea et/ou cette conférence de Daniel Stenberg, qui donnent encore des détails.
Quelle solution pour notre problème
La solution est très simple et ne demande aucun effort. Quelle chance !
Il vous suffira simplement d'utiliser le helper javascript_include_tag
pour importer dans votre vue le ou les fichiers JavaScript nécessaires.
Pour améliorer davantage la maintenance et la "testabilité" de notre code, une stratégie efficace consiste à diviser notre JavaScript en modules. Ces modules peuvent ensuite être importés dans un fichier (ou une vue) en fonction des besoins spécifiques. C’est très simple et en prime ça facilite grandement la réalisation des tests unitaires.
Attention cependant il ne faut pas oublier d'ajouter le chemin de vos fichiers dans assets/config/manifest.js
, pour qu'ils soient compilés !
Nous aurons alors un fichier application.js
allégé qui, idéalement, ne contiendra que les frameworks et les logiques utilisés sur l'ensemble de l'application.
Aller plus loin avec Stimulus
Avant toute chose je précise que l'utilisation de Stimulus n'est pas incompatible avec l'utilisation du helper javascript_include_tag
.
Stimulus et javascript_include_tag
répondent à des besoins différents : le premier permet une gestion plus modulaire et ciblée du JavaScript, tandis que le second offre un moyen de charger le JavaScript nécessaire pour une page donnée.
Stimulus, intégré automatiquement dans nos apps depuis Rails 7, nous offre l'avantage indéniable de pouvoir appliquer du JavaScript à un simple bloc HTML.
Regardons ensemble comment j'ai pu refactoriser une fonctionnalité de mon application grâce à Stimulus.
Je me trouve sur un formulaire où un utilisateur peut drag & drop sa pièce d'identité. Nous avons une fonction JavaScript qui sert à afficher le nom du document sélectionné dans une balise. Notez que dans notre application, cette fonction est aussi utilisée dans le formulaire d'ajout d'un avatar sur le profil.
Le chef de projet me remonte un bug avec notre fonctionnalité, ça ne marche plus. Voici le Javascript concerné :
On peut deviner que le HTML ciblé ressemble à quelque chose comme suit :
Nous sommes tous d'accord, ce code est difficilement compréhensible. Chaque développeur passé par là a dû y ajouter sa petite condition, ce qui nous offre aujourd'hui une forêt de "if" 🎄. Certaines conditions semblent être dupliquées, d'autres inutiles...
Grâce à mon copain console.log
, on comprend enfin quelques éléments. Par exemple cette ligne :
Elle se charge de retirer le préfixe C:\fakepath\
rajouté par le navigateur sur le nom de notre document chargé (ce préfixe est ajouté pour des raisons de sécurité).
Il me faudrait réunir les auteurs de ce code, avoir accès à des tests unitaires pour me servir de documentation ou y passer beaucoup de temps pour comprendre ce que fait concrètement cette fonction et à quoi servent tous ces id.
Je n'ai finalement jamais réussi à trouver d'où venait mon bug. Malgré la simplicité initiale de cette fonctionnalité, nous nous sommes fait submerger par sa complexité, en raison de problèmes d'organisation de nos fichiers et d'un manque de tests.
Autant tout réécrire avec Stimulus.
Avec notre controller Stimulus, nous déclarons les éléments du DOM dont nous avons besoin via les data attributes. Nous n'avons plus besoin d'utiliser des classes (comme .file-input
), qui sont conventionnellement réservées au CSS. Nous limitons aussi les déclarations d'éléments ou d'event listeners.
Stimulus nous offre une structure basique qui nous guide, sans être rigide. Cela donne rapidement des repères utiles à la prochaine personne qui devrait s'arrêter là. Ici, le HTML révèle clairement qu'un comportement JavaScript lui est associé et dans quel controller le trouver. La portée est minimale. Si nous nommons bien nos fonctions et nos targets, nous pouvons comprendre quel est le comportement attendu sans nécessairement aller lire le JavaScript.
Le scope de ce code ne se soucie que du bloc HTML dans lequel notre <div data-controller="documents">
a été déclaré.
Tout cela nous permet de réduire en une ligne la logique de notre précédente version, qui itérait sur de multiples éléments du formulaire avec trois niveaux de conditions. Quelle satisfaction !
Comme dit Jeff Atwood (co-fondateur de Stack Overflow) :
The best code is no code at all. Every new line of code you willingly bring into the world is code that has to be debugged, code that has to be read and understood, code that has to be supported.
🎁 Recette pratique de unit-test JavaScript
Il est maintenant de notre devoir, en tant que bon dév, de tester notre code. Gardez toujours en tête qu'en ajoutant des tests, vous inciterez les prochains à en écrire aussi. Il est bien plus simple d'ajouter un nouveau test à une suite existante que d'en écrire de zéro.
Pour mettre en place des tests, nous allons utiliser Vitest (yarn add vitest
). C'est un framework accessible, simple d'utilisation et qui fonctionne sans aucune configuration (bien qu'on puisse en ajouter). Ce framework est compatible avec la syntaxe de Jest, qui ressemble beaucoup à celle de RSpec.
Si vous avez décomposé votre JavaScript en modules, vous pouvez les tester directement en les important dans un fichier de test. À partir de là, vous pourrez tester votre module comme si vous testiez un PORO.
Cependant, comme la plupart du temps nous travaillons avec le DOM, nous allons devoir le simuler dans nos tests. Pour ça, je vous propose d'utiliser Happy DOM (yarn add happy-dom
). Il vous suffit de déclarer son utilisation via un magic-comment dans notre fichier de test pour le mettre en place.
Cette configuration demande peu d'effort. Elle est le meilleur compromis pour favoriser l'écriture rapide et systématique de tests.
Voici un test de notre controller Stimulus vu plus haut :
Happy DOM nous donne accès aux objets document
, window
, Vitest, et nous permet d'utiliser beforeEach
, describe
, expect
et it
.
Ici, nous testons le controller Stimulus nommé DocumentsController
, spécifiquement la méthode #showUploadedFileName
.
Pour ça, nous simulons un élément DOM contenant un input de type "file" et un paragraphe. Nous définissons des cibles pour ces éléments via les attributs data-*
qui correspondent à ce que le contrôleur attend pour fonctionner.
Ensuite, nous procédons à l'insertion d'un fichier factice dans l'élément de fichier, imitant l'action de sélection d'un fichier. Pour finir, nous évaluons si le paragraphe cible affiche le nom de fichier attendu (dans cet exemple, "myfile.txt").
— Tim
L’agence de recrutement pour les leads dévs RoR !
Comme vous le savez, derrière Ruby Biscuit, il y a Capsens 👋 , nous sommes une agence web qui fait du Ruby on Rails depuis 10 ans.
Avec le temps on s'est rendu compte que beaucoup de dévs choisissent leur entreprise un peu par hasard alors qu'ils pourraient davantage s'épanouir et se valoriser dans des structures qui leur correspondent mieux. De plus on sait à quel point les process de recrutement peuvent ne pas être adaptés à notre métier et nos profils.
Ce qui tombe super bien c'est que chez Capsens nous avons une excellente connaissance de l'écosystème RoR en France, avec un réseau d'entreprises considérable. La plupart étant des boites bien installées (+ de 5 ans), avec des équipes tech déjà présentes et qui recherchent avant tout des leads dévs et dévs séniors.
C'est pourquoi nous avons décidé de mettre à profit nos ressources pour vous aider à trouver le poste de vos rêves !
Alors tu as plusieurs années d’expériences ? Tu souhaites trouver le prochain poste de lead dév de tes rêves ?
Concrètement voilà ce qui va se passer :
Réponds à cette newsletter en te présentant en deux lignes !
Je t’envoie aussitôt notre test technique pour évaluer ta séniorité
Je te propose des créneaux pour un appel afin de faire ta connaissance et que tu me dises ce que tu cherches pour t’épanouir dans une entreprise.
Je te propose 3 entreprises qui correspondent à ton profil et tes aspirations. Pour chacune de ces entreprises :
Je me charge de te donner un max d’infos et répondre à toutes tes questions par message (horaires, ambiance, taille et séniorité de l’équipe, responsabilités, marge de manœuvre pour la négociation du salaire, localisation des bureaux, politique de télétravail, etc). Pas d’appels inutiles.
Avant de rencontrer le recruteur lui-même, je te mets en relation avec un développeur de leur équipe. Tu pourras alors te faire une idée de comment ça se passe de l’intérieur.
Enfin, le recruteur te recevra ! Il aura déjà eu toutes les informations que je lui aurai transmises sur toi ce qui vous permettra d’aller à l’essentiel !
Lance-toi, on attend ton e-mail ! Et si tu aimes déjà ton travail, ne nous contacte surtout pas ! Ou alors fais-le pour nous recommander ta boîte 😉
Tim & Mélanie