Accès clients

Le Blog

As a personal reminder, here's how to install PIL with jpeg and freetype support in a Python virtualenv with a little help from Homebrew:

$ brew install jpeg
$ wget http://mirrors.fe.up.pt/pub/nongnu/freetype/freetype-2.4.4.tar.gz
$ tar xvzf freetype-2.4.4.tar.gz && cd freetype-2.4.4
$ ./configure && make && sudo make install
$ cd .. && rm -rf freetype-2.4.4*
$ mkvirtualenv fubar --no-site-packages
(fubar)$ pip install PIL

You should obtain something like this at the end of the installation process:

--------------------------------------------------------------------
PIL 1.1.7 SETUP SUMMARY
--------------------------------------------------------------------
version       1.1.7
platform      darwin 2.6.1 (r261:67515, Jun 24 2010, 21:47:49)
              [GCC 4.2.1 (Apple Inc. build 5646)]
--------------------------------------------------------------------
--- TKINTER support available
--- JPEG support available
--- ZLIB (PNG/ZIP) support available
--- FREETYPE2 support available
*** LITTLECMS support not available
--------------------------------------------------------------------

That's all for today folks, thanks for your attention.

EDIT: If you want little-cms support, just run:

$ brew install little-cms
(fubar)$ pip install PIL

