Catégorie : Développement web

  • Recom­mençons à zéro — Javas­cript 101

    PHP a été mon langage favori pendant presque 10 ans. Ruby l’a suivi pour à peu près la même durée. Javas­cript a été l’ou­til secon­daire pas très atti­rant pour quand il n’y a pas d’autre choix, c’est à dire sur navi­ga­teur.

    Avant-hier j’ai eu un petit pince­ment de frus­tra­tion en cher­chant à déstruc­tu­rer un diction­naire lors d’une affec­ta­tion. Ruby n’a toujours qu’une demie solu­tion pas très élégante. PHP et Javas­cript ont tous deux énor­mé­ment évolué entre temps, au point qu’on ne les recon­nait qu’à peine.

    Si Ruby garde des capa­ci­tés inéga­lées pour faire de la méta-program­ma­tion, 10 ans c’est peut être le bon moment pour reprendre un autre langage de zéro.

    Je fais du web, j’aime beau­coup l’idée de l’iso­mor­phisme, il me faudra donc quelque chose qui sache compi­ler en Javas­cript. Elm pour­rait me faire du pied vu sa gestion des types mais c’est un pari osé consi­dé­rant qu’il est encore absent côté serveur.

    Ça sera donc Javas­cript. D’abord pour quelques scripts en ligne de commande, puis peut-être des appli­ca­tions plus sérieuses.

    Reprendre de zéro c’est l’oc­ca­sion de lais­ser le passé derrière soi et recom­men­cer comme si c’était un nouveau langage. Je ne prends que les dernières versions et outils récents dans la chaîne, voire ce qui n’est pas encore sec mais qui arri­vera demain.

    Il s’agit aussi de reprendre de zéro, pour apprendre. Exit donc les boiler plate et autres kits de démar­rage rapide.


    Première étape : instal­la­tion de npm (3.10.3) et nodejs (6.3.1).

    # Sur Mac avec Homebrew (http://brew.sh/)
    brew install nodejs

    et initia­li­sa­tion du projet (un peu de doc si besoin pour aider à remplir)

    mkdir my-js-project
    cd my-js-project
    npm init

    Ensuite l’idée c’est d’uti­li­ser le nouveau Javas­cript moderne et ne cher­cher aucune compa­ti­bi­lité passée. Instal­la­tion de Babel pour gérer ES2015 et + :

    npm install --save-dev babel-cli

    La plupart des sites recom­mandent des preset pour ES2015 ou React mais visi­ble­ment Nodejs 6.x gère déjà l’es­sen­tiel des fonc­tions nati­ve­ment. Il existe au moins trois preset spéci­fiques pour Nodejs 6 mais la seule confi­gu­ra­tion commune aux trois est le plugin qui trans­forme les modules ES2015 en modules Commonjs. Je suis donc reparti de là :

    npm install --save-dev babel-plugin-transform-es2015-modules-commonjs

    Puis l’édi­tion du .babelrc :

    {
      "presets": [ ],
      "plugins": [ "transform-es2015-modules-commonjs" ]
    }

    Si quelque chose manque à Nodejs 6, il faudra que j’ajoute le plugin corres­pon­dant. Voici les plugins sur lesquels mes trois presets étaient en désac­cord :

    • trans­form-es2015-func­tion-name,
    • syntax-trai­ling-func­tion-commas,
    • trans­form-es2015-destruc­tu­ring,
    • trans­form-es2015-para­me­ters,
    • trans­form-object-rest-spread,
    • trans­form-es2015-sticky-regex,
    • trans­form-es2015-unicode-regex,
    • trans­form-es2015-modules-commonjs,
    • et trans­form-class-proper­ties.

    Je n’ai pas pris le temps de les tester un par un pour voir lesquels étaient réel­le­ment néces­saires pour pas. Si quelqu’un a le courage de faire le travail…

    Reste à enfin permettre d’exé­cu­ter un premier script. J’ai tenté de suivre les usages en mettant les sources dans src/ et les fichiers compi­lés dans lib/ avec un index.js comme fichier prin­ci­pal.

    mkdir lib
    echo "Compiled files will go here." > lib/README.md
    echo "Leave these files untouched"  >> lib/README.md
    echo "and modify ../src/* instead." >> lib/README.md
    
    mkdir src
    echo "Source files will go here." > src/README.md
    echo "They should be compiled before any use." >> src/README.md
    touch src/index.js

    Et la confi­gu­ra­tion du package.json pour lancer tout ça (notez le main qui prend en compte notre nouveau chemin et le scripts qui liste nos deux nouvelles actions) :

    {
      "name": "my-project",
      "main": "./lib/index.js",
      "scripts": {
        "build": "babel src -d lib --source-maps",
        "watch": "babel src -d lib --watch --source-maps"
      },
      "devDependencies": {
        "babel-cli": "^6.11.4",
        "babel-plugin-transform-es2015-modules-commonjs": "^6.11.5"
      }
    }

    Un npm run build permet de compi­ler tous les fichiers, un npm run watch permet de surveiller en temps réel les modi­fi­ca­tions des fichiers sources pour mettre à jour les fichiers desti­na­tion corres­pon­dants.


    J’ai ajouté un README, le .giti­gnore proposé par Github pour un projet Nodejs, une licence, et ‘op, vous trou­ve­rez le tout sur Github.

    Ça fait déjà pas mal pour toujours aucune ligne de Javas­cript utile.

    Quelques feed­back après ce tout petit premier pas :

    1– C’est long et complexe

    Oui, j’ai cher­ché les ennuis, il existe des boiler plate tout faits, mais je ne crois pas avoir le même problème avec Python, Ruby, PHP ou d’autres.

    Le fait d’avoir une étape de compi­la­tion avec Babel n’aide pas mais Java pour­rait en dire tout autant. Pourquoi n’existe-t-il pas un babel init qui lit la version courante de Nodejs, m’ins­talle un .babelrc adapté à cette version et me modi­fie mon package.json pour m’ajou­ter les raccour­cis build et watch ?

    Oui, je pour­rais faire une pull-request, mais fallait-il m’at­tendre ?

    2– La doc aide peu

    Fran­che­ment c’est la plaie. Je ne dirais pas qu’il n’y a aucune doc, mais il y en a dans tous les sens. La plupart ne sont pas à jour, toutes semblent incom­plètes, et il est bien diffi­cile de savoir si telle ou telle a pris en compte les dernières évolu­tions.

    Peut-être n’ai-je pas trouvé la bonne réfé­rence ? Dites-moi.

    Rien que le npm init n’était indiqué sur aucune doc que j’ai croisé. Pire : si on l’ou­blie il n’y a pas de message rouge très expli­cite pour indiquer que les npm install –save-dev n’au­ront pas l’ef­fet demandé. Il a fallu comprendre qu’il fallait créer un package.json, puis décou­vrir qu’il n’était pas néces­saire de le faire à la main.

    Ce n’est que le début. Quand il va s’agir de choi­sir une biblio­thèque ou des outils, je sens que ça va être toute une histoire pour d’y retrou­ver. La doc de setup de Babel en donne un premier aperçu.

    J’ai l’im­pres­sion qu’à chaque étape la roue est en pleine recons­truc­tion, et qu’on en a fait plusieurs versions diffé­rentes en paral­lèle, pour tout.

    3– Qui a eu l’idée d’uti­li­ser des fichiers de config JSON ?

    Je suis aussi assez peu fana aussi des config dans des fichiers Json. J’ai l’im­pres­sion de retrou­ver les fichiers de confi­gu­ra­tion XML de Java. Je n’ai pas l’im­pres­sion que ce soit beau­coup plus lisible.

    La grosse diffé­rence c’est que Java a créé ces confi­gu­ra­tions avec tout un outillage autour. Le déve­lop­peur n’avait que rare­ment besoin d’y plon­ger manuel­le­ment. C’était géré auto­ma­tique­ment par les IDE et les scripts.

    J’ai ajouté un plugin au .babelrc décrit plus haut. Ce plugin a une option. Je me retrouve avec un Json de 7 lignes dont 4 qui contiennent autre chose que des crochets et acco­lades fermantes.… et déjà un diction­naire dans un tableau dans un tableau dans un diction­naire. Rien que ça.

    Même mon petit package.json me parait déli­cat à mani­pu­ler malgré l’in­den­ta­tion. Heureu­se­ment que les dépen­dances sont ajou­tées auto­ma­tique­ment par npm lors de leur instal­la­tion.

    Là, dès qu’on dépasse le simple clef valeur, j’ai l’im­pres­sion de me retrou­ver avec une double peine : Un fichier de confi­gu­ra­tion peu aisé à mani­pu­ler, mais pas pour autant d’ou­til pour le faire à ma place.

  • Et si on agençait des photos sur une page ?

    J’ai cher­ché à agen­cer des vignettes de photo de façon harmo­nieuse sur une page web. Le problème à l’air simple mais j’ai des photos de tous formats, dont certains vrai­ment atypiques.

    La grille

    La méthode la plus simple c’est de choi­sir un format d’images, choi­sir un nombre de colonnes, et de s’y tenir. Si une image n’a pas le bon ratio, il suffira de couper ce qui dépasse. 500px utilise encore ce système pour la plupart de ses vues.

    grillePour aller plus loin on peut mettre quelques photos en grande taille en prenant la place de plusieurs autres. On casse la mono­to­nie tout en permet­tant de mettre en avant les images les plus impor­tantes.

    En prévoyant plusieurs agen­ce­ments diffé­rents prédé­ter­mi­nés on peut réus­sir à caser des images en format diffé­rent, et ne pas trop déna­tu­rer  les formats portraits ou les images très étirées. On reste toute­fois fixés à des formats prédé­ter­mi­nés.

    Le système de grille est rapide et effi­cace. La seule contrainte est de choi­sir entre la capa­cité de respec­ter scru­pu­leu­se­ment l’ordre des photos et celle de choi­sir manuel­le­ment lesquelles seront mises en avant.

    L’al­go­rithme de Packery va encore plus loin sur ce chemin. À partir d’une grille fixée, il propose de défi­nir un nombre de lignes et de colonnes pour chaque image et de la placer à l’es­pace le plus adapté. L’ordre n’est alors qu’in­di­ca­tif et on doit toujours rogner les images pour les faire tenir sur un nombre fixe de lignes et colonnes, mais on y place des formats et des tailles très diffé­rentes. Pas de secret toute­fois, cet agen­ce­ment lais­sera forcé­ment des blancs. À vous de voir si c’est accep­table, quitte à tenter de les combler par quelques mots.

    Quel que soit le système de grille, le réel enjeu est de savoir ce qu’on peut couper ou pas dans chaque image pour qu’elle colle aux formats prévus. Certains algo­rithmes y arrivent main­te­nant assez bien, surtout quand il y a des visages.

    Même si mon cas d’usage me permet­trait de choi­sir manuel­le­ment comment décou­per chaque image au cas par cas, intel­lec­tuel­le­ment ça me gêne de couper des pixels que j’avais volon­tai­re­ment choisi de garder lors de la créa­tion initiale de l’image. Je crains aussi que le visi­teur s’en fasse une idée faus­sée et au final n’ouvre pas l’image dans son format voulu. C’est parti­cu­liè­re­ment vrai pour les images très en hauteur ou très en largeur, qui ne pour­ront jamais donner le même ressenti si elles sont tronquées.

    On empile

    L’autre méthode est d’em­pi­ler les photos par lignes ou par colonnes en respec­tant leur ratio.

    Tumblr le fait par colonnes en mettant chaque image sur la première place dispo­nible. On obtient une sensa­tion de vrac où l’ordre des images n’est qu’à moitié respecté mais le résul­tat est parfait pour leur cas d’usage. Toutes les images ne terminent pas à la même hauteur mais c’est là encore tout à fait légi­time pour le système de flux sans fin que repré­sente un Tumblr.

    On peut imagi­ner de mettre en avant des images en les passant sur deux colonnes mais, sauf à espé­rer un cas excep­tion­nel où les deux images du dessus s’ar­rêtent pile à la même hauteur, il faudra soit lais­ser du blanc soit couper un peu la plus longue des deux.

    Avec un algo­rithme un peu intel­li­gent on peut tenter de repé­rer quand deux images arrivent presque à la même hauteur et donc ne couper que quelques pixels qui ne se verront pas, mais ça veut aussi dire que l’image mise en avant est quasi­ment lais­sée au hasard. La proba­bi­lité d’en avoir une dépend direc­te­ment de la quan­tité de pixels qu’on accepte de rogner sur les images.

    Pour quelque chose de plus struc­turé Flickr a choisi une dispo­si­tion par lignes. On choi­sit une hauteur cible et on empile toutes les images une à une jusqu’à la fin de la ligne. Ça ne tombe jamais juste mais Flickr se permet alors de faire varier légè­re­ment la hauteur de la ligne à la hausse ou à la baisse. Si on respecte les ratios des images concer­nées, on finira forcé­ment par tomber sur la largeur de ligne souhai­tée. On peut choi­sir de garder la dernière image ou pas (respec­ti­ve­ment en dimi­nuant ou augmen­tant la hauteur de ligne) en fonc­tion de la dispo­si­tion la plus proche de la hauteur idéale.

    Avec un peu de complexité on doit pouvoir éviter les lignes incom­plètes en fin de page. Il suffit de tenter plusieurs combi­nai­sons sur les x dernières lignes puis voir laquelle respecte le mieux la hauteur cible pour chaque ligne parmi celles qui n’ont aucune ligne incom­plète. Je ne suis cepen­dant pas tota­le­ment certain que ça vaille le coup, et ça peut faire varier signi­fi­ca­ti­ve­ment la hauteur des dernières lignes.

    Ce système permet des mises en avant simples en mettant une image en pleine largeur de temps en temps. On peut même, si on le souhaite, avoir des mises en avant à largeur arbi­traire. Il suffit alors de mettre les mises en avant sur un des bord et de reprendre l’al­go­rithme stan­dard sur la largeur restante. Une fois arrivé proche du bas de l’image mis en avant, on la réduit ou l’agran­dit légè­re­ment (avec pour effet d’agran­dir ou de réduire propor­tion­nel­le­ment l’es­pace hori­zon­tal sur le côté, et donc la hauteur corres­pon­dante) jusqu’à ce que les deux corres­pondent.

    On peut aussi imagi­ner ne pas se limi­ter à une seule mise en avant par hauteur et les empi­ler sur un ou deux côtés, y compris sur des hauteurs diffé­rentes. La contrainte va être de toujours avoir les plus hautes à l’ex­té­rieur.

    Il reste que la dispo­si­tion en colonnes de Tumblr flatte les images verti­cales et vigné­tise à l’ex­cès des images orien­tées format paysage. La dispo­si­tion en lignes de Flickr fait l’op­posé et rend diffi­cile la lecture des images au format portrait.

    Si un format est très forte­ment majo­ri­taire, on peut imagi­ner utili­ser le système des mises en avant pour compen­ser.

    Je n’ai pas vu d’adap­ta­tion de l’al­go­rithme Flickr en ce sens. Il faut dire que ça complexi­fie­rait nette­ment un système qui est sinon rela­ti­ve­ment simple. Si j’ai un peu de temps, je vais peut-être tenter l’ex­pé­rience.


    Quelques liens si le sujet vous inté­resse :

  • [Lecture] Ideal HTTP Perfor­mance

    A common ques­tion about Server Push is “what if the client already has a copy in cache?” Because Push is inhe­rently specu­la­tive, there’s always the chance that you’re sending some­thing that the brow­ser doesn’t need.

    HTTP/2 allows the client to cancel the push in this situa­tion, with a RESET_STREAM. Howe­ver, even then, there’s roughly a round trip’s worth of wasted data in flight that could have been used for better things. Remem­ber, the ideal is to send only the data that the client needs to show the page.

    A propo­sed solu­tion for this is for the client to use a compact Cache Digest to tell the server what it already has in cache, so that the server knows what’s needed.

    Bon article de Mark Nottin­gham sur HTTP 2, qui dépasse les notions de base qu’on voit partout. On y parle même un peu des évolu­tions de TCP.

  • La langue des signes et Paris Web racon­tés par une licorne

    Une des choses dont je suis le plus fier vis à vis de Paris-Web, c’est l’ar­ri­vée de la langue des signes et de la vélo­ty­pie.

    Les inter­prètes LSF font un boulot magni­fique au milieu d’un trou­peau de geeks qui parlent en fran­glais plein de jargon et d’acro­nymes, à toute vitesse. En octobre dernier l’équipe a proposé à une de ces inter­prètes de racon­ter comment ça se passe de l’in­té­rieur. C’est une des trois confé­rences à ne pas manquer de cette édition, et elle est pour vous en vidéo :

    Confé­rence LSF from Paris-Web on Vimeo.

    Il s’agit d’un gros budget, pour quelques uns. Ce n’est pas facile tous les ans et à chaque fois que c’est un peu tendu je suis certain que la ques­tion revient sur la table. Pour autant à chaque fois ça tient, même quand il y a un défi­cit.

    Pour moi c’est dans les valeurs de l’évé­ne­ment mais c’est aussi une façon de montrer par l’exemple qu’on peut tenter d’in­clure tout le monde.

  • [Lecture] On a testé fonc­tion­nel­le­ment notre app JS

    Lu sur le web :

    L’uti­lité des tests fonc­tion­nels pour les appli­ca­tions web n’est plus à démon­trer (comment ça, vous ne testez pas encore vos apps ?). Malheu­reu­se­ment, tout ne peut pas être tota­le­ment testé fonc­tion­nel­le­ment, ou de façon aisée : je pense par exemple au player chez nous, un compo­sant stra­té­gique mais pauvre­ment testé fonc­tion­nel­le­ment de par sa nature un peu hybride (mélange de flash et de JS). Dans tous les cas, pour ce qui peut l’être, nous sommes parti­sans dans l’équipe Cytron d’user sans mesure (ou presque !) de cet outil de manière à être le plus zen possible au moment d’ap­puyer sur le bouton “deploy”.

    chez M6 Web

    Inté­res­sant, mais à deux moments j’ai l’im­pres­sion de tâton­ne­ments, de tests qui peuvent échouer sans raisons et qu’on relance pour s’as­su­rer que ça passe. Je trouve ça assez risqué comme approche. Ai-je mal compris ?

    il nous arri­vait que le compor­te­ment “hover” ne soit pas déclen­ché, mettant en échec la suite du test. Nous avons donc changé notre manière d’ef­fec­tuer le rollo­ver : on répète l’ac­tion grâce au waitUntil tant que l’éle­ment devant appa­raître au hover n’est pas visible

    Pourquoi l’évé­ne­ment n’est-il pas déclen­ché ? Et l’uti­li­sa­teur risque-t-il d’avoir le même bug ?

    Elle permet de stocker dans un fichier texte la liste des scéna­rios en échec pour les relan­cer ensuite afin de véri­fier qu’ils le sont réel­le­ment.

    Des faux posi­tifs ? Et si parfois le test échoue alors qu’il ne devrait pas, il y a-t-il un risque qu’il réus­sisse alors qu’il ne devrait pas ? Combien de fois le relan­cer pour être certain du résul­tat ?

    Le retour d’ex­pé­rience est extrê­me­ment inté­res­sant, mais ça laisse un goût de bidouille qu’il serait préfé­rable de corri­ger avant de consi­dé­rer la solu­tion comme stable.

  • Compo­ser paral­lel install plugin

    Bench­mark Example
    288s -> 26s

    hirak/pres­tis­simo, compo­ser paral­lel install plugin

    Non testé, mais je me dis qu’il y a peu de chances que ça fasse du mal

  • No, You Don’t Need To Struggle Testing React

    Writing tests in such an envi­ron­ment need to satisfy two rules:

    1. Writing specs should take the least amount of time possible.
    2. Each spec should test as much code as possible.

    Now I’ve proba­bly already lost half of you reading this. « Each spec should test as much as possible? How do you isolate failures?” Yeah, yeah, I agree, but at the end of the day, the only purpose of my testing suite is to make sure that the little CircleCI dot on Github turns red whene­ver I merge a brea­king change.

    — Stephen Grider

    C’est voir un seul côté du miroir, et au travers de lunettes de soleil.

    Le test auto­ma­tisé n’est pas là que pour l’in­té­gra­tion conti­nue. Il est aussi là pour prou­ver que le code qu’on a produit corres­pond bien à ce qui est souhaité. Il est aussi là pendant le déve­lop­pe­ment pour ne pas faire du pas à pas et du test manuel encore et encore jusqu’à ce que ça semble fonc­tion­ner. Il est là pour permettre à un tiers de comprendre et repro­duire.

    Je n’ai rien contre le fait d’avoir un scéna­rio avec plusieurs asser­tions – qui est le fond du billet cité – mais avec des tests les plus gros possibles, on loupe la moitié des objec­tifs. Pire : on se créé une jolie dette tech­nique pour quand on aura quelque chose à chan­ger. Au lieu de reti­rer ou modi­fier quelques tests bien précis, on a un gros truc qui passe au rouge, qu’on va devoir modi­fier au risque d’obli­té­rer des cas limites.

  • Le baro­mètre des salaires 2015 dévoile ses résul­tats

    Rien de très éton­nant ni nouveau mais tout de même inté­res­sant :

    • une année d’ex­pé­rience corres­pond en moyenne à 3 à 5% de salaire en plus
    • la rému­né­ra­tion variable conti­nue d’être assez rare dans nos métiers
    • on recrute hommes et femmes globa­le­ment au même salaire mais les augmen­ta­tions ne suivent pas le même rythme, pour arri­ver à 20% de diffé­rence dans la dernière tranche d’ex­pé­rience
    • les rému­né­ra­tions en Île de France sont 20% plus hautes que dans le reste de la France.

    Je comprends tout à fait l’ori­gine de cette dernière donnée mais sa perti­nence m’in­ter­roge.

    Pourquoi l’en­tre­prise dépen­se­rait plus pour des infor­ma­ti­ciens sur Paris plutôt qu’en région alors qu’ils vont produire la même chose ? Ou vu de l’autre côté, pourquoi paie­rait-on moins un infor­ma­ti­cien hors Île de France alors qu’en plus les locaux et l’en­ca­dre­ment y coûtent moins cher  pour l’en­tre­prise ?

    Si on consi­dère que c’est unique­ment l’en­tre­prise qui est légi­time à payer le moins possible, pourquoi donc est-ce qu’on conti­nue à tout concen­trer sur Paris ? Je ne vois pas la logique dans tout cela.

    Des entre­prises qui viennent de Paris et qui décident de migrer en région sans en chan­ger les salaires risquent d’avoir un avan­tage compé­ti­tif signi­fi­ca­tif tout en descen­dant leurs coûts (pas sur le salaire, mais sur les locaux, les négo­cia­tions annuelles, les coûts de recru­te­ments…)

    Atten­tion quand vous faites des filtres assez sélec­tifs, la base de réponses reste assez limi­tée.

  • La commu­nauté JS est actuel­le­ment une machine à créer de la dette tech­nique

    on est dans la phase de l’ado­les­cence. Ça cri, ça bouge, ça a plein d’éner­gie, et ça mérite des baffes.

    — par Sam & Max

    L’ins­tal­la­tion est deve­nue un enfer. Entre les dépen­dances dépré­ciées, les libs incom­pa­tibles, les diffé­rents outils de build, et les options de config et les plugins, c’est une merde incom­men­su­rable. Plusieurs stan­dards d’ins­tal­leurs (et outils joints) se tirent la bourre : AMD, CommonJS et Harmony. Vous vous souve­nez du temps ou on copiait juste jQuery dans le réper­toire static ?

    […] On peut faire plus de choses qu’a­vant. De manière plus propre […] Mais l’éco­sys­tème, c’est le bordel géné­ral, l’anar­chie totale, et ça conti­nue d’avan­cer, en lais­sant tout le reste derrière.

    Les projets JS s’ac­cu­mulent, de free­lances en SS2I qui se pointent, codent ou (surtout) recodent, et repartent en lais­sant un projet qu’il sera imbu­vable à reprendre, dépré­cié avant même d’être terminé, non docu­menté, non testé.

    Livré sans commen­taire, mais j’ai­me­rais bien les vôtres.

  • Deploying PHP 7

    Il y a de tout et de rien dans cette présen­ta­tion de Rasmus à Paris, mais je reste impres­sionné par le gain en perfor­mance annoncé partout pour PHP 7. Si on en obtient même la moitié de ça, ça reste une révo­lu­tion pour PHP.

    On parle de +30% de requêtes trai­tées dans le même temps au mini­mum, souvent +50% et plus, le tout en utili­sant moins de mémoire.

    J’ai envie de mettre des graphiques mais on va me dire qu’il ne sont pas repré­sen­ta­tifs, alors faites les vôtres.