Historique des méthodes de surcharge de code PHP dans RBS Change

Le but de cet article est de revenir sur les différentes méthodes de surcharge du code PHP qu'on a eu (et que pour beaucoup, on a encore) dans RBS Change depuis que je travaille dessus (au départ en faisant des sites avec puis par la suite en tant que développeur du produit).

Je n'entrerai pas dans les détails concrets d'utilisation, mais ça me semble intéressant de voir un peu l'évolution des concepts, les différentes tentatives qu'il y a eues et les raisons qui ont pu orienter certains choix.

En fin d'article, je présente ce qui sera fait dans la version 4.0 dont la sortie n'est prévue que courant 2013. En conséquence, ce que j'évoque pourrait potentiellement encore évoluer d'ici là et ne reflète que ce qui a été mis en œuvre pour l'instant, pas une vérité gravée dans le marbre.

Écrasement des fichiers dans la webapp

J'ai commencé à travailler avec Change sur la version 1.2 en 2006. À cette époque le seul moyen offert pour surcharger du code du produit était d'écraser certains fichiers (PHP ou autres, cela fonctionnait pour tous les fichiers des modules et même du framework : PHP, XML, templates, styles, etc) dans le dossier "webapp". La mécanique était (trop) simple : à chaque chargement de fichier PHP, l'application commençait par le chercher dans le dossier webapp. S'il était présent cette version était chargée, sinon celle du code standard l'était. Le fichier de surcharge était donc contraint de reprendre l'intégralité du code qu'il surchargeait, même au cas où il réécrivait seulement une méthode d'une classe en comptant cinquante.

C'était toujours un peu mieux que de modifier directement le code du produit, mais à peine puisque lors d'une mise à jour, il fallait passer sur chaque fichier surchargé pour réappliquer les modifications.

Dès la version 2, l'année suivante, la possibilité de surcharge de code PHP directement dans le dossier webapp a été supprimée, car elle posait beaucoup trop de problèmes. En remplacement, une autre méthode plus cadrée a été mise en place : "l'injection" de service et peu après, de document.

Injection de service et de document

L'injection de service (toujours d'actualité dans la version 3.6) fonctionne sur un principe qui reste très simple, mais qui est nettement plus cadré. Dans la configuration projet, on déclare des injections consistant à dire "le service A est à remplacer par le service B". Tous les services sont instanciés via une méthode getInstance() (selon le patron de conception singleton) qui inclut une mécanique qui va chercher dans la configuration projet si une injection a été déclarée sur ce service. S'il y en a une, on retourne une instance de la classe remplaçante au lieu de la classe appelée.

Le code travaille donc simplement avec une autre classe que celle qu'il a demandée. Pour que les choses marchent bien, il faut évidemment que la classe remplaçante étende la classe remplacée.

Peu après le même principe a été appliqué aux documents : en déclarant une injection de document, on remplace son modèle, sa classe finale et son service là encore en renvoyant des instances de classes étendant celles que le code demande en réalité.

