eZecosystem / Mirror / Damien Pobel

(En plus du flux RSS global, les billets veille et uniquement ceux là sont listés dans le flux RSS correspondant)

01/17/2019 07:41 pm   pwet.fr/blog   Mirror   Link  
  • How To Speed Up The Code Review (en) : un article intéressant si vous souffrez de revues trop lentes/difficiles. Clairement la taille est un facteur clé et essayer de ne pas mélanger refactoring, nouvelle(s) fonctionnalité(s) ou renommage semble plutôt une bonne idée.
  • Better naming convention (en) : des réflexions plutôt intéressantes surtout sur les préfixes/suffixes des classes/interfaces. Je suis plus partagé sur l'abandon de get pour les getters
  • Use React.lazy and Suspense to Code-Split Your App (en) : ou comment React 16.6 permet de faire du chargement à la volée (code splitting) sans bibliothèque externe.
  • Build a PWA in Just 10 Minutes (en) : faut vraiment que j'essaie avec mon site :)
  • Semantic Versioning - why you should care (en) : une bonne explication de ce qu'apporte le semantic versionning et de comment gérer les versions. C'est orienté PHP mais ça s'applique potentiellement à tous les langages.
  • New in PHP 7.4 (en) : PHP 7.3 est encore tout chaud, mais voici ce qui se profile pour la version 7.4 avec des fonctionnalités plutôt sympas (surtout les propriétés typées et la partie Improved type variance)
  • CPU Utilization is Wrong (en) : en fait, la notion d'occupation CPU est plus subtile qu'il n'y paraît ce qui fait que le pourcentage d'occupation CPU peut induire en erreur.
  • A declarative router for service workers (en) : une proposition d'API pour rendre l'écriture de Service Worker plus simple. Ce n'est qu'une proposition, mais le besoin est là il me semble.

(En plus du flux RSS global, les billets veille et uniquement ceux là sont listés dans le flux RSS correspondant)

01/16/2019 12:37 pm   pwet.fr/blog   Mirror   Link  

Et un peu hors-sujet :

(En plus du flux RSS global, les billets veille et uniquement ceux là sont listés dans le flux RSS correspondant)

01/16/2019 12:37 pm   pwet.fr/blog   Mirror   Link  

Comme je l'écrivais dans ma rétrospective 2018, je crois qu'avoir changé de travail m'a fait progresser sur pas mal d'aspects et pas seulement techniques. Je n'ai pas attendu d'être sur le point de terminer ma quatorzième année d'expérience et d'avoir le titre (un peu ronflant) de lead developer pour découvrir que développer un logiciel est un travail d'équipe mais je crois en avoir saisi quelques subtilités qui m'échappaient auparavant.

Deux bras de cuisinier en train
    de complèter des assiettes

C'est quoi une équipe ?

Cette question peut paraître anodine mais elle est en fait bien plus subtile qu'il n'y paraît. Pendant ma veille technologique (pas si technologique que ça pour le coup), j'étais tombé sur The First Question To Ask When Building Teams – Is This Really A Team?. Cet article reste l'une de mes meilleures lectures de 2018. L'auteur y décrit les différents types de groupes et finalement qu'une équipe est un groupe de personnes qui partagent un but et ont besoin des autres pour l'atteindre. Rapporter à mon expérience personnelle, j'ai compris (au moins en partie) pourquoi certaines collaborations n'ont pas donné tous les fruits espérés.

Agilité

Il y a quelques jours je relisais le manifeste pour le développement Agile de logiciels et les 12 principes sous-jacents où on peut notamment lire (le gras est de moi) :

La méthode la plus simple et la plus efficace pour transmettre de l’information à l'équipe de développement et à l’intérieur de celle-ci est le dialogue en face à face.

[…]

Les meilleures architectures, spécifications et conceptions émergent d'équipes autoorganisées.

À intervalles réguliers, l'équipe réfléchit aux moyens de devenir plus efficace, puis règle et modifie son comportement en conséquence.

Le concept d'équipe est au cœur de l'agilité et même mieux, l'agilité met l'équipe au centre en la reconnaissant comme une entité à part entière (en anglais on dirait a first class citizen). L'équipe s'auto-organise, réfléchit, modifie son comportement, s'améliore et développe un logiciel pour résoudre un problème… Je ne vois ça pas seulement comme un élément d'une méthode agile ou d'une autre mais comme un vrai état d'esprit à acquérir et à cultiver.

Travailler en équipe

En effet, il ne suffit pas de mettre des personnes sur un projet pour travailler efficacement en équipe. À vrai dire, en fonction des personnes, travailler efficacement en équipe n'est pas forcément simple et c'est rarement une évidence. J'écrivais il y a quelques temps que je regrettais ne pas avoir au moins été initié à cette pratique à l'école, je le pense d'autant plus maintenant.

