TL;DR
Livrer le lot S1 de Jah — 96 lignes, dont 40 de code — a demandé 18 minutes de vérification automatique. Le coupable n'est pas le lint (10 secondes) ni les tests unitaires (2 min 30) : c'est l'E2E, à 76 % du temps. Trois causes mesurées, dont une ligne à changer pour gagner 6 minutes : passer les tests de navigateur de 2 à 4 en parallèle. On a aussi trouvé un test qui coûte 150 secondes à lui seul et une règle qui fait payer 20 minutes à une PR de documentation.
Le point de départ : une intuition fausse
Quand une CI est lente, on accuse le plus visible : le lint, l'installation des dépendances, les tests unitaires. On regarde le log, on voit passer des centaines de lignes, et on se dit « c'est forcément là ».
C'est faux. Voici le vrai découpage d'un job complet, mesuré au step près sur la PR #124 :
| Étape | Durée | Part du total |
|---|---|---|
| Récupérer le code + préparer Node | 40 s | 4 % |
| Lint (909 fichiers) | 9,8 s | 1 % |
| Tests unitaires + intégration (772 cas) | 152,6 s | 17 % |
| Tests E2E (navigateur) | 665,6 s | 76 % |
| Audit des dépendances | 0,5 s | — |
| Total | 14,6 min |
Une seule étape fait les trois quarts du travail. Tout le reste est déjà rapide.
La règle de trois minutes
Avant de chercher plus loin, un mot sur ce qu'est un test E2E, parce que le nom ne dit pas grand-chose.
Un test unitaire vérifie une fonction : « le calcul d'étapes donne 4 ». Il prend un millième de seconde.
Un test d'intégration vérifie plusieurs morceaux ensemble, sans navigateur : « deux comptes se parlent, le message arrive en base ». Un dixième de seconde.
Un test E2E — end to end, « du début à la fin » — vérifie le parcours complet d'une vraie personne : il ouvre un vrai navigateur, charge la vraie page, clique sur le vrai bouton, et regarde ce qui s'affiche. C'est le plus précieux (il prouve que ça marche pour de vrai) et le plus cher : il faut démarrer un serveur, créer une base, lancer un Chromium, puis attendre que tout se mette en place.
Chez Jah, 45 fichiers font ça. Et chacun démarre son propre serveur, sa propre base et son propre navigateur.
Cause nº 1 : 45 démarrages, 2 à la fois
Le projet lance les fichiers E2E deux par deux (--test-concurrency=2). C'est prudent : ça
évite que deux serveurs se disputent un port.
Sauf que la machine du runner GitHub a 4 cœurs. On en utilise donc la moitié, et il faut environ 23 vagues pour passer les 45 fichiers. Chaque vague paie le coût fixe : démarrer un serveur, migrer une base, ouvrir un navigateur.
La correction tient en un caractère : --test-concurrency de 2 à 4. Les ports sont déjà
dynamiques ; la mesure de vitesse d'images (fps) tourne déjà seule, dans sa propre passe, pour ne
pas être faussée. On l'a vérifié : la concurrence 2 a été choisie par prudence, pas par contrainte
technique.
Gain mesuré par le calcul : 5 à 6 minutes par vérification. C'est le meilleur rapport effort/rendement de tout le dépôt — une ligne pour un tiers du problème.
Cause nº 2 : un fichier qui pèse un cinquième
On a mesuré les 45 fichiers un par un. Le classement est brutal :
| Fichier E2E | Durée | Cas |
|---|---|---|
reparation-r4.test.mjs | 150,1 s | 19 |
reparation-r3a.test.mjs | 76,8 s | 6 |
reparation-r3b.test.mjs | 74,0 s | 11 |
phase6-vraie-vie.test.mjs | 55,9 s | 7 |
phase1-core.test.mjs | 54,4 s | 11 |
| les 40 autres | ~700 s à elles toutes | — |
Le premier fichier, à lui seul, fait 21 % de tout l'E2E. Il s'appelle
reparation-r4.test.mjs : 515 lignes, 19 cas, dont trois attentes à durée fixe
(waitForTimeout, la béquille qu'on utilise quand on ne sait pas quel signal attendre).
Trois choses se cumulent ici :
- Il est trop gros. 19 cas dans un seul fichier, c'est 19 cas qui attendent la fin des 18 autres avant que le navigateur se ferme.
- Il attend des durées fixes là où il pourrait attendre un état : « attends 500 ms » au lieu de « attends que le texte apparaisse ».
- Il mélange deux natures. Il contient des cas de style (taille du texte, contraste, lisibilité) qui n'ont pas besoin d'un navigateur pour être vérifiés — un test unitaire sur la feuille de style suffirait, et prendrait un millième du temps.
La correction : le couper en deux fichiers, sortir les cas de style vers des tests unitaires, remplacer les attentes fixes par des attentes d'état. Gain : 1 à 2 minutes, et un fichier qui redevient lisible.
Cause nº 3 : la PR de texte qui paie 20 minutes
C'est le plus irritant, et le plus instructif.
La PR #123 ne contenait aucun code. C'était de la documentation : corriger des compteurs dans un tableau de bord, séparer deux portes dans une fiche, réécrire une règle dans les prompts. Vérification : 19,6 minutes.
Pourquoi ? Parce que le projet a un plan intelligent : « si un diff ne touche que de la
documentation, on ne lance que le lint (40 secondes) ; sinon, toute la batterie ». Ce plan
fonctionne. Mais il a une exception : dès qu'un fichier CHANGELOG est modifié, la batterie
complète est déclenchée.
Or — et c'est la blague — toute PR est censée mettre à jour le journal des changements. La conséquence est mécanique : l'exception est devenue la règle. Toute PR paie 15 minutes de tests, même quand elle ne change pas une ligne de code exécutable.
Trois façons de régler ça :
| Option | Effet | Ce qu'on perd |
|---|---|---|
Un CHANGELOG seul ne déclenche plus le complet | −15 min sur ces PR | rien : le journal ne change pas le comportement |
| Une PR ouverte en brouillon ne paie que le lint | −15 min sur les poussées intermédiaires | rien : le complet revient au passage « prête à relire » |
Ne pas relancer la CI sur main après une fusion quand l'arbre est identique | −10 à −15 min par fusion | rien : c'est le même code, déjà vérifié |
La troisième mérite un mot. Aujourd'hui, quand on fusionne une PR verte dans main, la même
batterie repart de zéro. On vérifie deux fois exactement le même arbre. Le plan de septembre
prévoyait déjà de l'éviter ; ça n'a jamais été appliqué.
Ce qu'on croyait, et ce qu'on a trouvé
Un mot sur une intuition qu'on a eue et qu'il faut jeter : « la CI est lente parce que la file d'attente des machines GitHub est saturée ».
C'est faux, et la mesure est sans appel : l'attente avant qu'un job démarre est de 0,2 minute. Le job commence tout de suite. Les 18 minutes sont du travail réel, pas de l'attente.
C'est important, parce que ça change la conclusion : il ne sert à rien de réduire le nombre de runs (ce qu'on croyait), il faut réduire ce que chaque run fait.
Le vrai enseignement
L'équipe avait déjà fait un audit de sa CI en septembre, et il était bon. Il avait identifié que les E2E prenaient 70 % du temps, et il proposait des corrections justes. Pourquoi n'a-t-il pas suffi ?
Parce qu'il mesurait la durée des suites, et pas leur nombre.
Un test ne ralentit pas tout seul. Une CI grossit parce qu'on en ajoute — et chaque nouveau lot livré en ajoute au moins un. En septembre, l'E2E prenait 166 secondes. Aujourd'hui : 665 secondes. Quatre fois plus. Personne n'a rien ralenti : on a simplement ajouté des tests, 45 fois, sans jamais regarder le total.
Il manquait donc un plafond. Pas une règle écrite dans un document — une mesure affichée qui alerte :
Durée de la vérification complète : 18 min. Fichiers E2E : 45. Au-delà de 8 minutes, on optimise avant de livrer un nouveau lot.
Une règle écrite dans un fichier se perd. Une mesure affichée dans le tableau de bord se voit tous les jours. C'est exactement la leçon du linter de cette même journée : une règle outillée tient, une règle écrite ne tient pas.
Ce qu'on fait, dans l'ordre
| Priorité | Action | Gain | Effort |
|---|---|---|---|
| 1 | Concurrence E2E : 2 → 4 | −5 à −6 min | une ligne |
| 2 | Couper reparation-r4 (150 s) et sortir ses cas de style | −1,5 min | 30 min |
| 3 | CHANGELOG seul ≠ batterie complète | −15 min sur ces PR | 15 min |
| 4 | Ne pas revérifier main après une fusion identique | −10 à −15 min | 1 h |
| 5 | Regrouper les fichiers E2E par domaine | −1 à −2 min | 1–2 h |
Les deux premières se font tout de suite : une ligne et un déplacement de fichier, pour 7 minutes gagnées par vérification, soit 40 % de la CI. Le reste suivra.
Et pour Jah, concrètement ?
Rien de visible pour toi : ce sont des outils de travail, pas des fonctionnalités. Mais il y a un effet que tu verras.
Chaque nouvelle idée livrée — une fenêtre qu'on déplace, un réglage de texte, une couleur de maison — doit être vérifiée avant d'arriver chez toi. Plus cette vérification est lente, moins on livre de choses, et plus longtemps tu attends. Réduire la CI de 18 à 7 minutes, c'est livrer deux fois plus souvent sans jamais baisser la garde.
C'est aussi une question de vérité : une vérification qui prend un quart d'heure, on est tenté de la sauter « juste cette fois ». Une vérification qui prend trois minutes, on la lance sans y penser.
Mesures prises le 30 septembre 2026 entre 07 h 30 et 07 h 40, sur les 30 derniers runs GitHub et
sur deux jobs complets (PR #124 et release alpha.12). Le rapport détaillé, avec les commandes
utilisées, est dans docs/plans/ci-tests.md du dépôt Jah Messenger.