Globalement ça marche assez bien, mais avec deux limitations :

  1. Dans le cas où on injecte une classe déjà étendue par ailleurs, les classes étendues ne profiteront pas de l'injection. Par exemple, si une classe C étend A et que j'injecte A par une classe B. Si je demande une instance de A, j'obtiendrai une instance de B mais si je demande une instance de C j'obtiendrai une instance de C étendant A et non B.

    Cette limitation peut rapidement s'avérer gênante quand on développe des modules complexes avec des documents de base étendus par d'autres (par exemple les différents types de produits dans le module Catalogue et boutiques).

  2. Une même classe ne peut être injectée qu'une seule fois. Cela peut devenir problématique si plusieurs modules veulent se greffer sur un module existant et ont besoin de remplacer du code : il faudra alors écrire une classe en spécifique dans le projet qui cumules les différentes modifications ce qui n'est pas très bon en terme de maintenabilité (on se rapproche à nouveau un peu de l'écrasement décrit plus haut même si ça reste plus restreint).

Points d'entrée spécifiques (stratégies, section "mvc" du project.xml, etc.)

Parallèlement à l'injection, d'autres mécaniques plus ciblées ont été mises en place dans certains modules pour permettre de remplacer du code standard par des implémentations spécifiques.

Les modules liés à l'e-commerce se sont ainsi enrichis d'un certain nombre de "stratégies" (du patron de conception strategy). Ces points d'entrée explicites permettent de choisir entre plusieurs implémentations pour un traitement donné. Par exemple, on proposait une stratégie sur l'arrondi des prix avec une implémentation standard qui se contentait d'arrondir à deux chiffres après la virgule, là où certains projets avaient d'autres règles.

On trouve encore ce genre de chose en version 3.6, en particulier sur les points le choix n'est pas unique au sein d'un projet donné (par exemple le calcul du montant d'un frais : deux frais peuvent s'appliquer avec des stratégies de calcul différentes).

On en trouve encore quelques autres dont l'implémentation est choisie une fois pour toutes dans la configuration projet, mais elles tendent à disparaitre (cette mécanique spécifiquement mise en place à chaque fois étant alors redondante avec les mécaniques génériques d'injection).

D'autres points d'entrée spécifiques ont été mis en place au fur et à mesure, notamment pour les différentes classes entrant en jeu dans le modèle MVC (notamment les différents contrôleurs) qui peuvent être remplacée via une section dédiée dans la configuration projet.

Dans tous les cas ces points d'entrée ont été mis en place spécifiquement dans certaines parties du code. Les mettre en place implique donc de prévoir volontairement que telle ou telle partie doit pouvoir être remplacé au contraire de l'injection qui fonctionne de manière plus globale.

Réécriture de classe et AOP

Dans la version 3.0 de Change, un nouveau mécanisme a été implémenté, tiré de certains concepts de la programmation orientée aspect (AOP).

Cette mécanique propose plusieurs choses reposant sur la réécriture des classes lors d'une phase de compilation.

Premièrement le remplacement d'une classe par une autre. Il s'agit là non pas de simplement retourner une instance d'une autre classe (comme c'était le cas avec l'injection décrite plus haut) mais bien de réécrire la classe pour y intégrer les modifications. Pour ce faire, on renomme la classe d'origine (avec un suffixe quelconque) puis on renomme la remplaçante avec le nom de l'originale. Ainsi le remplacement est totalement transparent pour le code qui l'entoure : il travaille bien physiquement avec une instance de la classe qu'il a demandé, à ceci près que cette classe a été modifiée.

Par exemple, on part d'une classe A que l'on veut remplacer par une classe B (qui doit étendre A). On commence par renommer A en A_replaced0. Puis on renomme B en A et on la fait étendre A_replaced0. On obtient alors une nouvelle classe A qui a toutes les méthodes et propriétés de la classe B, y compris celles de la classe A d'origine.

Cette méthode fait sauter la limitation de l'injection décrite plus haut sur les classes étendues puisque du coup une classe C qui étendrait notre classe A d'origine héritera du même coup du code introduit par B. Dans son implémentation proposé dans la version 3.0 (qui reste la même en 3.6), elle ne fait par contre pas sauter l'autre limitation liée aux sources multiples.