Je n'ai pas de recette magique pour bien travailler en équipe, mais j'ai pu remarquer qu'une bonne dose de capacité à communiquer (clairement), pas mal d'humilité, un peu d'empathie et une pointe de discipline aident beaucoup. En fait, toutes ces qualités (ce qu'on appelle parfois des soft skills) se traduisent par une large gamme de comportements qui font partie du développement en équipe :

  • accepter les remarques par exemple dans une revue de code et les discussions qui en découlent
  • faire l'effort d'écouter les autres membres
  • arriver à l'heure aux réunions
  • définir une procédure collectivement pour le bien de tous et s'astreindre à la suivre
  • savoir donner un coup de main sans infliger de l'aide
  • faire confiance aux autres membres
  • et se parler tout simplement ;-)

Avantages à travailler en équipe

Lorsqu'on parle de travail en équipe, j'aime beaucoup ce proverbe africain :

Tout seul on va plus vite, ensemble on va plus loin

À elle seule, cette petite phrase résume plutôt bien l'intérêt à travailler en équipe. Au delà des aspects purement humains, si on rentre un peu dans le détail du développement logiciel et qu'on parle d'un logiciel destiné à durer, le travail en équipe apporte ou améliore au moins deux aspects essentiels :

  1. un meilleur partage des connaissances
  2. un résultat de meilleure qualité

Une bonne répartition des connaissances permet notamment d'augmenter le bus factor. Sans même parler de catastrophe, cette répartition rend l'équipe plus résiliente à toute sorte de changements ou d'imprévus et facilitera d'autant les évolutions et la maintenance. En quelque sorte, travailler en équipe est un investissement pour l'avenir.

Le travail en équipe favorise la qualité du résultat (au sens général du terme, pas seulement la qualité logiciel) car il est probable que les problèmes à résoudre ne soient pas triviaux et dans ce cas là, plusieurs cerveaux et plusieurs points de vue amènent à les envisager sous différents angles là où un cerveau unique aura tendance à s'arrêter à la première solution. Au passage, cet effet positif sera d'autant plus amplifié que l'équipe sera composée de personnes diverses (à tout point de vue).

Performance d'une équipe

L'évaluation des performances est déjà une matière difficile lorsqu'on parle d'une seule personne, alors pour une équipe…

Plutôt que de vouloir mesurer à tout prix la performance, il est peut-être plus simple de se poser uniquement la question de comment l'améliorer. À ce niveau, que l'on parle de vitesse de réalisation, de qualité, de quantité de bugs ou de n'importe quelle autre métrique, j'ai souvent remarqué que la performance d'une équipe était en bonne partie calquée sur celle de ces membres les moins… performants. En d'autre terme, pour progresser collectivement il faut s'intéresser en premier lieu à ces membres pour les faire progresser individuellement. Cette progression individuelle aura des effets positifs multiples qui amélioreront d'autant les performances de l'équipe dans son ensemble. Ensuite, comme tout problème non trivial, l'équipe peut se prendre en main, et même si elle peut parfois manquer de recul sur elle-même, là encore l'intelligence collective d'une équipe auto-organisée et la liberté d'une équipe autonome permet d'envisager différentes solutions.


Après presque 1000 mots, il me reste à conclure en disant que travailler en équipe pour développer un logiciel, c'est non seulement quasi inévitable mais en plus c'est à la fois enrichissant sur le plan humain et potentiellement très efficient.

01/16/2019 12:37 pm   pwet.fr/blog   Mirror   Link  

Et un peu hors-sujet :

(En plus du flux RSS global, les billets veille et uniquement ceux là sont listés dans le flux RSS correspondant)

12/27/2018 05:35 am   pwet.fr/blog   Mirror   Link  

Et un peu hors-sujet :

(En plus du flux RSS global, les billets veille et uniquement ceux là sont listés dans le flux RSS correspondant)

12/20/2018 06:07 am   pwet.fr/blog   Mirror   Link  

Et un peu hors-sujet :

(En plus du flux RSS global, les billets veille et uniquement ceux là sont listés dans le flux RSS correspondant)

12/13/2018 05:41 am   pwet.fr/blog   Mirror   Link  

Et un peu hors-sujet :

(En plus du flux RSS global, les billets veille et uniquement ceux là sont listés dans le flux RSS correspondant)

12/06/2018 06:15 am   pwet.fr/blog   Mirror   Link  

Et un peu hors-sujet :

(En plus du flux RSS global, les billets veille et uniquement ceux là sont listés dans le flux RSS correspondant)

11/29/2018 06:59 am   pwet.fr/blog   Mirror   Link  

Et un peu hors-sujet :

(En plus du flux RSS global, les billets veille et uniquement ceux là sont listés dans le flux RSS correspondant)

11/22/2018 07:04 am   pwet.fr/blog   Mirror   Link  

Ce texte est une traduction de l'excellent Less Snake Oil, More Context par Surma.

Obtenir de bonnes performances sur le web est un défi permanent. Les développeur·ses essaient et essaieront encore d'en repousser les limites et c'est une bonne chose. Je ne veux pas changer cela.

