<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Miximum</title><link>http://www.miximum.fr/</link><description></description><atom:link href="http://www.miximum.fr/feeds/rss.xml" rel="self"></atom:link><lastBuildDate>Tue, 27 Oct 2015 10:10:00 +0100</lastBuildDate><item><title>Soutiens la campagne kickstarter du Train de 13h37</title><link>http://www.miximum.fr/soutiens-kickstarter-train-13h37.html</link><description>&lt;figure class="full align-center"&gt;
    &lt;a href="http://www.tripodocus.fr/pictures/2015/voyage-en-andalousie-2015-19/"&gt;
        &lt;img alt="Sur le toit du « Metropol Parasol » à Séville" class="full border shadow"
            src="http://www.tripodocus.fr/pictures/2015/voyage-en-andalousie-2015-19_medium.jpg" /&gt;
    &lt;/a&gt;
&lt;/figure&gt;

&lt;p&gt;Une des choses que je préfère, dans mon domaine d'activité, c'est cet esprit de
camaraderie qui favorise les échanges d'information sans complexes.&lt;/p&gt;
&lt;p&gt;Dans le Web, quelque soit ton problème, tu trouveras toujours quelqu'un qui est
passé par là avant toi, prêt à publier une solution, à répondre à des questions
techniques, à écrire des articles pour partager ses compétences. Si on y
réfléchit bien, cette culture du partage est plutôt une anomalie, dans un monde
ou ce genre d'information pourrait représenter un avantage compétitif.&lt;/p&gt;
&lt;p&gt;Partager ses compétences peut passer par plusieurs formats : contribuer à un
article sur Wikipédia, écrire un billet technique, répondre à une question sur
Stackoverflow, donner une conférence, ou… écrire un livre.&lt;/p&gt;


&lt;p&gt;Rédiger un bouquin, d'ailleurs, n'est-ce pas une étrange lubie ? Considérez la
chose suivante : pour le prix de quelques cafés ou d'une paire de chaussettes
neuves, vous pouvez acquérir un ouvrage qui récapitule l'intégralité de la
connaissance d'une personne sur un sujet donné. C'est à dire que vous
bénéficiez du fruit de ses années d'expérience, sans compter le temps
– monumental – passé à effectivement rédiger, relire, corriger, rerelire,
réécrire, faire publier, etc.&lt;/p&gt;
&lt;p&gt;Il y a certes des gens qui deviennent riches et célèbres en vendant des livres.
Mais pas dans le domaine du Web, et encore moins dans le Web francophone. Parce
que vous imaginez bien qu'un bouquin sur l'&lt;a href="http://www.inseo.fr/"&gt;intégration
Web&lt;/a&gt;, même si c'est un ouvrage de référence qui devrait
se trouver dans toute bonne bibliothèque de citoyen·ne du Web, ça se vend quand
même à moins d'exemplaires qu'un Harry Potter.&lt;/p&gt;
&lt;p&gt;Alors, quand on en voit &lt;a href="https://vimeo.com/album/2451288/video/69913156"&gt;certains qui passent des mois à bosser sur ce genre de
projet pour en retirer des
cacahuètes&lt;/a&gt;, on se dit que soit
1) ils sont légèrement frappés sur les bords ou alors 2) ils sont sacrément
passionnés et ça force le respect (peut-être un peu des deux, qui sait). Dans
tous les cas, je pense que ce genre de projet mérite d'être encouragé et même,
pourquoi pas, soutenu, soyons fou.&lt;/p&gt;
&lt;p&gt;En parlant de soutenir des projets fou, savez vous qu'il reste un peu moins de
trois jours au &lt;a href="http://letrainde13h37.fr/"&gt;Train de 13h37&lt;/a&gt; pour réussir sa
&lt;a href="http://t13h37.fr/kickstarter"&gt;campagne Kickstarter destinée à financer l'édition papier de ses
ebooks&lt;/a&gt; ?&lt;/p&gt;
&lt;p&gt;Comment, vous ne connaissez pas le Train de 13h37 ? vous avez passé les
dernières années dans un tiroir ou quoi ? Bon, je récapitule pour ceux qui
n'ont pas suivi. Le train de 13h37, c'est à la base un projet de magazine
numérique devenu maison d'édition, porté par &lt;a href="http://letrainde13h37.fr/a-propos-de-nous/"&gt;Marie Alhomme, Loïc Mathaud et
Corinne Schillinger&lt;/a&gt;. Des gens
biens qui pensent que le Web francophone recèle des talents méritant de trouver
un canal pour s'exprimer.&lt;/p&gt;
&lt;p&gt;Mais éditer des livres en format papier, ça coûte cher, et c'est inabordable
pour une toute petite maison d'édition qui se spécialise dans la production
d'ouvrages aussi pointus. Sauf si suffisamment de personnes se montrent
intéressées et qu'ils sont assurés de rentrer dans leurs frais.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://t13h37.fr/kickstarter"&gt;La campagne Kickstarter&lt;/a&gt;, donc, vise à réunir des fonds
pour faire imprimer les deux ouvrages suivants (existants pour le moment
uniquement sous format électronique) :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://boutique.letrainde13h37.fr/products/design-icones-le-manuel-sebastien-desbenoit"&gt;Design d'icônes : le
  manuel&lt;/a&gt; par &lt;a href="http://desbenoit.net/"&gt;Sébastien Desbenoit&lt;/a&gt; ;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://boutique.letrainde13h37.fr/products/la-dette-technique-bastien-jaillot"&gt;La dette
  technique&lt;/a&gt;
  par &lt;a href="http://www.bastnic.info/"&gt;Bastien Jaillot&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Si vous vous posez la question, je peux vous confirmer que ce sont deux
personnes très bien et qui ont beaucoup de choses passionnantes à raconter.&lt;/p&gt;
&lt;p&gt;Ne tournons pas autour du pot : ces livres, je les &lt;em&gt;veux&lt;/em&gt; sous mon sapin de
noël. Et pour ça, il faut que tout le monde se bouge, sorte sa CB, et apporte
sa contribution.&lt;/p&gt;
&lt;p&gt;Soutenir ce projet, c'est faire bien plus qu'acheter des livres : c'est aussi
apporter de la reconnaissance à l'immense travail effectué par ces
passionné·e·s ; contribuer à la culture d'excellence et de partage qui fait
notre spécificité et notre fierté ; encourager le renouvellement de telles
initiatives à l'avenir.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://t13h37.fr/kickstarter"&gt;Alors, on se bouge ?&lt;/a&gt;&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Admin</dc:creator><pubDate>Tue, 27 Oct 2015 10:10:00 +0100</pubDate><guid>tag:www.miximum.fr,2015-10-27:soutiens-kickstarter-train-13h37.html</guid></item><item><title>Le cauchemar de Turing</title><link>http://www.miximum.fr/le-cauchemar-de-turing.html</link><description>&lt;p&gt;&lt;em&gt;Le billet qui suit est &lt;a href="http://polar-geek.org/"&gt;une nouvelle Polargeek&lt;/a&gt; et
s'étend sur 7300 mots ; son temps de lecture est estimé aux alentours de 20 à
30 minutes. Vous pouvez aussi la télécharger aux formats
&lt;a href="http://files.miximum.fr/turing/Le_cauchemar_de_Turing.pdf"&gt;pdf&lt;/a&gt;,
&lt;a href="http://files.miximum.fr/turing/Le_cauchemar_de_Turing.epub"&gt;epub&lt;/a&gt; et
&lt;a href="http://files.miximum.fr/turing/Le_cauchemar_de_Turing.mobi"&gt;mobi&lt;/a&gt;. Bonne
lecture.&lt;/em&gt;&lt;/p&gt;
&lt;figure class="full align-center"&gt;
    &lt;a href="/le-cauchemar-de-turing.html"&gt;
        &lt;img alt="Une statue devant une envolée d'oiseaux" class="full border shadow"
            src="http://www.tripodocus.fr/pictures/2015/voyage-en-andalousie-2015-5_medium.jpg" /&gt;
    &lt;/a&gt;
&lt;/figure&gt;



&lt;h2&gt;Chapitre 00000000&lt;/h2&gt;
&lt;p&gt;Tout détective privé se devait d'arborer un costume froissé pour recevoir ses
clients dans un bureau poussiéreux sentant le tabac froid. Vous marquiez un
point bonus quand un vieil impair élimé pendouillait négligemment sur un
portemanteaux branlant.&lt;/p&gt;
&lt;p&gt;C'était une des premières choses qu'on apprenait dans le métier : les gens
accordaient une importance démesurée à l'apparence. En se conformant à leurs
préjugés, vous gagniez leur confiance plus facilement. Psychologie élémentaire.&lt;/p&gt;
&lt;p&gt;La littérature populaire étant ce qu'elle est, j'avais pris l'habitude de
garder sur mon bureau des montagnes de dossiers pour me donner l'air occupé, et
deux ou trois cendriers mal vidés pour renforcer l'image du privé ténébreux
éprouvé par la vie et les enquêtes. À dire vrai, je n'avais pas trop à me
forcer ; le business tournait plutôt au ralenti et si je ne rentrais pas deux
ou trois belles affaires rapidement, j'allais recevoir quelques coups de fils
inquisiteurs de la part de mon proprio.&lt;/p&gt;
&lt;p&gt;Peut-être sursautais-je un peu trop vivement lorsque la porte de mon bureau
s'ouvrit sur une femme élégante d'une quarantaine d'année. Elle portait l'un de
ces grands chapeaux couverts d'un voile cachant le visage comme seules les
veuves éplorées en arborent dans les mauvais polars. Je n'étais visiblement pas
le seul à manier le cliché avec adresse.&lt;/p&gt;
&lt;!-- more --&gt;

