dimanche 4 septembre 2011

Un nouveau coup de gueule

Dans le Hors-Série n°56 de GNU Linux Mag, mon dernier coup de gueule.

jeudi 12 mai 2011

XML à toutes les sauces

Y'en a marre d'avoir du XML partout, pour tout et n'importe quoi.

A l'origine était SGML (Standard Generalized Markup Language). Issu du besoin, dans les années 80, de partager des documents et d'assurer l'interopérabilité entre les outils, SGML permet d'encoder un document textuel sous la forme d'une structure hiérarchique en indiquant par des balises le rôle joué par une portion de texte dans le contexte du document.

Puis, le W3C crée une adaptation de SGML dans le contexte du Web, ce sera XML accompagné par les technologies de traitement :
- un langage de requête/navigation dans l'arbre : XPath
- un mécanisme de construction de grammaire : XML Schema
- des mécanismes de réutilisation/de combinaison des grammaires : les espaces de noms
- un langage de transformation : XSLT
- DOM

Disposant d'une syntaxe de structuration de l'information standardisée et indépendante des outils/systèmes d'exploitation, les développeurs et les industriels y voient le remède à tous leurs maux et vont le mettre à toutes les sauces : fichiers de configuration, idiomes de transport de l'information dans le cadre des Web services... Bref, ils vont oublier que XML est avant tout un modèle d'arbre conçu pour encoder des documents, vont en faire une syntaxe de transport des données structurées et, du même coup, oublier l'écosystème de standards autour de XML et qui en font un modèle de structuration puissant et souple.
XML propose une syntaxe facile – en théorie – à analyser et permettant d'exprimer de nombreuses structures de données. Chaque marqueur peut posséder des attributs, d'autres marqueurs ou du texte.
De plus, un marqueur peut posséder un identifiant unique ou référencer un autre marqueur via son identifiant. La syntaxe permet donc d'exprimer des propriétés via les attributs, des relations via les IDREF ou des agrégations via les marqueurs présents dans d'autres marqueurs.
La grande nouveauté par rapport à HTML, est la possibilité de fermer un marqueur facilement : <br/>.

Pour garder la compatibilité avec HTML, l'astuce consiste à fermer un marqueur en ajoutant un espace avant le slash : <br />. Ainsi, les analyseurs purement HTML comprennent : un marqueur br avec un attribut dont le nom est « / » et sans valeur. Les analyseurs XML comprennent qu'il s'agit d'un simple marqueur sans attribut et sans fils. Contrairement à HTML, tous les attributs doivent avoir une valeur. Il est donc possible d'écrire un fichier compatible HTML et XML.


Quels sont les avantages de XML ?
C'est un langage textuel donc portable, facile à manipuler ou à modifier par un humain ; permettant de structurer des documents, formant un tout ; dont la syntaxe peut être décrite ; acceptant des extensions à tous les niveaux via les espaces de noms et facile à analyser.

Facilité d'analyse
Un des avantages à utiliser XML est de ne pas avoir à écrire un analyseur de fichier. C'est toujours livré avec les librairies standard des langages de développement.
Il existe plusieurs stratégies. Une approche par flux générant des événements (SAX) en lecture seule ; une approche par flux sous le contrôle du programme en lecture et écriture (StAX) ou une approche par création d'un modèle complet du document en mémoire complètement manipulable (DOM).
Les approches par flux, SAX ou StAX sont généralement non rigoureuses sur la syntaxe des fichiers. Les programmes piochent les informations souhaitées en ignorant les autres. Un attribut mal orthographié ne génère aucun message d'erreur. Tant pis pour l'utilisateur.
La syntaxe étant analysée au fur et à mesure de la consommation du flux, il n'est pas possible de faire marche arrière pour connaitre le nœud père par exemple. Donc la gestion de l'historique doit être traitée par le programme. Les gestionnaires d'évènements sont souvent difficiles à écrire, à maintenir et à déverminer. Impossible d'avoir une requête XPath par exemple, avec l'approche par flux. Une partie de la syntaxe ne serait tout simplement pas disponible (../A).
Monter un arbre DOM complètement en mémoire n'est généralement pas une bonne idée, vu le nombre de nœuds à produire. Sur un serveur, c'est jouable, mais sur un téléphone portable, il ne faut pas y compter. Les approches DOM sont très sensibles aux variations de rédaction du flux XML. Un commentaire mal placé et le programme plante. Pourtant, la syntaxe est parfaitement conforme !
Pour améliorer cela, on peut proposer de décrire la syntaxe via une DTD ou un XML Schema. C'est très compliqué et cela produit généralement une syntaxe ou une grammaire fermée. Impossible d'ajouter un nœud ou un attribut sans faire évoluer la syntaxe. De plus, le temps de traitement du flux est alors fortement augmenté.
Sans syntaxe, impossible de traiter les attributs ID ou IDREF car l'analyseur ne peut, à priori, considérer tel attribut comme un identifiant. Donc, impossible de représenter les relations au sein du document. Seules les agrégations et les propriétés sont disponibles. Pour pouvoir traiter cela, le programme devient alors beaucoup, beaucoup plus complexe. Pour mémoire, les langages de développement possèdent tous la notion de relation, aucun ne possède la notion d'agrégation. C'est juste l'inverse avec XML.

Espace de noms
La possibilité d'avoir une syntaxe, combinée à l'utilisation des espaces de noms est une excellente idée, car cela permet d'offrir des points d'ancrage à une syntaxe, pour permettre son enrichissement. Avant XML, aucune autre approche ne permettait cela. Je vous invite à consulter l'excellent article « XML, ouverture sur le monde », dans le GNU Linux Mag N°103 de Mars 2009.
Personne ne comprend vraiment comment sont gérés les espaces de noms. Doit-on toujours préfixer chaque marqueur et chaque attribut ? Peut-on utiliser un espace de nom par défaut, et gagner ainsi 20% sur la taille du fichier ? Dans le doute, les rédacteurs du fichier ne font que recopier les exemples. Comme ces dernières sont loin d'être des modèles de vertu, ils recopient des âneries. Comme l'âne avance, tout le monde est content. Sauf le réseau et les puristes.
Comme personne n'y comprend rien, les syntaxes sont toujours fermées. Les espaces de noms ne servent alors à rien d'autre qu'à faire croire que l'on est un développeur qui maîtrise. Rafraîchissez-moi la mémoire, les espaces de noms ont été conçus pourquoi ?
Le seul avantage de XML par rapport aux autres approches normalisées de structuration des documents est alors perdu. XML perd de son ouverture car les développeurs ne le maîtrisent pas.