Depuis la version 3.0, c'est sur cette mécanique que repose l'injection de documents et plus sur la mécanique décrite auparavant (ainsi une injection du document catalog/product se répercutera sur tous les documents qui l'étendent : catalog/simpleproduct, catalog/productdeclination, etc.).

Au-delà de cette simple mécanique de réécriture (qui n'est pas de l'AOP à proprement parler, mais juste un préalable), d'autres possibilités ont été mises en place permettant d'ajouter du code avant ou après l'exécution d'une méthode, d'ajouter une méthode à une classe ou d'en remplacer une (il s'agit là effectivement des concepts de l'AOP). Là aussi, on fait de la réécriture de classes mais avec l'avantage de pouvoir faire venir des modifications de plusieurs sources.

L'inconvénient est que dans les faits, c'est un peu compliqué à comprendre et expliquer (le développeur web n'étant pas forcément familier avec ça) et pas forcément très lisible ni intuitif à écrire. Du coup finalement ça a été assez peu utilisé dans la pratique à part pour contourner la limitation sur les sources multiples ou bien pour agir sur des méthodes privées d'une classe (ce que permet dans une certaine mesure l’implémentation actuelle, mais relève à mon sens un peu de l'hérésie... même si dans certains cas c'est tentant, réécrire des méthodes privées c'est réécrire du code qui n'est pas censé être accessible par l'extérieur et c'est très mauvais lors des mises à jour).

Injection de blocs

La dernière mécanique en date à avoir été mise en place est l'injection de blocs qui arrive avec la version 3.5.

Lorsqu'on insère un bloc dans une page, on ne référence pas directement un nom de classe, mais un "type" (celui qui est indiqué dans le fichier blocks.xml du module) qui en pratique découle du nom de la classe. Mais la grosse différence avec les services, c'est que le bloc n'est pas utilisé en tant que tel par du code des modules. Rien n'oblige donc au final à ce que le bloc finalement rendu soit réellement le bloc identifié par le type.

Lorsqu'on utilise un service, on va faire appel à ses méthodes pour effectuer des traitements si une méthode manque ou n'a pas la bonne signature la plupart du temps ça finit avec une belle page dont le contenu commence par "Fatal error:". Lorsqu'on utilise un bloc, on va simplement lui demander de se rendre, mais son implémentation importe peu en termes de réussite ou d'échec de l’exécution.

L'injection de bloc peut donc être nettement plus libre que l'injection de service ou le remplacement de classe. En effet, elle se contente de changer le mapping entre le "type" du bloc et la classe PHP effectivement utilisée sans obligation que la nouvelle classe étende la classe d'origine (du moment que ça reste un bloc). Si on reste sur un fonctionnement proche, on étendra le bloc d'origine, mais si ce n'est pas le cas, on peut aussi repartir d'une feuille blanche et le remplacer par un nouveau bloc totalement différent (voire étendant un autre bloc existant qui se rapproche plus de ce qu'on veut faire).

À venir en 4.0 : unification et simplification

Comme on a pu le voir, les méthodes de remplacement de code disponibles sont très nombreuses avec chacune leurs limitations. De tout ce que j'ai pu évoquer, trois sortent du lot, car elles apportent vraiment un plus en termes de possibilités (hors des limitations liées à l'implémentation). En effet, toutes finalement visent à remplacer des classes par des classes qui les étendent une fois pour toutes globalement pour le projet à l'exception de :

  • l'injection de document est un peu à part puisqu'elle implique plusieurs classes (modèle, classe finale du document, service) mais au final reste du remplacement de classe
  • les stratégies dont le choix ne relève pas de la configuration projet, mais se fait au cours de l'exécution selon le contexte (exemples : calcul de frais, calcul de réductions, etc)
  • l'injection de blocs qui n'impose pas de reprendre le code du bloc existant, mais peut repartir de zéro dans une autre direction si le besoin s'en fait sentir

Toutes les autres (injection de service, remplacement de classe, AOP, stratégies définies dans la configuration projet, remplacement de classes spécifiques) répondent bien au même besoin : introduire du code spécifique dans une classe existante en ciblant au mieux la partie réécrite pour avoir le moins de problème possible lors des mises à jour. En version 4.0 ces différentes mécaniques parallèles seront donc supprimées au profit d'une seule et unique mécanique reprenant le meilleur de chacune.

Elle s'appellera "injection de classe" et pourra d'une manière générale être appliquée à n'importe quelle classe (à part une poignée qui sont chargées trop tôt dans l'exécution pour pouvoir en profiter). Elle reposera sur le concept de réécriture de classe (comme la mécanique de remplacement de classe) et sera déclarée dans la section injection sur le modèle de l'injection de service actuel.

Reste à couvrir un point que permettait l'AOP : injecter une classe avec du code provenant de plusieurs sources. Conceptuellement, rien ne l'empêche. C'est uniquement l'implémentation mise en œuvre dans les versions précédentes qui ne le permettait pas.

En version 4.0, on pourra donc déclarer des "injections chainées" en gros au lieu d'indiquer une seule classe de remplacement, on pourra en indiquer plusieurs séparées par des virgules. La mécanique de remplacement sera appliquée successivement dans l'ordre indiqué.

Par exemple, si une classe A est déclarée injectée par trois classes B, C et D (qui doivent toujours toutes étendre A), on obtiendra la chaine d'extension suivante : A_injected0 (anciennement A), A_injected1 (anciennement B) qui étend A_injected0, A_injected2 (anciennement C) qui étend A_injected1 et enfin A (anciennement D) qui étend A_injected2. De cette manière plusieurs modules peuvent proposer leurs injections spécifiques sans exclure les autres.

Ceci évitera au développeur de se retrouver à devoir choisir entre une multitude de mécaniques proches avec chacune ses limitations pour n'utiliser toujours qu'une seule et même mécanique simple à comprendre et à mettre en œuvre.

Conclusion

Voilà, j'ai fini mon petit tour de l'historique des méthodes de surcharge. Je me suis concentré sur ce qui est lié directement aux classes PHP, omettant volontairement tout ce qui a trait aux templates, styles et fichiers de configuration des modules (déjà comme ça ça donne un sacré pavé).

Comme je le disais en introduction, je ne suis volontairement pas entré dans les détails de mises en œuvre, pour cela, référez-vous à la documentation de Change ou posez vos questions sur le forum.

Comme je l'ai dit également, la dernière partie concernant la version 4.0 ne reflète que l'état actuel des développements et pourrait éventuellement évoluer encore un peu d'ici la sortie. Mais globalement les grandes lignes devraient rester valables. On notera du coup qu'il n'est pas conseillé d'abuser de l'AOP dans un projet actuel car cela nécessitera beaucoup de boulot de migration vers la version 4.0 au contraire d'un remplacement de classe qui reste le plus proche de ce qu'on aura au final (c'est d'ailleurs pour cela que l'AOP n'a pas été documentée dans le wiki).

Voilà, félicitations à ceux qui liront mon pavé jusqu'au bout... à supposer qu'il y en ait :euh:


Quelques trucs sur UNIX/Linux #3

Supprimer un grand nombre de fichiers

Quand on a un très grand nombre de fichiers dans un dossier, un simple rm xxx* ne fonctionne pas forcément. J'ai eu le cas notamment avec des sessions PHP qui ne s'effaçaient pas correctement et qui du coup s'accumulaient en masse.

Une solution qui fonctionne est de passer par find :

find . -name 'sess_*' | xargs rm

(source)

Supprimer les .svn d'un répertoire

SVN place un dossier .svn dans chaque dossier d'un projet (contrairement à GIT qui se contente d'un unique dossier .git à la racine).