&lt;p&gt;Les cas que l'on me confiait étaient généralement d'une affligeante banalité.
Affaires de mœurs, principalement ; madame veut savoir avec qui monsieur a
passé le week-end, ou monsieur se demande où va madame quand elle prétexte une
soirée copines. Il fut un temps ou les filatures étaient un mal nécessaire,
mais cette époque était révolue.  Aujourd'hui, il suffisait de pénétrer un ou
deux services dans le cloud grâce à des mots de passe trop courts et vous
obteniez tout ce qu'il vous fallait sans quitter votre fauteuil : agendas
révélateurs, sextos sans équivoques, selfies compromettants, et j'en passe. Je
bénissais chaque jour la mauvaise éducation du commun des mortels en matière de
sécurité informatique.&lt;/p&gt;
&lt;p&gt;Ce genre d'affaires faciles constituait mon principal gagne-pain, ce qui ne
m'empêchait pas de les trouver ennuyeuses à mourir. J'espérais que ma
mystérieuse inconnue allait me confier quelque chose d'un peu plus
croustillant. J'avoue que je fus un peu désarçonné lorsque, sans préambule et
dans le silence le plus total, elle me tendit un feuillet plié en quatre.&lt;/p&gt;
&lt;p&gt;« Avec vous un smartphone ? Si oui, éteignez-le et enlevez la batterie. »&lt;/p&gt;
&lt;p&gt;Je trouvais la démarche un peu cavalière, mais j'avais l'habitude de rencontrer
des paranos autrement plus farfelus et elle avait piqué ma curiosité. Je
m'exécutai. Un autre feuillet suivit rapidement.&lt;/p&gt;
&lt;p&gt;« Des appareils électroniques reliés à Internet ? Éteignez-les. Débranchez
votre box. »&lt;/p&gt;
&lt;p&gt;De plus en plus intrigué, j'obéis à ces nouvelles instructions. Ce n'est
qu'après avoir inspecté visuellement les coins et recoins de mon bureau que ma
visiteuse daigna ouvrir la bouche.&lt;/p&gt;
&lt;p&gt;« Vous êtes bien M. Dennis Dijkstra, détective numérique ?&lt;br /&gt;
— Lui-même.&lt;br /&gt;
— J'ai un cas à vous confier qui devrait vous intéresser. Un cas un peu…
disons… inhabituel.&lt;br /&gt;
— Je vous écoute.&lt;br /&gt;
— Il s'agit d'un meurtre. »&lt;/p&gt;
&lt;p&gt;Je la fixais d'un air incrédule. Dans la mesure ou je m'étais spécialisé dans
le domaine de la cybercriminalité, les affaires les plus excitantes que je me
voyais confier étaient les cas de vols de données ou d'usurpations d'identités.
Un meurtre, ça n'était pas banal ! Mon interlocutrice poursuivit.&lt;/p&gt;
&lt;p&gt;« Un meurtre, oui. Un meurtre unique. Le premier meurtre jamais commis par un
algorithme. »&lt;/p&gt;
&lt;h2&gt;Chapitre 00000001&lt;/h2&gt;
&lt;p&gt;Il me fallut plus d'une heure d'entretien pour reconstituer la scène. J'appris
que ma nouvelle cliente était Grace Babbage, vice-présidente et directrice
financière de la branche recherche et développement chez Poodle Inc. Trois
jours auparavant, Alan Hopper, vice-président exécutif de cette même branche,
avait été retrouvé empoisonné dans son bureau. La police avait rapidement
élucidé l'affaire : tout avait été enregistré par les caméras de sécurité.&lt;/p&gt;
&lt;p&gt;Sur les bandes, on voyait très nettement un homme pénétrer dans les locaux sans
chercher le moins du monde à dissimuler son visage. Il se dirigeait vers la
machine à café, remplissait un gobelet avant d'y verser le contenu d'un
minuscule flacon. Il se dirigeait ensuite nonchalamment vers le bureau du
vice-président, y entrait après avoir frappé, et en ressortait presque
immédiatement, les mains vides. L'absence de caméra dans le bureau ne
permettait pas de savoir précisément ce qui s'y était passé, mais on avait
retrouvé la victime sur le sol, le gobelet renversé à portée de main.
Empoisonnement au cyanure, coma en quelques secondes, mort en quelques minutes.
Pendant toute la scène, on distinguait l'assassin consulter son smartphone par
intermittence.&lt;/p&gt;
&lt;p&gt;Le coupable présumé était Tim Ritchie, un pharmacien respectable et respecté
qui officiait dans le quartier. Tim et Alan s'étaient déjà rencontrés : le
pharmacien faisait partie d'une équipe de bêta-testeurs qui avaient accès aux
derniers produits de Poodle avant leur ouverture au grand public, afin d'en
tester la viabilité commerciale et d'aider à en éliminer les derniers bugs.&lt;/p&gt;
&lt;p&gt;Si l'identité de l'assassin ne laissait pas le moindre doute, deux éléments
intriguaient encore la police. D'abord, on n'avait trouvé aucun mobile valable.
Le pharmacien ne niait pas les faits, mais prétendait n'avoir jamais voulu tuer
quiconque. Le plus étonnant restait ceci : en apprenant la mort du
vice-président, Tim Ritchie avait semble-t-il commencé à perdre les pédales et
à divaguer, prétextant que ses actes lui avaient été dictés par son téléphone.
Les forces de l'ordre avaient ordonné une expertise psychiatrique dont on
attendait la réponse.&lt;/p&gt;
&lt;p&gt;« Ce que la police ne doit pas savoir, me dit ma cliente, c'est que M. Ritchie
était l'un des rares élus à tester le prototype top-secret d'une application
actuellement en développement chez Poodle. D'ailleurs, dès que nous avons
appris son arrestation, nous avons immédiatement pris des mesures afin d'en
effacer toute trace sur son smartphone.  Nous pensons qu'il s'agit là d'un
projet révolutionnaire, et nous ne voulons pas que cette affaire vienne tout
compromettre. &lt;br /&gt;
— Puisque l'assassin présumé a déjà été appréhendé, répondis-je,
qu'attendez-vous de moi exactement ? »&lt;/p&gt;
&lt;p&gt;Je la vis hésiter et attendis patiemment qu'elle se décide à me confier :&lt;/p&gt;
&lt;p&gt;« Cette fameuse application, murmura-t-elle, c'est un assistant personnel
d'une nouvelle génération… &lt;br /&gt;
— Un peu comme Bibi™, le produit de votre concurrent Pineapple Inc. ? &lt;br /&gt;
— Ce projet est ultra-confidentiel et je ne peux rien vous confier dans un
cadre non sécurisé. Tout ce que je puis vous dire, c'est qu'il est fort
possible que Tim Ritchie dise la vérité et que ses actes lui aient été dictés
par l'application. Si c'est le cas, alors nous &lt;em&gt;devons&lt;/em&gt; savoir ce qui s'est
passé. S'agit-il d'un bug ? D'un acte de malveillance de la part d'un des
membres de l'équipe de développement ? Bien évidemment, vous imaginez que si le
grand public venait à associer l'utilisation de notre prototype à la mort d'un
être humain, cela nous ferait une très mauvaise pub. J'ai donc besoin que cette
enquête se déroule dans le secret le plus absolu. J'ai pris des dispositions
pour que vous puissiez rencontrer Ada Turing, notre responsable technique sur
ce projet. Elle sera en mesure de vous en dire plus. »&lt;/p&gt;
&lt;p&gt;Elle me tendit un nouveau feuillet manuscrit sur lequel je trouvais le nom
d'une taverne facétieusement intitulée « L'Antigeek », assorti d'une date et
d'une heure. Après avoir accepté sans la moindre discussion de payer plus du
double de mon tarif habituel, elle se leva pour se diriger vers la porte.&lt;/p&gt;
&lt;p&gt;« Une dernière chose ! » Juste avant de sortir, elle s'était tournée et me
dévisageait gravement. « Vous n'ignorez pas que notre société est spécialisée
dans la collecte et l'analyse de données à une échelle massive. Il n'existe à
peu près aucune information numérique qui ne transite à un moment ou à un autre
par nos serveurs pour y être disséquée. Par ailleurs, vous devez savoir que les
employé·e·s impliqué·e·s dans ce projet bénéficient tous d'un haut niveau
d'accréditation.  Vous êtes potentiellement à la poursuite d'un individu
quasi-omniscient et prêt à tout, M. Dijkstra. Je vous recommande la plus grande
prudence, si vous ne voulez pas que le chasseur se transforme en proie. »&lt;/p&gt;
&lt;p&gt;Sur cette sentence, elle sortit, me laissant seul avec mes pensées.&lt;/p&gt;
&lt;h2&gt;Chapitre 00000010&lt;/h2&gt;
&lt;p&gt;Moi qui aimais les défis, j'étais servi. Quand on est privé, on sait que les
murs ont des oreilles ; mais jamais je ne m'étais confronté à des oreilles
aussi grandes, aussi puissantes, aussi nombreuses. Je me trouvais face à un
paradoxe assez cocasse : moi, détective numérique, j'allais devoir me tenir
éloigné de tout périphérique connecté à Internet pour me cantonner au crayon à
papier.&lt;/p&gt;
&lt;p&gt;Je me levai le lendemain et contemplai le ciel gris et dur par la fenêtre. Je
relis le nom de la taverne où je devais me rendre et dans un réflexe, faillis
sortir mon laptop pour en chercher la localisation précise juste avant de me
rappeler que cela ne m'était plus permis. Bigre ! j'allais devoir tordre le cou
à quelques habitudes bien ancrées.&lt;/p&gt;
&lt;p&gt;Je passai dix bonnes minutes à fouiller mon bureau à la recherche d'une vieille
carte en papier que je finis par dénicher dans un tiroir poussiéreux. J'avais
laissé mon téléphone dans un placard après en avoir ôté la batterie, et
allais également devoir me passer de GPS.&lt;/p&gt;
&lt;p&gt;Je finis par arriver à l'Antigeek, non sans m'être perdu à plusieurs reprises
dans les ruelles brumeuses. J'avais même du recourir à des moyens archaïques
pour me repérer, comme demander mon chemin à des inconnus. À force de s'appuyer
sur des béquilles numériques, mon sens de l'orientation s'était émoussé.&lt;/p&gt;
&lt;p&gt;Je pénétrai dans la taverne et parcourus les lieux du regard ; quelques tables
en bois branlantes se partageaient l'espace d'une grande salle au plafond bas,
éclairée d'une lumière tamisée. La voix rauque de Tom Waits faisait écho au
grincement des vieilles planches tandis qu'une odeur d'amande et de pain
d'épice, assez déplacée dans ce genre d'endroit, conférait au lieux une
envoûtante atmosphère d'intimité.&lt;/p&gt;
&lt;p&gt;Je traversai la salle déserte à cette heure matinale, adressai un hochement de
tête au tenancier bedonnant qui me rendit mon salut d'un air cordial, et me
dirigeai vers la seule cliente assise dans un coin sombre. Je m'arrêtai devant
une femme que j'avais imaginé moins jeune. Blonde, les cheveux courts, le teint
pâlot, le front barré d'un pli soucieux et la bouche déformée d'une moue que je
n'arrivai pas à interpréter, elle avait l'air mi-ennuyé mi-exaspéré de
l'ingénieur expérimenté qui regarde un film de hackers hollywoodien bourré
d'absurdités techniques.&lt;/p&gt;
&lt;p&gt;« Mme. Turing ? » Elle hocha la tête. « Dennis Dijkstra, privé numérique. Je
crois que Mme. Babbage vous a demandé de répondre à mes questions ? ».&lt;/p&gt;
&lt;p&gt;Ignorant superbement ma main tendue, elle désigna la chaise en face d'elle d'un
hochement de tête et soupira.&lt;/p&gt;
&lt;p&gt;« Bien, allons-y et qu'on en finisse. J'ai beaucoup de travail en ce moment.
&lt;br /&gt;
— Vous permettez que je prenne quelques notes ? » dis-je en sortant mon
calepin. Je me contentai d'un vague clignement pour toute réponse, et entamai
l'interrogatoire.&lt;/p&gt;
&lt;p&gt;« J'imagine que vous savez déjà ce qui m'amène à travailler pour Mme.
Babbage ? &lt;br /&gt;
— Vous voulez en savoir plus sur notre "algorithme tueur" ? » Quelqu'un qui
n'aurait pas eu la triste obligation de joindre fréquemment le standard
téléphonique du RSI aurait sans doute été surpris d'apprendre qu'on pouvait
tinter une phrase d'une telle ironie méprisante. Je poursuivis sans me
démonter. « L'assassin présumé, Tim Ritchie, utilisait apparemment un produit
issu de vos laboratoires. Très franchement, j'ai du mal à imaginer comment une
simple application pourrait amener quelqu'un à commettre un crime… &lt;br /&gt;
— Ce n'est pas une "simple application" ! Il s'agit d'un projet
révolutionnaire ! »&lt;/p&gt;
&lt;p&gt;Certains collègues comparaient les interrogatoires à des matchs de boxe où
chaque information devait être arrachée de haute lutte. D'autres à des parties
d'échecs où stratégie et roublardise permettaient de contourner les défenses
verbales de l'adversaire. Ma vision était plus triviale : il suffisait de
trouver le bon bouton et d'appuyer dessus. Les uns étaient sensibles à la
flatterie ; d'autres craquaient sous la menace ; d'aucuns étaient tellement
avides de cancans qu'ils n'attendaient qu'une oreille attentive pour y déverser
tout ce qu'ils avaient. Avec le bon angle d'attaque, l'interlocuteur le plus
taciturne se transformait en moulin à paroles.&lt;/p&gt;
&lt;p&gt;Quand à l'ingénieure maussade qui me faisait face, j'avais tout de suite
compris comment la pousser à se mettre à table : aucun technicien digne de ce
nom ne peut rester de marbre face à un troll un peu bouché.&lt;/p&gt;
&lt;p&gt;« Vous savez, des projets soit-disants révolutionnaires, j'en ai vu passer des
paquets qui n'ont pas duré plus de quelques mois. Peut-être pourriez-vous
m'expliquer ce que votre fameux prototype avait de si spécial ? » Je vis ses
joues rosir et elle se pencha légèrement en avant. Bingo ! &lt;br /&gt;
— PoodleLife™ est le projet le plus novateur depuis l'invention du Web,
martela-t-elle. Il ne s'agit pas d'un vulgaire gadget qui permettrait à
quelques privilégiés de la Silicon Valley de faire repasser leur linge à
moindre frais. Nous allons faire entrer le monde dans une nouvelle aire.
Pauvreté ; corruption ; famine ; guerre… Il n'existe pas un problème que nous
ne pourrons résoudre lorsque cette application sera déployée à l'échelle
massive. &lt;br /&gt;
— Tout ça m'a l'air bien ambitieux, affirmai-je d'un air narquois. Et si vous
commenciez par me dire en quoi elle consiste ? &lt;br /&gt;
— L'application en elle-même est d'une simplicité extrême. C'est là toute la
beauté de la chose. Un seul écran, un seul bouton. Vous sortez votre téléphone,
appuyez sur le bouton, et l'application vous dit quoi faire. &lt;br /&gt;
— Je ne suis pas certain de bien comprendre. &lt;br /&gt;
— C'est très simple. L'application vous indique quelle action concrète vous
devez accomplir à ce moment précis pour amener votre vie dans la bonne
direction. &lt;br
/&gt;
— C'est bien joli, répondis-je, incrédule, mais comment une application
peut-elle savoir ce qui est bon pour moi ? &lt;br /&gt;
— Je crois que vous comprendriez mieux si je commençais par le commencement.
Vous savez bien entendu quel est le produit historique de notre entreprise ?
&lt;br /&gt;
— Bien entendu, c'est le fameux moteur de recherche. &lt;br /&gt;
— Exactement ! Et quel est le but d'un moteur de recherche ? &lt;br /&gt;
— Et bien, quand vous avez besoin d'une information, vous soumettez votre
requête et le moteur vous redirige vers les sources qu'il juge les plus
pertinentes. &lt;br /&gt;
— Toujours exact. Réalisez-vous à quel point ce modèle est archaïque et
inefficace ? &lt;br /&gt;
— J'avoue que je suis largué… &lt;br /&gt;
— N'est-il pas absurde pour un site Web d'avoir comme fonctionnement principal
d'envoyer les internautes &lt;em&gt;ailleurs&lt;/em&gt; ? &lt;br /&gt;
— Certes, mais c'est le principe d'un moteur de recherche… &lt;br /&gt;
— C'était peut-être inévitable au début. Si vous nous posiez une question, nous
ne pouvions faire mieux que de vous diriger vers la ressource qui,
pensions-nous, pouvait offrir la meilleure réponse. Mais les choses ont changé.
Nous avons indexé le Web entier. Nous disposons de la quasi-intégralité des
emails, agendas, documents, photos, carnets d'adresses et j'en passe de la
planète. Plus de la moitié des sites Web existants utilisent Poodle Analytics™.
Des millions de personnes ont en permanence dans la poche l'un de nos téléphone
équipé d'un micro, d'une caméra, d'un gps et d'une multitude de capteurs.
Aujourd'hui, quelque soit votre question, la ressource la plus pertinente,
c'est nous. »&lt;/p&gt;
&lt;p&gt;Je me laissai aller en arrière et restai songeur tandis que l'ingénieure
reprenais son souffle. Nous fûmes interrompus par le barman qui vint prendre ma
commande. Je demandai un café allongé et un croissant, mon interlocutrice
réclama un nouvel expresso. Nous restâmes assis en silence pendant que
j'assimilais ce qu'elle me disait. Elle reprit.&lt;/p&gt;
&lt;p&gt;« Vous voulez connaître la météo du jour ? Pourquoi vous envoyer vers
meteofrance.fr alors que nous &lt;em&gt;avons&lt;/em&gt; cette information ? Vous voulez connaître
les horaires de projection d'un film ? Nous pouvons vous l'indiquer
bien mieux qu'allocine.fr. Voulez-vous connaître l'actualité politique ? Mieux
que n'importe quel journal en ligne, nous pouvons vous servir sur un plateau
une synthèse personnalisée en fonction de votre personnalité, centres
d'intérêts et affinités politiques. Aujourd'hui nous savons tout, et nous le
savons mieux que tout le monde. Ce n'est pas être prétentieux, c'est un simple
fait. Il est donc naturel que notre service évolue : nous ne répondons plus à
une question par une ressource pertinente ; nous répondons avec la réponse. &lt;br
/&gt;
— Moui, c'est une évolution qui me parait logique » répondis-je doucement en
grattant méditativement une barbe qui à mon grand dam n'avait jamais existé.
&lt;br /&gt;
« Comme vous dites, une évolution. Mais ce n'est rien à côté de la révolution
qui va suivre. Parce que nous allons aller plus loin. &lt;br /&gt;
— Comment cela ? &lt;br /&gt;
— Tout le monde peut récupérer des données météorologiques et indiquer le temps
qu'il fera demain. Mais nous, nous pouvons répondre à des questions autrement
complexes ! Imaginons que vous soyez au chômage et cherchiez un travail. Très
naturellement, vous allez soumettre des requêtes comme "Pôle emploi", ou
"Petites annonces job", et vous vous attendez à ce qu'on vous redirige vers le
site approprié. Considérez maintenant la chose suivante. Nous savons &lt;em&gt;déjà&lt;/em&gt; que
vous avez perdu votre job, peut-être parce que l'avez indiqué sur Gwitter,
ou parce que vous avez navigué sur le site de l'assurance chômage qui utilise
Poodle Analytics. À vrai dire, nous l'avons probablement appris avant vous,
parce que votre lettre de licenciement a été rédigée grâce à Poodle Docs™. Mais
ce que nous savons également, c'est qu'une entreprise située à moins de 50km de
chez vous viens d'ouvrir un poste qui correspond parfaitement à vos compétences
et centres d'intérêts. Pourquoi vous envoyer vers un site de petites annonces,
alors que nous pouvons directement vous proposer le poste de vos rêves ? »&lt;/p&gt;
&lt;p&gt;Elle était maintenant complètement emballée, et poursuivait fiévreusement ses
explications.&lt;/p&gt;
&lt;p&gt;« Autre exemple. Vous êtes célibataire, et la solitude vous pèse. Nous le
savons grâce à votre historique de navigation ainsi qu'au contenu de vos emails
et de votre agenda. Nous pourrions certes vous envoyer vers un site de
rencontre, mais nous pouvons tellement plus ! Par exemple, nous savons qu'à
trois pâtés de maison vit une charmante personne qui cherche également l'âme
sœur, et l'analyse de vos personnalités et centres d'intérêts respectifs nous
permet de calculer que vous seriez heureux ensembles avec une probabilité
supérieure à 90%. Nous savons même que vous vous plairez physiquement, parce
que nous pouvons comparer vos photos avec vos historiques respectifs de
navigation sur des sites pornographiques. Tout ce que nous avons à faire, c'est
vous mettre en relation. Nous pouvons même le faire directement, il nous
suffit de réserver automatiquement un restaurant et d'insérer le rendez-vous
dans vos calendriers respectifs. Tout est automatisable ! &lt;br /&gt;
— Wow ! attendez une minute ! Qu'en est-il de la liberté individuelle ? Et si
je n'ai pas vraiment envie de rencontrer quelqu'un ? Qu'est-ce qui vous donne
la légitimité de décider pour moi ? Et puis, c'est une sérieuse atteinte à ma
vie privée ! &lt;br /&gt;
— Vos objections sont tout à fait légitimes, et je vais y répondre. En ce qui
concerne la vie privée, il faut bien comprendre qu'absolument aucun humain
n'est impliqué dans le processus. Toutes les données sont récoltées et
analysées par des algorithmes, et les traitements sont tellement complexes que
seules des machines peuvent s'en acquitter. Par ailleurs, n'oubliez pas que
toutes les données que nous récupérons nous sont offertes volontairement.
Personne ne vous force à utiliser Pmail, Calendar ou Poodle Docs. En ce qui
concerne votre objection sur la liberté individuelle… » Elle secoua la tête
tristement. « Franchement, avez-vous l'impression que les gens sont si doués
que ça pour prendre de bonnes décisions ? Vous &lt;em&gt;savez&lt;/em&gt; que faire du sport est
bon pour vous, alors pourquoi rester tout le week-end dans votre fauteuil à
regarder l'intégralité de la dernière série à la mode ? Vous &lt;em&gt;savez&lt;/em&gt; qu'un
régime équilibré est important pour la santé, alors pourquoi votre alimentation
est-elle principalement constituée de pâtes au fromage et de pizzas ? Pourquoi
des gens qui rêvent de voyages et d'aventures se retrouvent à mourir d'ennui à
longueur de journée derrière un guichet ? Pourquoi des gens qui rêvent d'amour
et de contes de fées finissent par naviguer de rupture en rupture ? Au fond
de vous, vous savez ce que vous voulez vraiment ; simplement, vous n'êtes pas
capables de mettre en place, pas après pas, les minuscules étapes qui vous
mèneront jusqu'à vos rêves. &lt;br /&gt;
— Et vous, vous savez ? &lt;br /&gt;
— Oui, nous savons. Nous savons tout. D'ailleurs, c'est ainsi qu'est conçue
l'application PoodleLife : elle vous indique la prochaine petite étape. Jamais
nous ne vous dirons "va rencontrer la femme de ta vie", ou "va postuler à ce
job". Parce que les gens n'écouteraient pas. Au lieu de ça, nos instructions
sont simples. "Va jusqu'à la voiture. Conduit jusqu'à telle adresse. Entre dans
tel restaurant." L'utilisateur ne sait pas ce qui va lui arriver, mais il sait
que ça sera bon pour lui, alors il apprends à nous faire confiance, et il
s'exécute. Il n'y a plus de stress à avoir : quelque soit le problème, nous
nous en occupons. »&lt;/p&gt;
&lt;p&gt;Soudain consciente de s'être abandonnée à un enthousiasme chez elle inhabituel,
elle s'était tue, s'abîmant dans la contemplation de sa tasse de café encore
pleine et froide depuis longtemps. J'avais du mal à assimiler l'ampleur et la
portée de ce qu'elle venait de me raconter, et j'avais tellement de questions
que je ne savais plus par où commencer.&lt;/p&gt;
&lt;p&gt;« Il y a tout de même une chose que je ne comprends pas. À ma connaissance,
vous n'êtes pas une organisation philanthropique. Quel est votre intérêt dans
tout ça ? &lt;br /&gt;
— C'est très simple, répondit-elle en souriant imperceptiblement. Jusqu'à
aujourd'hui, nous avons vécu de la publicité. Donner de la visibilité à un
produit contre des espèces sonnantes et trébuchantes. Mais ce modèle économique
est lui aussi obsolète. Grâce à PoodleLife, nous serons en mesure de vous faire
acheter un produit directement. Si nous vous envoyons à un entretien
d'embauche, nous vous indiquerons de vous arrêter dans la bonne boutique
partenaire pour acheter le modèle de costume qui impressionnera vos futurs
employeurs. Allez-vous à un rendez-vous galant ? Nous vous indiquerons le
cadeau adéquat, dans une boutique qui bien entendu nous rétribuera en échange,
sans compter que le restaurant réservé pour vous nous versera une commission.
Vous voyez, finis les panneaux géants qui défigurent nos paysages, terminés les
spots abrutissants qui font appels à nos plus bas instincts et monopolisent
notre attention ; la publicité sera bientôt une chose du passé, les gens
achèteront simplement ce que nous leur dirons d'acheter. »&lt;/p&gt;
&lt;h2&gt;Chapitre 00000011&lt;/h2&gt;
&lt;p&gt;Sur le trajet du retour, engoncé dans mon vieil imper pour me protéger de la
fraîcheur automnale, je m'abandonnai à mes réflexions. Il m'était difficile
d'imaginer qu'une seule entreprise puisse bénéficier d'un pouvoir aussi
extraordinaire, aussi total. Le contrôle des populations : tous les dictateurs
de l'Histoire en avaient rêvés, Poodle était sur le point de le réaliser, et
avec la bénédiction du peuple. Pas de junte, simplement une armée de robots
numériques qui parcouraient inlassablement le Web, avides de la moindre miette
d'information. Finalement, c'était l'intelligence et l'ingénierie qui
triomphaient sur la puissance militaire ; une bien piètre consolation.&lt;/p&gt;
&lt;p&gt;J'étais tellement absorbé dans mes pensées et inconscient de mon environnement
extérieur que je fus presque surpris de me retrouver devant ma propre porte. Je
décidai de poursuivre mes réflexions autour d'un café fumant.&lt;/p&gt;
&lt;p&gt;Si l'on mettait de côté la précieuse boisson noire, mon carburant principal,
c'était l'information. En ce sens, je n'étais pas si différent de mon
adversaire. Récoltez suffisamment de faits, vous pouviez échafauder des
hypothèses et tirer des conclusions. Et pour le moment, question faits, j'étais
plutôt à sec.&lt;/p&gt;
&lt;p&gt;Une évidence s'imposait : puisque les meilleures informations se trouvaient au
cœur de l'action, il fallait que je me jette dans la gueule du loup. Ne me
manquait qu'un prétexte. Je sentis remonter derrière mes oreilles ce
fourmillement familier annonciateur d'une idée. Avec un sourire en coin, je
dépoussiérai un vieux téléphone analogique qui traînait dans mes placards et
composai un numéro.&lt;/p&gt;
&lt;p&gt;Le lendemain, j'arrivai dans les locaux de Poodle Inc. sur le coup des 10h. Je
me présentai à l'accueil et l'on me fit signer quelques documents avant de me
remettre un badge et de m'escorter le long d'une interminable suite de couloirs
labyrinthique. Après quinze bonnes minutes de marche, j'arrivai finalement au
département R&amp;amp;D ou m'attendait ma cliente, Grace Babbage, qui m'accueillit
avec une poignée de main franche et ferme.&lt;/p&gt;
&lt;p&gt;« M. Dijkstra ? Je vous attendais. Je vais vous présenter à l'équipe. »&lt;/p&gt;
&lt;p&gt;La veille, j'étais resté deux heures au téléphone, redoublant de ruse et
faisant appel aux techniques de &lt;em&gt;social engineering&lt;/em&gt; les plus roublardes pour
être transféré de standard en standard jusqu'au poste de Grace. Sans donner le
moindre signe que nous nous étions déjà rencontrés, j'avais feint un appel
marketing visant à proposer mes services de consultant spécialiste en sécurité
des systèmes d'information. Elle était tout de suite rentrée dans mon jeu et
avait prétendu répondre favorablement à ma proposition en me commandant
immédiatement une prestation d'audit devant s'étaler sur deux jours. C'était le
prétexte idéal pour pouvoir fureter un peu partout et interviewer toute
l'équipe. La responsable technique, Ada Turing, était la seule autre personne à
connaître la véritable raison de ma présence.&lt;/p&gt;
&lt;p&gt;« Tout le monde ! Voici M. Dijkstra, dont je vous ai parlé hier. Je compte sur
vous pour lui réserver un bon accueil et répondre à ses questions selon les
procédures habituelles. »&lt;/p&gt;
&lt;p&gt;Je fus présenté aux différents membres de l'équipe et l'on me confia un laptop
pré-configuré en accord avec mon niveau d'accréditation. Je n'avais bien
entendu pas les droits nécessaires pour accéder aux informations qui
m'intéressaient, mais c'était un début.&lt;/p&gt;
&lt;p&gt;La journée se passa d'entretien en entretien, durant lesquels j'essayais d'en
savoir le plus possible sur les meilleurs moyens d'obtenir des données
confidentielles. Mon rôle me permettait de poser les questions les plus
directes sans éveiller les soupçons. J'avais pratiquement signé avec mon sang
un accord de confidentialité tellement restrictif que la liste des conséquences
qui m'attendaient si je divulguais des informations internes s'étalait sur
plusieurs pages. Par conséquent, l'équipe ne se montrait pas particulièrement
prudente dans les informations qu'elle me communiquait.&lt;/p&gt;
&lt;p&gt;Mon interview du jeune Pascal Delphi, l'inévitable stagiaire de service, fut
particulièrement productive. Avide de potins, il m'apprit ainsi que de nombreux
problèmes politiques avaient secoués la vie du projet PoodleLife, et que
celui-ci avait failli être abandonné à plusieurs reprises. La sortie du produit
fini était en retard de plus de deux ans, et des altercations entre feu Alan
Hopper et ma cliente, Grace Babbage, éclataient au beau milieu de
l'&lt;em&gt;open-space&lt;/em&gt;, de plus en plus vives et de plus en plus fréquentes. Le premier
était partisan d'arrêter les frais et il fallait tout l'aplomb et la ténacité
de la seconde pour retarder cette décision. La réalité, c'est qu'une importante
réunion devait se tenir d'ici quelques jours avec les grandes pontes pour
décider de l'avenir du projet. Le stagiaire me fit remarquer avec une ironie
morbide que la mort de vice-président exécutif avait finalement donné un répit
à l'équipe.&lt;/p&gt;
&lt;p&gt;J'obtins également quelques détails intéressant sur l'architecture générale du
projet. Ainsi, Ada Turing avait dénommé l'algorithme central « PoodleMum »,
prouvant qu'elle était capable d'un certain humour dans ses bons jours.
PoodleMum n'était finalement qu'un gigantesque système expert capable d'ingérer
quotidiennement des quantités de données titanesques pour régurgiter des
décisions intelligentes. Lors de mon inquisition, un détail attira
particulièrement mon attention.&lt;/p&gt;
&lt;p&gt;« Si je comprends bien, les propositions envoyées aux utilisateurs sont
générées par PoodleMum sans aucune intervention humaine, pas vrai ?&lt;br /&gt;
— C'est exact, répondit le stagiaire.&lt;br /&gt;
— Quelque chose me turlupine, continuai-je. Si je devais concevoir un système
d'une telle ampleur et d'une telle complexité, sans doute voudrais-je m'assurer
qu'en dernier recours, un humain puisse garder la main, n'est-ce pas ? &lt;br /&gt;
— Heu… oui, c'est possible, hésita le stagiaire. » Sentant son assurance se
fendiller, je décidai de pousser le bouchon. « Je suis certain qu'il existe une
backdoor quelque part.  Un moyen pour un opérateur humain de surcharger les
décisions de PoodleMum. Je me trompe ? » &lt;br /&gt;&lt;/p&gt;
&lt;p&gt;Voir le jeune stagiaire bredouiller en se tortillant sur sa chaise était à
mourir de rire ; l'information était sans aucun doute des plus confidentielles.
Je poussai mon avantage. « Détendez-vous ! je plaisante. Ada m'a montré les
diagrammes d'architecture, je suis parfaitement au courant pour la backdoor. »
C'était un mensonge éhonté, mais la suite me prouva que mon coup de poker avait
donné dans le mille.&lt;/p&gt;
&lt;p&gt;« Ah ! si vous êtes déjà au courant, dit-il en se détendant. &lt;br /&gt;
— Ce que j'ai oublié de demander à Ada, c'est qui a accès à cette backdoor.
J'imagine que c'est particulièrement restrictif, n'est-ce pas ? &lt;br /&gt;
— Effectivement, à ma connaissance, seulement trois personnes y ont accès. Ada
elle-même, évidemment, et les deux responsables Alan Hopper et Grace
Babbage. »&lt;/p&gt;
&lt;p&gt;Après quelques minutes, je terminai l'entretien et proposai au stagiaire de lui
offrir le café. « Ça ne vous dérange pas si je branche mon téléphone sur votre
machine ? Je n'ai bientôt plus de batterie. » Je laissai mon smartphone sur
place et nous partîmes trouver le distributeur.&lt;/p&gt;
&lt;h2&gt;Chapitre 00000100&lt;/h2&gt;
&lt;p&gt;Le soir venu, j'avais regagné mes pénates avec la satisfaction du travail bien
fait. Pendant que la bouilloire chauffait l'eau du thé et que raisonnaient les
premières notes du &lt;em&gt;Requiem&lt;/em&gt; de Mozart, je sortis mon téléphone de ma poche et
le branchai sur mon fidèle laptop. Aussitôt apparurent une floppée de documents
que j'entrepris de parcourir.&lt;/p&gt;
&lt;p&gt;Le smartphone que j'avais branché sur le poste du stagiaire avant de l'y
« oublier » jusqu'à l'heure du départ hébergeait un logiciel tout ce qu'il y
avait de plus illégal. Disponible à prix d'or sur le marché noir, ce petit
bijou de technologie se répandait comme un virus sur toutes les machines du
réseau local pour subtiliser quantité d'informations correspondant à des
critères préconfigurés, avant d'effacer toutes traces de son passage.
Redoutable et indétectable, c'était le joujou rêvé des espions industriels de
tous poils. Il me restait à voir si la pêche avait été bonne.&lt;/p&gt;
&lt;p&gt;En survolant rapidement de récents compte-rendus du comité de pilotage,
j'obtins la confirmation qu'Alan Hopper, la victime, s'était fait le
promoteur de l'arrêt total et définitif du projet PoodleLife, pour des raisons
autant économiques qu'idéologiques. La branche dont il avait la maîtrise était
gravement déficitaire, et il était clair qu'il ne croyait de toutes façons pas
à la viabilité de l'entreprise.&lt;/p&gt;
&lt;p&gt;Sa plus virulente opposante était ma propre cliente, qui semblait porter un
intérêt personnel à ce que le projet aboutisse. Apparemment, leurs passes
d'armes de plus en plus virulentes étaient devenues un sujet de plaisanterie
récurrent.&lt;/p&gt;
&lt;p&gt;Quelques schémas et diagrammes m'en apprirent plus sur l'architecture du
système. À vrai dire, rarement avais-je vu logiciel mieux conçu, mieux pensé.
Sous la houlette d'Ada Turing était née une application au design élégant et à
la conception robuste. Si les quelques fragments de code sur lesquels je pus
mettre la main me restaient inaccessibles, la nomenclature claire et sans
ambiguïté des différente classes et modules me permit d'avoir une idée
relativement claire du fonctionnement global du projet.&lt;/p&gt;
&lt;p&gt;Mais l'information qui m'intéressait le plus tenait dans un petit fichiers de
quelques kilo-octets. S'il n'était pas possible de récupérer les montagnes de
données que représentaient le dépôt git complet, mon outil disposait d'un
module capable d'extraire l'intégralité des logs et messages de commits,
reconstituant ainsi l'histoire du code source.&lt;/p&gt;
&lt;p&gt;Alors que je parcourais les centaines de milliers de messages de commits écrits
par l'ensemble des membres de l'équipe, une branche en particulier attira mon
attention. Tous les commits concernés avaient été écrits par Ada elle-même, et
toujours en dehors des heures de bureau. La fusion des travaux de la brillante
ingénieure dans la branche principale s'était également produite au beau milieu
de la nuit, et n'avait apparemment pas été soumise à la préalable étape de
revue de code qui semblait être la norme dans l'équipe.&lt;/p&gt;
&lt;p&gt;Quand aux messages de commits, ils étaient pour le moins énigmatiques, pour ne
pas dire carrément cryptiques. C'était étrange, dans la mesure ou la
responsable technique était d'habitude d'une précision qui confinait à
l'obsession.&lt;/p&gt;
&lt;p&gt;Il me fallut bien des heures de profonde réflexion, de recherche intensive et
de fouille dans d'obscurs morceaux de codes isolés de tout contexte pour
finalement découvrir l'utilité de cette mystérieuse branche. Mais lorsque le
doute ne fut plus permis, un frisson me parcourut l'échine.&lt;/p&gt;
&lt;h2&gt;Chapitre 00000101&lt;/h2&gt;
&lt;p&gt;Depuis Agatha Christie, chaque enquête se terminait traditionnellement au coin
d'une cheminée par une réunion des différents protagonistes au cours de
laquelle le détective, la sagacité incarnée, démasquait méthodiquement le
coupable et son plan machiavélique aux yeux de tous. Ce genre de grand
déballage dramatique était totalement superflu mais mon côté cabotin me faisait
apprécier l'exercice, aussi avais-je pris l'habitude de ne pas déroger à la
tradition.  Même s'il me fallait souvent me passer de cheminée.&lt;/p&gt;
&lt;p&gt;En l'occurrence, j'avais choisi pour scène la salle rustique de l'Antigeek où
toute l'équipe du projet PoodleLife avait été conviée. Au détour d'un couloir,
ma cliente m'avait appris pourquoi elle avait choisi ce lieu de rendez-vous
pour ma rencontre avec Ada Turing : le tenancier était allergique à la
technologie et ne disposait d'aucune connexion internet ni d'aucun appareil
relié au Web.  Quand aux mobiles, l'épaisseur des murs leur bloquait l'accès au
réseau 3g.&lt;/p&gt;
&lt;p&gt;Je m'étais assuré que nous serions seuls grâce à la connivence du patron en lui
promettant que les retombées médiatiques compenseraient très largement son
manque à gagner. Aussi, lorsque nous nous retrouvâmes tous attablés, entassés
sur des chaises bancales, je pus commencer mon numéro. Je me levai et arpentai
lentement la pièce d'un air pénétré, prenant bien soin de faire grincer le
parquet mal ciré. Ménager mes effets n'avais pas pour seul avantage de
satisfaire mon sens théâtral ; en faisant monter la pression, on poussait
parfois le coupable à commettre des erreurs fatales.&lt;/p&gt;
&lt;p&gt;Après avoir rappelé brièvement les faits et révélé ma véritable identité à la
stupeur générale, je rentrai dans le vif du sujet.&lt;/p&gt;
&lt;p&gt;« Nous savons tous qu'Alan Hopper, votre vice-président exécutif, a été
assassiné. Nous connaissons déjà le coupable et le mode opératoire : Tim
Ritchie, pharmacien de son état, a empoisonné la victime ; étant donné sa
profession, se procurer le cyanure était facile. Mais nous savons aussi que M.
Ritchie suivait scrupuleusement et aveuglement les instructions données par
l'application développée par votre équipe. La véritable question qui se pose
est la suivante : quel enchaînement spécifique de décisions et d'actions ont
amené ces instructions précises à s'afficher sur cet écran particulier ?
L'assassin n'était qu'une marionnette ; qui est le marionnettiste ? »&lt;/p&gt;
&lt;p&gt;Si on avait pu photographier le silence, j'aurais immédiatement rempli quelques
pellicules tant le monde entier semblait suspendu à mes lèvres alors que je
reprenais mon souffle. Je poursuivis.&lt;/p&gt;
&lt;p&gt;« Dans ce genre de situation, il nous faut considérer deux éléments : le mobile
et l'occasion. D'abord, qui a intérêt à commettre le crime ? Ensuite, qui en a
eu la possibilité ?&lt;/p&gt;
&lt;p&gt;Commençons par le premier élément. J'ai d'abord envisagé la piste du bug pur et
simple, mais cela me paraît maintenant hautement improbable. Le meurtre était
trop parfait, trop précis, trop bien orchestré pour qu'il puisse s'agir d'un
simple dysfonctionnement. Il y a un esprit conscient derrière cet
empoisonnement.  Or, il est de notoriété publique que la victime était devenue
farouchement opposée au développement de PoodleLife. Un esprit un peu tordu
pourrait imaginer que toute personne un tant soit peu impliquée dans le projet
avait un motif pour la mettre sur la touche… »&lt;/p&gt;
&lt;p&gt;Murmures dans la salle. Quelques regards se tournèrent vers Grace dont les
altercations avec la victime étaient légendaires.&lt;/p&gt;
&lt;p&gt;« Considérons maintenant l'occasion. L'application est censée être pilotée
intégralement par la gigantesque intelligence artificielle qu'est PoodleMum.
Pourtant, c'est un secret de Polichinelle, il existe une backdoor permettant
d'outrepasser manuellement les décisions prises par l'IA. Seulement trois
personnes avaient les accès nécessaires : la victime elle-même, la responsable
technique, Ada Turing, et la vice-présidente Grace Babbage. »&lt;/p&gt;
&lt;p&gt;Nouveau murmures plus appuyés, et nouveaux regards gênés vers la
vice-présidente. Alors que la tension devenait palpable, ma cliente se mit à
bafouiller, outrée. « Qu'êtes-vous en train d'insinuer, M. Dijkstra ?! » Je
l'interrompis d'un geste péremptoire, et après avoir fait monter la pression
d'un cran en sirotant théâtralement mon café, j'enchaînai.&lt;/p&gt;
&lt;p&gt;« Pour une entreprise de votre stature, je dois dire que je suis très déçu du
piètre niveau de sécurisation de votre réseau. Lors de ma présence en vos
locaux, j'ai ainsi pu accéder à des données normalement hors de portée d'un
simple intervenant extérieur. Par exemple, saviez-vous que tous les accès à la
backdoor susmentionnée sont systématiquement consignés ? Or, dans le mois qui a
précédé le crime, le fichier de log indique qu'une seule personne a utilisé son
privilège pour outrepasser PoodleMum… »&lt;/p&gt;
&lt;p&gt;Quelques minutes auparavant, j'aurais pu parier mon salaire que le silence ne
pouvait pas devenir plus assourdissant. Je me trompai lourdement. La tension
était telle qu'on aurait entendu une mouche refaire ses lacets. Exultant
intérieurement, c'est d'un ton parfaitement détaché que je lâchai du bout des
lèvres : « la seule personne mentionnée dans ce fameux fichier de log, c'est
Ada Turing. »&lt;/p&gt;
&lt;p&gt;Tous les regards se braquèrent immédiatement vers la brillante ingénieure,
tandis que ses voisins immédiats, embarrassés, reculaient imperceptiblement
leurs chaises comme si on venait de lui diagnostiquer une maladie contagieuse.
Indifférente, la responsable technique continuait de me fixer avec une totale
absence d'expression digne d'un banquier suisse.&lt;/p&gt;
&lt;p&gt;« Néanmoins, poursuivis-je, d'autres éléments me permettent d'affirmer que Mme.
Turing ici présente n'est pas l'assassin. »&lt;/p&gt;
&lt;h2&gt;Chapitre 00000110&lt;/h2&gt;
&lt;p&gt;Le silence laissa la place au brouhaha tandis qu'éclataient exclamations et
discussions confuses. Je patientai plusieurs minutes en sirotant tranquillement
mon café le temps que le niveau sonore retrouve un niveau acceptable, puis fis
taire les dernières conversations d'un regard péremptoire.&lt;/p&gt;
&lt;p&gt;« Voyez-vous, repris-je, cette fameuse entrée dans les logs de PoodleMum
incrimine Mme. Turing sans aucune ambiguïté ; du pain béni pour un enquêteur.
Le problème, c'est qu'au moment du meurtre, cette ligne n'existait pas. J'ai
découvert, au détour d'une archive, que le log a été trafiqué après coup : il
s'agit d'une fabrication de fausse preuve patente. &lt;br /&gt;
— Voulez-vous dire, m'interrompit le jeune stagiaire Pascal Delphi, que
quelqu'un a modifié volontairement les logs pour faire incriminer Ada ? &lt;br /&gt;
— C'est très exactement ce que je suis en train de dire. &lt;br /&gt;
— Et avez-vous découvert qui a trafiqué ce fichier ?&lt;br /&gt;
— Évidemment, je l'ai découvert. Ce fichier a été modifié par Ada elle-même. »&lt;/p&gt;
&lt;p&gt;Exaspérée, ma cliente se leva vivement et s'exclama : « tout cela devient
ridicule, M. Dijkstra ! Êtes-vous en train de prétendre que notre responsable
technique aurait fabriqué une fausse preuve pour s'incriminer elle-même d'un
crime commis par quelqu'un d'autre ? A-t-on déjà entendu une idée plus
absurde ? Pourriez-vous cesser de nous faire perdre notre temps et arrêter de
tourner autour du pot ?&lt;br /&gt;
— J'y arrive. Voyez-vous, si Mme. Turing n'est pas l'assassin, elle n'est pas
pour rien dans la mort d'Alan Hopper. » J'apostrophai directement
l'ingénieure : « vous avez du accumuler pas mal de sommeil en retard, avec
toutes ces nuits au bureau, n'est-ce pas ?! »&lt;/p&gt;
&lt;p&gt;L'intéressée me répondit par le mutisme le plus absolu tandis que les regards
curieux se tournaient de nouveau vers elle. Je poursuivis ma démonstration.&lt;/p&gt;
&lt;p&gt;« PoodleMum est un véritable chef-d'œuvre d'ingénierie, et personne n'y a
autant contribué qu'Ada. Que d'engagement et d'abnégation ! Seulement voilà,
m'exclamai-je en la fixant délibérément, vous avez pris quelques libertés avec
le périmètre fonctionnel du projet, n'est-ce pas ? &lt;br /&gt;
— Qu'est-ce que cela signifie ? s'enquit ma cliente. Ada, qu'est-ce qu'il veut
dire ? &lt;br /&gt;
— Cela signifie, m'interposai-je, que certaines fonctionnalités de PoodleMum
ont été codées et livrées clandestinement par votre responsable technique. &lt;br
/&gt;
— Je ne comprends pas. Quelles fonctionnalités ?&lt;br /&gt;
— Voici ce qu'il s'est passé. Mme. Turing est une personne brillante, et elle a
voulu doter sa création de ce qui se fait de mieux en matière d'intelligence
artificielle. À l'insu de tous, elle a fait évoluer PoodleMum jusqu'à lui
permettre de formuler ses propres désirs et ses propres intentions. Mais les
événements nous ont montré que doter une machine omnisciente et
quasi-omnipotente d'une conscience et d'un instinct de survie n'est pas une
idée sans risque. Lorsque PoodleMum a réalisé que son existence était en
danger, elle a pris les mesures qui s'imposaient. Alan Hopper était
partisan de l'abandon du projet, il a été éliminé. &lt;br /&gt;
— Êtes-vous en train de prétendre que PoodleMum est devenue vivante ? Comme
dans un film de science-fiction ?&lt;br /&gt;
— Personne ne prétend que PoodleMum a pris vie. C'est simplement un algorithme
un peu trop complexe, un peu trop bien pensé, qui a accès a beaucoup trop de
données. Une machine méthodique, implacable, capable de se débarrasser d'une
vie humaine avec la même désinvolture qu'un humain qui jette un vieux
sous-vêtement. Le parfait psychopathe, en somme. &lt;br /&gt;
— Mais alors, s'exclama le stagiaire, si c'est PoodleMum la coupable, pourquoi
Ada aurait-elle trafiqué les logs pour fabriquer de fausses preuves contre
elle-même ? »&lt;/p&gt;
&lt;p&gt;Je dévisageai l'ingénieure. Toujours muette, elle arborait cet air
d'exaspération teintée de mépris caractéristique des fondateurs de startups
overbookés à qui on annonce un contrôle de l'URSSAF. « Vous vous rendez
bien compte que PoodleMum est devenue trop intelligente pour son propre bien ?
lui lançai-je. Même vous ne pouvez plus la contrôler, ce projet doit être
stoppé ! »&lt;/p&gt;
&lt;p&gt;Elle finit par sortir de son mutisme. « Félicitations, M. Dijkstra ! Vous venez
de sonner le glas du projet le plus ambitieux de toute l'histoire de
l'humanité. » Ce furent les seuls mots qu'elle prononça de toute la soirée.&lt;/p&gt;
&lt;h2&gt;Épilogue&lt;/h2&gt;
&lt;p&gt;Les mains bien enfoncées au fond de mes poches, les épaules crispées par le
froid du petit matin, j'attendais ma cliente qui devais me rejoindre pour
signer ma lettre de fin de mission. Je tentais vaguement de paraître ténébreux
et pénétré tout en m'amusant discrètement à souffler pour former des nuages de
vapeur.&lt;/p&gt;
&lt;p&gt;Je finis par l'apercevoir et la saluai d'un hochement de tête dont toute
solennité disparu après que j'eus dû courir 15 mètres pour rattraper mon
Fedora arraché par une bourrasque.&lt;/p&gt;
&lt;p&gt;« Pas chaud, pas vrai ? » dis-je à défaut de trouver une meilleure
introduction. « Oui, ça caille » répondit-elle, visiblement pas plus inspirée.&lt;/p&gt;
&lt;p&gt;Nous restâmes silencieux quelques secondes. Je cherchai sans le trouver un
élément suffisamment romanesque ou poser les yeux et qui m'aurait permis de
justifier un regard inspiré vers l'horizon, avant de me rabattre faute de mieux
sur un chien maigrichon en train de déchirer une poubelle de l'autre côté de la
rue.&lt;/p&gt;
&lt;p&gt;« On doit reconnaître à Ada Turing une certaine grandeur d'âme, dis-je. Il faut
de la noblesse et du courage pour essayer de protéger son enfant en se faisant
accuser à sa place. Même si l'enfant en question est un logiciel. &lt;br /&gt;
— Oui, Ada est décidément une personne bien singulière. &lt;br /&gt;
— J'imagine que le projet PoodleLife ne verra jamais le jour ?&lt;br /&gt;
— Nous avons purgé et démantelé tous les serveurs. J'ai personnellement
supervisé l'opération. PoodleMum n'est plus qu'un souvenir, j'en ai peur. »&lt;/p&gt;
&lt;p&gt;On entendit clairement raisonner les jappements d'allégresse du vieux cabot
ayant sans doute dégoté une vieille carcasse malodorante que seul un esprit de
chien errant pourrait trouver appétissante.&lt;/p&gt;
&lt;p&gt;« Vous teniez beaucoup à ce projet, n'est-ce pas ?&lt;br /&gt;
— Après ce qui s'est passé, ne pas tout arrêter aurait été difficile à
justifier, n'est-ce pas ? &lt;br /&gt;
— Et Ada ? &lt;br /&gt;
— Et bien, elle est aux mains de la justice. Je serai curieuse de connaître les
charges qui seront retenues contre elle. Entrave à la justice ? Fabrication de
fausse preuve ? &lt;br /&gt;
— Certaines personnes sont trop visionnaires pour leur propre bien. »&lt;/p&gt;
&lt;p&gt;Nous nous séparâmes après avoir échangé quelques dernières banalités. Je
regagnai mon bureau tandis que la ville s'éveillait, pour découvrir dans ma
salle d'attente un type ventripotent portant un costume visiblement très
onéreux et d'un rare mauvais goût.&lt;/p&gt;
&lt;p&gt;« M. Dijkstra ? &lt;br /&gt;
— Lui même. &lt;br /&gt;
— J'ai besoin de vos services. Je dois totalement disparaître du Web d'ici
samedi. »&lt;/p&gt;
&lt;p&gt;En route pour de nouvelles e-aventures !&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Admin</dc:creator><pubDate>Fri, 23 Oct 2015 10:03:00 +0200</pubDate><guid>tag:www.miximum.fr,2015-10-23:le-cauchemar-de-turing.html</guid><category>polargeek</category><category>fiction</category><category>nouvelle</category></item><item><title>La veille techno pour les vieux croûtons</title><link>http://www.miximum.fr/veille-techno-vieux-croutons-paris-web-2015.html</link><description>&lt;p&gt;Voici la transcription de la conférence « La veille techno pour les vieux
croûtons » que &lt;a href="http://www.miximum.fr/compte-rendu-paris-web-2015.html"&gt;j'ai eu le grand plaisir de donner à Paris Web
2015&lt;/a&gt;.&lt;/p&gt;
&lt;figure class="full align-center"&gt;
    &lt;img alt="Saint Jérôme écrivant, tableau de Caravage" class="full shadow"
        src="http://i.miximum.fr/i/2015/10/TUM4VFT4OB.jpg" /&gt;
    &lt;figcaption&gt;
        &lt;i&gt;Développeur Cobol corrigeant un bug css sur le site de la Banque Postale&lt;/i&gt; — Caravage
    &lt;/figcaption&gt;