Langage à tout faire
Comme XML permet d'exprimer beaucoup de choses, pourquoi ne pas proposer justement un langage de programmation en XML ? XSLT en est l'exemple parfait. C'est, a priori, une excellente idée ! Proposer un langage manipulant une structure dont le langage lui-même est issu. Les programmes peuvent donc s'appliquer à eux-mêmes !
Sauf que le résultat est pitoyable. XSLT est d'une complexité folle ! Tout se mélange. Les données à produire, les données à injecter, les boucles, etc. Comme il n'est pas possible de tout exprimer en XML, il y a en fait plusieurs langages dans XSLT, selon que l'on produise un flux XML, un attribut, une requête XPath, etc. Et comment gérer les text() ? Un retour chariot entre deux marqueurs modifie le flux produit !
Qui peut me dire la différence fondamentale et l'impact sur les performances entre <xsl:template/> et <xsl:for-each/> ?
Au final, le code est complètement illisible, in-maintenable et non performant. La manipulation ne peut se faire qu'en DOM. Et cela, sans parler des choix spécifiques du langage, comme l'interdiction de modifier une variable après son initialisation. Il faut alors utiliser des ruses récursives pour générer le flux voulu. Un cauchemar.
Heureusement que XQuery est venu mettre de l'ordre dans tout cela, en acceptant clairement qu'un programme informatique ne puisse s'écrire en XML. À chacun sa syntaxe.

Structure complexe
Comme la structure des documents XML est finalement complexe à lire et à modifier, ou que l'on ne souhaite pas y placer des données confidentielles (confidentielles ? ... pas pour touts le monde), les frameworks proposent généralement la possibilité d'utiliser des variables. C'est-à-dire des données qui seront incluses dans le flux XML avant son analyse (où après si le framework est mal codé).
<serveur ip="${IP_SERVEUR}" user="root" password="${PASSWORD}"></serveur>
Et on rajoute une couche. Un peu de données dans le document XML, un peu ailleurs, dans un fichier de propriétés ou dans une base de données. Pourquoi ne pas tous mettre au même endroit ? Ça serait trop simple.
« La complexité marque l'étendue de mon talent ! » JP. Troll

Problème des nœuds text()
La plus grosse difficulté provient de la gestion des nœuds textes. En effet, les développeurs comprennent : j'ai un contenu textuel dans mon marqueur donc c'est facile à analyser.
<p>Mon Texte</p>
En fait, ce n'est pas comme cela que le modèle est conçu. Le marqueur possède plusieurs fils de différents types, dont des marqueurs de type text().

<p>
<!-- Un commentaire -->
Mon
<!-- Un autre commentaire -->
texte
</p>