Je veux changer comment nous — en tant que communauté — approchons, analysons et comprenons les problèmes de performances. Je vois souvent des questions du type « Quel est la meilleure manière de faire X ? », « Quel bibliothèque est la plus rapide pour réaliser Y ? ». Il semble que nous aimions les superlatifs mais lorsqu'il s'agit de performance, ils peuvent être contre-productifs.

Appliquer généreusement la poudre de perlimpinpin aux zones concernées

ou autrement dit « Des règles pas des outils ». Alex Russell a utilisé « Poudre de perlimpinpin » dans un tweet (NDT: Alex Russel étant anglophone, il a utilisé Snake oil) et je pense que cette expression transmet parfaitement à la fois l'opacité et le manque de fiabilité de ce type de traitement.

Quelques exemples :

  • Une animation est saccadée. Utilisez will-change: transform sur l'élément animé.
  • N'utilisez pas forEach(), les boucles for sont plus rapides.
  • Pour une chargement plus rapide, groupez les ressources.
  • N'utilisez pas le sélecteur * car il est lent.

Tout ceci est vrai dans un contexte particulier. Il faut bien comprendre une chose : la lenteur ou une animation saccadée n'est qu'un symptôme, pas une maladie. Ce qui est donc nécessaire ici, c'est une procédure de diagnostique différentiel. La fluidité d'une animation peut être gâchée pour de nombreuses raisons mais il est probable qu'une soit réellement en cause. Par exemple, si l'effet saccadé est causé par le ramasse miette traitant de gros morceaux de données à chaque frame, will-change: transform n'aura aucun effet positif. Au contraire, cette déclaration augmentera la pression sur la mémoire et pourrait même empirer le phénomène.

Je ne me souviens pas qui a énoncé « Si vous ne l'avez pas mesuré, ce n'est pas lent » mais cette phrase résonne en moi même si se concentrer sur la mesure peut mener à la Frénésie du Microbenchmark™️.

Note : pour le reste de ce billet, je vais parler d'optimisations en terme de vitesse mais tout ceci s'applique à d'autres types d'optimisations comme la réduction de l'empreinte mémoire.

Microbenchmarks

J'ai noté que récemment une grande attention était portée vers les microbenchmarks. Dans un microbenchmark, on essaie de départager deux approches en les exécutant plusieurs milliers de fois en isolation pour déterminer quelle solution est la plus rapide.

Comprenez-moi bien, les microbenchmarks ont une utilité, j'en ai même écrit et comme beaucoup d'autres avant moi. Ce sont des outils intéressants en particulier avec des frameworks comme BenchmarkJS qui permet d'obtenir des nombres statistiquement signifiants. En revanche, les frameworks de benchmark ne sont d'aucune aide pour s'assurer que votre benchmark a réellement un sens. Si vous ne connaissez pas ce que vous êtes en train de tester, les résultats peuvent mener à une mauvaise interprétation. Par exemple, dans mon billet sur le deep-cloning, je vérifiais les performances de const copyOfX = JSON.parse(JSON.stringify(x)). Il s'avère que V8 possède un cache d'objets. Le fait de réutiliser la même valeur x fois dans les tests a faussé les résultats. En réalité, je testais le cache plus qu'autre chose. Et si Mathias n'avait pas lu mon article, je ne l'aurais jamais découvert.

Compromis

Imaginons que vous ayez écrit ou trouvé un microbenchmark avec un sens. Il montre que vous devriez plutôt utiliser l'approche A au lieu de l'approche B. Il est important de comprendre que passer de B à A ne va pas uniquement rendre le code plus rapide. Quasiment toutes les optimisations de performance sont un compromis entre la vitesse et autre chose. Dans la plupart des cas, vous abandonnez un peu de lisibilité, d'expressivité et/ou d'idiomatisme. Ces propriétés ne se verront pas dans vos mesures pour autant il ne faut pas les ignorer. Le code devrait être écrit pour les humain·es (ce qui inclut le/la futur·e vous) mais pas l'ordinateur.

C'est là où les microbenchmarks nous abusent. Être capable de réaliser une opération plus rapidement ne signifie pas que le compromis en terme d'expressivité soit valable. En se basant sur des résultats de microbenchmarks, certaines personnes prendront pour évident que A est mieux que B et donc que vous devriez toujours mettre en œuvre A. C'est ainsi que la poudre de perlimpinpin est faite. Une partie du problème vient du fait qu'il est difficile de quantifier l'expressivité. À quel point un bout de code doit-il être plus rapide pour justifier une perte de lisibilité ? 10% ? 20% ?

Un point à propos de l'analyse statique et des transpilers s'impose. Il est possible d'écrire du code lisible et idiomatique tout en délivrant une version moins lisible et plus performante en production. Des outils comme @babel/present-env permettent d'écrire du JavaScript moderne et idiomatique sans avoir à se soucier de la prise en charge par les navigateurs et des implications en terme de performance. Le compromis ici se fait sur la taille et l'impénétrabilité du code généré. Certaines fonctionnalités ne peuvent être transformées qu'avec une importante augmentation de la taille du code ce qui détériore les temps de téléchargement et de compilation. La transformation des générateurs est un exemple extrême de ce phénomène. Un exécuteur de générateur doit être ajouté tout en rendant les fonctions génératrices significativement plus lourdes. Une fois encore, ce n'est pas une raison pour ne pas utiliser les générateurs ou pour ne pas les transformer. En revanche, c'est une information importante pour prendre une décision. Il s'agit encore et toujours de faire des compromis.