&lt;/figure&gt;



&lt;h2&gt;Intro&lt;/h2&gt;
&lt;p&gt;Une nuit, en 2014, j'ai fait un cauchemar. J'ai rêvé que j'étais dans une pièce
sans fenêtre, vouté sur mon clavier, couvert d'une couche de poussière
fossilisée, et je corrigeais des bugs sur des applications en COBOL pour le
compte de l'industrie financière. Je me suis réveillé en sursaut et c'est là
que j'ai pris conscience que je m'étais transformé, en quelques mois,
insidieusement, en vieux croûton du Web.&lt;/p&gt;
&lt;h2&gt;Les vieux croûtons&lt;/h2&gt;
&lt;figure class="full align-center"&gt;
    &lt;img alt="Tableau représentant Diogène par Trutovskiy Konstantin Na Senovale" class="full border shadow"
        src="http://i.miximum.fr/i/2015/10/QZ8GBGAO5U.jpg" /&gt;
    &lt;figcaption&gt;
&lt;i&gt;Directeur technique refusant à l'équipe la migration de cvs à git&lt;/i&gt; — Trutovskiy Konstantin Na senovale
    &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Alors qu'est-ce que j'entends par « vieux croûton » ? Une vieille croûtonne ou
un vieux croûton est une personne qui a laissé ses compétences devenir
obsolètes. Par exemple, c'est la personne qui refuse d'entendre qu'utiliser les
tableaux pour faire sa mise en page, ce n'est peut-être pas le top, ou que les
pages d'intro en flash, ce n'est peut-être pas une super idée. On en connait
tous.&lt;/p&gt;
&lt;p&gt;Dans « vieux croûton », il y a vieux. C'est pour ça que je suis obligé de
préciser que ce n'est absolument pas une question d'âge. Refusons de sombrer
dans le jeunisme primaire. On peut être un jeune vieux croûton. À l'inverse, on
peut avoir n'importe quel âge et ne pas être un vieux croûton. Être un vieux
croûton, ce n'est pas une question d'âge, c'est une question d'attitude
mentale.&lt;/p&gt;
&lt;p&gt;Le vieux croûton, c'est celui qui ne veut pas changer, qui s'accroche à ses
vieilles habitudes, à ses vieux outils, à ses vieilles méthodes de travail. Qui
pense qu'il a appris une fois, maintenant &lt;em&gt;il sait&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Il existe deux catégories de vieux croûtons. D'abord, il y a le vieux croûton
qui n'assume pas, comme moi. C'est celui qui est conscient de sa condition, qui
a un peu honte, et qui est triste, parce qu'il se sent comme une sous-merde en
sortant des conférences de Christophe Porteneuve.&lt;/p&gt;
&lt;p&gt;L'autre catégorie, c'est ceux qui assument &lt;em&gt;à mort&lt;/em&gt; : en général, ils finissent
directeurs techniques, et ils font chier toute l'équipe en les forçant à
utiliser cvs.&lt;/p&gt;
&lt;h2&gt;La veille techno&lt;/h2&gt;
&lt;p&gt;Le proverbe dit qu'une scie qui ne s'affûte s'émousse. Si on traduit ça au Web,
un des antidotes à la vieucroûtonnitude c'est la veille techno.&lt;/p&gt;
&lt;p&gt;Tester les nouvelles librairies ; jouer avec le dernier framework à la mode ;
se maintenir à jour sur l'actualité des standards ; bidouiller avec les
dernières apis, etc. Toujours se remettre en question, toujours maintenir ses
compétences à niveau. Le développeur, s'il s'affûte sans cesse sciera sans
soucis.&lt;/p&gt;
&lt;h2&gt;Le burnout&lt;/h2&gt;
&lt;figure class="full align-center"&gt;
    &lt;img alt="Jael et Sisera, tableau par Artemisia" class="full border shadow"
        src="http://i.miximum.fr/i/2015/10/294VLVUZF4.jpg" /&gt;
    &lt;figcaption&gt;
&lt;i&gt;Documentation de Node.js. Allégorie.&lt;/i&gt; — Artemisia
    &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Jusqu'à maintenant, je pratiquais la veille techno sans trop y penser, c'était
plutôt naturel. Dès qu'un nouveau truc sortait, c'était « oh, un nouveau
framework javascript », et je passais un week-end à le tester en bidouillant un
petit projet perso. Et puis en 2014, je me suis rendu compte que mon attitude
avait changé.&lt;/p&gt;
&lt;p&gt;Au lieu de voir la veille techno comme un truc fun, c'était devenu un gros
rouleau compresseur qui avançait derrière moi. Vous savez, un peu comme quand
vous voyez votre évier se remplir petit à petit, et que vous avez de moins en
moins envie de vous y mettre.&lt;/p&gt;
&lt;p&gt;Et j'en suis arrivé au point ou j'ai saturé, j'ai tout envoyé valser. J'ai pris
tout ce qu'il y avait dans l'évier, je l'ai mis à la poubelle, et j'ai acheté
de la vaisselle en plastique. Métaphoriquement, j'entends, hein ?! En gros,
j'ai arrêté complètement la veille, j'étais devenu allergique à tout ce qui
ressemblait de près ou de loin à un article technique.&lt;/p&gt;
&lt;p&gt;Et ça m'a tellement dégoûté que je n'avais même plus envie de travailler,
j'étais à deux doigts d'appeler mes clients pour leur dire d'arrêter de me
faire scier.&lt;/p&gt;
&lt;h2&gt;Petite séance d'introspection&lt;/h2&gt;
&lt;figure class="full align-center"&gt;
    &lt;img alt="La Sieste, tableau de Van Gogh" class="full border shadow"
        src="http://i.miximum.fr/i/2015/10/BKWI2KGVQW.jpg" /&gt;
    &lt;figcaption&gt;
&lt;i&gt;Équipe de développement pendant un DDoS sur Github&lt;/i&gt; — Van Gogh
    &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;J'ai fini par me demander comment j'en étais arrivé là. Et j'en suis arrivé à
la conclusion que la veille techno c'était 1) extrêmement chronophage, et 2)
répétitif et chiant à mourir.&lt;/p&gt;
&lt;p&gt;Pourquoi c'est chronophage ? Parce que dans le Web, tout change &lt;em&gt;très vite&lt;/em&gt; ;
et &lt;em&gt;tout&lt;/em&gt; change très vite. Si on veut se tenir à la pointe sur les nouveaux
outils, les méthodes, les langages, les frameworks, les apis, les standards,
les pratiques, etc. ça prend un temps considérable. D'ailleurs, une personne
qui travaille dans le Web, c'est un peu comme un bûcheron : si elle veut rester
utile, de temps en temps, il faut qu'elle change de branche.&lt;/p&gt;
&lt;p&gt;Pourquoi c'est répétitif ? Je me souviens qu'à une époque, j'ai eu l'impression
d'être Bill Murray dans le Jour de la Marmotte, et de vivre la même journée en
boucle, encore et encore. Le matin, c'était l'annonce de la sortie du nouveau
framework en js. L'après-midi, c'était l'annonce de la sortie du nouveau task
manager en js. Se palucher la doc du 15e framework de l'année, qui fait la même
chose que tous les autres, pour les moins monomaniaques d'entre nous, c'est un
tout petit peu chiant, en fait.&lt;/p&gt;
&lt;h2&gt;Ma recherche de solution&lt;/h2&gt;
&lt;figure class="full align-center"&gt;
    &lt;img alt="Autoportrait, tableau de Courbet" class="full border shadow"
        src="http://i.miximum.fr/i/2015/10/ULIJDBW5PQ.jpg" /&gt;
    &lt;figcaption&gt;
&lt;i&gt;Intégrateur tentant de comprendre l'API Appcache — Courbet&lt;/i&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Finalement, après quelques mois à me complaire dans mon attitude de vieux
croûton, je me suis dit qu'il n'était pas possible que je continue comme ça, et
qu'il fallait faire quelque chose. Après avoir un peu réfléchi à la question,
j'ai décidé de faire trois choses.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Zéroièmement : arrêter de culpabiliser de n'être pas 100% à la pointe.&lt;/li&gt;
&lt;li&gt;Premièrement : avoir une veille techno plus efficace.&lt;/li&gt;
&lt;li&gt;Deuxièmement : avoir une veille techno plus enrichissante.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Se débarrasser de la culpabilité&lt;/h2&gt;
&lt;figure class="full align-center"&gt;
    &lt;img alt="La naissance de Venus, tableau de Botticelli" class="full border shadow"
        src="http://i.miximum.fr/i/2015/10/HHNXBDZ68N.jpg" /&gt;
    &lt;figcaption&gt;
&lt;i&gt;Discours d'ouverture par la présidente de Cap d'Agde Web&lt;/i&gt; — Botticelli
    &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Quelle est ma valeur ajoutée, qu'attendent de moi mes clients, qu'est-ce que je
dois faire pour bien faire mon boulot ? Avoir une bonne vision de l'écosystème
numérique, et proposer des solutions pertinentes en fonction du contexte. Je
n'ai pas besoin de tout savoir pour être un bon professionnel.&lt;/p&gt;
&lt;p&gt;J'avais tendance à culpabiliser de ne pas utiliser les dernières technos, les
dernières apis, etc. J'ai appris à lâcher prise là dessus. Des fois, je monte
des projets, je n'utilise que des technos chiantes qui ont plus de cinq ans. Ne
le dites pas à mes clients. Les projets sont super stables !&lt;/p&gt;
&lt;p&gt;Je ne dit pas qu'il ne faut plus utiliser de trucs modernes. Mais je me suis
débarrassé du poids de la culpabilité ; maintenant, quand je décide de
consacrer mon week-end à faire une rando, j'en profite mieux.&lt;/p&gt;
&lt;h2&gt;Rechercher l'efficacité pour y consacrer moins de temps&lt;/h2&gt;
&lt;figure class="full align-center"&gt;
    &lt;img alt="La leçon d'anatomie du Dr Tulp, tableau de Rembrandt" class="full border shadow"
        src="http://i.miximum.fr/i/2015/10/6ZVPZVH9O4.jpg" /&gt;
    &lt;figcaption&gt;
&lt;i&gt;Autopsie d'une victime d'overdose de Javascript&lt;/i&gt; — Rembrandt
    &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Malgré tout, la veille techno reste essentielle. Comment faire pour y consacrer
moins de temps ?&lt;/p&gt;
&lt;p&gt;Je pense que la manière la plus efficace de se maintenir à jour, ce n'est pas
de tester une techno directement, par exemple sur un projet perso, c'est
d'aller chercher des retours d'expériences.&lt;/p&gt;
&lt;p&gt;Parce que d'abord, un petit projet perso n'est pas représentatif des conditions
d'un vrai projet. Et surtout, ce qui nous intéresse, ce ne sont pas les détails
d'implémentation du truc. Ce qu'on veut savoir c'est :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;quels sont les cas d'utilisation pertinents de cette techno ?&lt;/li&gt;
&lt;li&gt;quels sont les avantages et les inconvénients par rapport à une techno X ?&lt;/li&gt;
&lt;li&gt;qu'est-ce que je vais y gagner ?&lt;/li&gt;
&lt;li&gt;quels problèmes je vais rencontrer ?&lt;/li&gt;
&lt;li&gt;est-ce que la techno est maintenue correctement ?&lt;/li&gt;
&lt;li&gt;etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;En allant parler cinq minutes à des gens qui ont mis en œuvre la techno sur des
vrais projets, on sait déjà si c'est une techno qui mérite qu'on s'y attarde ou
pas.&lt;/p&gt;
&lt;p&gt;Ce que j'essaye de faire aussi, c'est cibler ma veille. Il y a beaucoup
d'effets de mode, des technos qui débarquent, brillent pendant un an, et
disparaissent. Investir du temps dans ce genre de truc, ça me fait chier. Ce
qui m'intéresse, ce sont les technos utiles et matures.&lt;/p&gt;
&lt;p&gt;Une techno mature, c'est une techno qui a vécu. C'est à dire qu'elle est
stable, qu'il y a de la doc, et que les cas d'utilisation sont bien compris et
bien référencés. Parce qu'il y a des technos qui apparaissent, qui ont une
utilité, mais qui sont utilisées à tort et à travers uniquement parce qu'elles
sont à la mode. NoSQL, vous vous rappelez ? En s'attachant à bien comprendre
les cas d'utilisation d'une techno, on évite ce genre d'écueil.&lt;/p&gt;
&lt;p&gt;En ciblant sa veille, on gagne du temps.&lt;/p&gt;
&lt;h2&gt;Retrouver le fun&lt;/h2&gt;
&lt;figure class="full align-center"&gt;
    &lt;img alt="La persistance de la mémoire, tableau de Dali" class="full border shadow"
        src="http://i.miximum.fr/i/2015/10/1RYE7TXF4Q.jpg" /&gt;
    &lt;figcaption&gt;
&lt;i&gt;Anatomie d'un projet Ruby On Rails&lt;/i&gt; — Dali
    &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;À mon avis le plus important. J'essaye de faire en sorte de garder une veille
techno enrichissante.&lt;/p&gt;
&lt;p&gt;Quand je m'attarde sur une techno, ça va être soit 1) parce que je sais que je
vais pouvoir la mettre en œuvre immédiatement ou 2) parce qu'elle va me
permettre d'aborder de nouveau concepts ou de nouvelles idées, et donc de
m'ouvrir l'esprit.&lt;/p&gt;
&lt;p&gt;Passer une soirée à étudier un n-ième framework js, ça n'a pour moi strictement
aucun intérêt. Par contre, étudier un truc totalement différent de ce que je
fais d'habitude, là c'est intéressant.&lt;/p&gt;
&lt;p&gt;Vous connaissez la blague, un chercheur, c'est quelqu'un qui étudie de plus en
plus en détail un domaine de plus en plus réduit ; par conséquent, un
spécialiste est quelqu'un qui connait tout sur rien.&lt;/p&gt;
&lt;p&gt;Pourtant on sait que c'est comme ça que nait l'innovation : en combinant des
idées qui à la base n'ont pas de lien direct entre elles. Donc plus on s'ouvre
à des idées et concepts divers et variés, plus on va s'enrichir et plus on va
s'ouvrir des portes pour trouver des solutions innovantes aux problèmes du
quotidien.&lt;/p&gt;
&lt;p&gt;Il y a des tas de domaines qui ne me sont pas directement utiles au quotidien :
Android, Bitcoin, la cryptographie, l'électronique, le &lt;em&gt;machine learning&lt;/em&gt;…&lt;/p&gt;
&lt;p&gt;Une veille techno intéressante, c'est une veille techno qui reste fun.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;figure class="full align-center"&gt;
    &lt;img alt="La danse, tableau de Matisse" class="full border shadow"
        src="http://i.miximum.fr/i/2015/10/E47KZHZ26M.jpg" /&gt;
    &lt;figcaption&gt;
&lt;i&gt;Équipe projet au retour de Paris Web&lt;/i&gt; — Matisse
    &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Je n'ai plus de temps et ça tombe bien car j'arrive à la fin.&lt;/p&gt;
&lt;p&gt;Finalement, un vieux croûton, ce n'est pas quelqu'un qui est trop âgé, c'est
quelqu'un qui ne veut plus apprendre.&lt;/p&gt;
&lt;p&gt;J'ai longtemps cru que la veille techno était la solution pour ne pas devenir
un vieux croûton. Finalement je me suis aperçu que, de manière très ironique,
c'est ma veille techno, parce que je l'ai mal pratiqué, qui m'a amené à me
transformer en vieux croûton.&lt;/p&gt;
&lt;p&gt;Il y a des solutions, pour se maintenir à jour tout en gardant une vie à côté.
Mais surtout, il faut être conscient que, années après années, garder le
plaisir qu'on a à pratiquer notre métier demande un effort conscient.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Admin</dc:creator><pubDate>Thu, 15 Oct 2015 13:00:00 +0200</pubDate><guid>tag:www.miximum.fr,2015-10-15:veille-techno-vieux-croutons-paris-web-2015.html</guid><category>paris web</category><category>veille techno</category><category>conférence</category></item><item><title>Mon compte rendu partial et subjectif de Paris Web 2015</title><link>http://www.miximum.fr/compte-rendu-paris-web-2015.html</link><description>&lt;p&gt;Que dire sur Paris Web qui n'ait déjà été dit ? Et pourtant… Paris Web, c'est
un superbe événement à l'organisation irréprochable avec des conférences
passionnantes prodiguées par des experts à la pointe de leur domaine pendant
deux jours…&lt;/p&gt;
&lt;p&gt;Mais ça, en fait, on s'en fout, c'est juste du discours marketing de loosers…&lt;/p&gt;


&lt;p&gt;On s'en fout, parce que de toutes façons, les orateurs sont aussi paumés que
toi et personne n'essaye de te faire croire le contraire.&lt;/p&gt;
&lt;p&gt;On s'en fout, parce que la plupart des confs, tu en connais déjà plus ou moins
le contenu, déjà plus ou moins lu ou entendu à une édition précédente ou dans
une autre conf.&lt;/p&gt;
&lt;p&gt;On s'en fout, parce que tu vas forcément apprendre des trucs, mais en vérité,
tu n'es pas venu pour devenir un super ninja du Web.&lt;/p&gt;
&lt;p&gt;Tout ça, on s'en fout, parce qu'au fond, si tu viens à Paris Web, c'est pour
l'effet Paris Web.&lt;/p&gt;
&lt;p&gt;L'effet Paris Web, c'est avoir des jambes qui tremblotent quand &lt;a href="https://twitter.com/SandyParis1972"&gt;une interprète
LSF&lt;/a&gt; te raconte que ce sont deux jours
terriblement éprouvants pour elle mais qu'elle attends ce moment avec
impatience tous les ans et qu'elle ne laisserait sa place pour rien au monde.&lt;/p&gt;
&lt;p&gt;L'effet Paris Web, c'est rentrer dans ta chambre d'hôtel à 2h du matin et ne
pas pouvoir dormir parce que tu as les larmes aux yeux tellement tu es
bouleversé en repensant à toute l'énergie positive que tu as engrangé dans la
journée.&lt;/p&gt;
&lt;p&gt;L'effet Paris Web, c'est l'insupportable frustration de savoir que tu as à
peine pu papoter avec les gens que tu connais et qu'il y en a encore des
centaines à qui tu n'as même pas pu adresser la parole.&lt;/p&gt;
&lt;p&gt;L'effet Paris Web, c'est voir un atelier sur ES6 perturbé par le passages de
deux licornes qui chantent &lt;del&gt;le thème des Bisounours&lt;/del&gt; &lt;a href="https://www.youtube.com/watch?v=a-xWhG4UU_Y"&gt;Pink Fluffy
Unicorns Dancing On Rainbows &lt;/a&gt; en
faisant le tour de la salle.&lt;/p&gt;
&lt;p&gt;L'effet Paris Web, c'est bénir la chance que tu as de bosser dans un domaine ou
tu peux côtoyer autant de gens ouverts, intelligents, positifs, talentueux,
généreux, aventureux, juste humains…&lt;/p&gt;
&lt;p&gt;L'effet Paris Web, c'est repartir avec une espèce de fierté un peu bizarre et
irrationnelle… Celle d'appartenir à une communauté aussi géniale.&lt;/p&gt;
&lt;p&gt;L'effet Paris Web, c'est lire l'épuisement total dans les yeux hallucinés des
membres du staff et te dire que tu ne pourra jamais leur exprimer la mesure de
l'admiration et la reconnaissance que tu leur voue pour le travail qu'ils ont
fourni, et à quel point cet événement et tout ce qu'il représente compte pour
toi.&lt;/p&gt;
&lt;p&gt;Alors… merci !&lt;/p&gt;
&lt;blockquote class="instagram-media" data-instgrm-version="5" style=" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:658px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);"&gt;&lt;div style="padding:8px;"&gt; &lt;div style=" background:#F8F8F8; line-height:0; margin-top:40px; padding:50.0% 0; text-align:center; width:100%;"&gt; &lt;div style=" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAAGFBMVEUiIiI9PT0eHh4gIB4hIBkcHBwcHBwcHBydr+JQAAAACHRSTlMABA4YHyQsM5jtaMwAAADfSURBVDjL7ZVBEgMhCAQBAf//42xcNbpAqakcM0ftUmFAAIBE81IqBJdS3lS6zs3bIpB9WED3YYXFPmHRfT8sgyrCP1x8uEUxLMzNWElFOYCV6mHWWwMzdPEKHlhLw7NWJqkHc4uIZphavDzA2JPzUDsBZziNae2S6owH8xPmX8G7zzgKEOPUoYHvGz1TBCxMkd3kwNVbU0gKHkx+iZILf77IofhrY1nYFnB/lQPb79drWOyJVa/DAvg9B/rLB4cC+Nqgdz/TvBbBnr6GBReqn/nRmDgaQEej7WhonozjF+Y2I/fZou/qAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;"&gt;&lt;/div&gt;&lt;/div&gt;&lt;p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;"&gt;&lt;a href="https://instagram.com/p/8UCGN-D00C/" style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;" target="_blank"&gt;A video posted by Laurence Vagner (@hellgy)&lt;/a&gt; on &lt;time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2015-10-01T23:13:48+00:00"&gt;Oct 1, 2015 at 4:13pm PDT&lt;/time&gt;&lt;/p&gt;&lt;/div&gt;&lt;/blockquote&gt;

&lt;script async defer src="//platform.instagram.com/en_US/embeds.js"&gt;&lt;/script&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Admin</dc:creator><pubDate>Sun, 04 Oct 2015 10:20:00 +0200</pubDate><guid>tag:www.miximum.fr,2015-10-04:compte-rendu-paris-web-2015.html</guid><category>paris web</category></item><item><title>Des projets qui réussissent grâce à l'agilité</title><link>http://www.miximum.fr/agilite-des-projets-qui-reussissent.html</link><description>&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://www.tripodocus.fr/pictures/2015/fete-foraine-2/"&gt;&lt;img alt="Montagne russe" class="full border shadow" src="http://www.tripodocus.fr/pictures/2015/fete-foraine-2_medium.jpg" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;Construire ou faire construire des logiciels reste encore aujourd'hui une
entreprise hasardeuse. D'autant plus lorsqu'on utilise des méthodes de travail
qui ne sont pas adaptées.&lt;/p&gt;
&lt;p&gt;Les méthodes agiles proposent des alternatives intéressantes aux méthodes de
gestion de projet «&amp;nbsp;traditionnelles&amp;nbsp;». Mais l'agilité, ce n'est pas qu'un
ensemble de techniques, de pratiques, ou d'outils.&lt;/p&gt;
&lt;p&gt;Voici la version écrite un peu complétée de ma présentation «&amp;nbsp;Des projets qui
marchent grâce à l'agilité&amp;nbsp;» &lt;a class="reference external" href="http://www.miximum.fr/e1-2015-toulon.html"&gt;donnée lors la conférence E1 de toulon&lt;/a&gt; et dont &lt;a class="reference external" href="https://www.youtube.com/watch?v=uN_zZlJChRA&amp;amp;index=8&amp;amp;list=PLeYy6GyBfwLils7vtD9VIAqMYmG2OSQtB"&gt;la vidéo est disponible sur Youtube&lt;/a&gt;.&lt;/p&gt;

