Pourquoi les micro-optimisations échouent à améliorer la véritable performance du JavaScript
Explique pourquoi se concentrer sur des métriques mesurables telles que la taille du paquet et les re-renderings manque souvent les véritables causes d’une expérience utilisateur lente dans les applications JavaScript.
La demande de fusion semblait solide sur le papier.
La taille du bundle avait diminué de 18 pour cent. Quelques dépendances inutilisées avaient été supprimées. Plusieurs composants avaient été enveloppés dans des mécanismes de mémorisation. Quelques opérations sur les tableaux avaient été remplacées par des boucles censées être plus efficaces. Le score de Lighthouse avait augmenté. Chaque indicateur décrit dans la demande de fusion indiquait une amélioration.
L’équipe l’a approuvée sans hésiter.
Deux semaines plus tard, les utilisateurs affirmaient encore que l’application était lente.
Pas une lenteur due à « quelques millisecondes supplémentaires selon les tests de référence ».
Pas un problème lié à des chiffres affichés sur un tableau de bord.
Ce genre de lenteur qui affecte réellement les utilisateurs.
Ils appuyaient sur un bouton sans être sûrs qu’il ait été enregistré.
Ils ouvraient une page et attendaient que quelque chose d’utile apparaisse.
Ils changeaient de filtre et voyaient l’interface se figer un instant avant de reprendre son fonctionnement normal.
L’équipe avait consacré beaucoup d’efforts au travail de performance.
Ils ciblaient simplement les mauvais éléments.
Cette tendance se retrouve constamment dans les projets JavaScript d’aujourd’hui.
Malgré l’amélioration des outils de profilage, des temps d’exécution plus rapides, des outils de compilation plus intelligents, des frameworks plus performants et des navigateurs de plus en plus puissants, les développeurs continuent de tomber dans la même habitude :
Optimiser ce qui est le plus facile à mesurer, plutôt que ce que ressentent réellement les utilisateurs.
JavaScript offre justement une liste infinie de choses que l’on pourrait optimiser.
Un composant se réaffiche quatre mille fois.
Alors on le corrige.
Un fichier compressé pèse 300 KB.
Alors on le réduit de taille.
Une fonction met 12 millisecondes à s’exécuter.
Alors on la réécrit.
Une dépendance occupe 40 KB.
Alors on l’élimine.
Un microbenchmark montre que l’approche A bat l’approche B de 7 pour cent.
Donc vous choisissez A.
Chacun de ces éléments peut constituer un travail légitime.
Mais aucun d’eux ne garantit automatiquement une meilleure expérience pour l’utilisateur de votre application.
Parfois, le code le plus rapide que vous écrivez ne résout qu’un problème qui n’a jamais dérangé personne.
Pendant ce temps, la requête de base de données lente, l’appel réseau redondant, la séquence de chargement mal conçue, le payload API encombré ou l’interaction mal structurée continuent de coûter silencieusement du temps réel aux utilisateurs.
Cette inadéquation est le véritable problème qui mérite d’être examiné.
Les performances ne sont pas synonymes de vitesse
L’un des enseignements les plus utiles tirés du travail sur des systèmes en production est que les performances ne peuvent être réduites à une seule métrique.
Imaginez une page qui suit cette séquence :
- Charger le noyau de l’application.
- Télécharger plusieurs blocs JavaScript.
- Démarrer le framework.
- Récupérer les données de configuration.
- Récupérer l’utilisateur actuel.
- Récupérer les données de permission.
- Récupérer le contenu du tableau de bord.
- Afficher le tableau de bord.
- Récupérer les notifications.
- Afficher les notifications.
Chaque étape individuelle peut être parfaitement rapide en soi.
Le véritable problème réside dans la chaîne globale des opérations.
Les utilisateurs se moquent du fait que chaque composant ait été optimisé indépendamment.
Ils s’inquiètent de devoir fixer un écran vide avant que quoi que ce soit n’apparaisse.
C’est pourquoi toute véritable investigation sur les performances doit commencer par une question :
Que l’interlocuteur de l’autre côté attend-il réellement ?
Et non :
Comment puis-je réduire le temps d’exécution de cette fonction ?
Ces approches relèvent de domaines d’enquête fondamentalement différents.
Pendant ce temps, la page reste inactive pendant 700 ms en attendant une demande qui n’aurait jamais dû avoir lieu.
Ce n’est pas une véritable optimisation.
C’est comme nettoyer les meubles alors qu’un tuyau éclate dans le mur derrière eux.
L’obsession des 5 millisecondes
Les développeurs JavaScript ont une prédilection pour les micro-optimisations.
Une partie de cette curiosité est véritablement utile.
Les gens débattent des boucles for par rapport à map().
Ils discutent des stratégies d’allocation mémoire.
Ils examinent les classes cachées, le comportement du collecteur de déchets, les closures, les coûts de déstructuration, la surcharge des appels de fonction et les optimisations JIT.
Tout cela repose sur des connaissances réelles et précieuses.
Le problème n’est pas de comprendre ces mécanismes.
Le problème, c’est d’y recourir sans aucune preuve qu’ils soient importants dans ce contexte.
Disons qu’une fonction s’exécute 100 fois au cours d’une seule interaction utilisateur.
Actuellement, elle nécessite 2 ms par appel.
Vous passez une demi-journée pour la réduire à 1 ms.
Economie totale : 100 millisecondes.
Cela peut avoir de la valeur.
Mais supposons que cette même interaction déclenche également un appel API inutile qui met 600 ms à s’exécuter.
Supprimer cet appel prendrait cinq minutes et permettrait d’économiser six fois plus de latence que toute votre après-midi passée à l’optimiser.
En termes simples, cela semble évident.
Pourtant, les discussions lors des revues de code se concentrent sur le premier type de problème, car il apparaît directement dans les différences de code.
L’appel réseau inutile, en revanche, peut être caché à trois niveaux d’abstraction de distance.
Cela entraîne un biais prévisible et dangereux :
Les équipes finissent par optimiser le code qui leur est visible, et non le système que vivent réellement leurs utilisateurs.
Le navigateur n’est pas votre fonction
Une autre erreur fréquente consiste à penser que le temps d’exécution du JavaScript révèle l’ensemble des aspects de la performance.
C’est loin d’être le cas.
Ce système comprend :
- La résolution DNS
- L’établissement de la connexion
- Les échanges TLS
- Le traitement côté serveur
- Les requêtes de base de données
- La sérialisation des réponses API
- Le temps de transfert réseau
- La parsing de l’HTML
- La parsing du JavaScript
- L’exécution du JavaScript
- Le rendu
- Le calcul de la mise en page
- La peinture des éléments
- La composition visuelle
Chacune de ces étapes peut influencer la vitesse perçue d’une opération.
Disons que vous parvenez à réduire le temps de rendu d’un composant React de 15 ms à 8 ms.
C’est vraiment un bon résultat.
Mais si le backend met 900 ms pour générer les données nécessaires à ce composant, votre amélioration est à peine perceptible.
Ou peut-être que le serveur répond rapidement, mais envoie 2 MB de JSON pour une vue qui n’a besoin que de 20 KB.
Vous gaspillez alors des cycles CPU et de la bande passante pour transférer des données qui n’auraient jamais dû exister sous cette forme.
Ou encore, la page télécharge une bibliothèque côté client très volumineuse avant même de pouvoir afficher quoi que ce soit d’utile.
Aucun de ces problèmes ne concerne le composant lui-même.
Ce sont des problèmes d’architecture.
C’est pourquoi le travail sérieux sur les performances ressemble souvent moins à « rendre le JavaScript plus rapide » et davantage à une enquête approfondie.
Vous remontez à la source du retard, où que cela vous mène.
L’opération la plus coûteuse est souvent celle que vous devriez omettre
Il existe un ordre de priorités utile lorsqu’on réfléchit aux améliorations de performance.
Raccourcir le temps d’exécution d’une opération est un bon résultat.
Omettre complètement cette opération est généralement encore mieux.
Imaginons ce fragment :
const results = expensiveTransform(items);
Le profilage révèle que cette transformation consomme 40 ms.
Votre instinct pourrait être de l’optimiser.
Peut-être introduirez-vous un cache.
Peut-être remplacerez-vous l’algorithme actuel par un plus intelligent.
Peut-être déplacerez-vous cette tâche ailleurs.
Mais avant de faire quoi que ce soit, demandez-vous :
Pourquoi cette transformation a-t-elle lieu en premier lieu ?
Peut-être que la sortie ne change réellement que lorsque le filtre est ajusté.
Peut-être que vous le redémarrez à chaque rendu, quoi qu’il arrive.
Peut-être que le backend pourrait vous fournir la version déjà traitée.
Peut-être que l’interface n’a réellement pas besoin de toutes ces 10 000 lignes.
Peut-être que vous récupérez des données que l’utilisateur n’ouvrira jamais vraiment.
La vraie solution ne se trouve peut-être même pas à l’intérieur de expensiveTransform().
Cela pourrait simplement consister à supprimer cette appel.
Cette façon de penser s’applique bien au-delà de cet exemple.
Omettez les requêtes que vous n’avez pas besoin d’envoyer.
Omettez le rendu des interfaces restées cachées.
Omettez le calcul de valeurs que personne n’utilisera.
Omettez les chemins de code que les utilisateurs n’exécuteront jamais.
Omettez le traitement de données que vous auriez pu filtrer en amont.
Omettez de refaire du travail dont le résultat n’a pas vraiment changé.
L’opération la plus rapide reste celle qui n’est jamais exécutée.
La taille du bundle n’est pas tout
La taille du bundle mérite attention. C’est vrai.
Mais étant devenue une mesure largement suivie, les équipes ont parfois tendance à la considérer comme un substitut de la performance elle-même.
Pendant ce temps, l’application envoie encore six requêtes consécutives avant que l’utilisateur ne puisse faire quoi que ce soit avec elle.
Bien sûr, le bundle a diminué de taille.
Cela ne signifie pas pour autant que l’expérience utilisateur s’est améliorée.
Rien de tout cela ne suggère que la taille du bundle est sans importance.
Cela signifie plutôt qu’il faut déterminer quand ce JavaScript en particulier est réellement utilisé.
Un script de 100 KB qui bloque l’interactivité initiale peut être bien plus important qu’un script de 300 KB chargé plus tard, pour une fonctionnalité que l’utilisateur utilise peut-être une fois par mois.
Le moment est crucial.
Le moment où le code s’exécute compte également.
Le dispositif sur lequel il fonctionne compte.
Le réseau par lequel il se connecte compte également.
Le fait qu’il y ait ou non un cache compte aussi.
Et, tout aussi important, ce que l’utilisateur tente réellement de faire compte énormément.
Si certaines fonctionnalités sont peu utilisées, charger tout ce dont elles ont besoin dès le démarrage peut être un mauvais compromis.
La segmentation du code aide à y remédier.
De même que le chargement différé.
Cependant, tous deux peuvent devenir des rituels inutiles si on les applique sans vraiment comprendre comment la page se charge en pratique.
La question à se poser n’est pas :
Comment pouvons-nous réduire encore davantage cette archive ?
C’est plutôt :
Qu’a besoin cet utilisateur en particulier à ce moment-là, et à quelle vitesse pouvons-nous rendre cette fonctionnalité utilisable ?
Cette approche vous mène bien plus loin.
Le composant que vous examinez n’est pas forcément en cause
Tout ceux qui ont travaillé avec React reconnaissent ce cycle.
useMemo.
const filtered = useMemo(
() => expensiveFilter(items, query),
[items, query]
);
const handleClick = useCallback(() => {
doSomething(id);
}, [id]);
const value = useMemo(
() => ({ user, permissions }),
[user, permissions]
);
Parfois, c’est vraiment la bonne solution.
D’autres fois, cela rend simplement le code plus difficile à suivre sans résoudre réellement aucun problème.
L’optimisation n’est pas gratuite.
La mémorisation en particulier entraîne des coûts réels.
Elle augmente la consommation mémoire, nécessite des tableaux de dépendances à gérer, ajoute une charge cognitive supplémentaire et crée de nouveaux endroits où des bugs subtils peuvent se cacher.
Mesurez avant d’y recourir.
Si un calcul coûte 0,2 ms et s’exécute rarement, l’optimiser ne vous apporte rien.
Si cela coûte 50 ms et s’exécute à chaque frappe, c’est une situation complètement différente.
L’objectif n’est pas de traquer chaque réaffichage.
L’objectif est de faire en sorte que les interactions importantes semblent suffisamment rapides.
Ces deux objectifs ne sont pas identiques.
Concentrez-vous sur les interactions, pas sur les composants individuels
C’est là que de nombreuses équipes pourraient bénéficier d’un changement de perspective.
Les utilisateurs ne perçoivent pas les composants comme des unités séparées.
Ils vivent ce qu’ils font.
Ils tapent une requête de recherche.
Ils remplissent des champs.
Ils se déplacent d’une page à l’autre.
Ils soumettent un formulaire.
Ils ouvrent un menu déroulant.
Ils passent d’une vignette à l’autre.
Ils téléchargent un fichier.
Ils font défiler un flux d’informations.
Ils restent assis en attendant que quelque chose apparaisse.
Plutôt que de se demander :
Cet élément est-il bien optimisé ?
Essayez de demander :
L’entrée dans ce champ de recherche semble-t-elle instantanée ?
Au lieu de :
Cette liste s’affiche-t-elle de manière efficace ?
Demandez plutôt :
Quelqu’un peut-il faire défiler cette liste en douceur, sans que l’interface ne rame ?
Au lieu de :
Avez-nous réduit le nombre d’affichages de React ?
Demandez :
En appuyant sur ce bouton, l’utilisateur reçoit-il une réponse immédiate et pertinente ?
Cette deuxième série de questions correspond beaucoup plus étroitement à ce qui est vraiment important pour le produit.
Une optimisation astucieuse qui laisse une interaction importante aussi lente qu’avant n’est pas nécessairement une solution technique valable, peu importe à quel point elle semble élégante dans un diff.
L’onglet Réseau est généralement supérieur aux boucles que vous réécrivez
Ce n’est pas une suggestion anodine.
Vous découvrirez fréquemment davantage de possibilités d’amélioration là-bas que dans des centaines de lignes de JavaScript ajustées manuellement.
Faites attention à des éléments tels que :
- la même requête envoyée plus d’une fois
- des requêtes exécutées une après l’autre alors qu’elles pourraient le faire en parallèle
- des requêtes lancées avant même que leurs données ne soient réellement nécessaires
- des réponses excessivement volumineuses par rapport à ce qui est affiché
- un cache qui devrait exister mais n’existe pas
- un polling effectué plus souvent que nécessaire
Une seule demande inutile peut annuler l’effet de dizaines de petites modifications en JavaScript.
Prenons la recherche comme exemple.
Voici une approche naïve :
User types "j"
→ request
User types "ja"
→ requestUser types "jav"
→ requestUser types "java"
→ request
Une équipe pourrait alors consacrer beaucoup d’efforts à accélérer l’affichage des résultats.
Mais le véritable goulot d’étranglement pourrait être le fait que l’application envoie quatre demandes distinctes alors qu’une seule suffirait.
L’ajout de mécanismes de débouncing, de suppression des demandes, de mise en cache et de requêtes mieux conçues permet généralement d’obtenir des améliorations bien plus significatives.
C’est ce genre d’amélioration qui se produit au niveau du système, et non à l’intérieur d’une seule fonction.
Ne pas optimiser le mauvais appareil
Faire des tests de performance sur sa propre machine de développement ne vous dit presque rien sur la réactivité réelle d’une application sur un téléphone ancien doté d’un CPU limité et d’une connexion instable.
Cela est particulièrement important pour JavaScript.
Le matériel moderne peut traiter une quantité surprenante de code sans que le développeur ne remarque aucune ralentissement.
Un ordinateur portable puissant peut masquer les problèmes.
Un téléphone haut de gamme peut masquer les problèmes.
Un réseau de bureau rapide peut masquer les problèmes.
Travailler localement peut masquer les problèmes.
Puis une personne réelle ouvre l’application sur un appareil économique via une connexion intermittente.
D’un coup, cette application soigneusement optimisée semble lourde et peu réactive.
C’est précisément pour cette raison que tester dans des conditions réalistes est essentiel.
Vous n’avez pas besoin de le faire constamment.
Mais vous devez le faire suffisamment souvent pour que l’équipe ait une idée réelle du comportement de l’application en dehors d’un environnement de développement confortable.
La bonne question n’est pas :
Cela semble rapide sur ma configuration ?
C’est plutôt :
Est-ce suffisamment rapide pour les utilisateurs réels ?
L’architecture l’emporte généralement sur la micro-optimisation
C’est sans doute la leçon la plus importante de toutes.
C’est l’architecture qui détermine le plafond des performances.
Si votre application doit effectuer cinq appels API en séquence avant de pouvoir afficher son écran principal, aucune manipulation astucieuse des tableaux ne pourra améliorer cette expérience.
Si chaque route charge l’intégralité du bundle de l’application, supprimer quelques fonctions d’aide n’aura aucun effet sur le véritable problème.
Si le client télécharge un ensemble de données très volumineux puis le filtre dans le navigateur, affiner la logique de filtrage vaut probablement moins que de corriger le contrat API lui-même.
Si une action de l’utilisateur efface une grande partie de l’état en mémoire cache, envelopper les composants dans une fonction de mémorisation ne corrigerait pas une stratégie d’invalidation défaillante.
Si vous affichez des milliers de nœuds DOM en même temps, changer la manière dont vous utilisez map() ne vous aidera pas.
Les améliorations qui ont réellement un impact consistent généralement à modifier où le travail est effectué, quand il est effectué, ou s’il doit même avoir lieu.
Ces décisions relèvent de l’architecture, et non de simples ajustements au niveau du code.
Ce que j’examine réellement lors d’une enquête sur les performances
Lorsqu’un système semble lent, il convient de résister à l’envie de se plonger directement dans le code.
Commencez par reproduire le problème.
Puis demandez précisément ce pour quoi l’utilisateur attend.
À partir de là, l’enquête suit généralement une séquence logique.
1. Chargement
Quelles étapes doivent être accomplies avant que l’utilisateur ne puisse voir et utiliser les parties importantes de la page ?
Tracez le chemin critique.
Quels ressources sont réellement nécessaires ?
Quelles requêtes entravent l’avancement ?
Qu’est-ce qui est chargé inutilement ?
2. Réseau
Ouvrez le panneau Réseau.
Recherchez des schémas en cascade.
Les séries de requêtes successives méritent une attention particulière.
De même pour les appels duplicés et les en-têtes de données trop volumineux.
3. Affichage
Ensuite, observez ce que fait le navigateur lui-même.
La page génère-t-elle une quantité énorme de DOM ?
Les étapes de mise en page et d’affichage sont-elles coûteuses ?
Des tâches coûteuses sont-elles exécutées en réponse à l’interaction de l’utilisateur ?
4. JavaScript
C’est seulement à ce stade que les détails au niveau des fonctions entrent en jeu.
Quelles opérations consomment réellement du temps de processeur ?
Lesquelles s’exécutent-elles en boucle ?
Lesquelles sont liées à une action récente de l’utilisateur ?
5. Mémoire
Si l’application devient de plus en plus lente au fil du temps, il convient d’examiner la mémoire.
Des fuites de mémoire et une croissance non contrôlée peuvent se manifester sous forme de lenteur générale.
6. Impact réel sur l’utilisateur
Finalement, reliez tout ce que vous avez découvert à l’expérience réelle de l’utilisateur.
Lancement plus rapide ?
Recherche plus rapide ?
Navigation améliorée ?
Un processus de travail devenu moins pénible ?
Si rien de tout cela n’a changé, il est utile de se demander si le véritable problème a bien été identifié.
Les budgets de performance sont utiles — à condition qu’ils soient liés à la réalité
Il est courant que les équipes établissent des règles telles que :
Garantir que le bundle JavaScript reste inférieur à 300 KB.
C’est un point de départ raisonnable, mieux que de ne pas avoir du tout de critères de contrôle.
Cependant, un budget basé sur l’expérience réelle s’avère généralement plus utile :
- Le contenu principal doit apparaître rapidement
- La recherche ne doit pas sembler interminable
- La navigation doit fournir une réponse immédiate
- Les pages clés ne doivent pas dépendre d’une série de requêtes successives
- Le JavaScript nécessaire pour la première interaction doit être minimal
- Les grands ensembles de données ne doivent pas être affichés d’un seul coup à l’écran
Il est plus difficile de réduire ces critères à un seul chiffre précis.
Cependant, ils correspondent beaucoup mieux à ce que les utilisateurs remarquent réellement et qui leur importe.
Les métriques sont utiles lorsqu’elles vous aident à comprendre ce qui se passe réellement.
Elles deviennent dangereuses dès que l’atteinte d’un chiffre devient l’objectif principal.
Le piège de l’optimisation
Il existe une influence psychologique subtile qui pousse les développeurs sur cette voie.
Optimiser quelque chose donne l’impression de progresser.
On peut alors pointer vers un commit et dire :
La taille du bundle a été réduite de 14 %.
Ou pointer vers un benchmark et dire :
Cette fonction s’exécute maintenant 32 % plus rapidement.
Ou montrer à quelqu’un une capture d’écran du profileur en guise de preuve.
Ces succès sont agréables à ressentir.
Mais certaines des corrections de performance les plus efficaces sont, franchement, peu spectaculaires.
Éliminer une appel API inutile ne constitue pas une démonstration passionnante.
Réorganiser la réponse d’un backend n’est pas non plus très impressionnant.
Ce n’est pas non plus raccourcir une chaîne de dépendances.
Ce n’est pas non plus corriger une requête qui récupère 5 000 lignes alors que 50 suffiraient.
Ce n’est pas non plus ajouter un cache.
Les tâches que l’on évite de faire sont généralement invisibles par nature.
C’est précisément pourquoi il est si facile de les négliger.
La meilleure amélioration en termes de performance peut se traduire par moins de code, moins de requêtes, moins de calculs et même moins d’éléments en cours d’exécution.
Il se peut qu’il n’y ait rien qui mérite d’être sauvegardé en capture d’écran.
L’application est simplement plus agréable à utiliser.
L’optimisation doit commencer par des preuves
Voici une règle à adopter systématiquement :
Ne pas optimiser le code. Optimiser les problèmes confirmés.
Cela ne nécessite pas de mettre en place un processus complexe d’ingénierie des performances pour chaque fonctionnalité livrée.
Cela signifie simplement rassembler suffisamment de preuves pour savoir où passe réellement le temps.
Faites appel à des outils de profilage.
Utilisez les panneaux de performance intégrés au navigateur.
Enregistrez les traces réseau.
Intégrez des données de télémétrie en environnement de production.
Mettez en place un suivi des utilisateurs réels là où c’est pertinent.
Testez sur des appareils correspondant à votre public réel.
Et surtout, reproduisez le problème tel qu’il a été signalé.
Lorsqu’un stakeholder affirme que « le tableau de bord est lent », résistez à l’envie d’aller directement dans le code du composant pour commencer à supprimer des éléments de rendu.
D’abord, déterminez ce que signifie réellement « lent ».
S’agit-il d’une réponse serveur lente au démarrage ?
Un appel API spécifique constitue-t-il le goulot d’étranglement ?
Le bundle JavaScript est-il trop volumineux ?
La phase de parsing prend-elle trop de temps ?
Le rendu est-il la partie coûteuse en ressources ?
Y a-t-il une tâche longue qui bloque le thread principal ?
Une requête de base de données fonctionne-t-elle de manière insuffisante ?
Y a-t-il des retards dus à une accumulation de demandes ?
Le navigateur est-il en attente inutilement d’une opération qui aurait pu commencer plus tôt ?
Ou bien l’application est-elle techniquement réactive mais ne fournit-elle aucun retour visuel à l’utilisateur ?
Le débogage des performances relève du travail d’enquêteur.
Ce n’est pas une course pour voir qui peut supprimer le plus de JavaScript.
La meilleure optimisation du JavaScript pourrait être d’en utiliser moins
Cela peut sembler étrange venant d’un développeur JavaScript.
Mais cela devient de plus en plus important à mesure que les applications grandissent.
Chaque morceau de code exécuté côté client a un coût.
Il doit être téléchargé.
Il peut falloir le parser.
Il peut falloir le compiler.
Il doit s’exécuter.
Il consomme de la mémoire.
Il rivalise avec le navigateur pour le temps d’affichage.
Il peut ajouter des obstacles lors des interactions de l’utilisateur.
Rien de tout cela ne signifie que JavaScript est intrinsèquement mauvais.
Cela veut dire que toute opération de calcul effectuée du côté client doit justifier son existence.
Parfois, la meilleure solution est de traiter les données sur le serveur.
Parfois, il s’agit de diffuser du contenu en continu plutôt que d’attendre sa fin.
Parfois, il faut déplacer toute la logique vers l’arrière-plan.
Parfois, il faut introduire un cache.
Parfois, il suffit de réduire ce que l’API renvoie.
Parfois, il faut charger les éléments progressivement plutôt que d’un coup.
Parfois, il suffit de supprimer simplement une fonctionnalité que personne n’utilise réellement.
Et parfois, le JavaScript déjà en place est tout à fait correct tel quel.
La leçon à retenir est d’arrêter de partir du principe que l’optimisation doit obligatoirement avoir lieu à l’intérieur même du JavaScript.
Ce que les développeurs expérimentés apprennent finalement
Débutant dans sa carrière de développeur, l’optimisation signifie généralement faire en sorte que le code existant fonctionne plus rapidement.
Avec plus d’expérience, l’attention se porte sur l’élimination des tâches qui n’étaient pas nécessaires.
Finalement, les questions deviennent plus profondes, portant sur la raison d’existence même de ces tâches.
C’est une évolution significative dans la façon de penser.
Au lieu de se demander :
Peut-on faire en sorte que cette boucle fonctionne plus vite ?
la question devient :
Pourquoi 20 000 enregistrements sont-ils traités par boucle dans le navigateur en premier lieu ?
Au lieu de se demander :
Comment empêcher ce composant de se rérender à nouveau ?
la question devient :
Pourquoi une seule interaction provoque-t-elle un changement dans l’ensemble de l’état de la page ?
Au lieu de se demander :
Comment peut-on réduire la taille de ce paquet ?
La question devient :
Pourquoi l’utilisateur doit-il télécharger ce code avant de pouvoir faire quoi que ce soit d’utile ?
Au lieu de se demander :
Comment peut-on rendre cette demande plus efficace ?
La question devient :
Est-ce que cette demande est même nécessaire ?
C’est en se posant ces questions plus profondes que l’on parvient à une architecture véritablement meilleure.
L’objectif n’est pas un code rapide
C’est la leçon qui met le plus de temps à être vraiment comprise.
Le travail sur les performances ne consiste pas à produire le JavaScript le plus rapide possible.
Il s’agit de créer un produit qui paraisse suffisamment rapide aux personnes qui l’utilisent réellement.
Ces deux objectifs ne sont pas identiques.
Seulement alors, travaillez à accélérer ce qui reste.
Cette séquence est importante.
Car l’optimisation la plus précieuse n’est pas nécessairement celle qui est la plus impressionnante sur le plan technique.
C’est celle qui fait que l’utilisateur cesse de remarquer que l’application était initialement lente.
Lectures complémentaires
- Les API natives des navigateurs qui substitueront les packages npm populaires en 2026 — Explique comment les fonctionnalités natives de JavaScript et CSS telles que Signals, l’opérateur pipeline, Temporal et le positionnement par ancre remplacent les packages npm courants.