Je viens de finir la lecture de l'excellent billet de Thibault, « Dialogue avec un client ». Ce billet présente point pour point ma vision de ce qu'est une collaboration efficace autour d'un projet informatique ; je n'y apprends rien de vraiment nouveau (ayant partiellement nourri la conversation qu'il y relate), mais je suis ravi de voir que je ne suis pas le seul à partager le sentiment que la collaboration est vraiment à réinventer dans ce métier.

Ce qui me chagrine plus en revanche, c'est la lecture de certains commentaires, relativement fatalistes et désabusés ; je ne resiste pas à la tentation d'en commenter certains.

La métaphore du concessionnaire automobile

Le client qui sait exactement ce qu’il veut voudra acheter son site comme une voiture.

Il veut choisir les options et la peinture tout de suite et connaitre l’enveloppe budgétaire dès le début.

Nous y revoila, la fameuse métaphore du concessionnaire automobile, ça faisait longtemps. Comment comparer le développement d'un produit sur-mesures à l'achat d'un produit de type industriel ?

voiture
funny car, par mayme

Je peux comprendre que certains, dans notre secteur, s'emploient à minimiser les coûts de production en génériquant des sites vitrines à base de CMS — voire de frameworks surpluginisés — généralement open-source, mais quand vous avez un client qui arrive avec un cahier-décharge de quelques lignes du type :

Mâcon, le 27 juin,

Monsieur,

Veuillez trouver ci-après notre cahier des charges pour notre projet d'application Internet :

  • Un réseau social orienté vente de clous rouillés online, sans oublier les groupes géolocalisés d'amateurs et de collectionneurs
  • Une place de marché permettant d'échanger des clous rouillés, avec des graphiques permettant de suivre les enchères en temps réel
  • Fournir un extranet fournisseurs leur permettant de saisir leurs prix et leurs quantités
  • En option l'intégration d'un service de location de marteaux d'occasion avec un partenaire qu'il nous reste à trouver
  • L'intégration avec notre outil CRM maison "SugarForce", codé en Delphi par Mr Paul, ancien interne polyvalent décédé depuis
  • Un dacheborde, parcequ'il parait que cela booste la productivité des forces de vente
  • Avec bien entendu des widgets 2.0 pour dynamiser l'ensemble

Non seulement c'est peu précis, vague, mal défini, mais quand vous lisez la dernière ligne, généralement l'envie vous prend d'arrêter ce métier séance tenante pour partir élever des chèvres dans le Larzac :

Pouvez-vous me fournir un devis à tiroirs et vous engager sur une livraison à la rentrée prochaine ?

PS : Je serai absent du 1er juillet au 31 août, voyez avec ma secrétaire (en congés du 15/07 au 15/08) pour toute questions complémentaires PPS : Je vous ai parlé de l'application Blackberry ?

Peut-on imaginer comparer ce type de demande à l'achat d'une voiture en concession ?

Allez, j'ai dix minutes à tuer, je vous imagine la discussion :

— Bonjour monsieur le concessionnaire
— Bonjour monsieur le client
— Je voudrais acheter une voiture
— Ma foi, vous êtes au bon endroit ! Quel modèle vous a tapé dans l'oeil ?
— Je veux quelque chose d'assez standard, comme celle-ci là bas, mais avec quelques ajustements mineurs ; j'aime bien l'arrière de celle-ci, l'avant de celle-là, l'aspect cabriolet de la petite là-bas, et le bas de caisse de celle cachée dans le fond, là. Il faudra aussi qu'elle soit amphibie, je compte aller pêcher avec quelques amis le week-end prochain. Idéalement, elle pourra également passer d'un mode cabriolet à mobile-home d'une simple pression sur un bouton en cas d'embouteillages prolongés. Avec le meilleur moteur que vous ayez à disposition, cela va sans dire. Ah, précisons tout de suite : il me la faut pour le week-end prochain, puisque j'ai une partie de pêche, vous vous souvenez ? Bien entendu, je me résèrve le droit de vous demander dans ce laps de temps quelques options supplémentaires auxquelles je n'ai pas pensé. Vous avez le tarif catalogue sous la main ?
PAN ! (bruit de déflagration)

Redevenons sérieux deux secondes. Le développement d'une application Web sera toujours spécifique et sur-mesures, arrêtons de nourrir l'illusion qu'en utilisant des outils Open Source relativement standardisés techniquement, nous standardisons fonctionnellement les développements. Ce n'est tout simplement pas vrai 99% du temps.

Et pour les fans de maquillage d'outils tout-prêts et autres produits industrial-wanabee, j'utilise souvent la formule suivante auprès de mes clients :

Un développement spécifique adaptera l'outil à votre métier ; un CMS adaptera votre métier à l'outil. La balle est dans votre camp (ou accessoirement dans votre pied.)

Ce qui m'amène à un deuxième constat que je fais de plus en plus au gré des avant-ventes ; il existe bel et bien deux métiers distincts, que beaucoup de prospects (et hélas, soyons francs, de prestataires) confondent allégrement :

  • le métier d'intégrateur de solutions ;
  • le métier de concepteur de solutions spécifiques.

J'identifie la part de demandes entrantes du premier type de prestation à environ 80%, si ce n'est plus. J'ai personnellement décidé en créant mon activité de me consacrer exclusivement aux 20% restants. Et ma vie a changé, en plus mieux. Juste pour dire.

« Parle pas aux cons, ça les instruit »

La moindre tentative de développement modulaire et par priorité sera rejetée et incomprise.

Les méthodes agiles seront rejetées par la grande majorité des clients par peur. J’en viens même à penser que c’est une perte de temps d’en parler.

Oui, n'en parlons plus et subissons sans broncher, c'est plus confortable intellectuellement. Et puis si ça se vend plus facilement comme ça, pourquoi diable hésiter ? Faut juste s'habituer à marcher en canard au troisième recommandé avec AR. Ah mais oui je suis con, le commercial ayant vendu la chose a déjà touché sa comm' et ne gère plus les conséquences de sa vente depuis longtemps, j'oubliais.

The Fear
The Fear, par stumayhew

Sérieusement, il faut se battre. Il faut chercher à convaincre, expliquer sans relâche. Et en cas de blocage rhédibitoire, ne rien lâcher, car d'expérience quelqu'un qui n'accepte aucune concession sur un plan méthodologique ne vous passera vraisemblablement rien sur les aspects commerciaux.

De plus dans les méthodes de développement modulaires et surtout évolutives, les litiges sont beaucoup plus complexes à gérer.

Si litige il y a, c'est vraisemblablement que les principes fondamentaux de l'agilité n'ont jamais été appliqués sur le projet en question. Quelques rappels issus du manifeste agile :

  1. Les individus et leurs interactions plus que les processus et les outils
  2. Des logiciels opérationnels plus qu’une documentation exhaustive
  3. La collaboration avec les clients plus que la négociation contractuelle
  4. L’adaptation au changement plus que le suivi d’un plan

Vous avez noté le point 3 ? Ah ben zut. Et je vous parle pas des trois autres, j'imagine que la lecture du compte-rendu de la décision de justice post-echec-projet vous accapare déjà un temps précieux.

Enfin, j'aimerai bien savoir qu'est ce qui rend les litiges plus évidents à gérer à grands coups d'avenants avec A/R sur un forfait plutôt qu'un bon vieux dialogue autour d'une table dans le cadre d'un projet agile ?

Gérer des litiges contractuels est un véritable métier, très pointu, qui demande de grosses compétences et un sang-froid certain. Ce métier ne me passionne personnellement pas, et je ne peux me résoudre à le considérer comme une fatalité inhérente à mon corps de métier. Je suis là pour fournir des réponses à des besoins précis, quitte à fournir une assistance pour faciliter leur formulation, dans la collaboration et le respect mutuel, au travers d'une communication de type horizontal. J'axe toute relation avant tout sur la confiance. Libre à chacun de se raconter que tout ceci n'est qu'utopie, mais force est de constater qu'après un an d'activité je trouve des gens (des clients pardon, je crois que je m'y ferai jamais) qui embrassent cette manière de fonctionner. Avec d'excellents résultats, par ailleurs.

Par contre, il est clair qu'avec de telles exigences, je ne donne pas suite à neuf appels entrants sur dix. Le mot est lâché : je refuse des opportunités business. SACRILÈGE ! Combien de gens me regardent avec des yeux ronds lorsque je raconte ça… C'est déprimant, d'un certain côté. Travailler plus pour gagner plus ? Et si on parlait plutôt de travailler mieux pour vivre mieux ?

Avoir le temps…

Courir plus pour s'essouffler plus

(…) si ça implique une absence de visibilité sur une enveloppe globale sur laquelle on s’engage, (c')est tout simplement rédhibitoire pour le client

La visibilité s'obtient par la connaissance de la capacité à produire en collaboration avec le client (on appelle ça la vélocité, dans le jargon agile). C'est complètement utopique de prétendre estimer une capacité à produire sans connaître la capacité du client à fournir les éléments nécessaires à l'obtention de cette productivité dans la collaboration. Et par pitié, qu'on ne me parle pas du cahier des charges de 400 pages que personne ne lit puisqu'obsolète au bout d'une semaine, le besoin ayant déjà changé par trois fois suite à la remontée de l'avis du numéro deux groupe/worldwide et de sa cousine Berthe qui est en deuxième année aux Gobelins.

Enfin, je réalise que beaucoup de gens comprennent l'agilité comme la capacité à faire du business malgré tout, voire à tomber du projet coûte que coûte, en s'adaptant aux obstacles au gré du vent et avec les moyens du bord (qui prennent souvent la forme de stagiaires chef de projet et autres experts techniques junior recrutés à la râche pour faire face à un surcroît de charge non prévue, soit dit en passant). C'est juste tout l'inverse ; je ne connais rien de plus structurant et contraignant en termes de processus que les méthodes agiles ! Il n'y a pas de vaudou, l'obtention de résultats est à ce prix. Ainsi que d'une certaine façon, au prix d'un lâcher-prise et d'une certaine acceptation du changement, de chaque côté de la barrière (si tant est qu'il y en ait une).

High Wire Act
High Wire Act, par beginasyouare

« Bon, tu conclues là ? J'vais rater l'apéro »

Oui, je suis un peu décontenancé à la lecture de ces quelques commentaires. On constate tous que bien des commanditaires ne comprennent pas les enjeux de leur métier d'acheteur de prestation de développements informatiques. Je découvre qu'il est des professionnels tellement désabusés qu'ils ont visiblement renoncé à toute vélléité d'éducation de leurs prospects sur les questions d'organisation méthodologique et collaborative du travail, part intégrante du champ d'expertise de tout prestataire de développement informatique un tant soit peu digne de ce nom et prétendant offrir un conseil de qualité. Je ne peux me résoudre à croire qu'ils n'ont pas le choix, que tout ceci est inévitable, et que « c'est la crise, il faut bien bouffer, ma pauvre dame ».

Je pense au contraire qu'une part importante de notre métier, j'irai même jusqu'à parler de notre devoir déontologique, est de les accompagner dans la compréhension de leur rôle dans les processus collaboratifs projet afin de maximiser le potentiel de réussite de ces derniers coûte que coûte. Et d'apprendre à dire non lorsque cela s'avère nécessaire, plutôt que de s'engluer dans cette vision fataliste, nivelant par le bas la qualité globale d'une partie grandissante des réalisations auquel le Web est confronté (france.fr anyone?).

Vous pouvez maintenant basher en commentaire, c'est fait pour ça ;)

I recently had to make a capacity planning study for a client of mine for which I've been developing a Symfony application. Despite the hardware/cloud architecture problem, I also tried to optimize application performances from a webserver software point of view (the application is currently hosted on a standard Apache2 server using mod_php5). I dug Google a bit and found some very enthusiastic comments on PHP-FPM, a PHP FastCGI implementation and the NginX web server.

While PHP-FPM has just made it to PHP core in version 5.3.3, the OS version of the linux server we are using, Ubuntu 10.04 LTS, only ships with 5.3.2. Fortunately, Brian Mercer released a PHP-FPM sapi for these particular OS and PHP versions.

So installing PHP and PHP-FPM on Ubuntu Lucid Lynx is as easy as:

$ sudo apt-get install install python-software-properties
$ sudo add-apt-repository ppa:brianmercer/php
$ sudo apt-get update
$ sudo apt-get install php5-cli php5-common php5-mysql php5-suhosin \
    php5-gd php5-fpm php5-cgi php-pear php5-memcache php-apc

Of course, feel free to add any supplementary packages you need. To start up the PHP-FPM service, run:

$ sudo service php5-fpm start

As a side note, the service will run on port 9000 by default, but you can tweak this up by editing its configuration file located at /etc/php5/fpm/php-fpm.conf.

Now, let's install the NginX web server:

$ sudo apt-get install nginx

That's it. Let's create a new site configuration for our Symfony application in a new /etc/nginx/sites-available/mywonderfulwebsite.org file:

server {
    set $website_host "mywonderfulwebsite.org";
    set $website_root "/var/www/mywonderfulwebsite/web";
    set $default_controller "index.php";
    set $symfony_root "/var/www/mywonderfulwebsite/lib/vendor/symfony";

    listen 80;
    server_name $website_host;

    # Gzip
    gzip on;
    gzip_min_length 1000;
    gzip_types text/plain text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript;
    gzip_disable "MSIE [1-6]\.";

    access_log /var/log/nginx/$website_host.access.log;

    root $website_root;

    index $default_controller;

    charset utf-8;

    location /sf {
        # path to folder where all symfony assets are located
        alias $symfony_root/data/web/sf;
        expires max;
    }

    location / {
        # If the file exists as a static file serve it directly without
        # running all the other rewite tests on it
        if (-f $request_filename) {
            expires max;
            break;
        }

        if ($request_filename !~ "\.(js|htc|ico|gif|jpg|png|css)$") {
            rewrite ^(.*) /$default_controller$1 last;
        }
    }

    location ~ "^(.+\.php)($|/)" {

        set $script $uri;
        set $path_info "/";

        if ($uri ~ "^(.+\.php)($|/)") {
            set $script $1;
        }

        if ($uri ~ "^(.+\.php)(/.+)") {
            set $script $1;
            set $path_info $2;
        }

        include /etc/nginx/fastcgi_params;
        fastcgi_pass 127.0.0.1:9000;

        fastcgi_param SCRIPT_FILENAME $website_root$script;
        fastcgi_param SCRIPT_NAME $script;
        fastcgi_param PATH_INFO $path_info;
    }
}

As you may have noticed, I'm taking part of the convenient custom variable system of the NginX configuration syntax, so you'll probably just have to adapt the $website_host, $website_root, $default_controller and $symfony_root variables to your project needs.

Enabling the website is achieved with a symbolic link, like this:

$ sudo ln -sf /etc/nginx/sites-available/mywonderfulwebsite.org \
              /etc/nginx/sites-enabled/mywonderfulwebsite.org

Last but not least, don't forget to launch the webserver:

$ sudo /etc/init.d/nginx start

I did some benchmarks but unfortunately the hardware configuration being quite different between the old and the new platform, the statistics generated did not make much sense. But the reactivity of the application, and memory footprint is much much better with the new setup (I know, this is not a very scientific statement).

Sources

With all the hype coming to server-side Javascript lately, especially around Node, I was feeling the need to give it a try to see how it goes. Also, getting back to work after three full weeks of unwired holidays was hard enough to worth deserving some playtime with cool and fun technologies.

Node is described as an Evented I/O Framework for Google's V8 JavaScript Engine. Think of it as a toolkit to produce high-performance distributed, event-driven and scalable non-blocking network servers. Okay, whatever the way I want to describe the project, it's buzzword-bingo™. Let's say it's mainly about catching events and react accordingly, to make load distribution and parallel processing easier and more effective.

Installing Node

Installation on my Mac went smoothly and took nearly two minutes by compiling it from the sources; here's how I did (there might be easier or better ways, I don't really care):

$ mkdir tmp
$ git clone http://github.com/ry/node.git
$ cd node
$ ./configure && make && sudo make install

You now have access to the node executable available on your system.

A simple example of a Node HTTP server (put the code below in a test.js file):

var http = require('http');

var server = http.createServer(function(req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.write('Hello World');
  res.end();
});

server.listen(3000, "127.0.0.1");

Then launch the created webserver using the command line:

$ node test.js

And point your browser at http://127.0.0.1:3000 to get printed Hello World. Neat, huh?

Introducing Express, a Web Framework on top of Node

Express is a Web framework built on top of Node, HTTP and Connect, allowing easy creation of full-fledged Web applications. It has routing, handles environments as well as several template engines and much more.

Installation is as easy as Node's one, so here we go:

$ git clone http://github.com/visionmedia/express.git
$ cd express
$ git submodule update --init
$ sudo make install && sudo make install-support

That's it. You can now write your own test application, eg. in a new hello.js file:

var app = require('express').createServer();

app.get('/', function(req, res){
    res.send('Hello World');
});

app.get('/hello/:name', function(req, res){
    res.send('Hello ' + req.param('name') + '!');
});

app.listen(3000, "127.0.0.1");

console.log('Server running at http://127.0.0.1:3000/');

Launch your webapp server by the command line:

$ node hello.js

Express and will create a Node server listening to the local port 3000, so head your favorite browser to http://127.0.0.1:3000/ then http://127.0.0.1:3000/hello/niko to get the picture of what the above code does. Those familiar with Web framework such as rails, django or symfony won't be much disturbed.

Express also ships with an express executable which provides useful commands. To create a new hello application skeleton, just run:

 $ express hello
   create : hello
   create : hello/app.js
   create : hello/logs
   create : hello/public/javascripts
   create : hello/pids
   create : hello/public/stylesheets
   create : hello/public/stylesheets/style.less
   create : hello/public/images
   create : hello/views/partials
   create : hello/views/layout.jade
   create : hello/views/index.jade
   create : hello/test
   create : hello/test/app.test.js

Above command just created an hello project directory where you can cd into and launch the server by its default front controller app.js:

$ cd hello
$ node app.js
Express server listening on port 3000

Note that the generated project skeleton implies using Jade as a tremplate engine and the Less CSS syntax, while one might want to use something else, which is perfectly possible by configuring the project differently.

Next steps documentation will be provided by official Express documentation.

Of course, Express might not be as full-featured as older well-established Web frameworks, but for simple needs it can be pretty easy to setup and deploy, and — probably equally importantly — fun to play with and learn.

Conclusion

As you can see, installing and using Node and Express is quite straightforward, even if you have to dig into the deeper Web to find docs, when they exist. Javascript is a great, agile and well-known language, and taking part of it server-side definitely makes sense if you want my opinion.

Let's see how this will evolve in the future, as there are not as many backend-oriented libs in JavaScript as there are in other languages like python, ruby or php yet. But more and more node modules are appearing day after day, such as Mongoose or Socket.IO, which I'll definitely be playing with as soon as possible.

Thanks for your attention, have fun, take care and don't break the Web.

Symfony propose une tâche de déploiement distant utilisant rsync fort pratique : une fois configurés les paramètres du serveur distant dans le fichier config/properties.ini de votre projet, un simple appel en ligne de commande synchronisera les fichiers du projet présents sur votre système de fichiers local vers l'hôte distant. Et si vous utilisez une clé SSH, l'opération ne vous demandera même pas de saisir votre mot de passe !

$ ./symfony project:deploy monserveur --go

Mais bien souvent, nous avons besoin de plus : préparer un certain nombre de fichiers statiques comme les assets CSS, JavaScript ou les images, ou vider le cache sur la ou les machines distantes pour prendre en compte d'éventuelles modifications de la configuration ou du templating sur la plateforme de production (qui exploite l'environnement du même nom, nous sommes bien d'accord).

Aussi, il est très simple de faire face à ces besoins spécifiques en créant vous même une tâche de déploiement projet, en surclassant la classe sfProjectDeployTask fournie en standard par Symfony. Par exemple, voici la tâche de déploiement que j'utilise pour la mise à jour du site Akei, exploitant mon plugin npAssetsOptimizerPlugin pour la gestion de la minification et l'assemblage des fichiers JavaScript, CSS et images PNG  :

<?php
class AkeiDeployTask extends sfProjectDeployTask
{
  /**
   * @see sfProjectDeployTask
   */
  protected function configure()
  {
    $this->addArguments(array(
      new sfCommandArgument('server', sfCommandArgument::REQUIRED, 'The server name'),
    ));

    $this->addOptions(array(
      new sfCommandOption('go', null, sfCommandOption::PARAMETER_NONE, 'Do the deployment'),
      new sfCommandOption('rsync-dir', null, sfCommandOption::PARAMETER_REQUIRED, 'The directory where to look for rsync*.txt files', 'config'),
      new sfCommandOption('rsync-options', null, sfCommandOption::PARAMETER_OPTIONAL, 'To options to pass to the rsync executable', '-azC --force --delete --progress'),
      new sfCommandOption('optimize', null, sfCommandOption::PARAMETER_OPTIONAL, 'The asset optimizations to make before deploying'),
    ));

    $this->namespace = 'akei';
    $this->name = 'deploy';
    $this->briefDescription = 'Deploys Akei website to a given server';
  }

  /**
   * @see sfProjectDeployTask
   */
  protected function execute($arguments = array(), $options = array())
  {
    // Assets
    if (!is_null($options['optimize']))
    {
      $this->logSection('deploy', 'assets optimization before deploying');
      $task = new npOptimizeAssetsTask($this->dispatcher, $this->formatter);
      $task->run(array('application' => 'main'), array('type' => $options['optimize']));
    }

    // Deployment
    $this->logSection('deploy', 'deploying...');
    parent::execute($arguments, $options);

    // Remote symfony cc
    if ($options['go'])
    {
      $this->logSection('deploy', 'remote cache clear');
      $this->getFilesystem()->execute('ssh user@monserveur.foo "/path/to/www/akei/symfony cache:clear"');
    }

    $this->logSection('deploy', 'done.');
  }
}

Comme vous pouvez le constater, cette tâche déclare un espace de nom akei et propose une option supplémentaire, optimize, permettant de préparer les assets avant déploiement :

$ ./symfony akei:deploy monserveur --optimize=stylesheet --go

Cette commande va tout simplement minifier et assembler les feuilles de style définies par la configuration du plugin npAssetsOptimizerPlugin, déployer les fichiers en production sur la machine monserveur et vider le cache sur la machine distante une fois l'opération effectuée.

Notez d'ailleurs l'emploi du très pratique appel à $this->getFilesystem()->execute(), qui permet d'executer des appels à la ligne de commande locale depuis la classe de tâche elle-même ; ici, une execution distante à travers SSH.

Bien entendu, cet exemple est très spécifique aux besoins du site Akei, mais vous pourriez en quelques minutes gérer plus finement une tâche de déploiement plus générique et configurable. Pensez-y pour vos projets ;)

PS : Ce billet a été écrit en 14 minutes. Merci de votre compréhension.

Derniers commentaires

Tweets