Exemple de transformation d'une fonction génératrice par Babel

Budgets

Dans ce domaine, les budgets peuvent aider. Il est important de budgétiser différents aspects de votre projet. Pour les applications web, les préconisations RAIL constitue un choix populaire. Si vous souhaitez construire une application tournant à 60 images par seconde, vous avez 16ms par frame. Pour produire une interface qui paraît fluide, il faut répondre visuellement aux action des utilisateur·rices en moins de 100ms. À partir du moment où vous avez des budgets, vous pouvez profiler votre application et vérifier si vous restez dans les limites fixées. Et si ce n'est pas le cas, vous savez par où commencer les travaux d'optimisation.

Les budgets contextualisent les coûts. Imaginons que vous ayez un bouton dans votre interface qui lorsqu'il est utilisé entraîne la récupération avec fetch() et l'affichage à l'écran des dernières données liées aux stocks. Avec l'appel réseau, le traitement des données et le rendu, le délai entre le clic de l'utilsateur·rice et l'affichage est de 60ms. Nous somme parfaitement dans les préconisations RAIL abordées plus haut avec même une marge de 40ms ! Si vous considérez l'utilisation d'un worker pour le traitement des données, la communication entre les fils d'exécution impliquera un délai supplémentaire. Par expérience, ce délai est de l'ordre d'une frame (16ms) ce qui donne un total de 76ms.

Si vous aviez à prendre une décision avec un état d'esprit microbenchmark — en regardant uniquement les nombres sans contexte — la solution à base de workers vous paraîtra une mauvaise idée. Cependant, la vraie question n'est pas « Quelle est la solution la plus rapide ? » mais plutôt « Quel compromis puis-je faire ? » ou encore « Mon budget me permet-il de le faire ? » Dans l'exemple du worker, nous payons 16ms mais cette dépense rentre facilement dans les 40ms de marge par rapport à notre budget RAIL. Ce que nous obtenons en retour dépend de votre perspective; dans cet exemple je souhaite me concentrer sur la robustesse. Si le serveur envoie une énorme structure JSON liée aux stocks, le décodage prendra un temps considérable pendant lequel le main thread sera bloqué. En décodant et traitant les données dans un worker, le main thread sera épargné et l'usage de l'application restera fluide.

Capture d'écran du benchmark six-speed

Prenons un autre exemple : jusqu'à il y a un an environ, utiliser une boucle for of pour parcourir un tableau était 17 fois plus lent qu'une boucle for classique (Note : six-speed a été mis en place en avril 2017. Depuis, de nombreux changements ont été apportés à V8 et Babel). À cause de ces résultats, certaines personnes évitent toujours les boucles for of.

Penchons nous sur des chiffres concrets : en parcourant un tableau de 100 éléments dans Chrome 55 (sorti en décembre 2016, avant le lancement de six-speed) avec un boucle for of puis un boucle for classique, j'obtiens :

  • boucle for of : 134µs
  • boucle for classique : 65µs

Sans conteste, la boucle for classique est plus rapide (dans Chrome 55) mais la boucle for of donne une vérification implicite des limites et rend le corps de la boucle plus lisibe en évitant l'utilisation d'un index. Y'a t il un intérêt à gagner ~60µs ? ça dépend mais la plupart du temps la réponse est non. Si vous utilisez des boucles for of dans un chemin critique (comme du code qui construit chaque frame dans une application WebGL), c'est peut-être le cas. Cependant, si vous ne parcourez que quelques dizaines d'éléments lorsque l'utilisateur·rice clique sur un bouton, je ne m'embêterais même pas à penser aux performances. Je choisis toujours la lisibilité. Et pour information, dans Chrome 70, les deux types de boucle ont exactement les mêmes performances. Un grand merci à l'équipe travaillant sur V8 !

Capture d'écran d'un benchmark de boucles for

Ça dépend (du contexte)

Bref, il n'existe aucune optimisation de performance qui soit toujours bonne. En fait, il n'y a pratiquement aucune optimisation de performance qui soit généralement bonne. Les pré-requis techniques, les audiences, les appareils et les priorités sont trop différentes d'un contexte à un autre. Ça dépend. Si vous voulez mon conseil, voici comme j'essaie d'aborder les optimisations :

  1. Définir un budget
  2. Mesurer
  3. Optimiser les parties qui explosent le budget
  4. Prendre une décision en tenant compte du contexte
11/12/2018 01:25 am   pwet.fr/blog   Mirror   Link  

Et un peu hors-sujet :

(En plus du flux RSS global, les billets veille et uniquement ceux là sont listés dans le flux RSS correspondant)

