Chez Hopwork, pour avoir une expérience utilisateur décente, nous servons nos pages de manière à ce qu’elles soient complètement rendues dès leur chargement par le navigateur, puis nous les améliorons. Cela a amené quelques défis présentés ici.

Qu’est-ce que le progressive enhancement ?

Comme tous les services en ligne, sur hopwork.fr nous avons des pages de contenu purement informatif, statiques du point de vue de nos utilisateurs (par exemple notre FAQ). Et nous avons aussi des pages avec lesquelles les utilisateurs sont invités à interagir. Par exemple, ils peuvent laisser des recommandations directement sur la page du profil d’un freelance. Ou encore le-dit profil peut-être modifié “en place” par son propriétaire.

Édition de profil sur Hopwork
Les zones éditables d’un profil sur Hopwork

Le cas de l’édition du profil, ou de la création d’un devis sont des exemples de parties de notre site plus applicatives qu’informatives. Détaillons quelques techniques pour développer de telles zones.

Technique n°1 : Chaque action est traitée côté serveur et retourne une nouvelle page à rendre. On enchaîne les formulaires et les rechargements de pages. Bienvenue à la fin du XXe siècle !

Technique n°2 : La page est entièrement construite côté client, en JavaScript, et les actions sont communiquées au serveur par appels Ajax (via des API HTTP, typiquement pseudo-RESTful). L’expérience est plus fluide, avec un inconvénient toutefois : il faut attendre que toutes les ressources de la page soient chargées pour que l’application s’initialise et que quelque chose s’affiche.
Un exemple typique est gmail. Il y a des techniques pour mitiger le problème (service workers + cache API, etc.), mais parfois cela n’est juste pas souhaitable.

Écran de chargement de Gmail
Écran de chargement de Gmail

Technique n°3 : Un entre-deux : rendre une première version de la page côté serveur, prête à consulter, puis l’améliorer côté client pour y inclure les fonctionnalités désirées. On commence alors à parler de progressive enhancement, ou amélioration progressive.
Un bon exemple est Twitter (historiquement Twitter a également utilisé l’approche précédente). Sur Twitter, on veut avant tout voir le dernier contenu, et ensuite peut-être interagir avec ce contenu. Il est donc très pertinent de l’afficher au plus tôt et de l’améliorer ensuite.

Contenu de Twitter chargé, mais aucune interaction possible
Contenu de Twitter chargé, mais aucune interaction possible

Or donc, chez Hopwork nous servons presque toutes les parties de notre site de cette manière.
(Aussi, afin de rester léger pour les navigateurs comme pour les développeurs, nous développons désormais tout en “vanilla JS” : sans framework, avec uniquement quelques libs. Je l’écris pour poser le contexte, mais on ne développera pas ce sujet ici.)

Tout cela est bel et bien, mais l’approche ne vient pas sans inconvénients, et nous en avons rencontré deux principaux, qui vont être le sujet des deux billets suivants.

Défi n°1, la testabilité

Ajouter du comportement via des scripts JS à des vues déjà rendues par le serveur complique l’écriture de tests automatisés pour ces scripts. En effet, la plupart de nos modules va manipuler une partie spécifique du document HTML, et les tests ont donc également besoin de cette partie du document. Comme elle est normalement rendue par le serveur, il faut au choix :

  • Se débrouiller pour lancer ces tests en intégrant toute la stack, en lançant un serveur et en pilotant tout ça avec un browser (PhantomJS, Selenium, etc.). Le but de ces tests étant d’être simples à écrire, rapides, stables et plus généralement de permettre le TDD, ça n’est pas une approche très séduisante. Nous préférons réserver l’emploi de Selenium à des tests end-to-end fonctionnels suivants des parcours utilisateurs réels.
    daria-bored
  • Écrire une vue minimale statique pour les tests. Les scripts testés s’associant généralement à un composant graphique bien précis et de taille raisonnable, cette solution simple est envisageable, et nous l’avons d’ailleurs utilisée un moment faute de mieux, mais très vite les problèmes surviennent : double maintenance du fait de la duplication de la vue et – pire – désynchronisation d’un composant graphique et des scripts associés (il est possible pour les tests de passer alors que le composant ne fonctionne plus “en vrai”, car la vue des tests a été modifiée comme il fallait, mais pas la vraie vue rendue par le serveur).copypaste-copypaste-everywhere
  • Faire en sorte que les tests soient capables de rendre les mêmes vues que le serveur, en se basant sur les mêmes sources. Il est courant et pratique de rendre des vues en utilisant des templates dont les “trous” sont remplis par les données calculées par le serveur. Si le moteur de templating utilisé par le serveur existe en JavaScript, c’est gagné (par exemple, Mustache est disponible pour un grand nombre de langages). Sauf que chez Hopwork, nous faisons du Java et nous utilisons des JSP pour nos templates (oui c’est vieux, mais ça fonctionne).

