af83

Les défis du développement des single page webapps

Les single page webapps, vous savez ces petites applications écrites totalement en HTML5, JavaScript et CSS3, sont à la mode. Elles permettent d'offrir une expérience utilisateur optimale, sans la lenteur des changements de page. Mais pour ça, encore faut-il qu'elles soient développées dans les règles de l'art.

Cet article vise à montrer quels sont les défis que l'on rencontre lors du développement de telles applications et présente comment, chez af83, nous y répondons.

La façon naïve de développer une single-page webapp

Un développeur expérimenté dans les sites web mais qui se lance pour la première fois dans une single page webapp sera tenté de faire comme à son habitude :

  • utiliser jQuery
  • sélectionner des plugins jQuery pour chaque fonctionnalité
  • vérifier rapidement que ces plugins sont d'une qualité suffisante
  • écrire un peu de code pour faire la glue entre tout ça.

Et, au début, ça ne va pas trop mal marcher. Oh, bien sûr, il y aura quelques bugs par-ci par-là, mais rien de bien méchant. Comme pour les sites web, certains plugins vont se marcher sur les pieds et il faudra corriger ça. Mais, pour notre développeur, cela reste du domaine connu.

Pourtant, au fur et à mesure qu'il va continuer à avancer, cette situation va vite se dégrader. Notre développeur va rencontrer de plus en plus de bugs. Et il passera plus de temps sur la correction de bugs, il avancera moins vite sur les fonctionnalités.

Puis, il va arriver à un point où il va devoir commencer à réécrire certains plugins jQuery pour réussir à corriger les bugs qu'il rencontre et continuer à avancer. Comme si ce n'était pas assez compliqué, notre développeur se rend également compte que les performances de son application se dégradent rapidement : ça devient moins fluide, le temps de chargement initial s'allonge. Mais comme il n'a pas le temps d'optimiser ça, il cache la misère en rajoutant des loaders.

Et le pire finit par arriver. Notre développeur n'arrive plus à s'en sortir dans son code : il ne comprend plus comment ça marche. Chaque modification ou nouvelle fonctionnalité entraîne des tas de nouveaux bugs. Même la correction d'un bug fait apparaître de nouveaux bugs.

Malgré tous ces efforts, l'application est lente et buggée et les utilisateurs ne sont donc pas au rendez-vous. Si vous en arrivez à ce point, il ne vous reste plus qu'une solution : vous accrocher à l'espoir d'une V2.

Mais quelles sont les clés pour comprendre cet échec ? Nous pouvons isoler 3 grandes limitations de cette approche :

  1. Le développement d'une telle application doit être maîtrisé et structuré, or les plugins jQuery rendent très difficiles la mise en place d'une telle structure.
  2. Il est crucial de prendre en compte les performances, et tout particulièrement dans le contexte de pages qui restent longtemps ouvertes.
  3. L'absence d'outils fait perdre beaucoup de temps au final.

Voyons plus en détails ces 3 défis.

Structure et maîtrise

Comme pour toute application, le développement sur le long-terme se passe d'autant mieux quand il est maîtrisé, c'est-à-dire quand les développeurs arrivent facilement à comprendre quels seront les effets des modifications qu'ils apportent mais également quand il est facile pour un développeur externe de rentrer sur le projet.

L'agglomération de plugins jQuery est problématique de ce point de vue, car, ils se comportent comme des boîtes noires mais surtout leurs interfaces sont très limitées. Le code qui fait la glue entre ces éléments se retrouve alors très fragile car il oblige à avoir un couplage fort entre les plugins.

Pourtant, il n'est pas si difficile de structurer le code d'une application JavaScript. Les principes sont d'ailleurs très similaires à ceux des applications web classique ou desktop :

  • Utilisation d'un framework MVC (chez af83, nous avons une préférence pour Backbone mais AngularJS ou Ember sont de très bonnes factures également)
  • Respect de conventions de codage (jshint)
  • Séparation du code en plusieurs fichiers avec une classe par fichier (nous utilisons les classes fournies par CoffeeScript)
  • Emploi de templates pour le code HTML généré (avec handlebars ou jade par exemple)
  • Utilisation d'événements/signaux au couplage fort entre composants (avec Backbone, cela vient naturellement via le module Events).

Performances et optimisations

Le deuxième défi des single page webapps est le coté performances. Cela couvre beaucoup d'aspects : temps de chargement, fluidité des animations, réactivité, endurance, etc.

J'aurais beaucoup à dire sur chacun de ces points mais globalement l'approche est toujours la même. On commence par se fixer des objectifs, idéalement de manière formelle mais, dans la pratique, c'est souvent un ressenti assez subjectif qui sera le déclencheur. Si un de ces objectifs n'est pas atteint, on va s'appliquer à améliorer ça. Mais avant tout changement, il convient de mesurer pour détecter où le bât blesse. Les optimisations en aveugle sont souvent inefficaces et on fait perdre beaucoup de temps. Donc, surtout, avant de commencer à optimiser un point, il faut être confiant que ce point entre bien en compte dans notre objectif. Enfin, il ne reste plus qu'à optimiser et vérifier que l'on s'est bien rapproché de notre objectif.