11/01/2018 07:46 am   pwet.fr/blog   Mirror   Link  

Et un peu hors-sujet :

(En plus du flux RSS global, les billets veille et uniquement ceux là sont listés dans le flux RSS correspondant)

10/04/2018 05:25 am   pwet.fr/blog   Mirror   Link  

Et un peu hors-sujet :

  • How to Build a Low-tech Website? (en) : j'adore la démarche, j'ai déjà le site statique (très) optimisé et plutôt low-tech et il me manque "juste" le serveur, le panneau solaire et la batterie pour faire pareil :-)

(En plus du flux RSS global, les billets veille et uniquement ceux là sont listés dans le flux RSS correspondant)

09/27/2018 08:15 am   pwet.fr/blog   Mirror   Link  

Et un peu hors-sujet :

(En plus du flux RSS global, les billets veille et uniquement ceux là sont listés dans le flux RSS correspondant)

09/20/2018 07:07 am   pwet.fr/blog   Mirror   Link  

(En plus du flux RSS global, les billets veille et uniquement ceux là sont listés dans le flux RSS correspondant)

09/13/2018 05:42 am   pwet.fr/blog   Mirror   Link  

C'est la rentrée alors c'est reparti pour la veille hebdomadaire !

Et un peu hors-sujet :

(En plus du flux RSS global, les billets veille et uniquement ceux là sont listés dans le flux RSS correspondant)

09/06/2018 06:55 am   pwet.fr/blog   Mirror   Link  
  • PHP: Never type hint on arrays (en) : array ne donne quasi aucune information et d'une manière générale (il y a toujours des exceptions…), un type hint sur un type précis spécifique au projet est mieux que l'utilisation des types primitifs du langage.
  • The First Thing That Ever Sold Online Was Pizza (en) : ahah :) et le plus génial, 24 ans plus tard, le formulaire marche probablement toujours (malheureusement le lien vers ce bout d'histoire d'Internet redirige sur le site de PizzaHut…)
  • Logging Activity With The Web Beacon API (en) : Une API web plutôt méconnue qui peut rendre service pour envoyer des messages de manière non urgente à un serveur.
  • Art of debugging with Chrome DevTool (en) : des astuces plutôt intéressantes et utiles, notamment le console.log en point d'arrêt conditionnel ou le console.log({ foo, bar }) au lieu de console.log(foo, bar) qui permet de logger à la fois la valeur et son nom. D'ailleurs, ces 2 là doivent fonctionner aussi dans Firefox.
  • How to Read an RFC (en) : quelques clarifications sur comment aborder les RFC, ces documents qui standardisent les protocoles sur Internet.
  • How to add product features without making it more complex (en) : une clé exposée dans cette article est de construire des interfaces ou des fonctionnalités utilisables par défaut.
  • The Cost Of JavaScript In 2018 (en) : Let’s design for a more resilient mobile web that doesn’t rely as heavily on large JavaScript payloads.

Et un peu hors-sujet :

(En plus du flux RSS global, les billets veille et uniquement ceux là sont listés dans le flux RSS veille)

08/02/2018 06:35 am   pwet.fr/blog   Mirror   Link  

(En plus du flux RSS global, les billets veille et uniquement ceux là sont listés dans le flux RSS veille)

07/26/2018 06:25 am   pwet.fr/blog   Mirror   Link  
Un chien sous une couverture

Le code coverage ou la couverture de code par les tests en bon français est probablement l'une des métriques les plus incomprise et l'un des concepts les plus maltraité lorsqu'on parle de tests logiciels et de qualité de code en général. C'est d'ailleurs un bon sujet de conversation pour animer un open space rempli de développeur·ses un tant soit peu intéressé·es par les tests. Mais au fait qu'est-ce que c'est au juste ?

D'après Wikipédia:

En génie logiciel, la couverture de code est une mesure utilisée pour décrire le taux de code source exécuté d'un programme quand une suite de test est lancée.

Si on met de côté le fait qu'en fonction des outils, des configurations ou de qui extrait cette valeur, ce taux est un pourcentage d'instructions, de branches, de fonctions ou de lignes, il n'y a rien de très compliqué. Mais généralement, les choses se corsent dès lors qu'il s'agit d'interpréter ce nombre. Doit on absolument viser 100% ? Je plafonne à 40%, est-ce grave ? À partir de quel pourcentage mon code est il bien testé ? Pourquoi je ne parviens pas à couvrir les 2 cas qui me permettrait d'arriver à 100% ? Et bien d'autres… À ces questions relativement légitimes s'ajoute le fait que ce sujet a le pouvoir de polariser bon nombre de personnes sur des positions extrêmes avec d'un côté les partisans du il faut atteindre 100% de couverture pour bien tester et de l'autre les défenseurs du le taux couverture des tests ne sert à rien.

