La normalisation du code front chez Meilleurs Agents
Chez Meilleurs Agents, l’équipe front est en constante croissance (8 à 16 développeur·se·s en 2020). Nous travaillons sur une base de code de 6 applications et 4 bibliothèques sur lesquelles nous intervenons de manière croisée entre impact teams (c’est comme ça qu’on appelle nos squads).
L’équipe regroupant plusieurs niveaux de séniorité et différentes expériences, nous cherchons avant tout à travailler sur l’aspect fonctionnel de nos revues de code plutôt que sur le style.
Il semblait donc naturel de s’orienter vers un outillage automatisé du lint, celui-ci permettant de s’abstraire des débats autour des règles de style pendant les revues de code.
Qu’est-ce que le lint ?
Lint, or a linter, is a static code analysis tool used to flag programming errors, bugs, stylistic errors, and suspicious constructs. Wikipédia
Traduit en français et simplifié, linter son code permet d’en améliorer sa qualité en s’évitant des erreurs bêtes, tout en normalisant/appliquant les bonnes pratiques.
Des exemples simples :
- une constante ne doit pas être modifiée/réaffectée
- une variable inutilisée doit être supprimée
- les instructions ne doivent pas être séparées de plus d’un espace
Le rôle du linter est d’identifier ces problèmes afin de nous le notifier pendant l’écriture de notre code.
Find and fix problems in your JavaScript code. ESLint
ESlint est l’outil que l’on a appelé précédemment linter, appliqué au JavaScript, au même titre que JSLint, JSHint, … . ESLint est cependant celui le plus suivi sur Github, mais également le plus utilisé, notamment par les grosses entreprises, comme on peut le voir sur la page dédiée de leur site.
ESLint propose 3 fonctionnalités :
- Identifier les problèmes
- Corriger automatiquement ces derniers (quand c’est possible)
- Personnaliser les règles permettant de les identifier
Pour approfondir les connaissances autour du lint et de sa mise en place, je vous invite à lire ces articles :
- https://mindsers.blog/fr/linting-good-practices/
- https://www.freecodecamp.org/news/what-is-linting-and-how-can-it-save-you-time/
L’utilisation chez Meilleurs Agents
Aujourd’hui, nous utilisons divers outils afin d’améliorer la qualité de notre code :
- ESLint
Comme expliqué précédemment, c’est lui qui nous permet de lint le code JavaScript que l’on écrit. Il permet de prendre en compte des ensembles de règles prédéfinies pour des technos (React, Typescript, …), mais également des règles de bonnes pratiques définies par d’autres personnes/entreprises (Airbnb, Google, Facebook, …). On peut retrouver pas mal ressources intéressantes sur le Github Awesome ESLint. - Stylelint
De la même façon qu’ESLint pour le JS, Stylelint permet de lint le code CSS (SCSS, inline CSS, …). - Prettier
Quant à Prettier, il ne s’agit pas d’un linter, mais d’uncode formatter
, il permet d'appliquer des règles d'écriture et non d'identifier des problèmes liées au code. Par exemple, l'indentation (tab ou espace), le nombre de caractères avant un retour à la ligne, la présence ou non du;
à la fin d'une déclaration, etc ...
Associés à des règles que l’on a choisi et aux règles de base de chaque techno (React, Typescript, SCSS), ces outils nous permettent de maintenir une base de code plus saine et une compréhension plus rapide pour n’importe quelle personne explorant du code front chez Meilleurs Agents.
Aérer et trier, combo gagnant de la lisibilité
Parmi les règles que l’on a mises en place dans nos bases de code, deux d’entre-elles concernent le tri et le regroupement par critères communs. La première concerne les imports dans nos fichiers JS/JSX/TSX, la seconde, les propriétés CSS.
Il s’agit ici de règles non critiques pour le bon fonctionnement de l’application (à l’exception des imports ayant des effets de bord), mais elles permettent une meilleure lisibilité.
Les imports
Avec les IDE, c’est devenu facile d’importer des composants/fonctions depuis d’autres fichiers, on écrit les premières lettres de ce que l’on veut importer dans notre code, l’autocompletion apparait et hop c’est importé. Sauf que la plupart du temps, le nouvel import est ajouté à la suite des autres et à la fin on se retrouve avec une liste d’import n’ayant aucun sens.
Pour contrer cela, on a mis en place ce plugin ESLint : eslint-plugin-simple-import-sort
Voici ce à quoi pouvait ressembler les imports avant l’utilisation de cette règle :
import React, { useEffect } from ‘react’;
import ‘./layout.scss’;
import ErrorProvider from ‘components/ErrorProvider’;
import Sidebar from ‘components/Sidebar’;
import { useLocation } from ‘react-router-dom’;
import { GA_PAGE_CATEGORY } from ‘../../constants/ga’;
import classNames from ‘classnames’;
import { getUserFromState } from ‘app/reducers/userReducer’;
Désormais, nous avons le droit à quelque chose de plus lisible :
import React, { useEffect } from 'react';
import classNames from 'classnames';
import { useLocation } from 'react-router-dom';import { getUserFromState } from 'app/reducers/userReducer';
import ErrorProvider from 'components/ErrorProvider';
import Sidebar from 'components/Sidebar';import { GA_PAGE_CATEGORY } from '../../constants/ga';import './layout.scss';
Voilà l’ordre des imports que l’on a souhaité :
- Imports à effet de bord en premier
- Imports Node.js (très peu utilisé)
- Imports de librairies,
react
en premier - Imports absolus au sein de l’app
- Imports parents
../
- Imports relatifs simples
./
- Imports des fichiers de style
Voici la configuration associée à cette règle pour respecter l’ordre souhaité :
"simple-import-sort/sort": [
"error",
{
"groups": [
// Side effect imports
["^\\\\u0000"],
// Node.js builtins.
[
"^(assert|buffer|child_process|cluster|console|crypto|dgram|dns|domain|events|fs|http|https|module|net|os|path|punycode|querystring|readline|repl|stream|string_decoder|sys|timers|tls|tty|url|util|vm|zlib|freelist|v8|process|async_hooks|http2|perf_hooks)(/.*|$)"
],
// Packages. `react` come first.
["^react$", "^@?\\\\w"],
// Internal imports. This rule doesn't manage webpack absolute imports, so we need a list of folders.
[
"^(app|components|containers|enums|constants|fonts|images|helpers|services|sagas|sass|types|setupTests)(/.*|$)"
],
// Parent imports. Put `..` last.
["^\\\\.\\\\.(?!/?$)", "^\\\\.\\\\./?$"],
// Other relative imports. Put same-folder imports and `.` last.
["^\\\\./(?=.*/)(?!/?$)", "^\\\\.(?!/?$)", "^\\\\./?$"],
// Style imports.
["^.+\\\\.s?css$"]
]
}
],
Les bénéfices de cette règle :
- Permet une meilleure lisibilité des imports
- Normalise la position des imports à effet de bord
Les inconvénients :
- Les imports absolus webpack (depuis le dossier source
./src
) ne sont pas gérés. On pourrait utiliser un préfix comme@/
ou~/
pour les identifier sans gérer de liste de dossiers.
Les propriétés CSS
Concernant les propriétés CSS, la problématique ne vient pas cette fois de l’autocompletion, mais plutôt du fait de ne pas ordonner nous même les propriétés lors de l’écriture du code. On se retrouve donc par exemple avec des propriétés de police d’écriture en plein milieu de propriétés concernant la position de l’élément. Afin d’éviter ça, on utilise la règle Stylelint suivante : stylelint-config-rational-order
On passe d’un ensemble de propriétés pouvant ressembler à ça :
.button {
display: inline-flex;
border-radius: 8px;
font-size: 16px;
padding: 4px;
line-height: 24px;
align-items: center;
text-decoration: none;
font-weight: 500;
border: 1px solid black;
cursor: pointer;
}
A cela :
.button {
display: inline-flex;
align-items: center;
padding: 4px; font-weight: 500;
font-size: 16px;
line-height: 24px;
text-decoration: none; border: 1px solid black;
border-radius: 8px;
cursor: pointer;
}
Les bénéfices de cette règle :
- Permet une meilleure lisibilité des propriétés CSS en aérant le style
- Regroupe les propriétés de manière logique/rationnelle
Les inconvénients :
- Certaines personnes sont habituées à une autre règle triant les propriétés de manière alphabétique
Aide à la mise en accessibilité
L’accessibilité des sites internet est de plus en plus prise en considération par les développeur·ses et les entreprises aujourd’hui. Cependant, en tant que développeur·se on n’est pas toujours formé pour connaître toutes les bonnes pratiques d’écriture du code afin de respecter les normes d’accessibilité et faire en sorte que notre site soit accessible pour n’importe qui.
Chez Meilleurs Agents, certaines personnes se sont penché·es sur le sujet et font en sorte aujourd’hui que le sujet soit pris au sérieux au sein de l’entreprise et du groupe dont on fait parti. Si vous voulez plus d’information sur l’accessibilité chez nous, c’est ici : Accessibility at Meilleurs Agents.
Malgré tout, il existe des règles de lint nous permettant d’avoir certaines bonnes pratiques d’accessibilité basiques, notamment sur React. Ces règles se trouvent ici : eslint-plugin-jsx-a11y
Parmi ces règles on trouve par exemple :
alt-text
: force l'utilisation de la propriétéalt
sur les composants qui la requiert (sur les images par exemple)label-has-associated-control
: permet de vérifier qu'un label a du texte et est bien associé à un inputheading-has-content
: permet de s'assurer qu'un titre a bien du contenu accessible.- etc …
Toutes ces règles ont leur importance, elles permettent d’avoir un minimum de bonnes pratiques liées à l’accessibilité et d’ouvrir la voie vers une meilleure connaissance de chacun sur les enjeux de l’accessibilité au sein d’un site internet.
Outiller pour automatiser
L’outillage en amont (sur nos ordinateurs)
Pour s’assurer que chaque développeur·se utilise les linter avant de pousser son code sur GitHub, nous avons mis en place des hooks qui exécutent les linters sur les fichiers modifiés à chaque commit.
Grâce à de tels outils, les retours pendant les revue de code peuvent se concentrer sur le contenu plutôt que la forme du code.
Pendant une revue, lorsqu’on est amené à commenter des erreurs qui pourraient être détectées par un outil ou un script, nous revoyons notre configuration de linter afin de l’améliorer continuellement.
L’outillage en aval (sur notre CI)
En plus de l’outillage mis en place sur nos machines, à chaque fois que l’on pousse des modifications sur notre branche dans GitHub, la CI s’occupe de vérifier que le code écrit fonctionne correctement en prévision du déploiement.
Cela passe par plusieurs vérifications en parallèle :
- Build : on s’assure que ça compile toujours bien (ce serait mieux 🤓 )
- Tests : Tous les tests unitaires doivent passer
- Lint : Aucune erreur de lint ne doit rester
Cela peut faire un peu redondance avec les hooks installés localement, mais on est jamais à l’abris d’une surprise, cela pourrait être dû à des hooks non installés chez un·e développeur·se, une modification faite directement sur github, …
Les améliorations à venir
Bibliothèque de règles
Un peu à la façon d’Airbnb, il est envisagé de créer une bibliothèque de règle commune afin de normaliser le code de nos divers projets. Cela évite la duplication de règle et simplifie la mise à jour de notre propre norme.
Il reste cependant une problématique à solutionner avant de créer cette bibliothèque de règles, celle concernant l’ordonnancement des imports absolus. Comme indiqué dans les inconvénients de cette règle, nous sommes obligé·es de lister l’ensemble des dossiers à la base du dossier ./src
, afin de la faire fonctionner. Il n'est donc pas possible de généraliser la règle dans une bibliothèque de règles en l'état, il faudra adopter une notation telle que @/
ou ~/
pour normaliser la règle.
Husky sur l’ensemble de nos projets
Comme évoqué précédemment, dans notre outillage local, nous avons des hooks git écrits en shell
qui nous permettent de s'assurer du bon lint de nos fichiers avant de commit.
C’est quelque chose qui fonctionne chez nous depuis longtemps et qui fait le travail mais qui a quelques inconvénients :
- Il faut installer les hooks une première fois dans notre projet local pour qu’ils soient pris en compte
- Ça analyse tout le fichier et pas seulement ce qui a été modifié (et donc on corrige potentiellement d’autres modifications qui ne concerne pas ce que l’on a fait)
- Il faut connaître le
shell
pour maintenir ces scripts
Afin de solutionner cette problématique, nous avons commencé à explorer la solution proposée par Husky, il permet d’attacher des hooks git directement dans un fichier de config .huskyrc
et de les relier à des scripts npm :
- Il n’est plus nécessaire d’installer les hooks, il suffit juste d’installer les packages npm (ce qui est nécessaire de toute façon pour faire fonctionner l’application)
- Il analyse seulement ce qui a changé grâce à lint-staged que l’on peut associer à Husky
- Il n’est plus nécessaire de connaître
shell
puisque les scripts sont écrits enNodeJS
Actuellement nous testons cela sur un de nos repositories git, nous allons bientôt généraliser son utilisation sur l’ensemble de nos projets Front.
Les autres lint possibles
On a pu parler ici de linter JS/React/TypeScript (ESLint), de linter de style (stylelint) et de formateur (Prettier), mais il en existe d’autres que l’on pourrait mettre en place :
- ls-lint : Il s’agit d’un linter de noms de répertoires et de fichiers, il permet de faire respecter des normes d’écritures basiques comme le
snake_case
, lecamelCase
, etc... mais également des normes plus complexes à base de regex. - commitlint : Comme son nom l’indique, il s’agit d’un linter pour message de commit. Afin de respecter la norme Conventional Commits, ce linter peut se greffer à Husky afin de s’assurer de la bonne écriture du commit avant même leur création. Pour le moment, nous avons seulement mis en place une Github Action pour vérifier la bonne écriture de nos commits.
Conclusion
Notre équipe front continue de grandir au quotidien et la mise en place d’outils automatisés nous permettent de garantir une normalisation de notre base de code tout focalisant nos efforts sur l’aspect fonctionnel de nos applications.
Nous avons solutionné une partie des problématiques rencontrées pendant les revues de code et à l’onboarding des nouveaux arrivants.
Il nous reste encore plusieurs pistes à explorer et nous utilisons le temps dédié au maintien en condition opérationnelle pour améliorer continuellement notre outillage.