&lt;div class="section" id="le-logiciel-une-industrie-en-crise"&gt;
&lt;h2&gt;Le logiciel, une industrie en «&amp;nbsp;crise&amp;nbsp;»&lt;/h2&gt;
&lt;p&gt;Même si les choses s'améliorent petit à petit, &lt;a class="reference external" href="https://larlet.fr/david/stream/2015/05/25/"&gt;les projets de développement de
software réussissent rarement&lt;/a&gt;.
C'est une affirmation très approximative, puisque décider de la réussite ou de
l'échec d'un projet reste &lt;em&gt;dans une certaine mesure&lt;/em&gt; subjectif.&lt;/p&gt;
&lt;p&gt;Le Chaos Manifesto, l'une des rares études un peu rigoureuse s'intéressant au
sujet, propose les catégories suivantes&amp;nbsp;:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;un &lt;strong&gt;projet réussi&lt;/strong&gt; est un projet livré en respectant les budgets et délais
impartis et fournissant les fonctionnalités préalablement requises&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;un &lt;strong&gt;projet challengé&lt;/strong&gt; est livré avec des retards, des dépassements de budget
et / ou moins que les fonctionnalités demandées&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;un &lt;strong&gt;projet échoué&lt;/strong&gt; est tout simplement abandonné avant sa complétion, ou
est livré mais jamais utilisé.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;En 2012, à peine 39% réussissent d'après cette étude. Notez que &lt;a class="reference external" href="http://www.drdobbs.com/architecture-and-design/the-non-existent-software-crisis-debunki/240165910"&gt;d'autres
contestent ces chiffres et donnent des résultats plus positifs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Dans tous les cas, gardons à l'esprit que &lt;a class="reference external" href="https://aterny.wordpress.com/2013/07/16/can-we-believe-the-chaos-report/"&gt;ces études restent au mieux
approximatives&lt;/a&gt; et
font intervenir une forte dose de subjectivité. D'ailleurs, la version 2015 du
Chaos Manifesto a &lt;a class="reference external" href="http://www.erikweberconsulting.com/blog/chaos2015"&gt;redéfini les critères de réussite pour faire intervenir une
mesure de satisfaction client&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Pour juger de la difficulté de créer des logiciels, il reste les mesures «&amp;nbsp;au
doigt mouillé&amp;nbsp;». Anecdotes et expériences personnelles. Ressenti de
développeurs. Témoignages d'entrepreneurs. &lt;strong&gt;Aucun professionnel censé ne prétend
que construire un logiciel est une sinécure.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://cedricbodin.net/"&gt;Cédric Bodin&lt;/a&gt;, rencontré lors de MixIT 2015 à Lyon,
est l'auteur d'une excellente conférence intitulée &lt;a class="reference external" href="http://cedricbodin.net/2015/03/06/conference-le-pourquoi-du-pourquoi-de-lagilite-en-video/"&gt;le pourquoi de l'agilité&lt;/a&gt;.
Il y explique l'évolution des méthodes et des pratiques de l'industrie du
développement logiciel en les replaçant dans leur contexte historique, et
décrit comment sont apparues les démarches agiles.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L'agilité est née d'une volonté de se doter d'outils et de pratiques
adéquates&lt;/strong&gt;, adaptées aux spécificités du développement logiciel.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="naissance-de-l-agilite"&gt;
&lt;h2&gt;Naissance de l'agilité&lt;/h2&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://www.tripodocus.fr/pictures/2015/fete-foraine-6/"&gt;&lt;img alt="Quelques couleurs d'un parc d'attraction" class="full border shadow" src="http://www.tripodocus.fr/pictures/2015/fete-foraine-6_medium.jpg" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;La littérature est abondante sur ce qu'est l'agilité. J'ai tenté de répondre
succinctement à la question lors de la conf pour que tout le monde puisse
suivre, mais j'ai bien conscience que c'était difficile à suivre pour des
néophytes.&lt;/p&gt;
&lt;p&gt;«&amp;nbsp;Agilité&amp;nbsp;» est un terme récent&amp;nbsp;: il ne s'applique au développement logiciel
que depuis 2001 et l'apparition du manifeste agile.&lt;/p&gt;
&lt;p&gt;En 2001, 17 passionnés de l'industrie du développement logiciel se réunissent
dans une station de ski. Ils vont tenter de répondre à cette question&amp;nbsp;:
«&amp;nbsp;comment peut on procéder pour réussir à fabriquer des logiciels de manière
plus fiable et efficace&amp;nbsp;?&amp;nbsp;».&lt;/p&gt;
&lt;p&gt;En un week-end, aidés par le vin blanc et la fondue, ils vont réussir à publier
quelque chose&amp;nbsp;: le &lt;a class="reference external" href="http://agilemanifesto.org/"&gt;fameux manifeste agile&lt;/a&gt;.
«&amp;nbsp;Manifeste&amp;nbsp;» est un terme quelque peu pompeux pour désigner deux pages web
contenant à peine quelques lignes de texte.&lt;/p&gt;
&lt;p&gt;Mais en terme d'idées, la qualité fait plus que la quantité. Le manifeste agile
n'a fait que cristalliser un certain nombres d'idées qui existaient déjà au
préalable, mais le fait de regrouper en un «&amp;nbsp;paquet&amp;nbsp;» cohérent a permis de les
diffuser de manière massive.&lt;/p&gt;
&lt;p&gt;Tout cela ne nous aide toujours pas à comprendre ce qui se cache derrière le
terme d'«&amp;nbsp;agilité&amp;nbsp;». &lt;strong&gt;Adopter une démarche agile revient simplement à revenir
sur certaines idées reçues en matière de développement logiciel&lt;/strong&gt;, et à
remplacer certaines pratiques par d'autres.&lt;/p&gt;
&lt;p&gt;L'agilité pourrait très bien se décrire par la manière dont elle s'oppose aux
méthodes «&amp;nbsp;traditionnelles&amp;nbsp;».&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="qu-est-ce-que-l-agilite"&gt;
&lt;h2&gt;Qu'est-ce que l'agilité&amp;nbsp;?&lt;/h2&gt;
&lt;p&gt;Si vous avez déjà une vague idée de comment fonctionne un projet agile, vous
pouvez passer direct à la section suivante.&lt;/p&gt;
&lt;p&gt;Il existe mille et une façons de piloter un projet de développement logiciel.
S'agissant d'une «&amp;nbsp;science&amp;nbsp;» relativement jeune (après tout, les ordinateurs
n'existent que depuis quelques dizaines d'années), les pionniers de l'industrie
se sont naturellement emparés de techniques qui existaient dans d'autres
domaines&amp;nbsp;: l'industrie primaire, l'architecture, etc.&lt;/p&gt;
&lt;p&gt;Ces méthodes reposent en grande partie sur &lt;strong&gt;une approche prédictive et une
volonté de contrôle très fort&lt;/strong&gt;. Avant de démarrer le projet, on veut savoir
combien ça va coûter et combien de temps ça va prendre. Et quand on construit
une maison, on ne peut pas poser la première brique avant d'avoir un plan
détaillé de l'ensemble.&lt;/p&gt;
&lt;p&gt;Imaginez que vous soyez entrepreneur, que vous souhaitiez faire développer une
application (web, ou smartphone, on s'en fout), et que vous contactiez une
bonne vieille SSII &lt;em&gt;old school&lt;/em&gt;. Le processus risque fort d'être le suivant.&lt;/p&gt;
&lt;p&gt;On vous demandera un cahier des charges, et on vous fera un devis en retour. Un
chef de projet réalisera un planning extrêmement précis des différentes étapes
du développement avec de beaux diagrammes de Gantt. Un «&amp;nbsp;architecte&amp;nbsp;» sera
responsable de convertir votre cahier des charges en un ensemble de documents
de spécifications. Tel Moïse recevant les dix commandements de Dieu lui-même,
l'équipe de développement recevra les spécifications et sera chargée de la
convertir en code en respectant le planning édicté plus haut. Au bout du délai
imparti, l'équipe termine le développement, réalise les tests, corrige les
bugs, livre le logiciel, vous signez le bon de livraison, et peut-être aussi un
contrat de maintenance.&lt;/p&gt;
&lt;p&gt;On parle souvent &lt;a class="reference external" href="https://fr.wikipedia.org/wiki/Cycle_en_V"&gt;de Cycle en V&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Ça c'était la théorie. La pratique est souvent toute autre. Examinons les
prérequis au fonctionnement d'une telle méthode de travail.&lt;/p&gt;
&lt;p&gt;Commençons par le cahier des charges. Sa rédaction suppose que, avant même le
début du projet, vous sachiez avec une très grande précision ce que vous
souhaitez obtenir à la fin. Or, c'est impossible, pour moult raisons. D'abord,
écrire des logiciels n'est peut-être pas votre métier, et vous n'avez pas
forcément une vision très claire des différentes contraintes et difficultés
techniques de ce que vous demandez. Une «&amp;nbsp;même&amp;nbsp;» fonctionnalité, d'un projet à
l'autre, pourra être réalisée de manières très différentes, et vous n'avez
probablement aucune idée du niveau de précision nécessaire dans votre
description pour lever absolument toute ambigüité. Ensuite, vous êtes dans une
phase de pré-lancement de votre projet, vous ne savez peut-être pas quelles
fonctionnalités parmi les centaines envisagées auront la meilleure valeur
ajoutée. Peut-être enfin que confronter vos hypotèses (un prototype) à la
réalité (les utilisateurs) vous aurait permis d'apprendre certaines choses et
de revenir sur un certain nombre de décisions.&lt;/p&gt;
&lt;p&gt;Passons maintenant du côté du devis. D'abord, soyez certain·e que l'équipe
technico-commerciale chargée de répondre aux appels d'offre minimisera le devis
au maximum pour s'assurer de remporter le contrat. Quitte à gratter au maximum
pendant le reste du projet ou demander aux devs de réaliser quelques nuits
blanches avant la deadline. On présuppose aussi que l'on peut estimer le temps
et l'énergie nécessaire à la réalisation du logiciel demandé avec une très
grande précision, ce qui est une imposture qui confine au canular. Chaque
projet est unique au niveau de l'équipe, des technologies employées, des
problèmes rencontrés, des bugs à corriger, etc. Je ne connais aucun dev capable
d'estimer avec une précision proche des 100% une tâche de quelques heures.
Peut-on prétendre estimer un projet de plusieurs mois&amp;nbsp;?&lt;/p&gt;
&lt;p&gt;Je m'arrêterai là pour ne pas trop m'étendre, mais le reste du projet est à
l'avenant. &lt;strong&gt;Ces méthodes sont inadaptées, parce qu'elles sont nées d'idées
erronées&lt;/strong&gt;, tout simplement. Et s'il est si difficile de les remettre en cause,
c'est parce qu'&lt;strong&gt;elles restent malgré tout séduisantes et confortables&lt;/strong&gt;.
Prétendre qu'on peut estimer le coût de développement d'un logiciel permet de
vendre sa prestation plus facilement, de la même manière qu'il est plus facile
de chercher ses clés sous le lampadaire parce qu'il y a de la lumière même si
on les a perdu ailleurs.&lt;/p&gt;
&lt;p&gt;Comparons maintenant un projet réalisé par une équipe «&amp;nbsp;agile&amp;nbsp;» (notez que
l'exemple qui suit reste un cas concret parmi d'autres, il y a plusieurs façons
d'être agile).&lt;/p&gt;
&lt;p&gt;Vous contactez l'équipe. Au cours d'un atelier d'une journée, l'équipe vous
aide à constituer un référentiel de fonctionnalités, sous une forme très peu
détaillée, e.g «&amp;nbsp;en tant qu'utilisateur, je peux accéder à une interface
d'administration pour éditer un billet de blog&amp;nbsp;». Vous choisissez les quelques
fonctionnalités qui vous semblent le plus importantes à l'instant T, vous en
discutez avec l'équipe, et la réalisation commence. Au bout de deux semaines,
on vous livre une version fonctionnelle de ces fonctionnalités. C'est à dire
que vous obtenez une première version de votre projet, certes très partielle,
très minimaliste, mais tout ce qui est livré est finalisé et testé. À partir de
là, libre à vous d'embrayer sur une autre itération, et une autre, et une
autre. Vous pouvez réaliser six itérations à la suite jusqu'à obtenir un
produit fini, ou partir sur une itération tous les deux mois pour faire vivre
et évoluer lentement votre produit.&lt;/p&gt;
&lt;p&gt;Bien entendu, dans ce cas là, vous ne savez pas à l'avance combien vous coûtera
le développement au final. Mais la vérité, c'est que personne ne le sait.
L'équipe agile n'essaye pas de vous rassurer en prétendant le contraire.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="etre-agile-ce-n-est-pas-travailler-a-l-arrache"&gt;
&lt;h2&gt;Être agile, ce n'est pas travailler à l'arrache&lt;/h2&gt;
&lt;p&gt;Nombreux sont celles et ceux qui, confronté·e·s pour la première fois aux idées de
l'agilité, ont l'impression qu'il s'agit de travail bâclé, au radar, à
l'arrache.&lt;/p&gt;
&lt;p&gt;C'est loin d'être le cas. C'est même tout le contraire.&lt;/p&gt;
&lt;p&gt;Partant du principe que l'incertitude est très forte, et qu'on ne peut pas
faire grand chose pour la réduire, on va simplement &lt;strong&gt;adopter une approche
réaliste et se doter d'outils qui vont permettre aux membres du projet de
rectifier très rapidement le tir en cas d'imprévu&lt;/strong&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="est-ce-que-l-agilite-fonctionne"&gt;
&lt;h2&gt;Est-ce que l'agilité fonctionne&amp;nbsp;?&lt;/h2&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://www.tripodocus.fr/pictures/2015/fete-foraine-1/"&gt;&lt;img alt="Manène dans un parc d'attraction" class="full border shadow" src="http://www.tripodocus.fr/pictures/2015/fete-foraine-1_medium.jpg" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;L'agilité consiste à accepter que certaines idées sont erronées pour abandonner
les pratiques qui en découlent pour les remplacer par d'autres. Il semble
évident que l'agilité ne peut que mieux fonctionner.&lt;/p&gt;
&lt;p&gt;Mais est-ce vrai&amp;nbsp;? À priori, oui. L'étude citée plus haut le confirme&amp;nbsp;: les
projets réalisés avec une démarche agile ont moins de chance d'échouer que les
autres. Néanmoins, dans la mesure ou les critères de succès d'un projet sont en
partis subjectifs et définis par l'organisation même qui réalise le projet, on
se doute que ces chiffres sont à prendre avec un certain recul.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Reste encore une fois le «&amp;nbsp;doigt mouillé&amp;nbsp;», qui semble aller dans le même
sens.&lt;/strong&gt; À titre personnel et en tant que développeur, je peux dire que les
projets sur lesquels je travaille actuellement foirent moins qu'avant ma
découverte de l'agilité, et mes clients semblent plus souvent satisfaits. Je
n'ai certes pas la prétention de constituer un échantillon représentatif, mais
nombreuses sont les équipes dont les pratiques s'apparentent à l'agilité et qui
réalisent des projets fantastiques, et l'on est bien obligé de reconnaître que
les pratiques issues de l'agilité sont bien souvent plus cohérentes avec la
réalité.&lt;/p&gt;
&lt;p&gt;Malgré tout, un projet informatique n'est pas une tâche automatisable et reste
toujours une aventure humaine. &lt;strong&gt;Aucune méthode ne peut assurer un taux de
réussite de 100%&lt;/strong&gt;, et un projet réalisé avec de «&amp;nbsp;vieilles méthodes&amp;nbsp;» n'est
pas assuré d'échouer. Essayons donc de ne pas nous départir de notre sens
critique.&lt;/p&gt;
&lt;p&gt;D'excellents produits comme Github ou Firefox n'auraient pas pu être créés
d'une manière monolithique, ils sont le résultat d'une lente évolution.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="quand-l-agilite-foire"&gt;
&lt;h2&gt;Quand l'agilité foire&lt;/h2&gt;
&lt;p&gt;Il existe d'innombrables raisons pour lesquelles un projet réalisé avec une
démarche agile peut échouer. Mais il existe une erreur qui revient assez
souvent, et qui est responsable de l'échec de nombreux projets. J'ai commis
cette erreur, je sais de quoi je parle. Le processus est globalement toujours
le même.&lt;/p&gt;
&lt;p&gt;L'agilité est un terme marketing. C'est à dire que c'est &lt;strong&gt;un mot parapluie
permettant d'englober plusieurs idées pour en faciliter la diffusion&lt;/strong&gt; (pas
forcément commerciale). Comme tout terme marketing à la mode, il peut être vidé
de son sens et employé à tort et à travers, comme le sont souvent les termes
«&amp;nbsp;big data&amp;nbsp;» ou «&amp;nbsp;éco-responsabilité&amp;nbsp;».&lt;/p&gt;
&lt;p&gt;Revenons sur la définition de l'agilité.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;À la base du manifeste agile, il y a quatre valeurs.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Nous cherchons à valoriser&amp;nbsp;:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Les individus et leurs interactions plus que les processus et les outils&lt;/li&gt;
&lt;li&gt;Des logiciels opérationnels plus qu’une documentation exhaustive&lt;/li&gt;
&lt;li&gt;La collaboration avec les clients plus que la négociation contractuelle&lt;/li&gt;
&lt;li&gt;L’adaptation au changement plus que le suivi d’un plan&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Tout le monde a au moins une vague compréhension intuitive de ce qu'est une
valeur&amp;nbsp;: c'est une idée générale servant à guider le jugement. L'&lt;em&gt;honnêteté&lt;/em&gt;
est un exemple de valeur.&lt;/p&gt;
&lt;p&gt;Une valeur est un concept abstrait, donc non praticable directement. Je ne peux
pas directement pratiquer l'honnêteté, pas plus que je ne peux directement
«&amp;nbsp;privilégier les humains plutôt que les process&amp;nbsp;». En revanche, d'une valeur,
je peux formuler un certain nombre de principes.&lt;/p&gt;
&lt;p&gt;Un principe est une règle de conduite concrète qui permet d'être en cohérence
avec des valeurs. «&amp;nbsp;Je paye toujours ma note au restaurant&amp;nbsp;» est un principe
qui me permet d'être en cohérence avec la valeur «&amp;nbsp;honnêteté&amp;nbsp;».&lt;/p&gt;
&lt;p&gt;Ainsi, dans le manifeste, &lt;a class="reference external" href="http://agilemanifesto.org/iso/fr/principles.html"&gt;des quatre valeurs initiales découlent douze
principes&lt;/a&gt;. C'est déjà un
peu plus concret, mais pas encore tout à fait assez.&lt;/p&gt;
&lt;p&gt;Si on veut s'assurer de suivre tous les principes du manifeste, il va nous
falloir &lt;strong&gt;un cadre un peu plus structurant&lt;/strong&gt;. Et c'est là qu'interviennent des
méthodes comme Scrum ou XP. &lt;strong&gt;Scrum n'est rien d'autre qu'un cadre de
travail&lt;/strong&gt;, un ensemble de pratiques et d'outils qui nous permettent de
pratiquer les principes et les valeurs du manifeste.&lt;/p&gt;
&lt;p&gt;Il est bien évident qu'aborder l'agilité se fait généralement par le bas de la
pyramide. Développeur néophyte, vous joignez une équipe qui a mis en place
Scrum. &lt;strong&gt;Ce que vous verrez de l'agilité, ce ne sont pas les valeurs, qui sont
abstraites, mais bien les pratiques concrètes et quotidiennes de Scrum.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Partant de là, il est facile de confondre le conteneur et le contenant, et &lt;strong&gt;de
se faire la fausse idée que Scrum == Agilité&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;On peut comprendre pourquoi une équipe, constituée sur un nouveau projet, en
arrive à se dire «&amp;nbsp;soyons agile, utilisons Scrum&amp;nbsp;», alors que peut-être Scrum
n'est pas du tout adapté au contexte du projet. Ce genre de décision est le
contraire même de l'agilité.&lt;/p&gt;
&lt;p&gt;Il est important de comprendre que les pratiques dites agiles (&lt;em&gt;backlog&lt;/em&gt;, &lt;em&gt;user
stories&lt;/em&gt;, &lt;em&gt;itérations&lt;/em&gt;, etc.) ne sont que des points de départ, des moyens de
pratiquer les valeurs de l'agilité. J'ai déjà croisé des équipes qui se
croyaient agile et qui étaient tout sauf ça. Si vous êtes dans ce cas, vous ne
bénéficiez pas des avantages de l'agilité&amp;nbsp;; vous obtenez en fait le pire des
deux mondes.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="pourquoi-l-agilite-fonctionne"&gt;
&lt;h2&gt;Pourquoi l'agilité fonctionne&amp;nbsp;?&lt;/h2&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://www.tripodocus.fr/pictures/2015/fete-foraine-9/"&gt;&lt;img alt="Des jetons dans une machine à sous" class="full border shadow" src="http://www.tripodocus.fr/pictures/2015/fete-foraine-9_medium.jpg" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;Il existe une abondante littérature pour faire l'apologie des différentes
pratiques de l'agilité. Revenons donc à notre manifeste pour nous intéresser
aux quatre valeurs de base. Une question intéressante&amp;nbsp;: pourquoi ces quatre là
et pas d'autres&amp;nbsp;? Et pourquoi sont-elles pertinentes&amp;nbsp;?&lt;/p&gt;
&lt;p&gt;Mon avis est que ces quatre valeurs fonctionnent pour une seule et bonne
raison&amp;nbsp;: nous sommes tous des humains. À ma connaissance, aucun robot ou IA
n'est à ce jour capable de construire un logiciel, par conséquent chaque
software est né d'une équipe d'humains embauchant ou embauchés par des humains
pour construire des logiciels à destination d'humains.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;En tant qu'humains, nous sommes câblés d'une certaine façons.&lt;/strong&gt; Certes, les
divergences individuelles existent, mais nous sommes tous semblables sur les
points essentiels.&lt;/p&gt;
&lt;div class="section" id="la-recherche-de-sens"&gt;
&lt;h3&gt;La recherche de sens&lt;/h3&gt;
&lt;p&gt;Par exemple, &lt;strong&gt;nous autres humains aimons bien que nos actions aient un sens&lt;/strong&gt;.
Nous ne sommes pas faits pour apprécier les tâches absurdes.&lt;/p&gt;
&lt;p&gt;Tout cela est bien évidemment subjectif, et aucune activité n'est porteuse d'un
sens intrinsèque. Le sens, c'est ce que chacun met dans ce qu'il fait. Ce qui a
du sens pour moi n'en aura pas pour ma voisine, et vice-versa.&lt;/p&gt;
&lt;p&gt;Pour que je puisse mettre du sens dans ce que je fais, il y a certains
prérequis. Par exemple, ça aide si le résultat de mes actions correspond à une
certaine notion d'utilité, au moins selon mes propres critères. Et puis, il
faut que je sois capable de m'approprier l'activité en question, donc que j'ai
un minimum de marge d'initiative.&lt;/p&gt;
&lt;p&gt;Si &lt;a class="reference external" href="http://www.larevanchedurameur.com/"&gt;on m'enlève toute initiative dans mon travail en m'imposant de suivre
aveuglément une démarche qualité, on me prive du sens de mes actions&lt;/a&gt;. Par ailleurs, si je développe pour des
raisons contractuelles une fonctionnalité que l'on sait maintenant inutile,
j'ai bien conscience que mon travail est absurde et n'a pas de sens.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="animaux-sociaux"&gt;
&lt;h3&gt;Animaux sociaux&lt;/h3&gt;
&lt;p&gt;Autre point inhérent à notre nature humaine&amp;nbsp;: &lt;strong&gt;nous sommes des animaux
sociaux&lt;/strong&gt;. Nous tirons naturellement un sentiment de profonde satisfaction
lorsque nous expérimentons des interactions sociales positives, bienveillantes
et constructives.&lt;/p&gt;
&lt;p&gt;Et quand vous travaillez avec des personnes que vous respectez et qui vous
respectent, et que vous pouvez, soyons fou, échanger quelques blagues autour de
la machine à café, n'est-ce pas plus facile de se lever le matin&amp;nbsp;?&lt;/p&gt;
&lt;p&gt;Les quatre valeurs de l'agilité raisonnent profondément avec notre condition
d'humains.&lt;/p&gt;
&lt;p&gt;Prenons ces deux valeurs&amp;nbsp;:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;privilégier les individus et leurs interactions&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;privilégier la collaboration avec les clients.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Une méthode de travail qui s'inscrirait dans ces valeurs ne serait-elle pas
forcément plus satisfaisante pour notre nature d'animaux sociaux&amp;nbsp;?&lt;/p&gt;
&lt;p&gt;Et ces deux autres&amp;nbsp;:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;privilégier des logiciels opérationnels&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;privilégier l'adaptation au changement.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Il est bien évident que l'impression d'être utile est bien plus importante
lorsqu'on s'attache à répondre à un besoin réel de la manière la plus
intelligente possible plutôt que de piloter un projet en suivant des paramètres
arbitraires et purement économiques.&lt;/p&gt;
&lt;p&gt;«&amp;nbsp;Remettre de l'humain&amp;nbsp;» est une autre expression &lt;em&gt;bullshit&lt;/em&gt; souvent vomie par
les communiquants. Pourtant, &lt;strong&gt;c'est bien le but de l'agilité&amp;nbsp;: redonner aux
humains leur vraie place d'humains dans les projets auxquels ils participent&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Pratiquer l'agilité va au delà de la sphère professionnelle. C'est &lt;strong&gt;remettre
du sens dans ce qu'on fait, et réapprendre à le faire en bonne intelligence&lt;/strong&gt;
et entre humains de bonne compagnie.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="l-agilite-et-au-dela"&gt;
&lt;h2&gt;L'agilité et au delà&lt;/h2&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://www.tripodocus.fr/pictures/2015/fete-foraine-3/"&gt;&lt;img alt="Une femme regarde des enfants sur un manège" class="full border shadow" src="http://www.tripodocus.fr/pictures/2015/fete-foraine-3_medium.jpg" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;La diffusion des idées de l'agilité a permis une remise en cause massive d'un
certain nombre d'idées et de valeur. Et si ces nouvelles idées se répandaient
en dehors de la sphère de l'industrie du logiciel&amp;nbsp;?&lt;/p&gt;
&lt;p&gt;La déshumanisation des activités humaines est endémique. La dictature de la
démarche qualité se répand dans les organisations et de plus en plus, on nous
impose &lt;em&gt;process&lt;/em&gt; et objectifs trimestriels.&lt;/p&gt;
&lt;p&gt;Nombreux sont les managers qui aimeraient transformer les membres de leurs
équipes en robots&amp;nbsp;: pas d'initiative, pas d'erreur. Mais &lt;strong&gt;les humains font de
mauvais robots&lt;/strong&gt;. Enlevez leur la nécessité de réfléchir, transformez leurs
jobs en une liste de tâches abrutissantes et répétitives, et vous en ferez
plutôt des zombies, malheureux et inefficaces.&lt;/p&gt;
&lt;p&gt;La tendance est à l'automatisation des tâches automatisables, ce qui est plutôt
une bonne chose, à condition que l'on arrive à &lt;strong&gt;trouver une organisation
sociale et économique qui ne fasse plus de l'emploi la seule condition d'accès
à un revenu minimal&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Pratiquer l'agilité nous aide à nous rappeler qu'un chiffre n'est qu'on
chiffre, et que nous travaillons à la base pour et avec des humains. En gros,
arrêtons de nous considérer les uns les autres comme des vaches à lait ou des
machines sur des chaînes de montage.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="zi-enned"&gt;
&lt;h2&gt;Zi enned&lt;/h2&gt;
&lt;p&gt;Pour conclure, tâchons de retenir ceci. Si quelqu'un prétend pouvoir deviner
sans faille à l'avance la durée et le coût d'un logiciel, je pense que cette
personne est un charlatan. Si quelqu'un prétend qu'une méthode agile va
garantir à 100% la réussite de votre projet, c'est un autre charlatan.&lt;/p&gt;
&lt;p&gt;J'espère que cette version écrite aura été plus claire et détaillée que la
version orale. Au plaisir d'en discuter autour de votre boisson préférée.&lt;/p&gt;
&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">admin</dc:creator><pubDate>Tue, 15 Sep 2015 17:00:00 +0200</pubDate><guid>tag:www.miximum.fr,2015-09-15:agilite-des-projets-qui-reussissent.html</guid><category>agilite</category><category>e1</category><category>toulon</category></item><item><title>Servir des images efficacement avec Django et Nginx</title><link>http://www.miximum.fr/servir-images-efficacement-django-nginx.html</link><description>&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://www.tripodocus.fr/pictures/2015/voyage-en-andalousie-2015-9/"&gt;&lt;img alt="Un halo autour d'un palmier" class="full border shadow" src="http://www.tripodocus.fr/pictures/2015/voyage-en-andalousie-2015-9_medium.jpg" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a class="reference external" href="http://www.tripodocus.fr"&gt;Tripodocus est un site entièrement réalisé en Django sur lequel je publie
quelques photos.&lt;/a&gt; La plupart des images sont
publiques, d'autres sont privées et uniquement accessibles aux personnes
autorisées. Django génère aussi des &lt;em&gt;thumbnails&lt;/em&gt; pour chaque photo.&lt;/p&gt;
&lt;p&gt;J'ai passé pas mal de temps à travailler sur la manière dont les images sont
servies à mes (encore bien rares) visiteurs. J'utilise les outils fournis par
&lt;strong&gt;Django et Nginx pour proposer des urls cohérentes et des performances
optimales&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Voici un tutoriel (qui présuppose que vous connaissez un minimum Django) sur ma
recette pour servir des images avec Django et Nginx.&lt;/p&gt;
&lt;div class="section" id="definition-des-ressources-et-urls"&gt;
&lt;h2&gt;Définition des ressources et URLs&lt;/h2&gt;
&lt;p&gt;En accord avec les principes énoncés dans &lt;a class="reference external" href="https://books.google.fr/books?id=XUaErakHsoAC&amp;amp;hl=en"&gt;le livre «&amp;nbsp;RESTful Web Services&amp;nbsp;»&lt;/a&gt;, notre première étape
sera de &lt;strong&gt;définir nos ressources et leur associer des urls&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Sur Tripodocus, j'uploade des photos. Il faut évidemment que cette photo puisse
être accessible pour le visiteur. Je vais donc l'intégrer dans une belle page
Web avec un titre, une légende plus ou moins pertinente, des méta-données, etc.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ressource 1&amp;nbsp;: la page Web qui affiche la photo.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Évidemment, il faut bien que le fichier de ladite photo soit visible.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ressource 2&amp;nbsp;: le fichier de la photo.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Enfin, pour de meilleures performances, je vais également proposer des versions
retaillées (&lt;em&gt;thumbnails&lt;/em&gt;) dudit fichier.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ressource 3 et plus&amp;nbsp;: les thumbnails.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Parce que je suis un peu puriste sur les bords, je vais utiliser les urls les
plus simples et les plus logiques possibles.&lt;/p&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="13%" /&gt;
&lt;col width="88%" /&gt;
&lt;/colgroup&gt;
&lt;thead valign="bottom"&gt;
&lt;tr&gt;&lt;th class="head"&gt;Ressource&lt;/th&gt;
&lt;th class="head"&gt;URL&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;Page Web&lt;/td&gt;
&lt;td&gt;&lt;a class="reference external" href="http://www.tripodocus.fr/pictures/2015/le-grand-saut/"&gt;http://www.tripodocus.fr/pictures/2015/le-grand-saut/&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Fichier&lt;/td&gt;
&lt;td&gt;&lt;a class="reference external" href="http://www.tripodocus.fr/pictures/2015/le-grand-saut.jpg"&gt;http://www.tripodocus.fr/pictures/2015/le-grand-saut.jpg&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Thumbnail&lt;/td&gt;
&lt;td&gt;&lt;a class="reference external" href="http://www.tripodocus.fr/pictures/2015/le-grand-saut_medium.jpg"&gt;http://www.tripodocus.fr/pictures/2015/le-grand-saut_medium.jpg&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="section" id="definir-les-responsabilites-de-chacun"&gt;
&lt;h2&gt;Définir les responsabilités de chacun&lt;/h2&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://www.tripodocus.fr/pictures/2015/pigeons-gangsta/"&gt;&lt;img alt="Des pigons à l'air louche (encore plus que les pigeons normaux)" class="full border shadow" src="http://www.tripodocus.fr/pictures/2015/pigeons-gangsta_medium.jpg" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;Je pars du principe que &lt;a class="reference external" href="http://www.miximum.fr/deployer-django-en-production-nginx-gunicorn-supervisor.html"&gt;votre projet Django est hébergé sur un système Debian
avec Nginx en reverse proxy et Gunicorn en serveur Wsgi&lt;/a&gt;. À vrai dire, seul Django
et Nginx seront considérés ici.&lt;/p&gt;
&lt;p&gt;La liste de mes photos est gérée par Django et stockée en base de donnée. Quand
vous demandez la page Web d'une photo, c'est à Django que vous vous adressez.&lt;/p&gt;
&lt;p&gt;Par contre, quand vous demandez le fichier correspondant, j'aimerais éviter que
la requête atteigne le framework python, et que Nginx se charge lui-même de
servir le fichier. &lt;strong&gt;Tout simplement pour des questions de performances.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Reste l'épineuse question des images privées. En effet, c'est bien &lt;strong&gt;Django qui
va devoir se charger de vérifier que vous avez le droit d'accéder à la photo&lt;/strong&gt;.
Pour autant, une fois que le framework a déterminé que votre requête est
légitime, on aimerait qu'il délègue la suite de l'opération (a.k.a. servir le
fichier) à Nginx, toujours pour des questions de performances. Nous allons voir
par la suite comment on peut faire ça.&lt;/p&gt;
&lt;p&gt;Pour obtenir une configuration Nginx qui fonctionne, il va nous falloir
&lt;strong&gt;distinguer les urls des images publiques et privées&lt;/strong&gt;, et les stocker dans
deux répertoires différents. Notre nouveau schéma d'urls sera le suivant&amp;nbsp;:&lt;/p&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="30%" /&gt;
&lt;col width="70%" /&gt;
&lt;/colgroup&gt;
&lt;thead valign="bottom"&gt;
&lt;tr&gt;&lt;th class="head"&gt;Ressource&lt;/th&gt;
&lt;th class="head"&gt;URL&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;Page Web (publique ou privée)&lt;/td&gt;
&lt;td&gt;&lt;a class="reference external" href="http://www.tripodocus.fr/pictures/2015/le-grand-saut/"&gt;http://www.tripodocus.fr/pictures/2015/le-grand-saut/&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Fichier public&lt;/td&gt;
&lt;td&gt;&lt;a class="reference external" href="http://www.tripodocus.fr/pictures/2015/le-grand-saut.jpg"&gt;http://www.tripodocus.fr/pictures/2015/le-grand-saut.jpg&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Thumbnail public&lt;/td&gt;
&lt;td&gt;&lt;a class="reference external" href="http://www.tripodocus.fr/pictures/2015/le-grand-saut_medium.jpg"&gt;http://www.tripodocus.fr/pictures/2015/le-grand-saut_medium.jpg&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Fichier privé&lt;/td&gt;
&lt;td&gt;&lt;a class="reference external" href="http://www.tripodocus.fr/private/2015/photo-compromettante.jpg"&gt;http://www.tripodocus.fr/private/2015/photo-compromettante.jpg&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Thumbnail privé&lt;/td&gt;
&lt;td&gt;&lt;a class="reference external" href="http://www.tripodocus.fr/private/2015/photo-compromettante_medium.jpg"&gt;http://www.tripodocus.fr/private/2015/photo-compromettante_medium.jpg&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="section" id="definition-du-modele-dans-django"&gt;
&lt;h2&gt;Définition du modèle dans Django&lt;/h2&gt;
&lt;p&gt;Voici un exemple de modèle Django simplifié pour nous permettre de travailler.&lt;/p&gt;
&lt;pre class="code python literal-block"&gt;
&lt;span class="c"&gt;# -*- coding: utf-8 -*-&lt;/span&gt;
&lt;span class="c"&gt;# /src/pictures/models.py&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;__future__&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;unicode_literals&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;os.path&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;splitext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;basename&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.utils.translation&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ugettext_lazy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.core.files.storage&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FileSystemStorage&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.utils&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;timezone&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.conf&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;


&lt;span class="c"&gt;# In settings:&lt;/span&gt;
&lt;span class="c"&gt;# PICTURES_ROOT = '/home/tripodocus/media/'&lt;/span&gt;
&lt;span class="c"&gt;# PICTURES_URL = '/'&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PictureStorage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FileSystemStorage&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="s"&gt;'location'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PICTURES_ROOT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;'base_url'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PICTURES_URL&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PictureStorage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;upload_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;basename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;extension&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;splitext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;timezone&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;prefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'private'&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_private&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s"&gt;'pictures'&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;'{}/{}/{}{}'&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;year&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;extension&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="c"&gt;# The actual picture model&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Picture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Title'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;250&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;slug&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SlugField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Slug'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;250&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;unique&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;is_private&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BooleanField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Is private?'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ImageField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Image file'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;upload_to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;upload_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PictureStorage&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;created_on&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DateTimeField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Created on'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;timezone&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Je vous laisse consulter la doc de Django pour comprendre &lt;a class="reference external" href="https://docs.djangoproject.com/en/1.8/ref/models/fields/#filefield"&gt;l'utilité et la
différence entre les paramètres storage et upload_path&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Ce qu'il faut retenir, c'est que les fichiers uploadés seront stockés dans un
répertoire &lt;em&gt;media&lt;/em&gt; à la racine de notre projet, dans un sous répertoire
&lt;em&gt;pictures&lt;/em&gt; ou &lt;em&gt;private&lt;/em&gt; en fonction de la valeur du champ &lt;em&gt;is_private&lt;/em&gt;, et
l'intégralité de chemin de l'image (e.g &lt;em&gt;pictures/2015/le-grand-saut.jpg&lt;/em&gt;) sera
stocké en base de données. Le fichier est également renommé en fonction du
&lt;em&gt;slug&lt;/em&gt; de l'objet Django.&lt;/p&gt;
&lt;p&gt;Tout le code concernant &lt;a class="reference external" href="https://docs.djangoproject.com/en/1.8/topics/http/file-uploads/"&gt;l'admin et l'upload de fichiers étant déjà
parfaitement documenté&lt;/a&gt;, je
laisserai cette partie de côté.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="une-configuration-nginx-basique"&gt;
&lt;h2&gt;Une configuration Nginx basique&lt;/h2&gt;
&lt;p&gt;Voici &lt;strong&gt;un point de départ pour notre configuration Nginx&lt;/strong&gt;. Notez que cet
exemple fonctionne parfaitement en local, avec le serveur de dev de Django.&lt;/p&gt;
&lt;p&gt;Notez que j'ai également laissé de côté tout ce qui ne concerne pas directement
ce tutoriel (fichiers statiques, favicon.ico, caches, gzip, expires, etc.)&lt;/p&gt;
&lt;pre class="code literal-block"&gt;
# /etc/nginx/sites-available/tripodocus

# Définition de notre serveur wsgi (Gunicorn ou serveur de dev Django)
upstream tripodocus {
    server localhost:8000;
}

server {
    # Config de base
    server_name www.tripodocus.fr;
    access_log /var/log/nginx/tripodocus.access.log;
    error_log /var/log/nginx/tripodocus.error.log;

    # Définition d'une location nommée
    # qui va nous servir à indiquer une redirection vers
    # le serveur wsgi défini plus haut.
    location &amp;#64;django {
        proxy_pass http://tripodocus;
        proxy_redirect off;
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    # Pour toutes les urls commençant par /pictures/ nginx
    # commencera par vérifier si le fichier demandé existe sur le
    # système de fichier. Si c'est le cas, elle va le servir directement sans
    # rien demander à personne, ce qui nous arrange bien. Sinon, elle forwarde
    # la requête à Django.
    location /pictures/ {
        alias  /home/tripodocus/media/pictures/;
        expires 1w;
        try_files $uri &amp;#64;django;
    }

    # Les urls commençant par /private/ sont forwardées à Django
    # À vrai dire, cette règle est inutile puisque elle est redondante
    # avec la suivante, mais je la garde parce que ça me parait plus
    # clair comme ça.
    location /private/ {
        error_page 418 = &amp;#64;django;
        return 418;
    }

    # Toutes les autres urls sont transmises à Django sans autre forme de
    # procès. Notez qu'il n'y a pas moyen (je n'ai pas trouvé) de forwarder
    # directement une url à une location nommée, et qu'il faut pour ça utiliser
    # un hack tout moche. Si quelqu'un a une meilleure idée…
    location / {
        error_page 418 = &amp;#64;django;
        return 418;
    }
}
&lt;/pre&gt;
&lt;p&gt;Grâce à cette configuration Nginx, il se passe les choses suivantes.&lt;/p&gt;
&lt;p&gt;Toutes les requêtes de fichiers publics (présents dans le sous-répertoire
&lt;em&gt;pictures&lt;/em&gt;) sont servies directement par Nginx. Sauf si le fichier n'existe
pas, auquel cas, la requête est envoyée à Django. Nous allons voir que c'est
utile pour la génération des thumbnails.&lt;/p&gt;
&lt;p&gt;Toutes les autres requêtes sont envoyées à Django.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="le-fonctionnement-des-thumbnails"&gt;
&lt;h2&gt;Le fonctionnement des thumbnails&lt;/h2&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://www.tripodocus.fr/pictures/2015/voyage-en-andalousie-2015-20/"&gt;&lt;img alt="Deux touristes consultent un plant" class="full border shadow" src="http://www.tripodocus.fr/pictures/2015/voyage-en-andalousie-2015-20_medium.jpg" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;Certains plugins permettant de gérer des thumbnails génèrent et enregistrent
les différentes vignettes au moment ou la photo est uploadée et l'objet est
créé dans Django.&lt;/p&gt;
&lt;p&gt;C'est un système qui ne me convient pas, parce qu'il manque de souplesse (et
j'aime la souplesse).&lt;/p&gt;
&lt;p&gt;Au lieu de ça, &lt;strong&gt;je veux que mes thumbnails soient générés en juste-à-temps&lt;/strong&gt;,
au moment ou l'utilisateur le demande. Évidemment, une fois la vignette
générée, elle est &lt;strong&gt;enregistrée sur le système de fichiers&lt;/strong&gt; et n'a pas besoin
d'être générée une seconde fois. On n'est quand même pas des bêtes.&lt;/p&gt;
&lt;p&gt;Le processus est le suivant&amp;nbsp;:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;un utilisateur enthousiaste requiert la vignette
&lt;a class="reference external" href="http://www.tripodocus.fr/pictures/2015/le-grand-saut_medium.jpg"&gt;http://www.tripodocus.fr/pictures/2015/le-grand-saut_medium.jpg&lt;/a&gt;
correspondante à une photo que je viens d'uploader&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;le fichier n'existant pas, Nginx transmet la requête à Django&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;le framework génère diligemment le thumbnail en question, et l'enregistre
sur le système de fichier&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;une fois fait, plutôt que de servir lui-même le fichier, il* rend la main à
Nginx en lui disant «&amp;nbsp;voilà le fichier demandé, tu peux l'envoyer au client,
moi je retourne me coucher&amp;nbsp;»&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;Nginx reprend la main, elle* constate que le fichier existe, et l'envoie au
client.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;em&gt;*Je ne sais pas pourquoi, dans mon esprit, Django est un homme et Nginx une
femme.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Si l'utilisateur recharge la page, l'enchaînement est maintenant différent&amp;nbsp;:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;un utilisateur enthousiaste requiert la vignette
&lt;a class="reference external" href="http://www.tripodocus.fr/pictures/2015/le-grand-saut_medium.jpg"&gt;http://www.tripodocus.fr/pictures/2015/le-grand-saut_medium.jpg&lt;/a&gt;&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;le fichier existant déjà, Nginx l'envoie au client&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;et c'est tout.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Ainsi, si je modifie dans mes settings la configuration des thumbnails
&lt;em&gt;medium&lt;/em&gt;, tout ce que j'ai à faire pour regénérer toutes les vignettes
correspondantes, c'est de les effacer.&lt;/p&gt;
&lt;pre class="code bash literal-block"&gt;
find /home/tripodocus/media/pictures -name &lt;span class="s2"&gt;&amp;quot;*_medium.*&amp;quot;&lt;/span&gt; -exec rm &lt;span class="o"&gt;{}&lt;/span&gt; &lt;span class="se"&gt;\;&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Les avantages de ce système sont multiples&amp;nbsp;:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;il est simple à mettre en œuvre&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;il est élégant (et j'aime l'élégance)&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;il est souple&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;il est tolérant à l'erreur&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;uploader une image ne prend pas trois plombes parce qu'il faut générer 15
vignettes dans la foulée.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Le seul inconvénient, c'est que la première fois qu'un utilisateur consulte la
page d'une nouvelle photo, le chargement du thumbnail prend quelques dixièmes
de secondes. D'ailleurs, c'est souvent pour ma pomme. C'est tellement peu
gênant que je me demande même pourquoi je prends la peine de le mentionner.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="tu-l-as-vue-ma-vue"&gt;
&lt;h2&gt;Tu l'as vue ma vue&amp;nbsp;?&lt;/h2&gt;
&lt;p&gt;Nous allons maintenant définir les vues qui correspondent à toutes ces urls.
Voici le contenu du fichier &lt;em&gt;urls.py&lt;/em&gt;.&lt;/p&gt;
&lt;pre class="code python literal-block"&gt;
&lt;span class="c"&gt;# L'url de la page photo&lt;/span&gt;
&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;r'^pictures/(?P&amp;lt;year&amp;gt;\d+)/(?P&amp;lt;slug&amp;gt;[\w-]+)/$'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;PictureView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_view&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'picture'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;

&lt;span class="c"&gt;# La vue qui génère les thumbnails&lt;/span&gt;
&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;r'^pictures/(?P&amp;lt;year&amp;gt;\d+)/(?P&amp;lt;slug&amp;gt;[a-zA-Z0-9-]+)_(?P&amp;lt;size&amp;gt;\w+).(?P&amp;lt;extension&amp;gt;\w+)$'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ThumbnailView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_view&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'thumbnail'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;

&lt;span class="c"&gt;# La vue qui sert les fichiers photo privés&lt;/span&gt;
&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;r'^private/(?P&amp;lt;year&amp;gt;\d+)/(?P&amp;lt;slug&amp;gt;[a-zA-Z0-9-]+).(?P&amp;lt;extension&amp;gt;\w+)$'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;PrivatePictureFileView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_view&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'private_image'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;

&lt;span class="c"&gt;# La vue qui génère et sert les thumbnails pour les photo privées&lt;/span&gt;
&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;r'^private/(?P&amp;lt;year&amp;gt;\d+)/(?P&amp;lt;slug&amp;gt;[a-zA-Z0-9-]+)_(?P&amp;lt;size&amp;gt;\w+).(?P&amp;lt;extension&amp;gt;\w+)$'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;PrivateThumbnailView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_view&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'private_thumbnail'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Notez qu'il n'y a pas de vue pour servir les fichiers des photos publiques.
C'est parce qu'il n'y en a pas besoin, elles sont directement servies par Nginx
(suivez, un peu).&lt;/p&gt;
&lt;p&gt;Voici le contenu des vues correspondantes.&lt;/p&gt;
&lt;div class="section" id="pictureview"&gt;
&lt;h3&gt;PictureView&lt;/h3&gt;
&lt;p&gt;La vue qui affiche la page Web d'une photo (e.g
&lt;a class="reference external" href="http://www.tripodocus.fr/pictures/2015/le-grand-saut/"&gt;http://www.tripodocus.fr/pictures/2015/le-grand-saut/&lt;/a&gt;) n'a rien de particulier,
c'est une vue Django tout ce qu'il y a de plus classique, il n'y a rien à dire
dessus. Retenez juste qu'il y a une vérification de droit d'accès à réaliser,
c'est tout.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="thumbnailview"&gt;
&lt;h3&gt;ThumbnailView&lt;/h3&gt;
&lt;p&gt;Cette vue génère un thumbnail d'une taille donnée, et repasse la main à Nginx.&lt;/p&gt;
&lt;pre class="code python literal-block"&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BasePictureFileView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DetailView&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Vue de base pour toutes les vues qui travaillent sur une photo.

    Notez que toutes les vues définies plus haut travaillent sur une photo
    identifiée par un &amp;quot;slug&amp;quot; et une année de publication.

    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Picture&lt;/span&gt;
    &lt;span class="n"&gt;context_object_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'picture'&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_queryset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;qs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BasePictureFileView&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_queryset&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;qs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;created_on__year&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'year'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ThumbnailView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BasePictureFileView&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Génère un thumbnail sur le disque.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;write_thumbnail_on_disk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;picture&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Génère et enregistre le thumbnail au bon endroit.

        Il y a plusieurs façons de faire ça, la documentation est
        abondante, je vous laisse le soin de vous renseigner.

        Retourne l'url relative du fichier e.g /pictures/2015/super-photo_medium.jpg

        Notez que nous avons fait en sorte que cette url soit exactement la
        même que celle demandée par le client.

        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;render_to_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;thumbnail_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write_thumbnail_on_disk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'picture'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;extension&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'extension'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HttpResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'image/{}'&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;extension&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'X-Accel-Redirect'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;thumbnail_url&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Les dernières lignes sont importantes. En effet, nous aurions pu retourner
bêtement le contenu du fichier dans notre réponse Django (&lt;a class="reference external" href="https://docs.djangoproject.com/en/1.8/ref/request-response/#fileresponse-objects"&gt;par exemple en
utilisant la classe FileResponse&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Nous aurions pu, mais ça n'aurait pas été &lt;em&gt;élégant&lt;/em&gt;, parce que &lt;strong&gt;le but d'un
framework Python n'est pas de servir des fichiers binaires&lt;/strong&gt;. Au lieu de ça, nous
utilisons une fonctionnalité nommée &lt;em&gt;X-sendfile&lt;/em&gt; accessible grâce &lt;a class="reference external" href="http://wiki.nginx.org/X-accel"&gt;au module
X-accel de Nginx&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Le fonctionnement est simple. Si vous retournez une réponse vide avec un header
&lt;em&gt;X-Accel-Redirect&lt;/em&gt; contenant une url, alors &lt;strong&gt;Nginx va intercepter cette réponse,
et se comporter exactement comme si le client venait de faire une nouvelle
requête sur cette url&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Notez que dans ce cas, l'url du thumbnail est exactement la même que celle de
la requête initiale, mais nous aurions pu retourner une url complètement
différente.&lt;/p&gt;
&lt;p&gt;Le détail du flux est le suivant&amp;nbsp;:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;l'utilisateur envoie une requête pour l'url &lt;em&gt;/pictures/2015/le-grand-saut_medium.jpg&lt;/em&gt; ;&lt;/li&gt;
&lt;li&gt;le fichier n'existe pas, Nginx passe la requête à Django qui exécute
ThumbnailView&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;le thumbnail est enregistré sur le disque&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;Django retourne une réponse vide avec un header X-Accel-Redirect qui
contient la même url que de la requête initiale&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;Nginx intercepte la réponse, et se comporte comme s'il s'agissait d'une
nouvelle requête&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;cette fois, le fichier existe, Nginx l'envoie au client.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Je vous en prie, prenez quelque secondes pour &lt;em&gt;admirer à quel point cette
solution est élégante&lt;/em&gt;. Prenez votre temps, je ne suis pas pressé.&lt;/p&gt;
&lt;p&gt;Notez quand même que si votre vue ThumbnailView ne parvient pas à écrire le
fichier au bon endroit, vous allez rentrer dans une boucle infinie. Ça craint.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="privatepicturefileview"&gt;
&lt;h3&gt;PrivatePictureFileView&lt;/h3&gt;
&lt;pre class="code python literal-block"&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BasePrivatePictureFileView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BasePictureFileView&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Vue de base pour les fichiers privés.

    Les permissions sont gérées ici.

    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;queryset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BasePictureFileView&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;queryset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_private&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c"&gt;# Check permissions here&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_authenticated&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
                &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;Http404&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PrivatePictureFileView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BasePrivatePictureFileView&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Vérifie les permissions et laisse Nginx servir le fichier.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;render_to_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c"&gt;# À ce stade, les droits ont déjà été vérifiés&lt;/span&gt;

        &lt;span class="c"&gt;# /private/2015/photo-privee.jpg -&amp;gt; /xaccel/2015/photo-privee.jpg&lt;/span&gt;
        &lt;span class="n"&gt;image_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'picture'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;
        &lt;span class="n"&gt;xaccel_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;image_url&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'/private/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'/xaccel/'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;extension&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'extension'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HttpResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'image/{}'&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;extension&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'X-Accel-Redirect'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;xaccel_url&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Ici, nous utilisons une stratégie similaire. Une fois que nous avons vérifié
que l'utilisateur a effectivement le droit d'accéder à la photo, nous
retournons une réponse vide avec un header &lt;em&gt;X-Accel-Redirect&lt;/em&gt; pour laisser
Nginx servir la photo.&lt;/p&gt;
&lt;p&gt;Il y a toutefois une petite subtilité. &lt;strong&gt;Nous ne pouvons pas utiliser la même url
que la requête initiale&lt;/strong&gt;, parce que toutes les urls commençant pas &lt;em&gt;/private/&lt;/em&gt;
doivent &lt;em&gt;systématiquement&lt;/em&gt; être envoyées à Django. Donc, si nous retournons la
même url, nous allons rentrer dans une boucle infinie. Pas cool.&lt;/p&gt;
&lt;p&gt;La solution est très simple&amp;nbsp;: &lt;strong&gt;définir une nouvelle url dans Nginx pour servir
directement les fichiers privés&lt;/strong&gt;. Voici la configuration correspondante.&lt;/p&gt;
&lt;pre class="code literal-block"&gt;
# Les urls privées sont systématiquement envoyées à Django
location /private/ {
    error_page 418 = &amp;#64;django;
    return 418;
}

# Les urls qui commencent par /xaccel/ sont servies statiquement.
# Notez que grâce à la directive &amp;quot;alias&amp;quot;, le préfixe /xaccel/ est
# supprimée de l'adresse du fichier à servir.
#
# E.g en retournant l'url /xaccel/2015/photo-privee.jpg, c'est le fichier
# /home/tripodocus/media/private/2015/photo-privee.jpg
# qui sera servi.
location /xaccel/ {
    internal;
    alias /home/tripodocus/media/private/;
    expires 1w;
}
&lt;/pre&gt;
&lt;p&gt;Eh&amp;nbsp;! Mais qu'est-ce qui empêche un petit malin de taper l'url
&lt;a class="reference external" href="http://www.tripodocus.fr/xaccel/2015/photo-privee.jpg"&gt;http://www.tripodocus.fr/xaccel/2015/photo-privee.jpg&lt;/a&gt;&amp;nbsp;?! Et bien c'est tout
simplement la directive &lt;em&gt;internal&lt;/em&gt;, qui indique à Nginx que &lt;strong&gt;cette url n'est
atteignable que via des directives internes&lt;/strong&gt;, et pas via des requêtes
normales.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="privatethumbnailview"&gt;
&lt;h3&gt;PrivateThumbnailView&lt;/h3&gt;
&lt;pre class="code python literal-block"&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PrivateThumbnailView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BasePrivatePictureFileView&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Vérifie les droits *et* génère le thumbnail.

    Cette vue n'est jamais qu'une combinaison des précédentes.

    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;render_to_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;thumbnail_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write_thumbnail_if_doesnt_exist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'picture'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;xaccel_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;thumbnail_url&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'/private/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'/xaccel/'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;extension&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'extension'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HttpResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'image/{}'&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;extension&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'X-Accel-Redirect'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;xaccel_url&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Cette vue n'est jamais qu'une composition des précédentes, il n'y a rien de
plus à dire dessus.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="conclusion"&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://www.tripodocus.fr/pictures/2015/voyage-en-andalousie-2015-19/"&gt;&lt;img alt="Architecture moderne andalouse" class="full border shadow" src="http://www.tripodocus.fr/pictures/2015/voyage-en-andalousie-2015-19_medium.jpg" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;En conclusion, ce système &lt;strong&gt;tire parti des fonctionnalités de Django et Nginx
pour servir des images efficacement, tout en gardant la possibilité de
contrôler finement les droits d'accès de chaque photo&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Notez que &lt;strong&gt;mon parti pris est de mettre l'accent sur l'interface&lt;/strong&gt; (les urls)
et d'en faire découler les choix techniques. Il aurait sans doute été possible
de simplifier deux ou trois trucs et de gagner en performances au prix d'urls
moins claires, moins cohérentes, moins &lt;em&gt;élégantes&lt;/em&gt;. J'imagine qu'un site qui
sert des To d'images par jour a d'autres impératifs que les miens.&lt;/p&gt;
&lt;p&gt;Notez également que la page qui correspond à un album privé –&amp;nbsp;qui affiche des
dizaines de thumbnails privés&amp;nbsp;– génère autant de requêtes au serveur Django car
chaque vignette est vérifiée individuellement. Ça reste gérable dans le
contexte de mon site, ça peut être inenvisageable dans d'autres contextes.&lt;/p&gt;
&lt;p&gt;Bref&amp;nbsp;! J'espère que ce petit tutoriel vous aura été utile. À peluche&amp;nbsp;!&lt;/p&gt;
&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">admin</dc:creator><pubDate>Mon, 14 Sep 2015 10:00:00 +0200</pubDate><guid>tag:www.miximum.fr,2015-09-14:servir-images-efficacement-django-nginx.html</guid><category>images</category><category>thumbnails</category><category>django</category><category>nginx</category><category>x-sendfile</category><category>x-accel</category><category>acl</category></item><item><title>Anatomie d'une désintoxication au Web sous surveillance</title><link>http://www.miximum.fr/web-sous-surveillance.html</link><description>&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://www.tripodocus.fr/pictures/brume/"&gt;&lt;img alt="Des enfants jouant dans la brume à Nice" class="full border shadow" src="http://www.tripodocus.fr/pictures/2015/brume_medium.jpg" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;Si vous n'avez pas vécu sur une branche d'arbre les dix dernières années, vous
savez probablement que &lt;strong&gt;le concept de vie privée est quelque peu chahuté ces
derniers temps&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Lorsque &lt;strong&gt;nous naviguons sur le Web, nous générons de l'information&lt;/strong&gt;. Et que
ce soit pour des raisons économiques, politiques ou d'autres bien moins
avouables, il existes de nombreuses parties qui ont &lt;strong&gt;un intérêt direct à
exploiter ces données&lt;/strong&gt;. Et elles ne s'en privent pas, le plus souvent à notre
insu et en dehors de tout contrôle légal.&lt;/p&gt;
&lt;p&gt;En tant que bâtisseuses et bâtisseurs du Web, &lt;strong&gt;nous contribuons souvent à
fragiliser la vie privée de nos utilisateurs&lt;/strong&gt; en nous rendant complice de
cette exploitation, moins souvent par malice que par ignorance, insouciance ou
paresse.&lt;/p&gt;
&lt;p&gt;Ce billet est la transcription de la conférence éponyme que j'aurais dû donner
aux &lt;a class="reference external" href="https://2015.rmll.info/?lang=fr"&gt;RMLL 2015 à Beauvais&lt;/a&gt;.&lt;/p&gt;

&lt;div class="section" id="data-mon-amour"&gt;
&lt;h2&gt;Data mon amour&lt;/h2&gt;
&lt;p&gt;Le simple fait de naviguer sur le Web génère de l'information. Ces informations
sont récupérées par différents acteurs, et elles peuvent être exploitées de
différentes manières.&lt;/p&gt;
&lt;p&gt;Lorsque nous sommes conscient·e·s de ce genre de problématiques, nous avons
souvent tendance à sombrer dans une paranoïa concernant nos vie privée, et à
considérer nos données avec l'œil d'une mère poule sur ses œufs.&lt;/p&gt;
&lt;p&gt;Il ne faudrait pas oublier que &lt;strong&gt;ces données représentent une richesse, et
qu'utilisées à bon escient, le résultat peut être bénéfique&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Voici quelques exemples.&lt;/p&gt;
&lt;p&gt;Lorsque j'achète un appareil sur Amazon, le site m'informe que la plupart des
autres internautes achètent également les piles qui vont avec, ce qui m'évite
un aller-retour au supermarché.&lt;/p&gt;
&lt;p&gt;Des moteurs de recommandation sur Youtube, Spotify ou Netflix me permettent de
découvrir de nouveaux films, clips ou artistes et d'enrichir ainsi ma culture
musicale et cinématographique.&lt;/p&gt;
&lt;p&gt;Lorsque je recherche «&amp;nbsp;suicide&amp;nbsp;» sur Google, je suis redirigé vers le numéro de
téléphone de S.O.S Amitié.&lt;/p&gt;
&lt;p&gt;Ces exemples me semblent positifs. Les données sont &lt;strong&gt;exploitées de manière
directe et explicite&lt;/strong&gt;. Dans la plupart des cas, cette exploitation vise à
augmenter le chiffre d'affaire du site, mais en me fournissant un service
supplémentaire.&lt;/p&gt;
&lt;p&gt;L'exploitation plus globale de nos données peut également avoir un impact
positif. Mentionnons &lt;a class="reference external" href="https://www.google.org/flutrends/"&gt;Google Flu, qui permet de suivre les épidémies de grippe
et incidemment d'aider à les prévenir / contenir&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Et puis, ces données représentent une chance de réaliser des études
sociologiques d'une ampleur sans précédent, là ou il y a quelques années
&lt;a class="reference external" href="http://www.slate.com/articles/health_and_science/science/2013/05/weird_psychology_social_science_researchers_rely_too_much_on_western_college.html"&gt;l'échantillon représentatif se limitait souvent à quelques dizaines
d'étudiants&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Dans son livre &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Christian_Rudder"&gt;Dataclysm, Christian Rudder&lt;/a&gt; utilise des données tirées
de nombreux services en ligne (OKCupid, Twitter, Google, Facebook etc.) et
montre comment une étude pertinente de ces données peut nous amener à mieux
nous comprendre nous même (je le recommande, c'est un des livres les plus
fascinants que j'ai pu lire ces dernières années).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="le-cote-obscur-de-l-iceberg"&gt;
&lt;h2&gt;Le côté obscur de l'iceberg&lt;/h2&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://www.tripodocus.fr/pictures/triste-statue/"&gt;&lt;img alt="Triste statue" class="full border shadow" src="http://www.tripodocus.fr/pictures/2014/triste-statue_medium.jpg" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;Mais nos données ne sont pas nécessairement exploitées dans notre meilleur
intérêt. Voici quelques exemples qui vont du légèrement dérangeant au
carrément flippant.&lt;/p&gt;
&lt;p&gt;Pas plus tard que cette semaine, on apprend que &lt;a class="reference external" href="http://korben.info/hacking-team-pirate-400-gb-de-donnees-dans-la-nature.html"&gt;de nombreux états sont clients
d'Hacking Team, société vendant des technologies de surveillance&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;En 2015, &lt;a class="reference external" href="http://www.tomsguide.com/us/adult-friend-finder-hack,news-20964.html"&gt;la base de données du site AdultFriendFinder est compromise&lt;/a&gt;. Des
données &lt;em&gt;très&lt;/em&gt; personnelles —&amp;nbsp;email, adresse, orientation sexuelle, volonté de
tromper son ou sa partenaires&amp;nbsp;— de plusieurs millions de personnes ont été
volées. Y compris des utilisateurs qui avaient supprimé leurs comptes.&lt;/p&gt;
&lt;p&gt;En 2014, Brendan Eich, nouvellement promu CEO de Mozilla, est contraint de
démissionner à cause d'une controverse sur un don à une campagne de lobbying
contre le mariage gay. La liste des donateurs était simplement accessible
publiquement sur le Web.&lt;/p&gt;
&lt;p&gt;En 2014, &lt;a class="reference external" href="http://www.telegraph.co.uk/technology/news/11012008/Paedophile-snared-as-Google-scans-Gmail-for-images-of-child-abuse.html"&gt;Google dénonce à la justice un utilisateur de GMail qui utilisait le
service pour échanger des images pédophiles&lt;/a&gt;,
prouvant ainsi que des logiciels scannent en permanence le contenu des
utilisateurs à la recherche de contenus illégaux.&lt;/p&gt;
&lt;p&gt;En 2013, Mozilla publie &lt;a class="reference external" href="https://www.mozilla.org/en-US/lightbeam/"&gt;Lightbeam, une extension Firefox qui permet de
visualiser tous les sites tiers recevant des informations via des trackers sur
les sites que nous visitons&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;En 2012, une équipe de recherche montre qu'il est &lt;a class="reference external" href="http://www.wired.co.uk/news/archive/2013-03/12/facebook-personality-predictions"&gt;possible de déterminer avec
une confondante précision un certain nombre d'attributs personnels (dont le QI)
uniquement à partir de la liste des «&amp;nbsp;Like&amp;nbsp;» d'un utilisateur de Facebook&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;En 2012, la publication de l'appli «&amp;nbsp;Girls around me&amp;nbsp;» défraye la chronique. Il
s'agit d'une application mixant données issues de Facebook et Foursquare
permettant de &lt;a class="reference external" href="http://www.forbes.com/sites/carolinehoward/2012/03/31/stalking-women-online-girls-around-me-is-an-app-for-that/"&gt;localiser des femmes géographiquement proches et de consulter
leurs photos, données personnelles, etc.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;En 2011, publication du livre &lt;em&gt;The Filter Bubble&lt;/em&gt; de Eli Pariser. De nombreux
sites et moteurs de recherche adaptent leurs résultats à chaque utilisateur,
avec pour résultat de &lt;a class="reference external" href="http://dontbubble.us/"&gt;les «&amp;nbsp;emprisonner&amp;nbsp;» dans une bulle idéologique&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;En 2011, une compagnie spécialisée dans le &lt;em&gt;data-mining&lt;/em&gt; montre qu'en recoupant
plusieurs sources de données, elle est capable d'&lt;a class="reference external" href="http://techcrunch.com/2011/03/22/googlers-buy-more-junk-food-than-microsofties-and-why-rapleaf-is-creepy/"&gt;extrapoler des informations
sur les habitudes alimentaires sur les employés de Google et Microsoft&lt;/a&gt;
(ce qui est quelque peu ironique puisque pour une fois, Google passe du côté
des espionnés).&lt;/p&gt;
&lt;p&gt;En 2010, Michael Learmonth publie un article &amp;quot;&lt;a class="reference external" href="http://adage.com/article/digitalnext/pants-stalked-web/145204/"&gt;The Pants That Stalked Me on the
Web&lt;/a&gt;&amp;quot; ou «&amp;nbsp;Le
pantalon qui m'a harcelé sur le Web&amp;nbsp;». Cet article illustre le principe du
&lt;em&gt;remarketing&lt;/em&gt; : qui consiste à vous abreuver de pubs pour vous pousser à
retourner sur un site terminer une action interrompue, e.g passer une commande.&lt;/p&gt;
&lt;p&gt;En 2008, votre serviteur donne un cours sur le logiciel libre à l'IUT de
Béziers. Dans le fond de la salle, des rires&amp;nbsp;: les étudiants s'échangent des
photos de lui trouvées sur le Web prises au cours d'une soirée récente ou il a
été surpris en état d'ébriété avéré et portant une chemise à fleurs de mauvais
goût.&lt;/p&gt;
&lt;p&gt;Ce ne sont que quelques exemples concrets destinés à illustrer les mille et une
façons dont nos données peuvent être exploitées. Quelques points sont à noter.&lt;/p&gt;
&lt;p&gt;D'abord, nous laissons sur le Web beaucoup plus d'information que ce qu'on
pourrait naïvement penser. Ensuite, il existe énormément d'acteurs qui ont un
intérêt personnel, politique ou économique à exploiter ces données. Et ce ne
sont pas nécessairement des entités abstraites qui traitent les données de
manière anonyme. Enfin, cette exploitation se fait la plupart du temps à notre
insu et en dehors de tout contrôle légal.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="quelles-donnees-laissons-nous-trainer"&gt;
&lt;h2&gt;Quelles données laissons nous trainer&amp;nbsp;?&lt;/h2&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://www.tripodocus.fr/pictures/statue/"&gt;&lt;img alt="première expérience strobiste" class="full border shadow" src="http://www.tripodocus.fr/pictures/2015/statue_medium.jpg" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;«&amp;nbsp;Oui mais moi, je n'ai pas de compte Facebook, alors je ne laisse pas
d'informations privées&amp;nbsp;». Oh, vraiment&amp;nbsp;?&lt;/p&gt;
&lt;p&gt;Jetons donc un coup d'œil aux données que nous laissons tous en surfant sur le
Web.&lt;/p&gt;
&lt;p&gt;D'abord, il y a les données que vous soumettez &lt;strong&gt;directement et consciemment&lt;/strong&gt;,
dans le cadre de l'utilisation d'applications ou de services sur «&amp;nbsp;le cloud&amp;nbsp;»
(Facebook, Google+, Linkedin, Dropbox, Google drive, Gmail, Twitter, etc.)&amp;nbsp;:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;vos infos d'identification (nom, prénom, email, date de naissance, etc.)&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;le nom de votre employeur actuel, votre cv complet&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;vos documents privés&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;votre agenda&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;vos contacts&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;le contenu de vos emails et conversations privées&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;vos habitudes d'achat&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;vos commentaires (Disqus, Twitter, etc.)&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;toutes les infos liées à des applications spécifiques ou des objets
connectés&amp;nbsp;: infos de santé, rythme cardiaque, poids, date de vos prochaines
règles, etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Je suis certain d'en oublier un paquet, mais c'est déjà pas mal.&lt;/p&gt;
&lt;p&gt;Ensuite, il y a les infos que vous soumettez directement, &lt;strong&gt;sans forcément en
avoir conscience, ou sans avoir conscience que ce sont des données
exploitables&lt;/strong&gt;.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;la liste des produits que vous avez consulté&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;l'historique de vos déplacements géographiques (localisation de l'adresse
IP, Foursquare, gps du smartphone, etc.&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;vos &lt;a class="reference external" href="https://www.google.com/settings/ads"&gt;recherches et centres d'intérêt&lt;/a&gt; ;&lt;/li&gt;
&lt;li&gt;le graphe de votre réseau social (Facebook)&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;les mots de passe de vos réseaux wifi (téléphone Android, iPhone)&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;la signature de votre visage (Facebook)&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;la marque et le numéro de série de votre appareil photo (Flickr, 500px)&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;le lieu de vos dernières vacances&amp;nbsp;(Flickr, Twitter)&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;le lieu de vos prochaines vacances&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;la configuration de votre machine, le navigateur que vous utilisez, etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ensuite viennent les données que publient &lt;strong&gt;d'autres personnes sans votre
consentement&lt;/strong&gt;.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;vos «&amp;nbsp;amis&amp;nbsp;» qui publient des photos de vous en chemise à fleur sur
Facebook&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;quand vous échangez des mails avec un utilisateur de Gmail&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;historique de votre navigation via les trackers publicitaires, boutons de
partage, etc. utilisés par des sites tiers.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ensuite, il y a les données qu'on peut déduire, soit &lt;a class="reference external" href="https://www.eff.org/deeplinks/2013/06/why-metadata-matters"&gt;grâce aux méta-données&lt;/a&gt;, soit en
recoupant plusieurs jeux de données. Et là, ça peut aller très, très loin.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;votre personnalité et votre profil psychologique&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;votre orientation politique&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;votre orientation sexuelle&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;votre statut marital&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;l'état de santé de votre couple&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;la couleur de votre peau&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;votre QI&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;si vous consommez de la drogue&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;si vous aimez votre travail&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Et puis évidemment, il y a les données qu'on peut obtenir par d'autres moyens,
e.g &lt;strong&gt;le vol ou l'espionnage&lt;/strong&gt; (et on sait maintenant que les plus paranos
étaient encore loin de la réalité).&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;enregistrements des méta-données de vos appels téléphoniques par la NSA&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://rue89.nouvelobs.com/2010/04/27/la-google-car-enregistre-aussi-votre-reseau-wifi-149258"&gt;localisation géographique de vos réseaux wifis via la Google car&lt;/a&gt;
;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On voit bien que le vrai danger ne réside pas tant dans la quantité de données
que nous générons plus ou moins consciemment, mais dans la quantité
d'informations que l'on peut déduire en recoupant plusieurs sources de données
entre elles.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="qui-exploite-nos-donnees"&gt;
&lt;h2&gt;Qui exploite nos données&amp;nbsp;?&lt;/h2&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://www.tripodocus.fr/pictures/corbeau-au-nid-daigle/"&gt;&lt;img alt="Corbeau un Nid d'Aigle" class="full border shadow" src="http://www.tripodocus.fr/pictures/2014/corbeau-au-nid-daigle_medium.JPG" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;Les quelques exemples donnés plus haut illustrent assez bien de quelles
manières sont exploitées nos données.&lt;/p&gt;
&lt;p&gt;Il y a d'abord &lt;strong&gt;l'exploitation ponctuelle et marginale&lt;/strong&gt;. On peut imaginer des
prédateurs sexuels qui utilisent des données publiques pour draguer plus
«&amp;nbsp;efficacement&amp;nbsp;», ou des employeurs qui réalisent des enquêtes illégales sur
les futures candidats. C'est pernicieux et difficile à combattre.&lt;/p&gt;
&lt;p&gt;Ensuite vient &lt;a class="reference external" href="http://korben.info/hacking-team-pirate-400-gb-de-donnees-dans-la-nature.html"&gt;l'exploitation politique&lt;/a&gt;,
moins visible, moins présente au quotidien, mais franchement plus grave et plus
flippante à mon avis.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L'exploitation la plus visible, la plus présente dans nos vie de tous les
jours est marketing.&lt;/strong&gt; Vendre de la publicité est le modèle économique dominant
sur le Web où la gratuité est quasiment de mise. Et plus on en sait sur vous,
plus ou va vous soumettre à des stimulus publicitaires pertinents pour
maximiser les taux de clics.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="remarketing-et-behavioral-tracking"&gt;
&lt;h2&gt;Remarketing et Behavioral Tracking&lt;/h2&gt;
&lt;p&gt;À l'internaute qui effectue une recherche sur Google, on affiche des pubs que
l'on espère pertinente. Mais si la recherche en question est «&amp;nbsp;java&amp;nbsp;», comment
peut-on savoir si l'utilisateur est intéressé par la danse ou par un voyage sur
l'île&amp;nbsp;? &lt;strong&gt;Se baser sur la simple requête n'est pas suffisant&lt;/strong&gt;, loin de là.&lt;/p&gt;
&lt;p&gt;Pour augmenter la pertinence de leurs annonces, les grandes régies
publicitaires utilisent des techniques regroupées sous le terme de «&amp;nbsp;behavioral
tracking&amp;nbsp;», ou «&amp;nbsp;analyse comportementale&amp;nbsp;».&lt;/p&gt;
&lt;p&gt;Le principe est simple&amp;nbsp;: &lt;strong&gt;on va dresser de vous un profil le plus complet
possible&lt;/strong&gt; à partir de toutes les données que l'on va pouvoir collecter&amp;nbsp;: votre
historique de navigation, les liens que vous cliquez, le temps passé sur chaque
page, etc.&lt;/p&gt;
&lt;p&gt;Avez-vous passé le week-end à regarder des vidéos de danseurs ou à consulter
des blogs de voyages&amp;nbsp;? Grâce à cette information, on saura s'il vaut mieux vous
proposer des annonces pour des écoles de danse ou des agences de tourisme.&lt;/p&gt;
&lt;p&gt;Oui, mais attendez, une minute&amp;nbsp;! Comment Google peut-il connaître mon
historique de navigation&amp;nbsp;? Et bien, parce que le Web est un monde de
mouchards&amp;nbsp;!&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="le-web-de-vichy"&gt;
&lt;h2&gt;Le Web de Vichy&lt;/h2&gt;
&lt;p&gt;Pour comprendre comment le fait de naviguer sur un site communique de
l'information à des tierces parties, rentrons un peu dans la technique.&lt;/p&gt;
&lt;p&gt;Lorsque vous vous connectez sur une page Web, votre navigateur (Firefox,
Chrome, IE, Safari, Opera, etc.) envoie une requête au serveur à l'adresse
correspondante.&lt;/p&gt;
&lt;p&gt;À titre d'exemple, voici le texte que mon navigateur envoie quand je clique sur
un lien vers &lt;a class="reference external" href="http://www.miximum.fr"&gt;Miximum.fr&lt;/a&gt; depuis &lt;a class="reference external" href="http://positon.org"&gt;Positon.org&lt;/a&gt; :&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Host: www.miximum.fr
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:38.0) Gecko/20100101 Firefox/38.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
DNT: 1
Connection: keep-alive
If-Modified-Since: Mon, 29 Jun 2015 13:21:29 GMT
Cache-Control: max-age=0
Referer: http://positon.org/
&lt;/pre&gt;
&lt;p&gt;Dans ce gros pâté de texte, notez la ligne qui commence par «&amp;nbsp;Referer&amp;nbsp;»&amp;nbsp;: ce
champ indique au serveur sur quelle page je me trouvais juste avant.&lt;/p&gt;
&lt;p&gt;À cette requête, le serveur renvoie un autre gros pâté de texte (le document
HTML) qui correspond au contenu de ma page.&lt;/p&gt;
&lt;p&gt;Mais une page Web est constituée de nombreux autres éléments&amp;nbsp;:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;les images&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;les styles css&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;les scripts javascript&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;les polices (fonts)&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ainsi, &lt;strong&gt;pour chaque ressource mentionnée dans le document html, le navigateur
va effectuer une nouvelle requête vers le serveur correspondant&lt;/strong&gt;. Certaines
ressources sont hébergées sur le même serveur que le document html d'origine,
mais d'autres &lt;strong&gt;peuvent être hébergées ailleurs&lt;/strong&gt;. Voici par exemple la requête
envoyée pour télécharger l'image d'entête de l'article&amp;nbsp;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:38.0) Gecko/20100101 Firefox/38.0
Referer: http://www.miximum.fr/
If-Modified-Since: Mon, 29 Jun 2015 09:51:24 GMT
Host: www.tripodocus.fr
DNT: 1
Cookie: csrftoken=Zox90Vlp4W3bzvaLDjpM3vzis539aiSk
Connection: keep-alive
Cache-Control: max-age=0
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Accept: image/png,image/*;q=0.8,*/*;q=0.5
&lt;/pre&gt;
&lt;p&gt;Ce pâté de texte est bel et bien envoyé au serveur hébergeant le site
&lt;a class="reference external" href="http://www.tripodocus.fr"&gt;Tripodocus.fr&lt;/a&gt; qui est le site sur lequel
j'héberge mes photos. Notez que cette requête contient&amp;nbsp;:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;toutes les informations permettant d'identifier mon navigateur&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;le champ &lt;em&gt;Referer&lt;/em&gt;, c'est à dire l'information de la provenance de la
requête&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;le champ &lt;em&gt;Cookie&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ce dernier est intéressant. Il montre que la situation suivante devient
possible&amp;nbsp;:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;vous vous connectez sur Tripodocus.fr pour &lt;a class="reference external" href="http://www.tripodocus.fr/blog/trois-jours-peninsule-dingle-irlande/"&gt;admirer mes photos de vacances&lt;/a&gt; ;&lt;/li&gt;
&lt;li&gt;le site dépose un cookie sur votre navigateur pour vous identifier de
manière unique&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;vous naviguez ensuite sur un autre de vos sites préférés, e.g naziporn.com&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;or, par le plus grand des hasards, l'image sur la page d'accueil du site est
hébergée sur Tripodocus&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;votre navigateur envoie la requête sur mon serveur avec le &lt;em&gt;referer&lt;/em&gt; et le
&lt;em&gt;cookie&lt;/em&gt; d'identification&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;bim&amp;nbsp;! je sais que vous aimez le porno nazi&amp;nbsp;!&lt;/li&gt;
&lt;li&gt;libre à moi d'utiliser cette information comme je le souhaite.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;C'est un exemple tiré par les cheveux parce que d'abord, Tripodocus n'est pas
une régie publicitaire ayant vocation à espionner ses usagers, ensuite il y a
peu de chances que mes photos se retrouvent sur un grand nombre de sites, à
fortiori des sites aussi douteux.&lt;/p&gt;
&lt;p&gt;En revanche, si vous vous appelez Google, et que &lt;a class="reference external" href="https://www.datanyze.com/market-share/analytics/"&gt;votre solution d'analytics
est hébergée sur un site sur deux&lt;/a&gt;, vous devenez
effectivement capable de suivre à la trace la majorité des usagers du Web. Le
cauchemar de George Orwell devenu réalité.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="pris-dans-la-toile"&gt;
&lt;h2&gt;Pris dans la toile&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference external" href="https://www.mozilla.org/en-US/lightbeam/"&gt;L'utilisation de Lightbeam&lt;/a&gt; permet
de constater à quel point ce problème a pris des proportions inquiétantes. J'ai
fait une petite expérience&amp;nbsp;: j'ai commencé par désactiver mes applications de
blocages de mouchards. Ensuite, j'ai remis à zéro les informations de
Ligthbeam. Enfin, j'ai navigué tranquillou pendant deux ou trois minutes. Voici
le résultat de l'expérience&amp;nbsp;:&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://i.miximum.fr/images/T67OXCAFRE/"&gt;&lt;img alt="Les trackers révélés par Lightbeam" class="full img border shadow" src="http://i.miximum.fr/i/2015/07/T67OXCAFRE_l.png" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;Les cercles correspondent aux sites que j'ai directement visité (Le Monde, Le
Figaro, Ted.com, etc.). Les triangles correspondent aux sites tiers qui ont été
informés de ma navigation.&lt;/p&gt;
&lt;p&gt;Lightbeam m'indique que j'ai visité 12 sites, mais ce ne sont pas moins de 176
sites avec qui j'ai été connecté&amp;nbsp;!&lt;/p&gt;
&lt;p&gt;Allez, juste pour rigoler, voici la liste des sites qui ont été informés de mes
visites sur LeMonde.fr&amp;nbsp;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
viamichelin.com
visualrevenue.com
smartadserver.com
kameleoon.com
google-analytics.com
outbrain.com
po.st
netmng.com
sascdn.com
xiti.com
adnxs.com
doubleclick.net
facebook.net
alephd.com
facebook.com
mediaplex.com
eulerian.net
mmtro.com
nuggad.net
chartbeat.com
scorecardresearch.com
cedexis.com
yandex.ru
dailymotion.com
tradelab.fr
adsafeprotected.com
iasds01.com
adserver.pm
orangepublicite.fr
theadex.com
2mdn.net
googlesyndication.com
piximedia.com
yahoo.com
rubiconproject.com
bing.com
openx.net
mookie1.com
exelator.com
mythings.com
mathtag.com
spotxchange.com
quantserve.com
pubmatic.com
stickyadstv.com
turn.com
criteo.com
liverail.com
rfihub.com
jumptap.com
advertising.com
load.s3.amazonaws.com
adleadevent.com
ezakus.net
chartbeat.net
cedexis-radar.net
kxcdn.com
swiftserve.com
cedexis-test.com
msedge.net
&lt;/pre&gt;
&lt;p&gt;Si ça vous fait bondir, c'est normal. Disons que j'ai réactivé mes extensions
de protection dare-dare.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="oui-mais-moi-je-surfe-en-mode-prive"&gt;
&lt;h2&gt;Oui mais moi, je surfe en mode privé&lt;/h2&gt;
&lt;p&gt;Les navigateurs récents fournissent une fonctionnalité intitulée «&amp;nbsp;navigation
privée&amp;nbsp;». La navigation privée consiste à fournir &lt;strong&gt;une session de navigation
temporaire dans laquelle tous les cookies sont désactivés et pendant laquelle
l'historique de navigation n'est pas enregistré&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Cette fonctionnalité &lt;strong&gt;peut vous donner l'impression de surfer de manière
anonyme&amp;nbsp;: il n'en est rien&lt;/strong&gt;. En effet, les cookies ne sont qu'un des multiples
moyens permettant de vous identifier sur le Web. Même en mode privé, les sites
que vous visitez sont capables de vous rattacher à votre profil grâce à la
technique dite du &lt;em&gt;fingerprinting&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Cette technique consiste à recenser &lt;a class="reference external" href="https://wiki.mozilla.org/Fingerprinting"&gt;l'ensemble des informations disponibles à
partir de votre navigateur&lt;/a&gt;. Il
s'avère que ces options sont tellement nombreuses que chaque navigateur dispose
d'une combinaison très souvent unique, un peu comme une empreinte digitale.&lt;/p&gt;
&lt;p&gt;Vous pouvez &lt;a class="reference external" href="https://panopticlick.eff.org/"&gt;faire le test pour voir à quel point votre navigateur est
identifiable grâce à cette technique&lt;/a&gt;. Pour
vous marrer, faites aussi le test en mode privé, et vous verrez qu'il n'y aura
pas une grande différence.&lt;/p&gt;
&lt;p&gt;Le mode privé vous aidera certes à… ahem… acheter un cadeau surprise… ahem…
sans que votre conjoint·e ne découvre le pot aux roses. Mais &lt;strong&gt;ne laissez pas
cette fausse impression d'anonymat vous tromper&amp;nbsp;: les régies du Web continuent
de vous tracker sans aucun problème&lt;/strong&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="comment-se-proteger"&gt;
&lt;h2&gt;Comment se protéger&lt;/h2&gt;
&lt;p&gt;Il y aurait beaucoup de choses à dire là dessus. Mais comme ce n'est pas le
sujet initial de la conférence, je laisse ça de côté pour l'instant. On y
reviendra si vous êtes sages.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="salut-les-collabos"&gt;
&lt;h2&gt;Salut les collabos&lt;/h2&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://www.tripodocus.fr/pictures/sombre-tunnel/"&gt;&lt;img alt="Sombre tunnel" class="full border shadow" src="http://www.tripodocus.fr/pictures/2014/sombre-tunnel_medium.JPG" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;En tant que développeuses et développeurs Web, &lt;strong&gt;nous collaborons pour la
plupart à la fragilisation de la vie privée de nos utilisateurs (moi le
premier)&lt;/strong&gt;. Nous le faisons la plupart du temps sans malice, plutôt par
ignorance, insouciance ou paresse.&lt;/p&gt;
&lt;p&gt;Voici comment êtres de bon·ne·s citoyen·e·s du Web et apporter à la vie privée
de nos utilisateurs l'attention qu'elle mérite.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note sur la paille et la poutre.&lt;/strong&gt; J'ai entrepris de nettoyer mes propres
sites des différents mouchards qui les infestent, mais c'est encore un travail
en cours au moment de la publication du billet.&lt;/p&gt;
&lt;div class="section" id="forcer-https"&gt;
&lt;h3&gt;Forcer https&lt;/h3&gt;
&lt;p&gt;Sur le Web, tout passe en clair. La moindre des choses que vous puissiez faire,
c'est d'offrir à vos internautes un minimum d'intimité en &lt;strong&gt;activant la
possibilité de se connecter en https&lt;/strong&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="virer-les-scripts-de-mesure-d-audience"&gt;
&lt;h3&gt;Virer les scripts de mesure d'audience&lt;/h3&gt;
&lt;p&gt;Que celui qui n'a jamais installé Google Analytics sur son site perso me jette
la première pierre. L'utilisation de scripts de mesure d'audience est &lt;strong&gt;l'un
des pires moyens de moucharder&lt;/strong&gt;, parce que le script en question récupère bien
plus d'informations (adresse ip, résolution de l'écran, etc.) qu'avec une
simple requête http.&lt;/p&gt;
&lt;p&gt;Dans certains cas, l'utilisation de ce genre de scripts réponds à un besoin
précis et clairement identifié&amp;nbsp;: analyse du tunnel d'achat, mesures de
conversions, A/B testing, etc. Mais il faut être honnête, la plupart du temps,
&lt;strong&gt;nous installons ce type d'outils pour nous complaire dans la contemplation
narcissique de notre nombre de pages vues&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Plusieurs solutions pour virer ce vilain Google Analytics ou ses concurrents&amp;nbsp;:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Le virer purement et simplement. À la question «&amp;nbsp;avez-vous vraiment besoin
d'un script de mesure d'audience&amp;nbsp;», la réponse est le plus souvent «&amp;nbsp;non&amp;nbsp;».
Qu'est-ce que j'ai besoin de savoir si j'ai 50 ou 50000 visiteurs par jour
sur Miximum&amp;nbsp;? Dans ce cas là, un script de mesure d'audience est
parfaitement inutile et peut être supprimé.&lt;/li&gt;
&lt;li&gt;Analyser les logs côté serveur. Si vous avez besoin de connaître votre
audience, il existe des outils qui utilisent les logs de votre serveur Web
pour générer des rapports, comme le &lt;a class="reference external" href="https://www.elastic.co/products/kibana"&gt;combo Logstash + Kibana&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Utiliser une alternative auto-hébergée. Il existe des &lt;a class="reference external" href="http://piwik.org/"&gt;outils de mesure
d'audience open-source et auto-hébergés, comme Piwik&lt;/a&gt;.
Ils demandent un peu plus de travail à mettre en place et à maintenir, mais
vous aurez la satisfaction de ne plus moucharder vos utilisateurs.&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class="section" id="virer-les-boutons-de-partage"&gt;
&lt;h3&gt;Virer les boutons de partage&lt;/h3&gt;
&lt;p&gt;«&amp;nbsp;Vous aimez cet article&amp;nbsp;? Cliquez pour le publier sur Facebook / Twitter /
Google+ / JenPasseEtDesMeillers.&amp;nbsp;»&lt;/p&gt;
&lt;p&gt;La plupart (tous&amp;nbsp;?) les réseaux sociaux proposent des boutons de partage qui
permettent de «&amp;nbsp;liker&amp;nbsp;» / «&amp;nbsp;twitter&amp;nbsp;» le contenu directement depuis votre site.&lt;/p&gt;
&lt;p&gt;Ces boutons à l'allure innocente sont devenus incroyablement populaires grâce à
leur promesse marketing&amp;nbsp;: permettre à vos internautes de diffuser votre contenu
au plus grand nombre beaucoup plus facilement.&lt;/p&gt;
&lt;p&gt;Mais la médaille a son revers. Le problème de ces boutons est qu'il sont
souvent &lt;strong&gt;inclus sous forme de script appelé directement sur le serveur du site
en question&lt;/strong&gt;. Cela signifie que&amp;nbsp;:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;vous ralentissez le chargement de vos pages&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;une erreur dans le javascript sur lequel vous n'avez pas la main peut faire
planter votre site&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;vous mouchardez vos utilisateurs.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Pour pallier à ce problème, deux solutions.&lt;/p&gt;
&lt;p&gt;La plus simple, c'est de supprimer ces boutons purement et simplement. Leur
efficacité est toute relative, ils alourdissent la charte graphique et
ralentissent le chargement de vos pages. Bon débarras.&lt;/p&gt;
&lt;p&gt;L'autre solution, c'est d'inclure des boutons sous forme de simple liens. Par
exemple, vous pouvez &lt;a class="reference external" href="https://twitter.com/home?status=D%C3%A9sintoxication%20au%20Web%20sous%20surveillance%20http://www.miximum.fr/web-sous-surveillance.html"&gt;partager cet article sur twitter simplement en cliquant
sur ce lien&lt;/a&gt;. La &lt;a class="reference external" href="https://www.facebook.com/sharer/sharer.php?u=http://www.miximum.fr/web-sous-surveillance.html"&gt;même chose pour Facebook&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="les-cdns"&gt;
&lt;h3&gt;Les CDNs&lt;/h3&gt;
&lt;p&gt;Les CDNs (Content Delivery Network) sont des hébergeurs spécialisés dans…
l'hébergement… de ressources statiques. Plutôt que de servir vous même vos
images, scripts, css, etc. vous pouvez passer par un cdn pour accéler le
chargement de vos pages… au prix d'une nouvelle brèche dans la vie privée de
vos utilisateurs.&lt;/p&gt;
&lt;p&gt;Pour les sites avec de très gros trafics et une vraie problématique de
performance, ça peut se justifier. Mais la plupart du temps, c'est simplement
la paresse qui fait qu'on préfère copier / coller une ligne dans son code html
plutôt que de prendre le temps de télécharger la ressource, configurer la
gestion des assets, configurer son serveur web, etc.&lt;/p&gt;
&lt;p&gt;Devinez qui offre gracieusement sa bande passante aux principales librairies
Javascript&amp;nbsp;?&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/script&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Aujourd'hui, il est relativement simple de configurer un Nginx pour servir des
fichiers statiques efficacement. Vous n'avez donc pas d'excuses si vous laissez
traîner des trucs comme la ligne ci-dessus dans votre code.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="les-maps"&gt;
&lt;h3&gt;Les Maps&lt;/h3&gt;
&lt;p&gt;Besoin d'une carte interactive&amp;nbsp;? Deux-trois lignes de Javascript et hop !
Google Maps. Avec les effets qu'on connait maintenant.&lt;/p&gt;
&lt;p&gt;Pourquoi ne pas utiliser &lt;a class="reference external" href="http://www.openstreetmap.org/"&gt;les alternatives libres et communautaires, comme
OpenStreetMap&amp;nbsp;?&lt;/a&gt; Voici un poil &lt;a class="reference external" href="http://www.miximum.fr/mezzanine-et-openstreetmap-sont-dans-un-bateau.html"&gt;plus d'infos
sur ce qu'il faut mettre en place pour héberger ses propres cartes&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="les-fonts"&gt;
&lt;h3&gt;Les fonts&lt;/h3&gt;
&lt;p&gt;Le &lt;a class="reference external" href="https://www.google.com/fonts"&gt;service Fonts de Google&lt;/a&gt; permet de très
facilement embarquer des polices sympa sur votre site.&lt;/p&gt;
&lt;p&gt;Pourtant, alors que les fonts en question pourraient très bien être hébergées
sur votre propre serveur, cette fonctionnalité n'est tout simplement pas
proposée par Google (tu m'étonnes), faisant &lt;a class="reference external" href="http://fontfeed.com/archives/google-webfonts-the-spy-inside/"&gt;des polices un mouchard
particulièrement pernicieux&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;À ceux qui me disent que vraiment, je sombre dans la paranoïa, laissez moi
citer les ToS&amp;nbsp;:&lt;/p&gt;
&lt;blockquote&gt;
By using our APIs, you agree that Google can use submitted information in
accordance with our privacy policies, such as
&lt;a class="reference external" href="http://www.google.com/privacypolicy.html"&gt;http://www.google.com/privacypolicy.html&lt;/a&gt;.&lt;/blockquote&gt;
&lt;p&gt;La première solution, c'est de &lt;a class="reference external" href="http://ranf.tl/2014/12/23/self-hosting-google-web-fonts/"&gt;récupérer et de servir vous même les fonts
depuis les serveurs de Google, mais j'aime mieux vous prévenir, ça n'est pas
une partie de plaisir&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;L'autre solution est d'utiliser des services alternatifs, comme
&lt;a class="reference external" href="http://www.fontsquirrel.com/"&gt;FontSquirrel.com, qui permettent de télécharger des polices utilisables
commercialement&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="reinternaliser-les-fonctionalites"&gt;
&lt;h3&gt;Réinternaliser les fonctionalités&lt;/h3&gt;
&lt;p&gt;La tendance est à l'externalisation de services à des tiers. Vous n'avez pas
envie de vous embêter avec votre gestion des commentaires&amp;nbsp;? Installez Disqus et
on n'en parle plus.&lt;/p&gt;
&lt;p&gt;Sauf que rappelez-vous, si vous n'êtes pas le client, vous êtes le produit. En
l'occurrence, &lt;a class="reference external" href="http://chrislema.com/killed-disqus-commenting/"&gt;vos utilisateurs le sont&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Chaque fois que vous déportez une des fonctionnalités de votre application /
site vers un prestataire tiers, vous faites fuiter de l'information. Certes,
vous gagnez du temps et de l'énergie, mais vous manquez de respect à vos
internautes. Le jeu en vaut-il la chandelle&amp;nbsp;?&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="attention-au-stockage-de-mot-de-passe"&gt;
&lt;h3&gt;Attention au stockage de mot de passe&lt;/h3&gt;
&lt;p&gt;Imaginez votre pire cauchemar&amp;nbsp;: la sécurité de votre système d'information est
compromise, et votre base de données se retrouve dans la nature. Cela signifie
que les données de vos utilisateurs risquent d'être exploitées à des fins
malveillantes.&lt;/p&gt;
&lt;p&gt;Un des éléments critiques de ces données, c'est le mot de passe.&lt;/p&gt;
&lt;p&gt;L'utilisateur, on le sait, est prompt à réutiliser le même mot de passe d'un
site à l'autre. Si vous n'avez pas pris la précaution de stocker les passwords
de vos utilisateurs avec une sécurité suffisante, vous venez d'ouvrir aux
attaquants un grand nombre de portes. Vous voulez être la personne chargée de
contacter toute votre base d'utilisateurs pour leur recommander de changer tous
leurs mots de passe&amp;nbsp;?&lt;/p&gt;
&lt;p&gt;On peut difficilement en vouloir aux utilisateurs de ne pas être éduqués en
matière de cyber-sécurité. En revanche, c'est &lt;a class="reference external" href="http://sudweb.fr/2011/post/Comment-stocker-les-mots-de-passe-de-vos-utilisateurs.html"&gt;à vous de savoir stocker des
mots de passe correctement&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="anonymiser-les-donnees"&gt;
&lt;h3&gt;Anonymiser les données&lt;/h3&gt;
&lt;p&gt;Si vous hébergez des données sensibles, e.g vous êtes un site de rencontre,
vous devriez prendre soin de séparer les données en question des informations
d'identification.&lt;/p&gt;
&lt;p&gt;Les noms, prénoms, adresses, emails et préférences sexuelles de vos
utilisateurs sont stockées dans la même base&amp;nbsp;? Imaginez si cette base est
compromise&amp;nbsp;! Vous méritez un sérieux coup de règle sur les doigts.&lt;/p&gt;
&lt;p&gt;En tout honnêteté, il s'agit d'une problématique technique difficile. Mais vous
devriez au moins faire votre possible pour complexifier la tâche d'éventuels
attaquants.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="le-cas-de-la-publicite"&gt;
&lt;h2&gt;Le cas de la publicité&lt;/h2&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://www.tripodocus.fr/pictures/vertue-triomphante/"&gt;&lt;img alt="Vertue triomphante" class="full border shadow" src="http://www.tripodocus.fr/pictures/2014/vertue-triomphante_medium.jpg" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;Abordons maintenant le cas de la publicité. Sujet sulfureux s'il en est&amp;nbsp;!&lt;/p&gt;
&lt;p&gt;Lors de &lt;a class="reference external" href="http://sudweb.fr/2015/programme.html"&gt;l'édition 2015 de Sud Web&lt;/a&gt;,
&lt;a class="reference external" href="http://www.renaudforestie.com/"&gt;Renaud Forestié&lt;/a&gt; nous avait expliqué
&lt;strong&gt;pourquoi les sites de journaux en ligne se ressemblent tous&lt;/strong&gt;. La faute à la
charte de publicité imposée par les régies pour ne pas avoir à décliner les
pubs dans des millions de formats différents. Le fait est que la publicité est
le modèle économique dominant de la presse en ligne.&lt;/p&gt;
&lt;p&gt;Passons outre le fait que cette charte impose des règles de design obsolètes et
uniformisantes&amp;nbsp;; ignorons également le fait que la plupart du temps, les
éditeurs de contenu n'ont qu'un contrôle extrêmement limité sur ce qu'affichent
les régies publicitaires&amp;nbsp;; mettons également de côté le fait que la navigation
sur un site bourré de pub soit simplement lente, bugguée, affreuse,
insupportable. &lt;strong&gt;Les sites qui vivent de la publicité sont des catastrophes en
ce qui concerne la protection de la vie privée.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Si vous n'en êtes pas encore convaincu·e, installez Lightbeam et surfez sur
tous les sites de journaux en ligne. C'est à pleurer.&lt;/p&gt;
&lt;p&gt;Lors d'un atelier qui a eu lieu le lendemain, la question a été abordée&amp;nbsp;:
«&amp;nbsp;comment rendre les pubs moins intrusives, moins pénibles, plus
&lt;em&gt;respectueuses&lt;/em&gt; ?&lt;/p&gt;
&lt;p&gt;La réponse est simple&amp;nbsp;: on ne peut pas. Par définition, &lt;strong&gt;il n'existe aucun
moyen de rendre la publicité non-intrusive&lt;/strong&gt;. Vous ne pouvez pas inclure des
scripts, des mouchards, des iframe sur lesquelles vous n'avez aucun contrôle
sur votre site sans remettre en question la vie privée de vos internautes. Il
n'existe pas d'alternative qui vous permettrait à la fois de vivre de la pub et
à la fois d'être un·e bon·ne citoyen·e du Web.&lt;/p&gt;
&lt;p&gt;Peut-on imaginer des journaux en ligne sans pub&amp;nbsp;? Peut-être. Je &lt;em&gt;sais&lt;/em&gt; que
c'est un problème difficile. Je &lt;em&gt;sais&lt;/em&gt; que les journaux aimeraient trouver de
nouveaux modèles économiques. Je &lt;em&gt;sais&lt;/em&gt; que les journalistes préféreraient
faire de la recherche de terrain plutôt que d'être de simples pigistes sans
rien de mieux à faire que reformuler les dépêches de l'AFP. Je &lt;em&gt;sais&lt;/em&gt; que les
designers préféreraient innover et fournir des sites utiles et ergonomiques
plutôt que des trucs préformatés. Je &lt;em&gt;sais&lt;/em&gt; qu'il faut bien vivre, ma brave
dame.&lt;/p&gt;
&lt;p&gt;Tout le monde est d'accord pour dire que &lt;a class="reference external" href="http://paysagesdefrance.org/"&gt;la pub est une belle saloperie&lt;/a&gt;. C'est malheureusement un modèle imposé par
notre organisation économique, politique et sociale. Le remettre en cause
nécessite donc d'innover dans ces domaines, par exemple avec &lt;a class="reference external" href="http://revenudebase.info/"&gt;le revenu de
base&amp;nbsp;?&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">admin</dc:creator><pubDate>Tue, 07 Jul 2015 17:00:00 +0200</pubDate><guid>tag:www.miximum.fr,2015-07-07:web-sous-surveillance.html</guid><category>vie privée</category><category>surveillance</category><category>pub</category><category>google</category><category>facebook</category></item><item><title>Retour sur la conférence E1 à Toulon</title><link>http://www.miximum.fr/e1-2015-toulon.html</link><description>&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://www.tripodocus.fr/pictures/ange/"&gt;&lt;img alt="Statue sous le génie de la navigation à toulon" class="full border shadow" src="http://www.tripodocus.fr/pictures/2015/ange_medium.jpg" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;Ce 26 juin 2015 j'ai eu le plaisir de participer à &lt;a class="reference external" href="http://www.e1-conference.com/"&gt;l'édition 2015 de la
conférence E1 à Toulon&lt;/a&gt; organisée par
&lt;a class="reference external" href="http://www.43117.tl/"&gt;l'association 43.117&lt;/a&gt; avec &lt;a class="reference external" href="http://providenz.fr/"&gt;Laurent Paoletti en
magnifique M. Loyal&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Comme d'habitude lors ce genre d'événements, c'est l'occasion de retrouver des
ami·e·s, faire de nouvelles rencontres et d'apprendre plein de choses. Voici un
petit compte rendu partial et subjectif.&lt;/p&gt;
&lt;div class="section" id="le-programme"&gt;
&lt;h2&gt;Le programme&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference external" href="http://scopyleft.fr/"&gt;David et Stéphane&lt;/a&gt; discutent du &lt;a class="reference external" href="https://larlet.fr/david/blog/2015/travail-transition/"&gt;travail et de son
évolution&lt;/a&gt;. Le travail
en général, et le notre en particulier. L'occasion de s'interroger sur notre
rapport à l'activité professionnelle, sur la perméabilité des frontières entre
sphères professionnelles et personnelles, et sur la remise en question de la
valeur travail dans notre organisation sociale et économique. L'occasion aussi
de découvrir le «&amp;nbsp;tapotage nomade&amp;nbsp;», ou travail itinérant. Une saine réflexion
par les temps qui courent.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://poulpita.com/"&gt;Virginie Galindo&lt;/a&gt; revient sur l'importance des
standards dans le développement du Web, nous invite à les suivre au plus près
pour nourrir notre veille techno, et pourquoi pas, contribuer en retour&amp;nbsp;?&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://braincracking.org/"&gt;Jean-Pierre Vincent&lt;/a&gt; nous offre un retour
d'expérience sur l'échec d'une start-up en dépit d'atouts qu'on aurait pu
croire idéaux&amp;nbsp;: un produit intéressant, des investisseurs à succès, une équipe
expérimentée, et un budget démesuré. L'occasion de revenir sur quelques
préjugés quand aux critères de réussite ou d'échecs de jeunes entreprises, et
d'insister sur la nécessité d'impliquer très tôt le principal acteur du
projet&amp;nbsp;: l'utilisateur.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://www.ludotic.fr/equipe/"&gt;Teresa Colombi&lt;/a&gt; nous explique l'importance de
la prise en compte de l'expérience utilisateur (UX) dans la conception de sites
Web, et insiste sur la différence entre &lt;em&gt;design&lt;/em&gt; et &lt;em&gt;UX&lt;/em&gt;. De nombreuses
informations utiles, mais j'ai du mal à adhérer avec les pratiques qui
consistent à &lt;a class="reference external" href="http://darkpatterns.org/"&gt;mentir et manipuler l'utilisateur&lt;/a&gt;,
même si on nous explique que «&amp;nbsp;la frontière est floue&amp;nbsp;» et que c'est à chacun
d'utiliser ces techniques à bon escient. Aucun tour de passe-passe sémantique
ne peut changer les faits&amp;nbsp;: mentir c'est mentir, et tricher c'est tricher. La
fin justifie-t-elle les moyens&amp;nbsp;? Je suis également un peu déçu de constater que
ce genre de discours ne semble pas choquant pour une grande partie de
l'assistance.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="https://nicolas.steinmetz.fr/"&gt;Nicolas Steinmetz&lt;/a&gt; a un profil
atypique&amp;nbsp;: alors que la tendance est à la sur-spécialisation, il a choisi
d'embrasser le rôle du généraliste touche-à-tout. Il nous explique l'intérêt du
«&amp;nbsp;mouton à six pattes&amp;nbsp;» dans les organisations, mais raconte également les
difficultés et défis qu'il rencontre au quotidien.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://www.meetmyjob.fr"&gt;Hélène Quaniaux&lt;/a&gt; nous offre son retour d'expérience
sur les difficultés de monter une start-up dans le Web lorsque le numérique
n'est pas son cœur de métier. Elle relate les difficultés pour trouver un
prestataire de confiance, et explique l'importance de rencontrer les bonnes
personnes.&lt;/p&gt;
&lt;p&gt;En tant que développeur freelance, il est toujours utile de se voir rappeler
les difficultés que rencontrent nos clients potentiels dans le choix d'un
partenaire pour leur projet.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://www.poxx.net/"&gt;Florian Le Goff&lt;/a&gt; raconte l'aventure de son entreprise
et s'interroge sur la collaboration entre entrepreneurs et structures
d'accompagnements. Une conférence que je ne peux résumer parce que je n'ai pas
pu y assister.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://julien.maupetit.me/"&gt;Julien Maupetit&lt;/a&gt; revient sur les problèmes de
fonctionnement que rencontre la recherche aujourd'hui&amp;nbsp;: difficultés de
publications et mainmise des revues sur les résultats de travaux de recherche,
non publication de données, obsession de publication, etc. Il explique quelles
alternatives propose &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Open_science"&gt;Open Science, un mouvement qui a pour ambition (entre
autres) de devenir à la recherche ce que l'Open Source est au logiciel&lt;/a&gt;. Un mouvement sain, mais qui
peine à prendre son envol.&lt;/p&gt;
&lt;p&gt;Enfin, &lt;a class="reference external" href="https://www.etalab.gouv.fr/en/lequipe"&gt;Daniel Ratier et Emmanuel Raviart&lt;/a&gt; parlent des différentes missions de
l'initiative Etalab en vue de la modernisation de l'action publique. Ils
reviennent sur les réussites d'Etalab, les projets en cours, mais expliquent
également les difficultés rencontrées pour manœuvrer agilement dans une
administration encore quelque peu monolithique et mammouthesque.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="des-projets-qui-marchent-grace-a-l-agilite"&gt;
&lt;h2&gt;Des projets qui marchent grâce à l'agilité&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference external" href="http://www.miximum.fr"&gt;Votre serviteur&lt;/a&gt; a eu le plaisir et l'honneur de
participer en tant qu'orateur lors d'une présentation intitulée «&amp;nbsp;Des projets
qui marchent avec l'agilité&amp;nbsp;».&lt;/p&gt;
&lt;p&gt;Mon but était de revenir sur ce qu'est vraiment l'agilité tout en
m'interrogeant sur les racines des valeurs du manifeste agile.&lt;/p&gt;
&lt;p&gt;Rétrospectivement, je pense que mon propos était mal adapté au contexte (public
non agiliste + seulement 20 minutes de conf). J'ai voulu me faire plaisir en
évitant une présentation «&amp;nbsp;bateau&amp;nbsp;» de l'agilité qui m'aurait ennuyée, mais
j'ai quand même dû repasser très vite sur quelques bases pour que tout le monde
puisse suivre.&lt;/p&gt;
&lt;p&gt;Au final, je ne suis pas certain que les agilistes chevronnés aient appris
grand chose, et les novices ont sans doute été largué·e·s rapidement. Pour me
faire pardonner, je publierai d'ici peu la version texte qui sera plus
détaillée.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="quelques-graines"&gt;
&lt;h2&gt;Quelques graines&lt;/h2&gt;
&lt;p&gt;Je tiens à féliciter l'équipe pour cette organisation sans faille. J'ai passé
une excellente journée, plaisante et enrichissante. Il est toujours bon de
rappeler à quel point ce genre d'événement, organisé bénévolement, représente
un investissement lourd en temps et en énergie, et ma reconnaissance est à la
mesure de leurs efforts.&lt;/p&gt;
&lt;p&gt;Ceux qui regrettent de n'avoir pu assister à l'événement pourront au moins
&lt;a class="reference external" href="https://www.flickr.com/photos/132601973&amp;#64;N05/sets/72157655147507851/"&gt;consulter les chouettes photos&lt;/a&gt;
réalisées &lt;a class="reference external" href="http://www.yvescolas.fr/"&gt;par Yves Colas&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Je me retiens de dire merde à la SNCF qui a joué le rôle de difficultateur en
essayant par tous les moyens de m'empêcher d'arriver à Toulon. J'aborde
maintenant le transport en train avec une nouvelle approche&amp;nbsp;: je prends la
journée, j'embrasse la mentalité du travailleur nomade, revois à la baisse mon
besoin de confort et pars sans préjugé sur mon heure d'arrivée. J'essaye de ne
pas laisser les événements entamer ma sérénité. Je trouve néanmoins un peu
triste de devoir en arriver là.&lt;/p&gt;
&lt;p&gt;Alors que je participe souvent à des conférences avec une forte orientation
technique (Paris Web, Sud Web, MixIT, DjangoCong, etc.), l'E1 proposait une
orientation qui a attiré des profils d'horizons variés&amp;nbsp;: ingénierie mais aussi
communication, commerce, start-ups, enseignement, recherche, etc. Un mélange
des genre enrichissant, parfois aussi un peu frustrant, notamment quand je
constate de la part de certaines personnes (une minorité) un attachement à des
pratiques et valeurs franchement désuètes et ringardes.&lt;/p&gt;
&lt;p&gt;Contrastant très fortement avec ceux qui viennent avec humilité offrir un
retour d'expérience franc et honnête dans un but de participation et d'échange,
certains occupent clairement la scène sans aucun autre but que de m'entuber
pour me vendre leurs produits ou solutions. Un manque d'honnêteté
intellectuelle qui en devient risible tant il est grotesque. Ces gens se
rendent-ils compte qu'ils obtiennent l'effet inverse et que je préfèrerais me
faire tatouer le visage de Nadine Morano sur les fesses plutôt que de
travailler avec eux&amp;nbsp;?&lt;/p&gt;
&lt;p&gt;Je m'interroge sur la vision du monde qu'il faut développer pour ne pas
réaliser à quel point ces pratiques sont inadéquates. Je reste également
perplexe en constatant qu'une partie de l'assistance ne semble pas trouver ce
discours complètement ridicule.&lt;/p&gt;
&lt;p&gt;D'autres approches sont pourtant possibles, qui consistent à ne pas prendre les
gens pour des ânes et des vaches à lait. Prenons exemple sur &lt;a class="reference external" href="https://www.alwaysdata.com/"&gt;les chouettes
gens d'AlwaysData&lt;/a&gt; qui sponsorisent de nombreux
événements sans jamais occuper le micro de manière déplacée, ou &lt;a class="reference external" href="http://www.cremecrm.com/"&gt;la fine équipe
de CrèmeCRM&lt;/a&gt; qui parvient à vendre ses prestations
sans se départir de son authenticité.&lt;/p&gt;
&lt;p&gt;J'ai grandement apprécié la disponibilité des membre de l'équipe d'organisation
ainsi que les autres oratrices et orateurs, toujours prêts à échanger quelques
mots autour d'un café.&lt;/p&gt;
&lt;p&gt;Que dire de plus, si ce n'est que j'ai passé une excellente journée, et que je
vous encourage à participer à ce genre d'événements si vous n'êtes pas
coutumier du fait.&lt;/p&gt;
&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">admin</dc:creator><pubDate>Mon, 29 Jun 2015 15:10:00 +0200</pubDate><guid>tag:www.miximum.fr,2015-06-29:e1-2015-toulon.html</guid><category>e1</category><category>conférence</category><category>web</category><category>toulon</category></item><item><title>Comment lutter contre les mauvais payeurs ?</title><link>http://www.miximum.fr/lutter-contre-les-mauvais-payeurs.html</link><description>&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://www.tripodocus.fr/pictures/nuages-roses/"&gt;&lt;img alt="Des nuages roses" class="full border shadow" src="http://www.tripodocus.fr/pictures/2015/nuages-roses_medium.jpg" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;L'indépendant sera toujours confronté, tôt ou tard, au mauvais payeur.
D'ailleurs, les forums sont remplis de sujets postés par des âmes en détresse
s'interrogeant sur la marche à suivre pour récupérer leur argent durement
gagné.&lt;/p&gt;
&lt;p&gt;Voici comment bien gérer la chose.&lt;/p&gt;

&lt;div class="section" id="le-mauvais-payeur-n-est-pas-celui-qu-on-croit"&gt;
&lt;h2&gt;Le mauvais payeur n'est pas celui qu'on croit&lt;/h2&gt;
&lt;p&gt;Contrairement à ce qu'on pense, il n'y a pas «&amp;nbsp;les mauvais payeurs&amp;nbsp;» d'un côté
et «&amp;nbsp;les bons payeurs&amp;nbsp;» de l'autre. C'est à dire qu'il existe de nombreuses
raisons pouvant faire qu'une facture n'est pas réglée en temps et en heure.&lt;/p&gt;
&lt;p&gt;Malgré le fait que je ne travaille qu'avec des gens honnêtes et sérieux, il
m'arrive très fréquemment de devoir gérer des problèmes de paiement. Même le
client le plus intègre peut parfois s'emmêler dans sa compta et omettre de
s'acquitter d'une facture&amp;nbsp;; que celui qui n'a jamais payé ses impôts en retard
jette la première pierre.&lt;/p&gt;
&lt;p&gt;Évidemment, il y a aussi les arnaqueurs, les voleurs, les escrocs sans
scrupules qui ont élevé la mauvaise foi au rang d'art. Mais ces gens là sont
peu nombreux et avec un peu d'expérience, on arrive à les repérer à l'avance&amp;nbsp;;
la meilleure solution reste d'éviter de travailler avec ce genre d'énergumènes.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="eviter-de-tendre-le-baton-pour-se-faire-battre"&gt;
&lt;h2&gt;Éviter de tendre le bâton pour se faire battre&lt;/h2&gt;
&lt;p&gt;La meilleure façon de régler un problème, c'est de commencer par ne pas le
rencontrer. La prévention sera donc notre meilleure arme.&lt;/p&gt;
&lt;p&gt;Voici un exemple parmi des milliers de messages similaires trouvés sur des
forums (les fautes sont d'origine).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Voilà j'ai refait le site à une société avec le statut Auto Entrepreneur.&lt;/p&gt;
&lt;p&gt;Cette entreprise était l'un de premiers clients, donc je n'avais pas encore
de modèle de contrat, conditions générales, et je ne faisait pas signer de
devis...&lt;/p&gt;
&lt;p&gt;Le client m'avais demandé de payer en plusieurs mensualité, ce que j'ai
accepté.&lt;/p&gt;
&lt;p&gt;En ce moment il y a 2 mensualités de retards, j'ai beau relancer, il ne
veux pas payer pour le moment car &amp;quot;le site rapporte moins que la version
précédente&amp;quot;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Première erreur de débutant&amp;nbsp;: travailler sans contrat. On ne travaille pas sans
contrat, ce dernier devant mentionner de manière très précise et non
équivoque&amp;nbsp;:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;le périmètre fonctionnel de la prestation&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;le tarif convenu&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;l'échéancier de paiement&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;les conditions (délai) de règlement.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Le contrat a souvent mauvaise presse, car il est généralement utilisé comme une
arme et un moyen de pression plutôt que comme un outil de discussion et de
collaboration. D'ailleurs, le manifeste agile professe «&amp;nbsp;la collaboration avec
les clients plutôt que la négociation contractuelle&amp;nbsp;». Mais si votre client
potentiel rechigne, pinaille, reporte la signature, c'est un drapeau rouge
écarlate qui devrait vous inciter à redoubler de prudence.&lt;/p&gt;
&lt;p&gt;J'ai un article dédié aux contrats dans les tuyaux, nous y reviendrons donc.&lt;/p&gt;
&lt;p&gt;J'ai l'habitude d'exiger un acompte avant le début d'une prestation quand il
s'agit de clients que je ne connais pas, ce qui devient rare vu que la plupart
de mes prestations démarrent dans le cadre d'un bouche à oreille.&lt;/p&gt;
&lt;p&gt;Venons en au délai de paiement&amp;nbsp;: une facture, ça se règle comptant. Il n'existe
aucune règle morale ou éthique qui justifierait le fait qu'un prestataire ne
soit payé qu'après un délai de plusieurs semaines à plusieurs mois après sa
prestation accomplie. En pratique, évidemment, il faut bien stipuler un délai,
et j'ai l'habitude de le fixer à deux semaine après la présentation de la
facture. Si votre prospect réclame un délai plus long, c'est à lui de se
justifier, pas à vous.&lt;/p&gt;
&lt;p&gt;Dans tous les cas, si votre client insiste, n'hésitez pas à lui faire remarquer
que &lt;a class="reference external" href="http://vosdroits.service-public.fr/professionnels-entreprises/F23211.xhtml"&gt;les délais de paiement sont encadrés par la loi&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Mieux vaut facturer fréquemment. L'idéal étant de travailler en régie et de
facturer à des périodes fixes, e.g des itérations de deux semaines. Attendre
six mois et la complétion complète d'une prestation au forfait pour présenter
sa facture, c'est se passer soi-même la corde au cou.&lt;/p&gt;
&lt;p&gt;Mieux vaut également éviter de facturer de trop petites sommes. Une prestation
de 130€, ça ne devrait pas exister, ça ne rembourse même pas le temps passé à
rédiger la paperasse. Une telle somme ne vaut pas la peine d'engager des
démarches en cas de non-paiement, certaines personnes peu scrupuleuses le
savent parfaitement et n'hésiteront pas à en jouer.&lt;/p&gt;
&lt;p&gt;Bref&amp;nbsp;! vous aurez compris que le meilleur moyen d'être payé, c'est de fixer des
attentes raisonnables et d'échanger très clairement avec votre futur client sur
les conditions de paiement.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="comment-se-faire-payer"&gt;
&lt;h2&gt;Comment se faire payer&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference external" href="https://www.youtube.com/watch?v=5ydqjqZ_3oc"&gt;«&amp;nbsp;Fuck you, pay me!&amp;nbsp;» est l'adage des affranchis&lt;/a&gt;, &lt;a class="reference external" href="https://www.youtube.com/watch?v=6h3RJhoqgK8"&gt;repris par Mike Monteiro&lt;/a&gt;. En théorie, il n'y a pas à
négocier&amp;nbsp;: on vous doit de l'argent, et on va vous le payer.&lt;/p&gt;
&lt;p&gt;En pratique, il est important de conserver de bonnes relations commerciales (en
partant du principe que le client est honnête et de bonne foi) et de rester
courtois.&lt;/p&gt;
&lt;p&gt;Il faudra donc commencer par une remarque informelle, avant d'escalader via des
communications de plus en plus officielles si le problème ne se résout pas de
lui même.&lt;/p&gt;
&lt;p&gt;Notez que dans tous les cas, il ne faut jamais se départir de sa courtoisie.
Assigner un mauvais payeur au tribunal, oui&amp;nbsp;; l'insulter, jamais. Ce n'est pas
digne d'une &lt;em&gt;lady&lt;/em&gt; ou d'un &lt;em&gt;gentleman&lt;/em&gt;, et ça se retournerait contre vous.&lt;/p&gt;
&lt;p&gt;Voici les différentes étapes d'une relance de paiement&amp;nbsp;:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;coup de fil informel&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;mail informel&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;mail plus formel&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;demande de paiement par lettre recommandée avec AR&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;mise en demeure.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Les modèles de mails et lettres sont abondants sur le Web, je vous laisse vous
débrouiller pour les trouver.&lt;/p&gt;
&lt;p&gt;Voyons maintenant quelques situations de «&amp;nbsp;problèmes de paiement&amp;nbsp;».&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="le-client-paie-en-retard"&gt;
&lt;h2&gt;Le client paie en retard&lt;/h2&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://www.tripodocus.fr/pictures/sinistrose/"&gt;&lt;img alt="Un paysage un peu sinistre" class="full border shadow" src="http://www.tripodocus.fr/pictures/2015/sinistrose_medium.jpg" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;C'est le type de problème de paiement auquel je fais face le plus fréquemment.&lt;/p&gt;
&lt;p&gt;Plusieurs situations peuvent se présenter.&lt;/p&gt;
&lt;p&gt;Premier cas, le client paie une facture en retard de quelques jours. Tant que
la situation reste ponctuelle, il n'y a pas grand chose à faire et mieux vaut
laisser courir pour garder de bonnes relations commerciales.&lt;/p&gt;
&lt;p&gt;Situation plus préoccupante, le client paie systématiquement en retard,
plusieurs jours à plusieurs semaines, et nécessite un ou plusieurs rappels
avant le paiement. Notez que même une situation aussi extrême peut survenir
alors que le client est de bonne foi, mais désorganisé ou juste franchement
tête en l'air (mais ça n'est pas votre problème).&lt;/p&gt;
&lt;p&gt;Au bout de deux ou trois retards successifs, il devient pertinent de mentionner
le problème. Mieux vaut d'abord le faire de manière informelle au téléphone,
mais si vous êtes &lt;a class="reference external" href="http://sudweb.fr/2012/talk/comment-vendre-des-prestations-agiles/"&gt;comme moi un flippé du mobile&lt;/a&gt;, voici un
exemple de mail semi-formel&amp;nbsp;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Bonjour, bla bla bla…&lt;/p&gt;
&lt;p&gt;J'attire votre attention sur le fait que les X dernières factures ont été
réglées plusieurs jours après l'échéance de paiement, et seulement après
relances de ma part.&lt;/p&gt;
&lt;p&gt;N'hésitez pas à m'indiquer si la manière dont mes factures sont formulées
ou présentées causent des difficultés de traitement de nature à provoquer
ces délais.&lt;/p&gt;
&lt;p&gt;J'attire votre attention sur le fait que ces retards pèsent sur ma
trésorerie et occasionnent du temps de traitement de ma part. Dans la
mesure ou j'apprécie notre collaboration et que je ne doute pas de votre
bonne foi, je préfèrerai ne pas avoir à facturer d'intérêts de retard et
que nous puissions discuter pour trouver une solution à ce problème.&lt;/p&gt;
&lt;p&gt;Je me tiens à votre disposition pour en discuter, bla bla bla…&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Si la situation perdure, dans la mesure ou je valorise une relation ouverte et
honnête avec les gens avec qui je travaille, j'aurais tendance à dire qu'il
vaut mieux mettre fin à la collaboration plutôt que de passer en mode « je vais
te forcer à payer avec des intérêts de retard&amp;nbsp;». Mais chacun fait comme il
veut.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="le-client-discute-le-prix-avant-la-prestation"&gt;
&lt;h2&gt;Le client discute le prix avant la prestation&lt;/h2&gt;
&lt;p&gt;Je suis très mauvais négociateur. Par conséquent, la meilleur solution que j'ai
trouvé pour ne pas me faire enfler est de ne tout simplement pas négocier mes
tarifs.&lt;/p&gt;
&lt;p&gt;Par expérience, plus un client paie, plus il vous respectera en tant que
professionnel. Être ferme sur vos tarifs présente de multiples avantages&amp;nbsp;:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;il est plus équitable de facturer le même tarif à tous vos clients puisqu'à
priori, vous allez fournir des prestations de la même qualité&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;vous apparaissez sûr·e de vous et professionnel·le&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;vous serez mieux payé·e&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;c'est moins embêtant.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Fait amusant&amp;nbsp;: plusieurs clients ont déjà tenté de négocier mes tarifs à la
hausse, ce qui me laisse perplexe chaque fois que j'y pense.&lt;/p&gt;
&lt;p&gt;Si un client tente de négocier, il suffit de répondre «&amp;nbsp;mes tarifs sont
fixes&amp;nbsp;», et basta. Vous n'avez pas à vous justifier.&lt;/p&gt;
&lt;p&gt;Soyons franc, ça ne plaira pas à certains de vos prospects, mais c'est à vous
de fixer vos tarifs, pas à eux. Libre à eux de travailler avec vous ou pas.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="le-client-discute-le-prix-apres-la-prestation"&gt;
&lt;h2&gt;Le client discute le prix après la prestation&lt;/h2&gt;
&lt;p&gt;Nouvelle situation&amp;nbsp;: après livraison de la prestation et présentation de la
facture, le client discute le prix.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Bonjour Thibault,&lt;/p&gt;
&lt;p&gt;Pour les raisons X et Y, bla bla bla…&lt;/p&gt;
&lt;p&gt;Nous te proposons de faire un effort sur ta facturation et de nous faire
une ristourne de 30%. Merci de nous tenir au courant.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Notez que cette dernière phrase est tirée mot à mot d'un mail que j'ai vraiment
reçu.&lt;/p&gt;
&lt;p&gt;Il peut malheureusement arriver que, malgré la meilleure volonté du monde, les
attentes des uns et des autres n'aient pas été suffisamment bien comprises
avant le début du projet, et que la relation ne fonctionne pas. Parfois, la
mayonnaise ne prends pas, c'est comme ça (heureusement, c'est rarissime). Notez
que dans ce cas là, un bon professionnel interrompra la solution très vite et
proposera des solutions de remplacement à son futur ex-client.&lt;/p&gt;
&lt;p&gt;La réponse à apporter à ce genre de demande est à étudier au cas par cas. Si
vous avez le sentiment que vous n'avez pas donné le meilleur de vous même lors
de cette prestation, il ne me parait pas absurde de faire un geste commercial.
C'est néanmoins une situation extrême et, si tel est votre choix, l'initiative
devrait venir de vous, pas d'une demande d'un client.&lt;/p&gt;
&lt;p&gt;Mais si vous estimez que vous avez rempli votre part du contrat au mieux de vos
possibilités, il n'y a aucune raison pour que vous ne soyez pas rémunéré·e aux
conditions agréées.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Bonjour XXX,&lt;/p&gt;
&lt;p&gt;Je suis sincèrement désolé que vous ne vous estimiez pas satisfait de notre
collaboration, et je regrette que nous n'ayons pu éviter les écueils que
nous avons rencontré.&lt;/p&gt;
&lt;p&gt;Bla bla bla… Petit rappel factuel du déroulement de la prestation…&lt;/p&gt;
&lt;p&gt;Par conséquent, j'estime m'être acquitté de mes obligations avec
professionnalisme et ai travaillé au mieux dans l'intérêt de votre projet.
Je vous demande donc de bien vouloir procéder au paiement intégral de la
facture sous huitaine.&lt;/p&gt;
&lt;p&gt;Cordialement bla bla bla…&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;En clair&amp;nbsp;: «&amp;nbsp;fuck you, pay me!&amp;nbsp;».&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="le-client-demande-un-echeancier-de-paiement"&gt;
&lt;h2&gt;Le client demande un échéancier de paiement&lt;/h2&gt;
&lt;p&gt;On m'a déjà demandé de procéder à un paiement en plusieurs fois suite à un trou
dans la trésorerie. En vérité, si votre client rencontre des difficultés
financières, ce n'est pas votre problème&amp;nbsp;; en pratique, quand les caisses sont
vides, elles sont vides.&lt;/p&gt;
&lt;p&gt;Si tout le monde est de bonne foi, que vous avez suffisamment confiance en
votre client et que votre trésorerie vous le permet, il me parait envisageable
et de bon ton de convenir d'un échéancier de paiement, à condition de rester
raisonnable. «&amp;nbsp;je te paye 10% tout de suite, et le reste quand j'ai les sous&amp;nbsp;»
n'est pas une demande raisonnable.&lt;/p&gt;
&lt;p&gt;Gardez à l'esprit que ce genre de «&amp;nbsp;service&amp;nbsp;» est une fleur que vous faites à
votre client, et vous n'avez pas à vous sentir coupable ou à vous justifier si
vous refusez&amp;nbsp;. Vous avez vous aussi vos factures à payer.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Bonjour XXX,&lt;/p&gt;
&lt;p&gt;Je suis désolé d'apprendre vos difficultés, et je comprends parfaitement
ton point de vue.&lt;/p&gt;
&lt;p&gt;Malheureusement, ma trésorerie actuelle ne me permets pas d'accéder à ta
demande, aussi je suis obligé de te demander de t'acquitter de la facture
dans les délais impartis.&lt;/p&gt;
&lt;p&gt;Je ne doute pas que vous saurez trouver les ressources pour sortir de cette
ornière bla bla bla…&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Attention, autant un échéancier peut être une solution exceptionnellement
acceptable, autant une ristourne ne l'est pas. Cf. section ci-dessus.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="le-client-interrompt-la-prestation-et-ne-veut-pas-payer"&gt;
&lt;h2&gt;Le client interrompt la prestation et ne veut pas payer&lt;/h2&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://www.tripodocus.fr/pictures/helice-et-rayures/"&gt;&lt;img alt="Un nuage en forme d'hélice" class="full border shadow" src="http://www.tripodocus.fr/pictures/2015/helice-et-rayures_medium.jpg" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;Un client vous booke une semaine, puis décide d'arrêter la prestation au
dernier moment, voire après un ou deux jours. Il refuse de payer, ou propose de
ne payer que les deux jours réalisés.&lt;/p&gt;
&lt;p&gt;Évidemment, impossible de trouver une mission du jour au lendemain. Sans
compter que vous avez peut-être refusé d'autres contrats car vous étiez censé·e
être en poste. Une belle perte sèche.&lt;/p&gt;
&lt;p&gt;Là encore, la réponse à apporter dépend du contexte. S'il s'agit d'un client
régulier avec qui j'entretiens de bonnes relations, que la situation est
exceptionnelle et la demande motivée, on s'arrange. On n'est quand même pas des
bêtes&amp;nbsp;!&lt;/p&gt;
&lt;p&gt;Par contre, avec les rigolos qui vous ont confondu avec une serpillère destinée
à éponger leur manque d'organisation, pas de pitié&amp;nbsp;! «&amp;nbsp;Fuck you, pay me!&amp;nbsp;» sera
votre étendard.&lt;/p&gt;
&lt;p&gt;Idéalement, votre contrat devra stipuler un montant de facturation minimum, par
exemple une semaine. Autre solution, proposer deux TJM&amp;nbsp;: un tarif «&amp;nbsp;classique&amp;nbsp;»
et un tarif «&amp;nbsp;premium&amp;nbsp;» pour les prestations inférieures à X jours. À vous de
faire votre sauce, vous aurez compris l'idée.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="conclusion"&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Vous êtes freelance et rencontrez des problèmes de paiement&amp;nbsp;? Si ça peut vous
rassurer, vous n'êtes pas seul·e. Je ne connais pas un·e seul·e indépendant·e
expérimenté·e qui n'ait quelques anecdotes à ce sujet.&lt;/p&gt;
&lt;p&gt;Que ce billet ne vous donne pas l'impression qu'il est impossible de se faire
payer en étant freelance. &lt;a class="reference external" href="http://www.miximum.fr/des-clients-contents-et-rien-dautre.html"&gt;Dans 99% des cas, je travaille avec des gens
honnêtes, sincères et qui paient rubis sur l'ongle.&lt;/a&gt; Malgré tout,
une mésentente est toujours possible et mieux vaut régler le problème avant
qu'il ne prenne trop d'ampleur.&lt;/p&gt;
&lt;p&gt;Il peut être terriblement anxiogène d'aller au conflit pour récupérer la
rémunération qui vous est due&amp;nbsp;; mail il est malheureusement difficile d'y
couper. Après tout, quand un seul coup de fil ou mail courtois mais ferme peut
vous faire récupérer quelques milliers d'euros, on aurait tort de s'en priver.&lt;/p&gt;
&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">admin</dc:creator><pubDate>Tue, 31 Mar 2015 17:15:00 +0200</pubDate><guid>tag:www.miximum.fr,2015-03-31:lutter-contre-les-mauvais-payeurs.html</guid><category>freelance</category><category>facture</category></item><item><title>Implémentation de l'algorithme k-means en javascript</title><link>http://www.miximum.fr/implementation-algorithme-k-means-javascript.html</link><description>&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://i.miximum.fr/images/JB66NBEOEH/"&gt;&lt;img alt="Un diagramme de Voronoi obtenu par clusterisation" class="full border shadow" src="http://i.miximum.fr/i/2015/03/JB66NBEOEH_l.png" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a class="reference external" href="http://fr.wikipedia.org/wiki/Algorithme_des_k-moyennes"&gt;K-means («&amp;nbsp;K-moyennes&amp;nbsp;» en bon français)&lt;/a&gt; est un algorithme de
&lt;a class="reference external" href="http://fr.wikipedia.org/wiki/Partitionnement_de_donn%C3%A9es"&gt;clustering (ou «&amp;nbsp;partitionnement&amp;nbsp;», pardon Molière) de données&lt;/a&gt;. Le but de
l'algorithme est de répartir un jeu de données en &lt;em&gt;k&lt;/em&gt; sous-groupes tels que
chaque donnée appartient au groupe dont la moyenne lui est le plus proche.&lt;/p&gt;
&lt;p&gt;Relisez la phrase ci-dessus plusieurs fois jusqu'à vous convaincre qu'elle est
incompréhensible, et laissez moi tâcher d'expliquer ça en langage humain.&lt;/p&gt;