Assez paradoxalement, chaque camp n'a pas totalement tort. Ce paradoxe tient au fait que le taux couverture ne mesure que l'exécution d'une portion de code, ou en d'autres termes, un bout de code peut être couvert mais mal voire pas du tout testé. Par exemple, si dans un test vous appelez une fonction sans jamais vérifier directement ou indirectement sa valeur de retour, elle fera partie du code couvert mais on ne peut pas considérer qu'elle soit bien testée. À partir de là, on comprend bien la faiblesse de cette métrique. Elle est d'ailleurs d'autant plus faible qu'il est possible de gonfler artificiellement le taux de couverture. En effet, la plupart (tous ?) des outils permettent d'ignorer des morceaux de code soit grâce à des annotations dans le code, soit en se basant sur le nom des fichiers. Hormis lorsqu'il s'agit d'exclure des dépendances, j'ai toujours trouvé étrange l'utilisation de cette fonctionnalité qui transforme une métrique déjà faible en une métrique faible et fausse.

Malgré tout, le taux de couverture peut être un outil intéressant. Si sa valeur absolue à un instant t reste relativement anecdotique, mesurer son évolution dans le temps est un indicateur déjà plus pertinent. En fonction de l'historique du projet et si l'équipe a décidé de diriger des efforts sur les tests (et d'écrire de bons tests), le code coverage devrait soit rester relativement stable, soit augmenter dans le temps mais sauf exception, il ne devrait pas baisser significativement. Une autre manière de voir ce phénomène est de considérer que, passée la phase de prototypage où généralement on écrit peu de tests, toute modification du projet devrait venir avec des tests qui couvrent au maximum ces changements. Au fur et à mesure des évolutions et par refactorings successifs, le taux de couverture va donc mécaniquement et progressivement augmenter. Dans cet exercice, le rapport de couverture constitue un outil précieux en permettant de naviguer dans le code source tout en différenciant les parties couvertes du code qui ne l'est pas. D'ailleurs, viser un maximum de couverture pour chaque (petit) changement a aussi la vertu de pousser vers une certaine simplification du code pour éliminer du code inutile et donc difficilement testable.


Si on en fait pas une religion, le code coverage peut être un outil intéressant. Mais plutôt que de le considérer sur l'ensemble du projet, il me semble nettement plus pertinent de le prendre en compte sur de petites modifications où il est plus simple de vérifier que la couverture provient de tests de qualité. À noter qu'il est possible de tester l'efficacité des tests et la réalité du taux de couverture avec des tests de mutations (mutation testing) mais ce sera pour un autre billet.

07/23/2018 03:45 pm   pwet.fr/blog   Mirror   Link  
  • Objects should be constructed in one go (en) : réflexions intéressantes sur l’instanciation des objets et surtout sur comment garantir la cohérence de ceux-ci à tout moment dans une application.
  • Programming Sucks (en) : bon peut-être pas à ce point là mais on s'en approche parfois :-)
  • Defining Component APIs in React (en) : des bons conseils et en y regardant de plus près, en changeant quelques mots, ce sont pour la plupart des déclinaisons façon React de conseils de programmation qui s'appliquent partout ou presque.
  • What's So Great About OOP? (en) : Remember, OOP is about more than just a single object here or there. The benefits of OOP emerge in a community of objects, when objects begin collaborating with each other.
  • When 7 KB Equals 7 MB (en) : quelques subtilités dans la gestion du cache de ressources venant de CDN dans une Progressive Web App
  • Automating Accessibility and Performance Testing with Puppeteer and AxeCore (en) : une chouette utilisation de puppeteer pour tester sur une plateforme d'intégration continue à la fois l'accessibilité et les performances d'une application. Pour ce dernier cas, Chrome est configuré avec puppeteer pour simuler un CPU et/ou un connexion lente.
  • I Want Scalar Objects in PHP (en) : MOI AUSSI :)

Et un peu hors-sujet :

(En plus du flux RSS global, les billets veille et uniquement ceux là sont listés dans le flux RSS veille)

07/19/2018 06:45 am   pwet.fr/blog   Mirror   Link  

Et un peu hors-sujet :

(En plus du flux RSS global, les billets veille et uniquement ceux là sont listés dans le flux RSS veille)

07/12/2018 06:26 am   pwet.fr/blog   Mirror   Link  

Et un peu hors-sujet :

(En plus du flux RSS global, les billets veille et uniquement ceux là sont listés dans le flux RSS veille)

07/05/2018 07:31 am   pwet.fr/blog   Mirror   Link  
  • I discovered a browser bug (en) : L'histoire de la découverte d'un bug de sécurité dans les navigateurs…
  • React Native at Airbnb (en) : Une série d'article expliquant pourquoi Airbnb abandonne React Native pour ses applications mobiles. Plutôt instructif même si il semble que l'abandon ne soit pas uniquement une question technique.
  • Pixels vs. Ems: Users DO Change Font Size (en) : il semble que oui certains utilisateurs changent la taille de police par défaut des navigateurs. Un argument de poids pour utiliser des unités relatives.
  • Le danger de l’élégance (fr) : et même le piège de l'élégance, comme souvent le piège se refermera quand il faudra maintenir ce code
  • Integers and Estimates (en) : de la difficulté l'impossibilité d'estimer :-)
  • Introducing the Single Element Pattern (en) : des règles et des suggestions intéressantes (et un outil pour vérifier leur application) à suivre lors de l'écriture de composant notamment React.
  • Anti-If: The missing patterns (en) : Quelques patterns pour se débarrasser des if. Tous ne sont pas à supprimer mais au delà de ça, ces conseils sont plutôt bons (surtout les 3 premiers)
  • DevTube (en) : Des vidéos pour développeur·se, encore des vidéos de pour dév…