Pour supprimer l'ensemble des fichiers .svn d'une arborescence, on peut passer par cette commande qui utilise encore une fois find :

find -type d -a \( -iregex ".*/.svn" \) -exec rm -rf {} \;

Exclure des dossiers d'un grep

Pour exclure des dossiers d'un grep, on utilise l'option --exclude-dir. Par exemple pour exclure le dossier .git lors d'une recherche dans des fichiers :

rgrep 'toto' modules/ framework/ --exclude-dir=.git

Du coup pour mes recherches dans le code de Change, je me suis fait cet alias dans mon .bashrc :

alias crep='rgrep --exclude-dir=.git'

On peut également passer par l'option --exclude.

(source)

Exclure des dossiers d'un tar

Les archives de mon serveur devenant un peu trop grosses, j'ai dû mettre en place quelques exclusions de dossiers (par exemple ne plus sauvegarder les logs, caches et médias formatés de mes sites sous Change).

Pour cela, je suis passé par l'option -X (ou --exclude-from=) de tar qui permet d'indiquer un fichier contenant les motifs à exclure.

Exemple :

tar cvf archive.tar /home/change -X exclude.txt

Avec un fichier exclude.txt contenant :

/home/change/repository
/home/change/instances/*/log
/home/change/instances/*/cache
/home/change/instances/*/build
/home/change/instances/*/media/formatted
/home/change/instances/*/www
/home/change/instances/*/repository