Et encore, je fais abstraction des retours chariots et autres espaces, de ci de là. Comment traiter le marqueur <p> qui respecte pourtant complètement XML ? Les programmes plantent généralement. C'est d'ailleurs un bon moyen de savoir si le programme utilise SAX ou DOM ;-)
XML est fait pour décrire des documents, pas pour des structures !
De plus le développeur hésite souvent en concevant la syntaxe, pour savoir s'il faut utiliser un attribut ou un texte inclus. Ce n'est pas la même chose ! Et cela ne se code pas du tout de la même manière. Le plus simple, du point de vue du développeur, est d'utiliser un attribut. Au moins, il n'y a pas de difficulté à l'extraire du fichier.
Au niveau sémantique XML, il est préférable d'utiliser un/des nœuds text() fils. Les attributs sont là pour raffiner le marqueur englobant (<div type="para"/>). Mais cela n'a un sens qu'avec un document textuel, pas avec une structure de donnée !
Les choses se compliquent si le contenu peut avoir des retours chariots ou autres caractères exotiques. Dans ce cas, un texte fils est obligatoire. Et les rédacteurs du fichier vont savoir qu'il faut encadrer la valeur de <!--[CDATA[ … ]]--> ? C'est simple non ? « On ne vous l'avait pas dit ? Relisez les spécifications XML avant de générer des fichiers foireux ».
C'est encore plus amusant lorsque l'utilisateur exploite les variables. Quid si la valeur d'une variable possède un des caractères du diable ? Vous savez, les caractères inférieur, supérieur et esperluette... « Comme je suis un développeur rigoureux, j'encadre toujours tous les textes d'une gousse d'ail <!--[CDATA[ … ]]-->, ou plus souvent, j'allume un cierge ».
Au niveau sémantique, quelles informations l'utilisateur souhaite utiliser pour dresser des listes, des index ou mettre en place des interrogations ? Les réponses à ces questions peuvent guider sur la bonne syntaxe, entraînant de fait, une complexité dans le code d'analyse. En effet, ce dernier doit être capable de traiter plusieurs nœuds text() séparés de commentaire, de processing-instruction, etc.
Un autre problème important est que chaque marqueur doit être fermé. Pourtant, certains projets produisent des logs en XML ! Donc, en cas de crash du programme (l'un des objectifs des logs), la syntaxe n'est pas valide car le marqueur principal n'est pas fermé. Et si on ferme le marqueur racine juste avant la fin du programme, faut-il l'enlever lors de son redémarrage ? Les logs ne sont alors qu'un ersatz d'XML. Pas tout à fait du XML mais presque. Du Canada Dry.

Jeux de caractères
Un autre problème, normalement correctement traité par les analyseurs XML, est le jeu de caractères utilisé pour écrire le fichier XML. Comme le fichier est facilement modifiable par un humain, avec un simple éditeur de texte, très rapidement, le fichier UTF-8 devient un fichier ISO8859-1 sous Windows, même si l'en-tête du fichier XML exprime le contraire. La portabilité du fichier est alors rapidement perdue, la facilité d'édition également.
« Je ne comprends pas, les accents ont disparus. Pourtant chez moi ça fonctionne ! »

Sécurité
Comme il est possible d'inclure une DTD dans un fichier XML, il est possible d'ajouter un fichier venant de n'importe où.

<!--?xml version="1.0" encoding="UTF-8"?-->
<!DOCTYPE hack [ <!ENTITY include SYSTEM "/etc/passwd"> ]>
<hack>
&include;
</hack>

C'est très sympa d'avoir un petit fichier de paramètres dont on n'a pas la maîtrise car on n'a pas codé l'analyseur... Et hop, chaque fichier XML présente une faille de sécurité ! C'est encore plus drôle lorsque le flux XML est utilisé pour communiquer avec un serveur...

Syntaxe ouverte
Un autre problème avec XML est qu'il est possible d'exprimer la même chose de différentes façons. En ajoutant ou non des espaces, des commentaires, etc. Donc, il n'est plus possible de signer numériquement un document XML sans le normaliser. Les outils de signature doivent alors analyser le fichier pour le normer, afin de pouvoir calculer un hash sur le flux. Cette normalisation n'est pas triviale, car il faut tenir compte de la police de caractère, des entités (&monentiteàmoi;) et autres subtilités. Cela représente un gros travail avant de simplement vérifier une signature numérique.
« Mais on travaille avec du XML. Donc les performances, la consommation mémoire et autres problèmes du pauvre, on s'en moque. Tans pis pour la planète. »

Services Web
Et nous avons utilisé cela pour communiquer entre serveurs Web ! Enfin, au début. Pourquoi pas ? Au moins, XML est un esperanto cross systèmes d'exploitation ! Même si cela est coûteux en termes de volume échangé, temps d'analyse, place mémoire, sécurité, il est possible d'invoquer un service Windows/PHP avec un service Linux/Java.
Le problème est que les services marketings ont trouvé là du grain à moudre pour faire croire à la nouveauté (on propose des invocations distantes normalisées depuis bien avant les services Web, rappelez-vous RPC, CORBA, RMI, ASN.1, etc.). On a alors vendu à tour de bras des architectures SOA, des bus logiciels, des routeurs spécialisés capables de transformer les messages ! « Oui, oui, ma petite dame. Sans modifier les programmes on peut les faire communiquer ! ». Mon œil ! Qui fera croire à un développeur que n'importe quel modèle est transformable en un autre ? Comme les vendeurs sont convaincants (ont-il payé des bakchichs ?), les services informatiques ont accepté et ont acquis des produits hors de prix, capables de faire la pluie et le beau temps, en modifiant deux trois paramètres. Enfin, … sur le papier ou dans les démos.
Dans la vraie vie, celle des développeurs - qu'on ose parfois appeler ingénieurs -, seuls les projets internes communiquent via des services Web. Donc, le client et le serveur sont intimement liés. Ils se connaissent tellement bien, qu'il n'est pas possible de modifier l'un sans modifier l'autre.
Pourquoi ne pas utiliser alors des approches plus efficaces, en binaire ? Il ne faut pas oublier que le rapport signal / bruit des flux XML est très, mais alors très, très mauvais ! Pour quelques octets, il faut dépenser plusieurs Ko. (Certains vont jusqu'à modifier les marqueurs pour les raccourcis à un caractère. Donc, on sacrifie la lisibilité des flux XML sans vraiment gagner en performance.) Si c'est une communication interne, les services Web ne servent à rien !
On économise un peu de volume en utilisant JSON, un format simplifié en JavaScript, mais ce n'est pas terrible à présenter devant un client. Alors... « Avec XML on ne me reprochera rien. » Finalement, XML est l'IBM ou l'Oracle des formats d'échange. Personne ne vous reprochera de choisir ces fournisseurs, mêmes s'ils vous on vendu de mauvais produits ou des produits ne correspondant absolument pas à vos besoins. L'effet « parapluie ».

Format binaire
Comme de plus, le format est textuel, impossible de l'utiliser pour envoyer des images, des pièces jointes, etc. Pour cela, on utilise une superbe astuce, l'encodage Base64, conçu pour Usenet en 1979 ! Bonjour la nouveauté ! En gros, on augmente de 30% la taille du flux pour chaque fichier binaire. Comme chacun sait, ce sont justement des petits fichiers... C'est pour cela qu'on souhaite les envoyer en binaire... L'absurdité de cette approche est que l'encodage Base64 a été conçu pour du texte en 7bit. Avec un jeu limité de 64 caractères, donc 6 bits utiles par octets. Le fichier XML peut être en Unicode, soit en 16 bit ! Même si on doit payer 2 octets pour chaque caractère XML de la syntaxe classique, on y gagne largement pour les attributs binaires. À quand un encodage binaire to Unicode ? Un Base65530 ?
Coté serveur, il va falloir décoder tout cela pour générer le flux. On décode en mémoire ? Directement sur disque ? Si le flux final n'est pas trop gros, utiliser le disque dégrade grandement les performances. Si le flux est très gros, le décoder en mémoire consomme trop de ressources et peut faire planter le serveur. Que choisir ? Et le décodage s'effectue en flux ou en DOM ? Si c'est en DOM, j'ai donc le Base64 en mémoire. Une copie complète du flux binaire mais avec un bonus de 30%. Il faut bien entendu prévoir un autre tampon pour la version décodée. Donc, pour une charge utile de 100, j'ai droit à 230 ! Pour des petits fichiers comme chacun sait. Petit, le temps des tests unitaires. C'est ensuite que ça plante. En production, quand cela ne gêne personne.
Des architectures SOA en production consomment 70% de la CPU et du réseau pour gérer les flux XML. Donc, sur dix machines, sept ne servent à rien d'autre qu'à faire plaisir aux marketeux et aux vendeurs de matériel. HP, Oracle et IBM vous disent merci.

Qui a dit que XML était plus simple ? En tout cas, dans les faits, c'est une approche rigide (chaque document est un tous, sans relation avec son environnement), très fragile (syntaxe généralement non vérifiée, fichiers textes avec une police non conforme), présentant des risques majeurs de sécurité (inclusions de ressources via des URL), dont les bénéfices uniques sont rarement exploités (espaces de noms et syntaxe ouverte), dont la programmation propre est complexe (DOM, SAX, StAX, DTD, XMLSchema), non efficace (signature, parsing, vérification de la syntaxe, binaire), et consommant beaucoup de mémoire et de CPU (pas de commentaires...).
Enfin, est-ce vraiment pour cela qu'il était prévu ? A-t'on vraiment conscience de ces chausse-trappes ? En un mot : « XML les pinceaux » ! Qui réfléchit plus de deux minutes avant de concevoir une grammaire XML ? Que voulons-nous réellement encoder ? Et pourquoi ? « Je ne sais pas, mais ça marche » répond en cœur l'ingénieur.
Le problème majeur vient d'une mauvaise compréhension de XML par les développeurs. Ils n'ont jamais reçu de formation sérieuse sur le sujet. Comme cela paraît simple, ils pensent avoir tout compris en cinq minutes. La plupart ignorent ce qu'est une DTD ou un schéma, ignorent XPath ou XSLT et pensent maîtriser XHTML.
XML est un langage de structuration de l'information DOCUMENTAIRE. Un développeur/ingénieur ne sait fondamentalement pas ce qu'est une donnée, une information, un document et s'en fout ! Comment voulez-vous faire sérieusement du XML si fondamentalement vous n'avez pas compris comment fonctionne l'information encodée, sa structure... ?
Les développeurs pensent processus en premier, alors qu'avec XML il faut penser donnée en premier. La donnée a un fonctionnement intrinsèque, il faut le respecter. Si les développeurs pensaient un peu plus en termes d'informations/de données, peut-être utiliseraient-ils moins XML n'importe comment ?

« Un bon fichier XML est un fichier qui peut être validé suivant un schéma » Mickael Sperberg-McQueen

En effet, XML n'a aucun sens en tant que syntaxe local de transport de l'information, il a été créé pour gérer l'interopérabilité dans l'encodage d'informations équivalentes ; il permet à plusieurs organisations de partager l'information en s'appuyant sur une syntaxe commune, une grammaire commune (le schéma XML) et un ensemble de standards normalisés indépendants d'un langage ou d'un système d'exploitation. Il a été créé dans le monde de l'édition de textes et est orienté document.

Parce qu'y'en a marre des technologies innovantes utilisées ce pourquoi elles n'ont pas été conçues ! Faites manger de la farine animale à une vache et elle devient folle... donc attention à ce que vous faites consommer à vos programmes ! Quelle que soit la sauce qu'on vous a vendue avec pour lui donner un bon goût de renouveau et de panacée universelle...


<document>
<prã©nom>Jean-Pierre</prénom>
<nom>troll< nom="">
<email>jp.troll@gmail.com<email>
</document>

Jean-Pierre règle ses comptes

Au fur et à mesure de mes coups de gueules dans ce magazine, je me dévoile chaque fois un peu plus. Aujourd'hui j'avoue avoir travaillé dans plusieurs sociétés de service informatique, ce qui me permet de faire ma petite analyse critique du marché du service en France...

Le principe de concurrence c'est bien. Dans la théorie libérale cela garantit au consommateur de disposer du choix et pousse les différents fournisseurs à se démarquer par des produits de meilleur qualité. Ça c'est la théorie. En pratique, et surtout lorsqu'on l'applique à l'industrie du service informatique, c'est très souvent le moins disant qui gagne les projets mis en concurrence, pas le meilleur.
Et franchement y'en a marre des économies de bouts de chandelles qui coûtent au final des milliers d'euros de dérapage quand ce n'est pas tout simplement l'échec du projet !
Comme les appels d'offres fonctionnent selon le principe de l'enchère inversée, chaque société de service candidate va chercher à répondre aux besoins exprimés dans le cahier des charges au coût le plus faible. Ceci pour avoir une chance d'être retenu et éventuellement, si jamais c'est le cas, de s'assurer une marge financière.
Le problème est que le projet démarre déjà dans le rouge car il a été sous vendu pour pouvoir être retenu. Et cela s'aggrave en général lorsque le commercial, account manager, global partner enfin quel que soit le nom donné à Monsieur Loyal par sa société, décide d'accorder une ristourne commerciale de 15% comme preuve de bonne volonté. Une chose est sure, comme les traders, Monsieur Loyal touchera sa commission lors de la signature du contrat, avant le démarrage du projet et ne sera pas le moins du monde embêté lorsque le projet sera en pleine hémorragie financière, de ressources et de mauvaise image de l'entreprise.
Et comme le projet est déjà dans le rouge, il faut absolument démarrer avec des profils peu chers, tant pis s'ils sont tous débutants. Impossible d'investir dans un expert ou un architecte pour leur mettre le pied à l'étrier et cadrer les choix structurants. Procéder de la sorte relève, bien évidemment, de l'hérésie totale : pourquoi chercher à sécuriser le projet au calme, lorsque c'est encore possible ? Il est préférable de tenter de le faire à l'arrache, juste avant la livraison ou les tests de charge, lorsque tout le monde est en mode panique, quand c'est beaucoup plus complexe voire trop tard. C'est généralement à ce moment là qu'on se rappelle de l'existence d'experts, ceux là même qu'il aurait fallu associer au projet dès le début. Ils ne vont pouvoir intervenir qu'en mode pompier, remettant souvent en cause les choix faits il y plusieurs mois ou années. Reportez vous à mes coups de gueule précédents sur les abus d'architecture et de composants pour voir ce que le manque de recul et de bon sens peut amener les développeur à faire...

« Le bon sens est la qualité la moins partagée de ce monde » Jean-Pierre Troll
Le taux journalier moyen de l'équipe projet n'est d'ailleurs pas le seul poste d'économie. On va également réduire les coûts sur tout ce qui est considéré comme superflus... à court terme : la documentation, les tests unitaires, la définition de règles de développement, la mise en place d'outils de vérification de la qualité des développement, la gestion de la performance, les transactions, la haute disponibilité, la tolérance aux pannes, la sécurité, etc. Coûts initiaux que l'ont va devoir souvent payer au centuple sur le moyen et long terme. Qui n'a pas connu LE problème de performances rédhibitoire juste avant la mise en production ? Qui sait parfaitement que l'application mise en production est une vraie passoire au niveau sécurité ? A nouveau on va déclencher en urgence l'alarme des pompiers... ou faire l'autruche, la tête dans le sable mouvant.
Tout cela risque bien de dégoûter les développeurs juniors complètement pressurisés par un planning qui n'a jamais été réaliste et les chefs des projets dont le budget est constamment dépassé. On ne parle pas encore de suicide dans les sociétés de service. En effet, il suffit juste de démissionner. Cela prend en général trois mois, trois mois pendant lesquels on décide souvent d'ignorer le problème. Aveuglés par l'accumulation des problèmes, les managers vont oublier de véritablement gérer le transfert de compétences, le renouvellement des ressources, l'audit par les pères, etc. Ce qui amène généralement à une situation encore pire que précédemment. Combien ai-je connu de situations catastrophiques où plus personne ne connaissait l'historique du projet, la justification des choix techniques car cela faisait déjà les troisièmes ou quatrièmes chef de projet et architecte qui « reprenaient » les choses en main ? Reprise en main jusqu'à la prochaine démission ou décapitation par le haut du management, agacé d'avoir à se justifier devant le PDG du client mécontent du retard et de la qualité des livraisons...
Et le client, parlons en du client. Il impose souvent des choix techniques absurde, mais demande à la société de service de s'engager alors sur les performances ! Impossible. Il exprime ses demandes en termes de « solution technique » et non en termes de « besoin ». Comme personne n'ose lui dire non, le projet se précipite sur les solutions préconisées, même s'il est évident qu'elles ne peuvent répondre au besoin. Combien de projets plombés par une règle considérée comme « imposée par le client », qui en fait, n'était qu'une « suggestion » de ce dernier ? Bien plus tard, on indique on client que tel problème est le fait de tel choix qui a été imposé. Le client répond alors, très justement, que cela n'a jamais été le cas !
La bonne approche selon moi ? Comme pour les traders, payer les primes des commerciaux à la fin du projet, et non au début, suivant les résultats réels. Faire intervenir des experts au début du projet, pour structurer le code, rédiger l'infrastructure et les couches techniques, et les éloigner du projet dès que possible. Ensuite, et seulement ensuite, faire intervenir des débutants et autres ressources moins couteuses.
Les anglo-saxons ont un dicton que je trouve formidablement imagé et évocateur. Ainsi, plutôt que de dire que l'on va droit dans le mur, ils suggèrent : « When the shit hits the fan... » (Quand la m... atteint le ventilateur). Et c'est bien vers cet événement que dérive naturellement le projet, le clash, le vrai, celui qui va éclabousser tout le monde. Que faire dans une telle situation ? Se protéger en passant beaucoup de temps à « blinder » le projet pour ne pouvoir être attaqué en justice. On ouvre le dossier contentieux avec le client, dès les premières lignes de code. Ou bien il faut sauter du navire lorsqu'il est encore temps... On voit qu'au final, on est bien loin de la promesse théorique de la meilleure offre.
Donc que cela soit clair une bonne fois pour toute :l'offre la moins chère n'est probablement pas la meilleure, et se transforme probablement pas non plus dans le projet informatique le moins cher. Au contraire. Il faut suivre cette stratégie :

« Éliminez l'offre la moins chère de tout appel d'offre, elle sens mauvais » - Jean-Pierre Troll.
Quelle (r)évolution cela ferait ! Une simple règle comme celle-ci obligerait les soumissionnaires à plus de pragmatisme et de réalisme.
Un collègue m'évoquait qu'en Allemagne, il y a peu de dérive sur les projets informatiques. Pourquoi ? Car s'il est estimé qu'il faut 1 an pour le faire, on ne le vend pas pour six mois. C'est simple. Les clients le savent, et son prêts à payer pour un résultat. En France, les clients préfèrent payer pour un échec. Allez comprendre.
Alors que faire devant un tel constat ?
Peut-être faudrait-il s'inspirer de l'industrie du BTP ? Elle est certes souvent décriée comme une industrie pourrie, où existent de nombreuses pratiques douteuses... Certes. Et si les différentes sociétés de services se mettaient d'accord pour fixer des prix minimaux sur les appels d'offres voire signeraient une charte de déontologie financière (mais là c'est du rêve éveillé) ?
Une autre approche, consiste à une entente illicite, de partage du marché. Untel répond à tel client. En échange, untel peut répondre convenablement pour tel autre client. Des générateurs de réponse à appel d'offre ont été découverts dans certaines entreprises pour pouvoir générer des offres « trop disantes » et ainsi simuler la concurrence.
Avec un tel partage du marché, plutôt qu'une guerre des prix (complètement inefficace dans une activité de service), peut être que l'industrie du service en France serait mieux portante ? L'atmosphère dans les projets des sociétés de services serait plus saine et plus vivable, peut être même (soyons fous !) que plus de projets arriveraient à leur terme et surtout, conformes aux besoins des clients.

mercredi 30 juin 2010

Conférence à l'université d'Orsay

Ma première conférence à l'université d'Orsay. Un très bon accueil. La suite, vendredi matin.

dimanche 27 juin 2010

Conférence à Orsay

Conférences à Orsay, plateau du Moulon, mercredi 30 matin et vendredi 2 au matin. Polémique en perspective.

vendredi 1 janvier 2010

Comment être indispensable à un projet


Y en a marre que « mon génie » ne soit pas reconnu à sa juste valeur.

Comment être indispensable à un projet ? Certains savent parfaitement le faire soit volontairement, soit sans le faire exprès. Dans tous les cas, cela flatte l'ego - « Sans moi, tout s'écroule !» - et cela présente quelques avantages. Il y a en effet plusieurs stratégies pour bénéficier de ce privilège.

Il faut tout d'abord être seul. Ne jamais travailler en équipe. Sur un projet, il faut s'arranger pour récupérer une mission stratégique pour le projet, mais pas trop complexe pour être seul à en être chargé. Une petite couche de communication fera l'affaire. Si de plus, l'environnement de développement est complexe et coûteux, c'est l'idéal. On ne va pas donner quatre serveurs à chaque développeur !

Ensuite, il faut coder en respectant certains principes. Le premier, évident, est l'absence de documentation. Des commentaires peuvent être présents pour faire croire à une bonne volonté, mais ceux-ci ne doivent être que des plagiats du code. « /* Met zéro dans xzy */ ». Ou bien, si le chef de projet est plus exigeant, il faut proposer une documentation qui n'est pas à jour. Elle doit être rédigée comme cela, décalée de la réalité. À aucun moment, le document ne doit être conforme à l'application. Personne ne le saura sans entrer dans les sources.

Le code doit générer, en mode DEBUG, de très nombreuses traces, toutes plus incompréhensibles les unes que les autres et quelques warning. Utilisez des abréviations ou un vocabulaire rare, montrant la profondeur de votre analyse. Les messages ne doivent en aucun cas avoir un sens, sauf pour vous. Vous pouvez utiliser un code numérique par exemple et garder la fiche d'explication pour vous. « Erreur 23 ? Mauvais paramètre IP ». À la moindre occasion, activez ces traces pour montrer à quel point votre code est complexe. Ne loupez aucune occasion de le rappeler. Si on vous demande des explications pour la présence des warnings, répondez invariablement par « c'est normal ». Personne n'ira plus loin. Demandez régulièrement une copie des traces pour analyse. Comme elles sont volumineuses, personne ne souhaitera s'y plonger.

L'architecture doit être la plus éloignée possible de l'architecture évidente à l'énoncé de la mission. C'est assez facile à obtenir. Il suffit d'écrire un framework très ouvert, avec de nombreuses possibilités d'extensions, sans que cela soit nécessaire à l'application. « On ne sait jamais. Si un jour tu as besoin de … alors tu pourras grâce à ... ». Complétez vous-même.

Il faut ensuite utiliser toutes les techniques les plus absconses. L'introspection est un bon candidat. Ainsi, le code n'est pas lisible, car l'exécution est le fruit de traitements plus complexes, n'apparaissant pas directement dans le code. L'idéal est de rédiger une sorte de machine virtuelle, avec un automate à états, contrôlé par des données et des syntaxes binaires. Bien entendu, aucune documentation n'accompagne la syntaxe. Ainsi, la seule façon de comprendre le code est de le suivre en pas à pas. Deux, trois automates à états feront également l'affaire. Rien n'est plus incompréhensible qu'un énorme switch, complété de if en cascade ou un tableau de transition d'états, avec des valeurs numériques réparties apparemment aléatoirement.

Il est également possible d'abuser des inners-classes anonymes pour répondre à des interfaces. Ainsi, les classes importantes sont cachées dans le code des méthodes. L'absence de classe implémentant une interface ne manquera pas de laisser pantois tout développeur normalement constitué. Une classe externe peut également hériter d'une classe interne à une autre.

Les types génériques sont également un bon fermant pour du code illisible, à condition de les utiliser pour autre chose que pour les conteneurs. Comme personne ne sait comment cela fonctionne, il y a peu de chance que l'on critique votre code.

« Si debugger, c'est supprimer des bugs, alors programmer ne peut être que les ajouter » - Edsger Dijkstra

La gestion d'erreurs doit également être complexe. Cela peut s'effectuer par l'utilisation d'une variable de thread et/ou une variable globale, maintenant la dernière erreur et/ou son contexte. Une petite modification d'un attribut d'un objet avant de propager l'erreur est également une bonne stratégie. Ainsi, le code des exceptions entraîne des effets de bords, impossible à suivre. Par exemple, utilisez des « return » ou des « continue » dans des blocs « finally » ; convertir des exceptions à la moindre erreur, pour empêcher de connaître l'origine du problème, etc.

Abusez également du multitâche. À la moindre occasion, lancez une tâche pour effectuer le traitement, et endormez la première en attente de la réponse. Cela ne sert à rien, mais complexifie le code de telle sorte que personne n'osera y toucher.

Certains frameworks peuvent aider à offusquer le code. La programmation par aspect est une merveille pour cela. Une petite règle par-ci par-là, et le code de l'application ne correspond plus du tout au code exécuté. « Si A invoque la méthode f() de B, alors invoque la méthode g() à la place ».

Choisissez judicieusement le nom des méthodes. L'idéal est de pervertir les conventions. Par exemple, les méthodes getX() doivent avoir un effet de bord, comme, par exemple, supprimer un élément d'un conteneur. Utilisez le même nom de méthode pour différentes classes, mais avec des sémantiques très différentes. Ainsi, le sens ne peut être connu sans vérifier à qui la méthode s'adresse. Dans les classes, ajoutez des méthodes de même nom que dans les sous-classes, mais avec plus de paramètres. Ainsi, l'intuition fera croire à l'appel de la méthode de la super classe, alors que ce ne sera pas le cas.

Utilisez plusieurs langages de programmation. C'est très efficace, car chaque projet est constitué d'une équipe « mono langage ». En choisissant des langages complémentaires abscons, Perl par exemple, ou plus confidentiel, Awk, Python, personne n'aura les compétences croisées permettant de maîtriser votre code.

Bien entendu, le code ne doit en aucun cas être efficace et performant. Ainsi, à la moindre occasion, il faudra faire appel à vous pour intervenir, analyser, tester, patcher (plusieurs fois évidemment). Bien entendu, à chaque sollicitation il faut répondre invariablement : « Oh là, là ! Cela va remettre en cause l'architecture. Je vais étudier les impacts avant de te répondre ». Une semaine est un minimum avant de revenir avec une solution qui ne doit jamais être complète. Elle doit imposer des modifications un peu partout dans le code de l'application. Eh bien oui, l'impact n'est jamais neutre, même pour remplacer un texte de message par un autre. « Tu comprends, je continue à améliorer mon code. Tu as ainsi la nouvelle version qui est vachement plus rapide ! » (quelques micro-secondes gagnées ici en en perdant plusieurs milli-secondes là).

Les paramètres. C'est très important les paramètres. Ils doivent être très nombreux, très dépendants les uns des autres, de telle sorte qu'il soit difficile d'en modifier un seul à la fois. Chaque modification doit au minimum en concerner deux. Pour cela, il faut que chaque décision de codage se traduise par un paramètre. Il ne faut jamais prendre aucune décision dans le code. Tout est le fruit de paramètres. Faut-il utiliser tel framework sous-jacent ou tel autre ? L'application accepte les deux, mais un seul à la fois. Un paramètre pour cela. On ne sait jamais. Faut-il privilégier la taille du code ou les performances ? Un paramètre. Faut-il activer la découverte automatique du contexte d'exécution ou paramétrer en dur les adresses IP ? Quel port réseau faut-il utiliser ? Bien entendu, il doit être strictement le même qu'un autre paramètre ailleurs, voire sur un autre serveur. Des conventions peuvent également valoriser d'autres paramètres s'ils ne sont pas renseignés. Port+1 pour tel paramètre, port-1 pour tel autre. Faut-il récupérer les paramètres en XML, dans un annuaire, en clef=valeur ? L'idéal est d'utiliser toutes les approches, via plusieurs frameworks. Certains paramètres peuvent être valorisés avec les trois approches, mais pas toujours dans le même ordre, d'autres seulement en XML et d'autre seulement en clef/valeur. Ainsi, aucune intuition ne peut s'appliquer à la valorisation des paramètres. Bien entendu, il faut que certains paramètres soient en dur dans le projet, afin d'obliger de le régénérer à chaque modification de déploiement. Plus il y a de paramètres, plus vous êtes indispensable.

Et les frameworks et autres composants Open Source ? Il faut les multiplier, en mélangeant les frameworks connus à des extensions inconnues de ces frameworks. « Tel plugin de log4j est indispensable ! ». Ainsi, vous présentez une façade séduisante, tout en cachant le diable dans les détails.

L'écriture d'un driver ou pseudo driver est un bon candidat pour rester à tout jamais dans un projet, sans être dérangé. Des algorithmes apparemment complexes peuvent également vous faciliter la tâche. « Mon algo calcule la répartition des tâches pour l'écriture des logs en parallèle de manière optimisée ! » Ou bien, il faut travailler sur des sujets très récents, dont les normes et les spécifications évoluent continuellement. La diversité des téléphones sans fil en est un bon exemple. Chaque jour présente son modèle, différent des autres, qu'il faut intégrer. Pour coder une implémentation d'une norme, il faut suivre l'approche de Microsoft, consistant à l'implémenter de telle sorte que toutes les décisions d'implémentation non décrites, soient des choix arbitraires, rigides, absolument incompatibles avec les autres implémentations. Le code est presque conforme HTML, mais le « HTML Microsoft ».

Bien entendu, juste avant vos congés, il faut apporter une modification « mineure » qui fera tout planter. L'équipe devra reprendre la version précédente du composant, en attendant votre retour. Vous saurez alors que vous avez gagné.

Comme le projet aura du retard sur le planning, on vous demandera de faire des heures supplémentaires. Vous les effectuerez sans oublier de signaler que cela vous coûte, que vous devez annuler des rendez-vous très importants, que c'est très exceptionnel, que c'est uniquement pour le bien du projet. Peu importe que la dérive du projet soit, entre autres, de votre fait, ce qui compte c'est que les chefs voient que vous vous investissez pour la réussite du projet. On remercie les efforts pour sortir de la panade, jamais les efforts ayant évité d'y être. C'est une règle importante à comprendre, dès le début de tout projet.

« Attends le dernier moment pour régler les problèmes, les félicitations n'en seront que plus importantes. » - Jean-Pierre Troll

Attention, il faut se méfier des audits internes ou externes. Les audits, surtout s'ils sont pratiqués par des consultants compétents – cas rare – risquent de vous nuire. Un auditeur consciencieux peut découvrir que votre code est fait en dépit du bon sens. L'auditeur devant regarder tout le code de l'application, il ne peut consacrer beaucoup de temps à votre œuvre. Pour éviter qu'il y mette trop de zèle, prenez rendez-vous en dernier, un vendredi soir veille de fête, et très tard. Vous êtes trop occupé à régler les problèmes des autres, ces incompétents. Arrivez en retard au rendez-vous et noyez le de discours, de justification tardive, de solutions alternatives sans jamais en choisir une, enfin, faite durer la réunion. Commencez par l'interroger sur son parcours, ses motivations. Demandez lui son avis sur les autres membres de l'équipe et saisissez toutes les occasions de déléguer vos responsabilités sur les choix des autres.

Vous avez une arme très forte : il est trop tard pour tout ré-écrire. Expliquez que les demandes initiales étaient plus ambitieuses, que c'est pour cela que vous avez anticipé les évolutions avec une architecture si complexe, etc. Enfin, il faut noyer le poisson. À chaque question difficile, expliquez que c'est plus compliqué qu'il n'y paraît, à cause d'une compatibilité ascendante, d'un historique, du respect d'une norme, etc. Enfin, justifiez vos arguments en faisant référence à des sources d'informations que l'auditeur ne peut connaître.

Il est temps d'aller voir votre manager pour demander une augmentation. Le couteau sous la gorge, et conforté par les sollicitations nombreuses de toute l'équipe à votre égard, il ne pourra que s'exécuter.

Quel est le risque ? Vous finirez inévitablement par vous lasser de votre code. D'une part, il est très difficile à déverminer, d'autre part, c'est fatiguant à la longue. Comment s'en débarrasser pour pouvoir faire autre chose ? Impossible de transmettre le bébé à un autre. C'est très difficile et il risque de découvrir la supercherie. En général, avec un peu de patience, le projet finit par échouer lamentablement. L'équipe est dissoute et l'application n'est jamais mise en production.

L'aura que vous aurez acquise ainsi vous permettra d'avoir de nombreuses sollicitations pour d'autres projets. Bis repetita.


« Si les ouvriers construisaient les bâtiments comme les développeurs écrivent leurs programmes, le premier pivert venu aurait détruit toute civilisation » - Gerald Weinberg

Parce qu'y'en a marre de la médiocrité et des parasites nuisibles, auréolés par des ignorants.


Jean-Pierre Troll

jp.troll@gmail.com

Parce que vous n'êtes pas prêts de vous débarrasser de moi.

vendredi 20 novembre 2009

Spam, la Peste électronique


Véritable déluge numérique, qu'ii touche nos boîtes aux lettres, nos forums/wikis/blogs favoris, nos messageries instantanées ou nos téléphones portables, le spam (et non je refuse d'utiliser les termes de pourriel ou pollupostage !!!) est un véritable fléau des temps modernes. Un fléau à l'échelle d'Internet, c'est à dire celle de la planète.

Le Messaging Anti-Abuse Working Group (MAAWG), sur la base de mesures régulières réalisées sur plus de 100 millions de boîtes aux lettres électroniques depuis plus de 3 ans, estime que le spam représente plus de 85% du nombre total d'emails reçus (cf. http://www.maawg.org/about/EMR/). C'est tout bonnement effrayant... Concentrons nous donc le spam de nos boîtes aux lettres.
Cette gangrène emailistique est difficile à combattre car elle ne se diffuse pas de manière centralisée mais plutôt de manière infectieuse, à l'intérieur de réseaux zombies ou botnets. Il s'agit de véritables réseaux cachés au sein d'Internet, regroupant l'ensemble des ordinateurs asservis au centre de contrôle du botnet. Ces ordinateurs asservis, ou bots1, le deviennent suite à une infection via des chevaux de Troie, des vers ou des backdoors. Un fois un ordinateur infecté, il rejoint le botnet et augmente ainsi sa puissance de nuisance, en tentant de contaminer les contacts déclarés sur le poste et en les ajoutant à la liste des cibles du spam. Lorsque le bot asservi en recevra l'ordre, il enverra le spam qui lui a été communiqué, à l'ensemble de ses contacts. Le centre de contrôle du botnet, parfois véritable plate-forme « Spam As a Service » commerciale et mafieuse, permet aux utilisateurs autorisés de composer et d'envoyer leurs déferlantes sur Internet en utilisant les ordinateurs disponibles dans le botnet.
Prenons quelques exemples pour bien appréhender l'ampleur du phénomène :
Lorsque l'hébergeur américain McColo a été forcé d'arrêter ses activités, le 11 novembre 2008, le spam mondial a été réduit de deux tiers. En effet, McColo hébergeait un des noeuds du centre de commande de Srizbi, considéré comme le plus gros botnet mondial avec 450 000 bots et une capacité de 60 milliards de spams par jour (oui... vous avez bien lu milliards) !! Depuis, le contrôle du botnet aurait été transféré en Europe.
Conficker, un ver exploitant (une faille de) Windows - dont l'éditeur est prêt à donner 250 000 dollars à quiconque aiderait à l'arrêt des malveillants - est relié à un botnet dont la taille est estimée à 9 millions d'ordinateurs et la capacité à 10 milliards de spams par jour.
En 2005, trois jeunes hollandais ont été arrêtés pour avoir créé et utilisé à des fins malveillantes un botnet (CodBot ou ToxBot) de plus d'un million et demi de machines.
Bref le spam de boîtes aux lettres est un phénomène réel et de grande ampleur. Mais pourquoi tant de haine ?! Pourquoi en 2009 le spam est-il toujours en croissance ? A mon avis c'est à la fois un phénomène sociologique et économique.
Sociologique d'abord, car il joue sur les : crédulité, luxure (les sites Web pornographiques sont de vrais pièges à bots) et cupidité. Rien de bien nouveau sous le soleil me direz vous, mais là, l'échelle est plus grande et les barrières techniques beaucoup moins hautes. De quoi attirer les personnes peu scrupuleuses voire malveillantes.
Mais également au niveau économique : avec une force de frappe qui se compte en milliards d'emails et un relativement faible coût d'investissement, il suffit qu'une très infime portion des destinataires commandent effectivement le viagra qu'ils ne recevront jamais ou envoient un acompte pour débloquer un compte fictif à l'étranger, pour que toute l'opération devienne rentable. De quoi attirer toutes les mafias du monde en quête d'argent facile, qu'il provienne de l'utilisation de botnets ou de la location de services offerts par les botnets. Et c'est vrai que c'est quand même bien pratique pour suivre l'évolution du cours du vi@gra ou des plugins d'extension de l'anatomie masculine!!
Mais alors que faire devant un tel fléau ?

Se résigner ?
Une première solution est de ne plus utiliser sa messagerie. Bon d'accord, c'est radical ! Mais est-ce qu'ignorer le problème dans sa globalité, en continuant de nettoyer à la mano chaque jour sa boîte aux lettres (ou déléguer ce nettoyage à l'hébergeur), ce n'est pas quand même adopter la politique de l'autruche ? Un fois le réseau complètement saturé, pollué et inutilisable, on ne pourra peut être plus utiliser sa messagerie...

Réagir techniquement ?
Il existe différentes solutions techniques développées pour lutter contre le spam a posteriori. Que ce soient des solutions d'antispam coté client ou coté serveur de messagerie, des solutions de blacklisting, greylisting, whitelisting (à quand le rainbowlisting ?) ou encore des solutions à base de DNS spécialisés, elles s'attachent toutes à détecter et traiter le spam en aval plutôt qu'à l'éradiquer en amont. C'est un peu comme un traitement individuel en cas d'épidémie...

Réagir légalement ?
Certains pays (comme ceux de l'Union Européenne, le Canada, l'Australie ou encore les États-Unis) ont déclaré comme illégale la pratique du spam. Cela a permis de démanteler certains botnets, en s'attaquant aux centres de contrôles des réseaux zombies. Mais souvent il s'agit d'une solution temporaire car les centres de contrôles sont souvent constitués de plusieurs noeuds se sécurisant les uns les autres, et disposent de certains de ces noeuds situés hors des zones d'application des lois antispam comme en Estonie ou en Russie.
Donc, en attendant un consensus mondial via des règles de gestion nationales (euh... pardon, des lois nationales, déformation professionnelle !) homogènes, voire en attendant une résolution de l'ONU (!!), nous voilà bien démunis par rapport au chaos rampant...

Devant tant d'impuissance, pourquoi finalement ne pas voir le bon coté des chose et se réjouir ?
Mais oui ! Au final cette maladie électronique a clairement été inventée par des informaticiens bien trapus, et pas par les utilisateurs finaux des offres de « Spam As a Service ». Cela prouve au moins qu'il est techniquement possible de déployer des systèmes hautement distribués sur Internet. Et cela fait plaisir à l'architecte logiciel qui sommeille en nous : c'est nuisible, c'est pas cool mais c'est beau ! De même, à l'heure du buzz sur le Cloud Computing, quelle meilleure preuve de concept que l'Elastic Spam (on détecte de plus en plus de spams en provenance de plates-formes de cloud computing).
Pour y voir plus clair, peut-être faut-il se demander à qui profite le crime ?... Car je ne pense pas que les bénéfices engrangés par les méchants spammers soient comparables à ceux qui sont indirectement versés à l'industrie informatique, qu'il s'agisse des fournisseurs de matériel, de logiciels (antispams, antivirus et anti-spyware) ou de services (accès Internet2, service antispam payant de l'hébergeur) !! A mon avis il ne s'agit pas du tout des mêmes ordres de grandeur...
Donc finalement, si toi lecteur tu es comme moi, payé par l'industrie informatique... ben, quelque part tu es aussi en partie payé par le spam !!
Alors si la sensation de cautionner une épidémie mondiale de Peste électronique polluant Internet te dérange un peu, ben en tant qu'informaticien, commence par faire du développement durable et sécurisé. Utilise des logiciels non infestés de chevaux de Troie, vers et autres backdoors, comme GNU/Linux et plus généralement les logiciels libres. Explique à ta famille et à tes amis pourquoi ils devraient faire de même (et donc se mettre à lire « l'excellent » GNU/Linux Magazine France). Et peut-être qu'on pourra, par effet tâche d'huile, réduire la taille des botnets et ainsi les rendre moins efficaces et donc moins intéressants à exploiter.
Restons exigeant sur la qualité des logiciels que la société de consommation nous propose, nous avons le choix. Des produits de mauvaise qualité peuvent nous rendre malades et ces maladies devenir contagieuses et poser un problème de santé publique !
Parce qu'y'en a marre de la médiocrité, des logiciels avariés, des développeurs en batteries et de la disparition des petits artisans soucieux du travail sain et bien fait.

Jean-Pierre Troll
jp.troll@gmail.com (vous l'aurez compris désormais, m'envoyer un e-mail s'est s'enregistrer sur TrollNet).