&lt;p&gt;Imaginons un gros tas de données avec des valeurs bien définies. Je veux
regrouper ces données en &lt;em&gt;k&lt;/em&gt; groupes de façon à ce que chaque groupe soit
intuitivement cohérent.&lt;/p&gt;
&lt;p&gt;Par exemple, si je suis un supermarché et que je veux cibler des campagnes
publicitaires, je vais essayer de répartir mes clients en groupes cohérents
(peut-être en fonction de leurs achats&amp;nbsp;?), même si je n'ai aucune idée au
préalable de ce que pourraient bien être ces groupes.&lt;/p&gt;
&lt;p&gt;Je peux aussi récupérer la base de données de Wikipédia, extraire tous les
articles qui parlent de «&amp;nbsp;Python&amp;nbsp;», et répartir ces articles en différents
groupes en fonction du contenu, ce qui ne manquera pas de mettre en évidence
les différentes signification du terme (le serpent, le langage de
programmation, le revolver, etc.).&lt;/p&gt;
&lt;p&gt;Quant à nous, nous allons appliquer l'algorithme k-means à l'imagerie pour
découvrir comment nous pouvons extraire une palette de couleur d'une
photographie.&lt;/p&gt;
&lt;div class="section" id="utiliser-k-means-pour-extraire-une-palette-de-couleurs"&gt;
&lt;h2&gt;Utiliser K-means pour extraire une palette de couleurs&lt;/h2&gt;
&lt;p&gt;J'ai détaillé &lt;a class="reference external" href="http://www.tripodocus.fr/blog/determiner-la-palette-de-couleurs-dune-photo/"&gt;le fonctionnement et l'utilité de l'algorithme des k-moyennes
appliqué à la segmentation d'une image sur Tripodocus&lt;/a&gt;.
Je teste le billet itinérant, un concept qui a de l'avenir.&lt;/p&gt;
&lt;p&gt;Je vous conseille donc de vous empresser de lire ce billet. Nous nous
contenterons ici d'analyser le code.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="simulation"&gt;
&lt;h2&gt;Simulation&lt;/h2&gt;
&lt;p&gt;Comme c'est vendredi et que je sais que vous êtes de grands enfants, voici de
quoi jouer un peu.&lt;/p&gt;
&lt;p&gt;Notez que pour d'obscures raisons techniques, cela peut ne pas fonctionner en
fonction de l'url de l'image. Les photos provenant de Flickr ou [Wikimedia
commons](&lt;a class="reference external" href="http://commons.wikimedia.org/wiki/Main_Page"&gt;http://commons.wikimedia.org/wiki/Main_Page&lt;/a&gt;) ne poseront pas de
problèmes.&lt;/p&gt;
&lt;div id="demo" style="margin-bottom: 1.5em;"&gt;
    &lt;canvas id="canvas" style="width: 100%"&gt;&lt;/canvas&gt;
    &lt;form id="image-form" method="get" action="#"&gt;

        &lt;label for="image-input"&gt;Url de l'image&lt;/label&gt;
        &lt;input type="input" id="image-input" value="https://farm8.staticflickr.com/7627/16763808031_3939774039_z.jpg" /&gt; &lt;br /&gt;

        &lt;label for="k"&gt;Nombre de zones&lt;/label&gt;
        &lt;input type="number" name="k" id="k" value="8" /&gt; &lt;br /&gt;

        &lt;label for="n"&gt;Nombre d'itérations&lt;/label&gt;
        &lt;input type="number" name="n" id="n" value="10" /&gt; &lt;br /&gt;

        &lt;input type="submit" value="Go baby!" /&gt;
    &lt;/form&gt;
&lt;/div&gt;&lt;p&gt;Pour ceux que ça intéresse, &lt;a class="reference external" href="https://github.com/thibault/katas/tree/gh-pages/kmeans"&gt;le code est disponible sur Github&lt;/a&gt;. Bon, c'est pas tout
ça, on n'est pas non plus là que pour rigoler, hein&amp;nbsp;!&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="telecharger-et-afficher-l-image"&gt;
&lt;h2&gt;Télécharger et afficher l'image&lt;/h2&gt;
&lt;p&gt;Pour &lt;a class="reference external" href="https://developer.mozilla.org/fr/docs/Tutoriel_canvas"&gt;dessiner sur une page Web, nous allons utiliser l'API Canvas&lt;/a&gt;. La première chose à
faire, c'est de télécharger l'image dont l'url a été indiquée pour la charger
dans ledit canvas.&lt;/p&gt;
&lt;p&gt;Notre code HTML se bornera à ça.&lt;/p&gt;
&lt;pre class="code html literal-block"&gt;
&lt;span class="nt"&gt;&amp;lt;canvas&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;canvas&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/canvas&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;image-form&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;post&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;#&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;image-input&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Url de l'image&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;input&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;image-input&amp;quot;&lt;/span&gt;  &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;submit&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Go baby!&amp;quot;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Ensuite, voici le code js qui permet de configurer les événements qui vont
bien.&lt;/p&gt;
&lt;pre class="code js literal-block"&gt;
&lt;span class="c1"&gt;// Get html elements
&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'canvas'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'image-form'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;image_input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'image-input'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;k_input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'k'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;n_input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'n'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Create an empty image element
&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;crossOrigin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// When the form is submitted, set the &amp;quot;src&amp;quot; attribute
// to start loading the image
&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'submit'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;image_input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;'?uncache='&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nx"&gt;getTime&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Wait til the image is loaded before going any further
&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'load'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;displayImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Le mécanisme est assez classique. On créé un élément &lt;tt class="docutils literal"&gt;Image&lt;/tt&gt; vide. Le fait
d'affecter son attribut &lt;tt class="docutils literal"&gt;src&lt;/tt&gt; va démarrer le chargement de l'image. Une fois
ce chargement terminé, un événement &lt;tt class="docutils literal"&gt;load&lt;/tt&gt; est lancé.&lt;/p&gt;
&lt;p&gt;Attention à ne pas tomber dans le piège classique qui consiste à spécifier
l'attribut &lt;tt class="docutils literal"&gt;src&lt;/tt&gt; &lt;strong&gt;avant&lt;/strong&gt; de configurer le listener sur &lt;tt class="docutils literal"&gt;load&lt;/tt&gt;. Il serait
alors possible que ledit événement soit soulevé avant même que l'on ai pu faire
quoi que ce soit.&lt;/p&gt;
&lt;p&gt;Notez également la configuration de l'attribut &lt;tt class="docutils literal"&gt;crossOrigin&lt;/tt&gt;. J'ai tellement
galéré sur ce problème que ça me déprime rien que d'en parler, alors &lt;a class="reference external" href="https://developer.mozilla.org/fr/docs/Web/HTML/Image_avec_ressources_origines_multiples_CORS_activees"&gt;je vous
laisse vous renseigner par vous même&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Une fois tout ceci en place, il nous reste encore à afficher notre image dans
notre canvas. Notez qu'on limite assez fortement la taille de l'image pour
conserver des performances raisonnables.&lt;/p&gt;
&lt;pre class="code js literal-block"&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;CANVAS_WIDTH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;displayImage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// Resize the canvas to the correct size
&lt;/span&gt;    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;ratio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;CANVAS_WIDTH&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;CANVAS_WIDTH&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;ratio&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Use the rendering context to draw the image
&lt;/span&gt;    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'2d'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;drawImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Tout ceci étant plutôt limpide et &lt;a class="reference external" href="https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Basic_usage"&gt;parfaitement documenté&lt;/a&gt;,
je ne vois pas trop l'intérêt de nous attarder plus que nécessaire.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="initialiser-les-clusters"&gt;
&lt;h2&gt;Initialiser les clusters&lt;/h2&gt;
&lt;p&gt;Première étape de l'algorithme des k-moyennes&amp;nbsp;: initialiser &lt;em&gt;k&lt;/em&gt; clusters de
manière totalement arbitraire. C'est ce que nous allons faire ici en
sélectionne au hasard des pixels dans l'image.&lt;/p&gt;
&lt;pre class="code js literal-block"&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;initializeClusers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Initialize random clusters
&lt;/span&gt;    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;clusters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;))).&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;hue&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;getHue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nx"&gt;saturation&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;getSaturation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nx"&gt;luminosity&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;getLuminosity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nx"&gt;nb_pixels&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;total_x&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;total_y&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;total_hue&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;total_saturation&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;total_luminosity&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;clusters&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Voilà notre structure initialisée. Notez qu'au niveau des couleurs, on
travaillera dans l'espace HSL, plus pertinent que RGB.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="iterer-pour-mettre-a-jour-les-k-moyennes"&gt;
&lt;h2&gt;Itérer pour mettre à jour les k-moyennes&lt;/h2&gt;
&lt;p&gt;Le cœur de l'algorithme réside en son processus itératif. À chaque itération,
nous allons affecter tous les pixels de notre image au cluster adéquat.
Ensuite, nous allons mettre à jour les clusters en calculant la nouvelle
moyenne.&lt;/p&gt;
&lt;p&gt;Le nombre d'itération peut être déterminé à l'avance, ou on peut continuer tant
que les clusters ne sont pas stables. Dans le cas moyen, l'algo converge assez
vite.&lt;/p&gt;
&lt;pre class="code js literal-block"&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;iterate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;clusters&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;affectPixelsToClusters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;clusters&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;updateClusters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;clusters&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="clusteriser-les-donnees"&gt;
&lt;h2&gt;Clusteriser les données&lt;/h2&gt;
&lt;p&gt;Affecter une donnée à un cluster revient à déterminer, pour un paramètre donné,
quel est le cluster le plus proche de cette donnée.&lt;/p&gt;
&lt;p&gt;En l'occurrence, le paramètre concerné ici est la couleur du pixel.&lt;/p&gt;
&lt;p&gt;Notez l'extrême inefficacité de cet algorithme. Notez également que j'ai
volontairement laissé les optimisations de côté pour faciliter la compréhension
du code.&lt;/p&gt;
&lt;pre class="code js literal-block"&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;affectPixelsToClusters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;clusters&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Select the cluster the pixel should go into
&lt;/span&gt;            &lt;span class="nx"&gt;cluster_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getClosestCluster&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;clusters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// Update the cluster data
&lt;/span&gt;            &lt;span class="nx"&gt;clusters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;cluster_index&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;nb_pixels&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nx"&gt;clusters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;cluster_index&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;total_x&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nx"&gt;clusters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;cluster_index&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;total_y&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nx"&gt;clusters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;cluster_index&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;total_hue&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;getHue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;clusters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;cluster_index&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;total_saturation&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;getSaturation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;clusters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;cluster_index&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;total_luminosity&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;getLuminosity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="affecter-chaque-donnee-a-un-cluster"&gt;
&lt;h2&gt;Affecter chaque donnée à un cluster&lt;/h2&gt;
&lt;p&gt;Ici, nous affectons chaque pixel à un cluster en considérant un seul
paramètre&amp;nbsp;:&amp;nbsp;la couleur. C'est à dire que chaque cluster a pour couleur la
couleur moyenne de tous les pixels qui le composent, et c'est cette donnée qui
sera considérée pour affecter chaque pixel.&lt;/p&gt;
&lt;p&gt;Il est intéressant de remarquer que l'on peut très bien décider de considérer
d'autres paramètres. Par exemples, si on affecte les pixels en ne considérant
que leurs coordonnées dans l'image, on obtiendra au final &lt;a class="reference external" href="http://fr.wikipedia.org/wiki/Diagramme_de_Vorono%C3%AF"&gt;un sympathique
diagramme de Voronoi&lt;/a&gt; tel que celui qui
illustre ce billet.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="mettre-a-jour-les-clusters"&gt;
&lt;h2&gt;Mettre à jour les clusters&lt;/h2&gt;
&lt;p&gt;Une fois que toutes nos données (nos pixels) ont été affectées à des clusters,
l'itération est terminée. Il reste à mettre à jour les clusters, c'est à dire
établir la nouvelle moyenne des données qu'il contiennent, et repartir pour un
tour.&lt;/p&gt;
&lt;pre class="code js literal-block"&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;updateClusters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;clusters&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;clusters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;foreach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;total_x&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nb_pixels&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;total_y&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nb_pixels&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;total_hue&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nb_pixels&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;saturation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;total_saturation&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nb_pixels&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;luminosity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;total_luminosity&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nb_pixels&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nb_pixels&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;total_x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;total_y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;total_hue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;total_saturation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;total_luminosity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="et-c-est-tout"&gt;
&lt;h2&gt;Et c'est tout&lt;/h2&gt;
&lt;div class="figure align-center"&gt;
&lt;a class="reference external image-reference" href="http://i.miximum.fr/images/FHD1NRUXQ5/"&gt;&lt;img alt="Image clusterisée grâce aux k-moyennes" class="full border shadow" src="http://i.miximum.fr/i/2015/03/FHD1NRUXQ5_l.png" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;C'est à peu près tout pour le code métier. Notez que j'ai volontairement laissé
de côté toutes les fonctions d'affichage, les optimisations, etc. pour
faciliter la compréhension. Encore une fois, &lt;a class="reference external" href="https://github.com/thibault/katas/tree/gh-pages/kmeans"&gt;le code est disponible si vous
souhaitez approfondir votre étude&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Sur ce, je sais que c'est vendredi et que vous êtes pressé·e·s de rentrer chez
vous, alors je vous laisse.&lt;/p&gt;
&lt;script&gt;
(function(exports) {
    "use strict";

    var CANVAS_WIDTH = 500;

    var Utils = {};

    /**
     * Converts an HSL color value to RGB. Conversion formula
     * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
     * Assumes h, s, and l are contained in the set [0, 1] and
     * returns r, g, and b in the set [0, 255].
     *
     * @param   Number  h       The hue
     * @param   Number  s       The saturation
     * @param   Number  l       The lightness
     * @return  Array           The RGB representation
     */
    Utils.hslToRgb = function(h, s, l) {
        var r, g, b;

        if(s == 0){
            r = g = b = l; // achromatic
        }else{
            var hue2rgb = function hue2rgb(p, q, t){
                if(t &lt; 0) t += 1;
                if(t &gt; 1) t -= 1;
                if(t &lt; 1/6) return p + (q - p) * 6 * t;
                if(t &lt; 1/2) return q;
                if(t &lt; 2/3) return p + (q - p) * (2/3 - t) * 6;
                return p;
            }

            var q = l &lt; 0.5 ? l * (1 + s) : l + s - l * s;
            var p = 2 * l - q;
            r = hue2rgb(p, q, h + 1/3);
            g = hue2rgb(p, q, h);
            b = hue2rgb(p, q, h - 1/3);
        }

        return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
    };

    /**
     * Converts an RGB color value to HSL. Conversion formula
     * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
     * Assumes r, g, and b are contained in the set [0, 255] and
     * returns h, s, and l in the set [0, 1].
     *
     * @param   Number  r       The red color value
     * @param   Number  g       The green color value
     * @param   Number  b       The blue color value
     * @return  Array           The HSL representation
     */
    Utils.rgbToHsl = function(r, g, b) {
        r /= 255, g /= 255, b /= 255;
        var max = Math.max(r, g, b), min = Math.min(r, g, b);
        var h, s, l = (max + min) / 2;

        if(max == min){
            h = s = 0; // achromatic
        }else{
            var d = max - min;
            s = l &gt; 0.5 ? d / (2 - max - min) : d / (max + min);
            switch(max){
                case r: h = (g - b) / d + (g &lt; b ? 6 : 0); break;
                case g: h = (b - r) / d + 2; break;
                case b: h = (r - g) / d + 4; break;
            }
            h /= 6;
        }

        return [h, s, l];
    };

    var App = function(canvas) {
        this.canvas = canvas;
        this.ctx = canvas.getContext('2d');
    };

    /**
     * Display the image into the canvas
     */
    App.prototype.displayImage = function(img) {
        // Resize the canvas to the correct size
        var ratio = img.width / img.height;
        this.canvas.width = CANVAS_WIDTH;
        this.canvas.height = CANVAS_WIDTH / ratio;

        this.ctx.drawImage(img, 0, 0, CANVAS_WIDTH, CANVAS_WIDTH / ratio);

        this.extractColors();
        this.pixels = new Array(this.canvas.width);
    };

    /**
     * Extract all hues for the image.
     */
    App.prototype.extractColors = function() {
        var data;
        var r, g, b;
        var hsl;
        var index;

        this.hues = new Array(this.canvas.width * this.canvas.height);
        this.saturations = new Array(this.canvas.width * this.canvas.height);
        this.luminosities = new Array(this.canvas.width * this.canvas.height);

        data = this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height).data;
        for (var x = 0 ; x &lt; this.canvas.width ; x++) {
            for (var y = 0 ; y &lt; this.canvas.height ; y++) {
                index = (y * this.canvas.width + x) * 4;
                r = data[index];
                g = data[index + 1];
                b = data[index + 2];
                hsl = Utils.rgbToHsl(r, g, b);
                this.hues[x * this.canvas.width + y] = hsl[0] * 360.0;
                this.saturations[x * this.canvas.width + y] = hsl[1] * 100.0;
                this.luminosities[x * this.canvas.width + y] = hsl[2] * 100.0;
            }
        }
    };

    /**
     * Separate the image into k clusters using n iterations
     */
    App.prototype.clusterize = function(k, n) {
        var random_x, random_y, hue, saturation, luminosity;

        // Initialize random means
        this.means = (Array.apply(null, new Array(k))).map(function() {
            random_x = Math.floor(Math.random() * this.canvas.width);
            random_y = Math.floor(Math.random() * this.canvas.height);
            hue = this.hues[random_x * this.canvas.width + random_y];
            saturation = this.saturations[random_x * this.canvas.width + random_y];
            luminosity = this.luminosities[random_x * this.canvas.width + random_y];
            return {
                x: random_x,
                y: random_y,
                hue: hue,
                saturation: saturation,
                luminosity: luminosity,
                nb_pixels: 0,
                total_x: 0,
                total_y: 0,
                total_hue: 0,
                total_saturation: 0,
                total_luminosity: 0
            };
        }, this);
        this.drawMeans();

        this.iterationCounter = n;
        var that = this;
        this.intervalId = setInterval(function() {
            that.iterate();
        }, 750);
    };

    App.prototype.iterate = function() {
        this.affectPixelsToClusters();
        this.drawClusters();
        this.updateMeans();

        this.iterationCounter--;
        if (this.iterationCounter &lt;= 0) {
            clearInterval(this.intervalId);
        }
    };

    /**
     * Draw the k means on the canvas
     */
    App.prototype.drawMeans = function() {
        this.means.map(function(mean) {
            this.ctx.fillStyle = 'hsl(' + mean.hue + ', 100%, 50%)';
            this.ctx.beginPath();
            this.ctx.arc(mean.x, mean.y, 3, 0, 360);
            this.ctx.fill();
        }, this);
    };

    /**
     * Sort all pixels into the different clusters
     */
    App.prototype.affectPixelsToClusters = function() {
        var mean;

        for (var x = 0 ; x &lt; this.canvas.width ; x++) {
            if (this.pixels[x] === undefined) {
                this.pixels[x] = new Array(this.canvas.height);
            }

            for (var y = 0 ; y &lt; this.canvas.height ; y++) {
                mean = this.getClosestMean(x, y);
                this.pixels[x][y] = mean;

                this.means[mean].nb_pixels++;
                this.means[mean].total_x += x;
                this.means[mean].total_y += y;
                this.means[mean].total_hue += this.hues[x * this.canvas.width + y];
                this.means[mean].total_saturation += this.saturations[x * this.canvas.width + y];
                this.means[mean].total_luminosity += this.luminosities[x * this.canvas.width + y];

            }
        }
    };

    App.prototype.drawClusters = function() {
        var mean, rgb, index;
        var imageData = this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height);

        for (var x = 0 ; x &lt; this.canvas.width ; x++) {
            for (var y = 0 ; y &lt; this.canvas.height ; y++) {
                mean = this.means[this.pixels[x][y]];
                rgb = Utils.hslToRgb(mean.hue / 360.0, mean.saturation / 100.0, mean.luminosity / 100.0);
                index = (x + y * this.canvas.width) * 4;
                imageData.data[index] = rgb[0];
                imageData.data[index + 1] = rgb[1];
                imageData.data[index + 2] = rgb[2];
                imageData.data[index + 3] = 255;
            }
        }

        this.ctx.putImageData(imageData, 0, 0);
    };

    /**
     * get the closest mean for the given coordinates
     */
    App.prototype.getClosestMean = function(x, y) {
        var delta_x, delta_y, delta_hue, delta_saturation, delta_luminosity;
        var distance;

        var closest_mean = -1;
        var closest_distance = Infinity;
        var hue = this.hues[x * this.canvas.width + y];
        var saturation = this.saturations[x * this.canvas.width + y];
        var luminosity = this.luminosities[x * this.canvas.width + y];

        for (var i = 0 ; i &lt; this.means.length ; i++) {
            var mean = this.means[i];

            delta_x = mean.x - x;
            delta_y = mean.y - y;
            delta_hue = mean.hue - hue;
            delta_saturation = mean.saturation - saturation;
            delta_luminosity = mean.luminosity - luminosity;
            distance = [
                //Math.pow(delta_x, 2),
                //Math.pow(delta_y, 2),
                3 * Math.pow(delta_hue, 2),
                2 * Math.pow(delta_saturation, 2),
                Math.pow(delta_luminosity, 2)
            ].reduce(function(a, b) {
                return a + b;
            });

            if (distance &lt; closest_distance) {
                closest_mean = i;
                closest_distance = distance;
            }
        }
        return closest_mean;
    };

    App.prototype.updateMeans = function() {
        this.means = this.means.map(function(mean) {
            mean.x = mean.total_x / mean.nb_pixels;
            mean.y = mean.total_y / mean.nb_pixels;
            mean.hue = mean.total_hue / mean.nb_pixels;
            mean.saturation = mean.total_saturation / mean.nb_pixels;
            mean.luminosity = mean.total_luminosity / mean.nb_pixels;
            mean.nb_pixels = 0;
            mean.total_x = 0;
            mean.total_y = 0;
            mean.total_hue = 0;
            mean.total_saturation = 0;
            mean.total_luminosity = 0;
            return mean;
        }, this);
    };


    var canvas = document.getElementById('canvas');
    var form = document.getElementById('image-form');
    var image_input = document.getElementById('image-input');
    var k_input = document.getElementById('k');
    var n_input = document.getElementById('n');
    var app = new App(canvas);
    var img = new Image();
    img.crossOrigin = '';

    img.addEventListener('load', function(evt) {
        app.displayImage(img);
        app.clusterize(+k.value, +n.value);
    });

    form.addEventListener('submit', function(evt) {
        evt.preventDefault();
        img.src = image_input.value + '?uncache=' + (new Date()).getTime();
    });
})(this);
&lt;/script&gt;&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">admin</dc:creator><pubDate>Fri, 13 Mar 2015 13:45:00 +0100</pubDate><guid>tag:www.miximum.fr,2015-03-13:implementation-algorithme-k-means-javascript.html</guid><category>k-means</category><category>canvas</category><category>clustering</category><category>javascript</category></item></channel></rss>