đȘ đ€đ» RĂ©duis la portĂ©e de ton JavaScript pour faciliter sa maintenance
đš 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, sâ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