Je pourrais entrer plus dans les détails mais cette méthode est très classique et je ne pense pas que cela apporterait grand chose.

Par contre, je souhaite parler d'un point un peu particulier qui est souvent négligé : ces webapps sont souvent utilisées pendant une longue période de temps sans rechargement et cela a un fort impact sur les performances.

En effet, le nombre d'objets JavaScript, de nœuds dans le DOM, de listeners d'événements, etc. a tendance à grossir et à consommer beaucoup de mémoire, au point de rendre la webapp très lente.

Par exemple, Google a montré que Gmail pouvait facilement prendre plusieurs Go de mémoire vive et a expliqué les optimisations qu'ils ont mises en place pour réduire ce gouffre en termes de performances.

Pour les objets JavaScript, les Garbage collectors des navigateurs ont fait de grand progrès ces derniers temps. En respectant quelques principes, comme éviter de créer des objets inutiles dans des boucles, il y a peu de surprises.

Plus difficile, la gestion des nœuds du DOM et des listeners associés a un impact plus conséquent et est plus difficile à maîtriser. Il convient de faire particulièrement attention à ne pas leaker des fragments de DOM. Pour des pages web que l'on recharge régulièrement, le problème passe inaperçu mais sur une webapp qui reste ouverte pendant quelques dizaines minutes, cela peut ralentir très fortement les navigateurs.

Jusqu'à récemment, le « ménage » de ces éléments avec Backbone devait se faire manuellement. Heureusement, un grand travail a été effectué ces derniers mois et la version 0.9.9 permet d'en récolter les fruits. La gestion de la mémoire s'en trouve grandement simplifiée si l'on respecte les best practices de Backbone. Il reste des cas où l'expertise d'un dév front confirmé reste nécessaire, mais ces cas restent très rares.

Sans outils, point de salut

J'ai déjà cité pas mal d'outils ci-dessus : Backbone, CoffeeScript, Handlebars, etc. Mais, je vais insister sur ce point.

Pour construire un site web avec quelques effets JavaScript, on pouvait facilement s'en sortir en faisant tout manuellement ou avec quelques scripts maison. Mais, comme pour les applications web coté serveur, c'est l'utilisation de frameworks, outils et bibliothèques qui a permis de passer à la vitesse supérieure. Grâce à eux, nous avons pu gagner fortement en vélocité et nous attaquer à des projets bien plus complexes.

Le même cheminement est valable pour les applications coté client. Cela peut faire peur aux nouveaux venus dans ce domaine car la barrière à l'entrée est plus importante. Mais c'est un fonctionnement indispensable pour réussir des projets plus conséquents.

Un de ces outils est grunt. C'est une sorte de Makefile pour le front dev. Il vient avec de nombreuses extensions pour compiler des fichiers CoffeeScript ou Sass, concaténer des fichiers, réduire le poids des fichiers générés, lancer des tests, etc. Sans lui, il faudrait faire toutes ces tâches manuellement (ou avec des scripts maison), ce qui serait une grosse perte de temps et peut conduire à introduire des bugs dans ce processus.

Autre exemple, Bower. C'est un outil qui permet d'installer facilement des bibliothèques CSS et JS et d'en gérer les mises à jour. Pour un site web, on peut facilement s'en passer : on va sur le site web de chaque bibliothèque, on regarde ce qu'elle fait et si ça nous convient, on télécharge le code et on le met quelque part.

Pour une application, c'est plus problématique. D'une part, le nombre de bibliothèques utilisées est plus important. D'autre part, ces bibliothèques sont plus complexes et ont souvent des dépendances. On se retrouve alors à devoir jongler entre les versions pour trouver un ensemble cohérent.

Puis, vient le problème des mises à jour. Quand on veut mettre à jour une bibliothèque, il faut retrouver où on l'avait téléchargée, quelles sont ses dépendances, etc. Ce n'est pas forcément très compliqué mais on peut vite y perdre pas mal de temps.

Avec bower, la liste des bibliothèques est déclarée dans un fichier component.json, avec la version utilisée. En une ligne de commande, on peut mettre à jour une bibliothèque ou en installer une nouvelle.

Bien entendu, je pourrais continuer à lister d'autres outils (intégration continue, scripts de déploiement, couverture de code…) mais je pense que vous avez compris l'idée !

En deux mots

Le développement front a souvent été considéré comme étant plus facile que le développement back. Je pense que cette époque est révolue et que les single page webapps sont au moins aussi complexes que les applications plus classiques. Pour les réussir, il convient donc de changer d'état d'esprit et réussir à transposer les clés de la réussite des applications web classiques :

  • Utiliser un framework pour structurer son code
  • Écrire des tests
  • Sélectionner un ensemble efficace d'outils
  • Se fixer des objectifs de performances, les mesurer et optimiser lorsque c'est nécessaire
  • S'appuyer sur des développeurs expérimentés pour poser le cœur de l'application.

blog comments powered by Disqus