Attention les motifs sont comparés aux chemins tels que vous les avez décrits. Dans mon exemple, j'ai un chemin absolu pour le dossier à archiver, il faut dont des motifs qui matchent ce chemin. Si j'avais donné un chemin relatif, j'aurais dû revoir mes motifs d'exclusion en conséquence.

On peut également utiliser --exclude en indiquant directement un motif à exclure. Mais c'est moins pratique quand on a toute une liste d'exclusions.


Plus on enlève de code, mieux ça marche

Encore récemment, dans le cadre du chiffrage d'une migration d'un projet vers la dernière version d'RBS Change (CMS / e-commerce dont j'ai déjà parlé plusieurs fois ici), l'un des développeurs disait en parlant de certaines fonctionnalités développées sur le projet en spécifique et qui entre temps ont été implémentées dans le produit que maintenant que le code spécifique était écrit, ça ne coûtait pas cher de le garder tel quel plutôt que de prendre le temps de le remplacer par des appels au code du produit.

À part si un besoin spécifique n'est pas compatible avec le code du produit, je suis intimement convaincu que c'est faux. Et ce pour un certain nombre de raisons.

Le point le plus évident pour moi, c'est le coût en maintenance. S'il y a bien une chose que j'ai apprise en travaillant sur un logiciel qui fait plusieurs centaines de milliers de lignes de code, c'est que plus on a de code, plus c'est couteux à maintenir. D'une part parce que chaque ligne ajoutée peut comporter des bugs ou s'avérer incompatible avec d'autres parties du logiciel et d'autre part parce que plus il y a de code, plus il est difficile de retrouver la source d'un problème. C'est d'autant plus vrai si l'équipe chargée du projet change. Quand le développeur est le même pendant des années, il peut connaitre assez bien son code pour s'y retrouver parmi les implémentations parallèles (et encore... que celui qui ne s'est jamais senti perdu en se replongeant dans du code qu'il avait écrit rien qu'un an plus tôt lève la main) mais un nouvel arrivant sur le projet mettra beaucoup plus de temps à s'y retrouver s'il doit apprendre à connaitre les implémentations parallèles en plus du produit lui-même.

On en arrive du coup à un second point connexe au précédent : l'évolutivité. Naïvement, on se dit que vu que la fonctionnalité est codée spécifiquement, on peut faire ce qu'on veut avec et donc on est bien plus libre qu'en utilisant une fonctionnalité native du produit sur laquelle on n'aura pas autant la main. Ce n'est pas faux. Mais cela implique de se couper en partie des évolutions du produit et de devoir tout faire soi-même de son côté. De plus, se pose le même problème qu'évoqué précédemment où tout nouvel arrivant devra oublier ce qu'il sait déjà du produit pour apprendre ce que fait le projet.

Ensuite, on a les coûts d'interface utilisateur et d'apprentissage. De deux choses l'une : soit on laisse les deux implémentations parallèles accessibles dans l'interface et là c'est l'utilisateur qui se sentira perdu, ne sachant quoi choisir (ce qui implique du coût de formation et de réparation de ce que l'utilisateur aura mal fait), soit on doit masquer de l'interface les éléments relatifs à l'implémentation standard pour les remplacer par l'implémentation spécifique (ce qui implique un coût initial plus un coût à chaque mise à jour pour revalider les choses et les réadapter si besoin).

Après, on peut avoir du mal à jeter le produit de nombreuses heures de développement. C'est normal, mais pour un développeur, c'est une chose à laquelle il faut s'habituer. Un logiciel qui n'évolue pas, à part s'il est extrêmement ciblé sur un besoin très pointu qui n'évolue pas du tout (chose très rare), c'est un logiciel mort. Un logiciel vivant évolue continuellement au gré des nouveaux besoins, des nouvelles possibilités et des nouvelles idées. Seulement on ne peut pas se contenter d'empiler de nouvelles choses dessus, sous peine de voir l'ensemble s'effondrer sous sa complexité, de devenir inmaintenable et incompréhensible au nouvel arrivant.

Des choses qui semblaient — et potentiellement étaient réellement — pertinentes à un moment donné ne le seront plus un an plus tard parce que le besoin aura évolué ou simplement parce qu'à force d'y greffer des verrues, on aboutit à un ensemble qui ne ressemble plus à rien. Il ne faut donc pas hésiter à remplacer des fois des pans entiers du logiciel pour repartir sur des bases plus saines. Qui elles-mêmes dégénèreront plus ou moins rapidement (selon la qualité de l’implémentation et la vitesse à laquelle les besoins liés évoluent) avant d'être à leur tour remplacées à nouveau.

Ce principe vaut aussi bien au niveau macroscopique (une API entière finit par devenir trop lourde et doit être remplacée) qu'au niveau microscopique (une méthode donnée peut souvent être réécrite en cinq fois moins de code parce qu'elle prévoyait des cas qui n'existent plus ou bien parce qu'à force de refactoring, le code se répète trop). Il faut toutefois prendre garde à ne pas sauter trop vite aux conclusions et tout réécrire continuellement, sans quoi d'une part on n'avance plus et d'autre part, à aller trop vite, on passe à côté de subtilités qui ne sautent pas aux yeux sans examen approfondi (pour ce dernier point, des tests unitaires ou autres peuvent aider à éviter les régressions, encore faut-il avoir le temps ou simplement prendre le temps de les mettre en place).

Comme souvent, il s'agit de trouver un juste milieu entre tout réécrire et conserver à tout prix l'existant. Mais quand la réécriture consiste à utiliser quelque chose qui existe par ailleurs et qu'on n'aura donc pas à maintenir soi-même, je pense qu'il n'y a pas à hésiter : si fonctionnellement c'est compatible, ça vaut le coup de jeter le spécifique pour utiliser du natif.

Voilà voilà, félicitations si vous avez tout lu jusqu'ici et n'hésitez pas à réagir dans les commentaires si vous avez quelque chose à ajouter sur le sujet ou des objections à formuler :)