Notre première solution à ce problème est un contournement utilisant cette dernière technique. Elle est présentée dans le billet : “Tester un composant défini en JSP + JavaScript“. La résolution du défi suivant nous offre également une solution plus classique pour nos nouveaux développements.

Défi n°2, rendu isomorphique

Notre second défi est en quelque sorte une évolution du premier. Les tests nous ont fait sentir un besoin, et très vite le code de production a émis le même besoin : pouvoir rendre une même vue côté serveur et côté client. En effet, pour créer des composants évolués, manipuler de petits morceaux du DOM (un span par-ci, un div par-là) est lourd, et il est bien plus pratique de pouvoir rendre à nouveau côté client des composants entiers de la vue, de la même manière que le serveur les a créés.

Et là, il est définitivement hors de question de dupliquer la logique de rendu. Il faudrait idéalement avoir une stack technique permettant la construction d’applications dites isomorphiques. Une stack basée sur Node.js a cet avantage : avec du JavaScript des deux côtés, on peut facilement partager du code, et notamment les vues.
Mais comme dit plus haut, nous faisons du Java, et cela ne sera donc pas possible. Il reste toutefois possible d’utiliser un même moteur de templating des deux côtés. À condition d’écrire des templates légers, sans logique – ce qui est notre cas – c’est une technique envisageable.
Nous avons donc introduit Handlebars côté client et côté serveur, et commencé à partager des templates.

Handlebars logo

Par contre, pour pouvoir s’y mettre sans tout réécrire d’un coup, nous voulions pouvoir utiliser des composants Handlebars dès le début, même au sein de nos JSP existantes !

La mise en place de notre solution à ce deuxième problème est présentée dans le billet Vues Tout Terrain avec Handlebars.

Conclusion

L’enrichissement progressif d’une page Web construite côté serveur vient avec son lot de challenges, et ne profite pas nécessairement d’outils pour toutes les stacks techniques. Nul doute que notre stack Java/JSP existante et notre décision de ne pas utiliser de framework JS ne nous auront pas facilité la tâche, mais en nous tenant à notre approche nous pensons avoir le meilleur environnement pour nous, avec les technos qui nous rendent les plus productifs côté backend et frontend.

Et puis, à condition de bien choisir ses combats et de ne réinventer que certaines petites roues, créer ses propres outils peut permettre d’atteindre le graal de l’artisan logiciel : juste ce qu’il faut pour le job, simple, et totalement modifiable. Et en plus c’est marrant 🙂

Un outil bizarre
Pas ça donc

Enfin, notons que les solutions que nous avons mises en oeuvre n’ont probablement de sens que pour une petite équipe comme la notre (5 développeurs), avec des sensibilités très back-end, d’autres très front-end, mais surtout avec des touche-à-tout. Il est probable qu’en grossissant nous arrivions à une situation plus classique de services applicatifs consommés par des services dédiés aux vues Web (c’est déjà en partie notre cas), et donc permettant la mise en place d’une stratégie de type “application isomorphique”.


Nous nous sommes concentré ici sur le côté “comportement” (JS) de l’amélioration progressive, mais le côté “style” (CSS) apporte lui aussi ses défis.

Leave a Reply

Your email address will not be published. Required fields are marked *