(En plus du flux RSS global, les billets veille et uniquement ceux là sont listés dans le flux RSS veille)

06/28/2018 06:00 am   pwet.fr/blog   Mirror   Link  

Et un peu hors-sujet :

(En plus du flux RSS global, les billets veille et uniquement ceux là sont listés dans le flux RSS veille)

04/05/2018 05:12 am   pwet.fr/blog   Mirror   Link   @8

Et un peu hors-sujet :

(En plus du flux RSS global, les billets veille et uniquement ceux là sont listés dans le flux RSS veille)

03/29/2018 05:12 am   pwet.fr/blog   Mirror   Link   @12

Et un peu hors-sujet :

(En plus du flux RSS global, les billets veille et uniquement ceux là sont listés dans le flux RSS veille)

03/22/2018 06:12 am   pwet.fr/blog   Mirror   Link   @8
  • En finir avec les bugs (fr) : non pas de recette magique dans ce billet, plutôt un plaidoyer pour regarder la vérité en face :)
  • Being in control of time in PHP (en) : Une manière élégante de rendre explicite et testable du code qui utilise une date.
  • The Practical Test Pyramid (en) : Une approche pratique et relativement pragmatique des tests automatisés (unitaires, fonctionnels, d'intégration, end-to-end, ...). C'est un peu long mais plein de bons conseils.
  • Why GitHub Won't Help You With Hiring (en) : Je serais pas aussi catégorique (et je dis pas ça parce mon compte est approximativement le 64 392 plus suivi :-)). Le profil Github est une information comme un joli CV, des recommandations sur Linkedin, un blog ou autre. Mais en effet, se baser uniquement là dessus est totalement ridicule.
  • How to write a git commit message that won't disappoint your future self (en) : sans doute discutable sur certains aspects mais trop souvent, les messages de commits ne sont assez pris au sérieux et pourtant ils sont là pour rester.
  • The Struggle (en) : tellement vrai :-)
  • Notes for new Make users (en) : pour les nouveaux utilisateurs de Make (et aussi pour les plus anciens qui auraient oublié quelques subtilités avec les années ;))

Et un peu hors-sujet :

(En plus du flux RSS global, les billets veille et uniquement ceux là sont listés dans le flux RSS veille)

03/15/2018 06:12 am   pwet.fr/blog   Mirror   Link   @6

Et un peu hors-sujet :

(En plus du flux RSS global, les billets veille et uniquement ceux là sont listés dans le flux RSS veille)

03/08/2018 05:12 am   pwet.fr/blog   Mirror   Link   @10

Compressing served files is a very usual trick to increase the loading performance of a website. The principle, defined in HTTP 1.1, is quite simple: when requesting a file, the browser announces the encoding it accepts in the Accept-Encoding header (for instance gzip) and thanks to it, the server knows how it can serve the file.