Récupérer sa bibliothèque iTunes

Je ne m'étais jamais penché sur comment fonctionnait iTunes ou ce qu'il faudrait que je sauvegarde... Au départ, il ne contenait rien d'important du coup c'était sans importance (en gros exclusivement mes CD encodés qui sont également sur mon iPhone). Puis j'ai fini par m'abonner à quelques podcasts et du coup sans trop m'en rendre compte, il en est venu à contenir des choses qu'il aurait fallu sauvegarder.

Je savais que je risquais de perdre des choses en cas de crash disque ou en cas de gros plantage du système (virus ou autre). Mais je ne soupçonnais pas qu'un vidage de la batterie et une extinction violente (et sans préavis) du mac pourrait suffire à perdre toutes les données de bibliothèque. Pas les fichiers de son, hein, juste métadonnées (notes des morceaux, playlists, etc) et surtout - dans mon cas la donnée la plus importante - les abonnements de podcasts.

Ce avec quoi je n'étais pas non plus très familier, c'est un gros fichier fourre-tout qui s'il est endommagé n'est pas réparable. J'ai l'habitude de bases de données MySQL où quand on a un problème, 99,9% du temps une réparation de la table fautive suffit (au pire on perd quelques entrées, mais l'essentiel est toujours là). J'ai l'habitude aussi de fichiers XML ou autre qu'on peut éditer au besoin pour corriger les erreurs. Mais là, rien de tout ça. Juste un gros fichier qui peut que vraquer complètement si l'ordinateur s'éteint alors qu'on est juste en train d'écouter de la musique.

En cherchant un peu sur le net, différents mot-clés, je suis tombé principalement sur des solutions du type "efface tout et réimporte ta musique". La belle affaire. La partie que tu perds dans un cas comme ça est justement celle qui m'importe.

Une autre piste proposée par Apple consiste à récupérer ses podcasts dans le fichier "iTunes Music Library.xml". J'ai bien trouvé ledit fichier, mais manque de bol, quand iTunes a trouvé que son fichier de bibliothèque n'était pas valide, il en a d'office créé un nouveau et... s'est empressé d'écraser le fichier XML susnommé qui contient une déclinaison human readable des infos de podcasts (entre autres, mais c'est ça qui m'intéresse). C'est ballot.

En fouillant dans le dossier iTunes, je suis tombé par hasard sur un dossier "Previous iTunes Libraries" contenant... des copies de sauvegarde du fichier de bibliothèque. Je ne sais pas quand il les fait effectivement, mais je soupçonne que ce soit lors des mises à jour d'iTunes (celles que j'ai sont réparties tous les 2 à 5 mois)... Quoique, pas seulement puisqu'après que je lui ai refilé la dernière sauvegarde et relancé iTunes, il s'est empressé d'en faire une nouvelle. Super. C'est AVANT le plantage que ça servait à quelque chose, hein. Là c'est un peu tard.

Donc j'ai pu repartir avec une version d'il y a trois mois. C'est toujours ça. J'ai plus qu'à réimporter ce qui manque et réabonner les podcasts récents.

Alors bon, je vais maintenant automatiser une sauvegarde plus fréquente de ce fichier (et du XML aussi tant qu'à faire) pour pas me faire avoir une nouvelle fois, mais je pensais pas que c'était aussi peu robuste...


Quelques petites BD #1

Au collège et lycée, je dessinais beaucoup, puis j'ai progressivement arrêté. Ces dernières années, ça me reprend une ou deux fois par an, mais comme la plupart du temps, je n'arrive pas à faire ce que je veux, ça ne dure pas.

Depuis quelques semaines, j'ai commencé à faire quelques petites BD allant de quatre cases à une page présentant de petits gags inspirées pour la plupart (parfois assez librement) d'anecdotes de bureau.

Pour une fois, ça semble tenir un peu. Principalement, je pense, parce que le dessin n'est ici qu'un support pour un gag et pas une fin en soi. Le fait que je trouve la plupart assez ratés techniquement n'est donc pas un si gros problème que d'habitude.

Je les publie au fur et à mesure sur Twitter ou encore deviantART mais je vais aussi les récapituler ici.

Voici donc la première fournée :

BD #1 - 09/04/2012 BD #2 - 21/04/2012 BD #3 - 30/04/2012 BD #4 - 12/05/2012
Images sous licence CC-BY

Concernant la dernière, quelques précisions pour mieux comprendre :

  • Boulder Dash est une célèbre série de jeux vidéos démarrée en 1983 (presque aussi vieille que moi :peur2: ), probablement le jeu sur lequel j'ai passé le plus d'heures dans ma vie (même si Guild Wars se défend bien aussi), les captures d'écran sont des captures d'écran du jeu
  • RBS Change, j'en ai déjà parlé ici plusieurs fois, c'est le CMS / solutions e-commerce auquel je contribue au boulot
  • Magento est une solution e-commerce concurrente (l'une des plus connues)
  • Les deux logos dans la dernière case sont les logos des deux solutions

Côté réalisation, je fais mes brouillons sur papier (parce que ça reste encore le support où j'arrive le mieux à quelque chose), puis je les scanne je mets au propre, mets en page et colorise via Photoshop Elements et tablette graphique Bamboo de Wacom (ça faisait depuis septembre dernier que je l'avais, il était temps de la rentabiliser un peu ^^).

Là ce sont les premières, donc je tâtonne un peu sur la technique, mais j'espère m'améliorer un peu au fil du temps et notamment arriver à les faire un peu plus vite (là même celles qui comporte peu d'illustrations différentes me prennent facilement trois heures... du coup ça bride un peu les choses).