Typically, this is done on the fly by the web server with a dedicated module, for instance in Apache, mod_deflate does that pretty well (I've been using it for years (fr)) and nowadays this requires almost no configuration besides being activated unless you want to support the venerable most-hated browser of all time aka Internet Explorer 6 :)

Alternatively, it's possible to pre-generate compressed files along with the normal ones to serve the best one supported by the browser visiting your website. This has the advantage of requiring almost no resource on the web server while allowing you to use the highest compression level available even this takes a bit of time. And depending on the static site generator this is maybe super simple to setup.

So in this post, I'm gonna try to compress each page with Gzip and Brotli and to configure Apache to serve the best possible version.

Brotli ?

According to Wikipedia:

Brotli is a data format specification for data streams compressed with a specific combination of the general-purpose LZ77 lossless compression algorithm, Huffman coding and 2nd order context modelling. [...]

Brotli was first released in 2015 for off-line compression of web fonts. The version of Brotli released in September 2015 by the Google software engineers contained enhancements in generic lossless data compression, with particular emphasis on use for HTTP compression.

Brotli logo

If I believe caniuse.com, Brotli is now supported by most browsers. As usual, only Internet Explorer (11 and below) and Safari (before High Sierra) are a bit behind so for those and for probably tons of bots out there, Gzip compressed files or uncompressed files are still useful.

Brotli files are said to offer a higher compression rate than Gzip while remaining fast to decode. On the other hand, Brotli is also known to be slower to compress especially if you are using the highest compression level. Let's have a look at that.

Compressing files

Since I'm using Metalsmith to generate this web site, I can use metalsmith-gzip and metalsmith-brotli to compress generated documents. Both plugins are very similar and are configured to compress files matching the regular expression /\.[html|css|js|json|xml|svg|txt]/. I just had to configure metalsmith-gzip to compress at level 9 instead of 6 by default.

If you use Metalsmith, that's pretty much it! Of course, it's possible to do the same with a "simple" shell oneliner, something like:

find path/to/files -type f -a \( -name '*.html' -o -name '*.css' -o -name '*.js' \
-o -name '*.json' -o -name '*.xml' -o -name '*.svg' -o -name '*.txt' \) \
-exec brotli --best {} \+ -exec gzip --best -k {} \+

Apache configuration

This part is a bit tricky, at least it took me some time to figure it out, especially the part about preventing the double compression when you still need mod_deflate for other websites.

First, you need to make sure that mod_mime, mod_headers and mod_rewrite are enabled in Apache. Under Debian, if you are unsure just run as root:

# a2enmod mime
# a2enmod headers
# a2enmod rewrite

Then, the VirtualHost for your website requires a bit of configuration. Here is the relevant configuration excerpt for serving my pre-compressed website:

# Otherwise Content-Language: br is added
# Only needed if mod_mime configures that language
# in /etc/apache2/mods-enabled/mime.conf
RemoveLanguage .br

# Encoding for Brotli files
AddEncoding br .br

# Set gzip encoding instead of setting as a Content Type
RemoveType .gz
AddEncoding x-gzip .gz

# Mapping foo.suffix.gz or foo.suffix.br => Type
# see following repositories for recognized suffixes
# https://github.com/michel-kraemer/metalsmith-brotli
# https://github.com/ludovicofischer/metalsmith-gzip
AddType "text/html" .html.br .htm.br .html.gz .htm.gz
AddType "text/css" .css.br css.gz
AddType "text/plain" .txt.br txt.gz
AddType "text/xml" .xml.br .xml.gz
AddType "application/javascript" .js.br .js.gz
AddType "application/json" .json.br .json.gz
AddType "image/svg+xml" .svg.br .svg.gz
# Depending on what you compress, some more might be needed

# Proxy configuration
Header append Vary Accept-Encoding

RewriteEngine On

RewriteCond %{HTTP:Accept-Encoding} br
RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME}.br -s
RewriteRule ^(.*)$ %{DOCUMENT_ROOT}/%{REQUEST_FILENAME}.br [E=no-gzip,L]

RewriteCond %{HTTP:Accept-Encoding} gzip
RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME}.gz -s
RewriteRule ^(.*)$ %{DOCUMENT_ROOT}/%{REQUEST_FILENAME}.gz [E=no-gzip,L]

So to explain it shortly:

  • this changes the configuration so that .br and .gz files have the same type for Apache as the ones without those suffixes. This has to be in sync with what is done by the static site generator or your shell script.
  • if a browser accepts Brotli compressed files and the requested file exists with a .br suffix, serve this file.
  • if a browser accepts Gzip compressed files and the requested file exists with a .gz suffix, serve this file.
  • in both cases, if the file with the suffix is served, the no-gzip environment variable is set so that mod_deflate does not try to compress again the file.

And that's it for serving pre-compressed files! You can see in the network panel that files are now served with Content-Encoding: br and maybe loading feels a bit snappier.

Screenshot of Firefox dev
    tools showing the HTTP headers

Some stats

This little experiment is a good opportunity to look at some numbers about Gzip vs. Brotli vs. no compression.

Time to compress files

At their maximum compression level, Brotli is way slower than Gzip. At the time of writing, 1452 files are matching the regular expression mentioned above. On my Macbook pro, metalsmith-gzip takes less than 400 milliseconds to compress those while for Brotli, this takes almost 6 seconds! Using the shell version, I find out that it takes almost 24 seconds to compress those files with Brotli and a bit more than 1 second for Gzip.

Even if in this setup, this does not matter much, it's interesting to note that the difference is somehow of an order of magnitude.

Resulting sizes

After all, that's the point of compressing, so let's have a look at the resulting size of some files (unless mentioned, sizes are in bytes).

File(s) Size Gzip Brotli Gzip - Brotli
Homepage 8293 239929% 199124% -408
RSS feed 57368 1963634% 1699830% -2638
Main stylesheet 10943 303428% 259124% -443
robots.txt 24 44183% 28117% -16
Posts index 6772 189228% 158723% -305
Latest post in English 13814 443732% 368127% -756
Total 11.65Mb 4Mb34% 3.4Mb29% -579Kb

Almost no surprise here, Brotli compressed files are about 5% (of the initial size) smaller than the Gzipped one. Given that most of my pages are quite small already, that's not a lot in absolute value but still an interesting gain. Only exceptions to that are very small files like my robots.txt where compressed ones are bigger than the original. So for the sake of completeness, I should not compress those but we are talking of 4 or 20 bytes depending on the algorithm :)

03/05/2018 02:10 am   pwet.fr/blog   Mirror   Link