The 2008-12-12 at 22:27 by Loïc d'Anterroches filed under Pluf - Framework en PHP5.
Via Laurent, je suis tombé ce soir sur une présentation de Fabien Potencier sur le découplage du code et effectivement, la présentation est une jolie présentation. Les 22 premières slides sont de bonne facture, avec un rappel des fondamentaux d’un design de qualité dans tout type d’application (web, desktop, serveur, etc.). Slides 23 à 38, vous avez ensuite une illustration assez claire de ce que cela veut dire. Si vous êtes un utilisateur de Pluf, vous connaissez cela depuis le début, votre fichier de configuration est là pour vous permettre de changer comme bon vous semble certains éléments de votre application.
Les slides 41 et 42 vous donne la conclusion importante, si votre code est bien conçu, vous pouvez facilement passer utiliser une implémentation ou une autre. Par exemple avec Pluf, vous utilisez PostgreSQL, MySQL ou SQLite pour la base de données, vous utilisez memcached ou un stockage dans des fichiers pour la mise en cache, le tout en changeant une ligne dans votre fichier de configuration. Très bien.
Mais attention, slide 48, il y a un "retour à la case départ" et le retour, c’est pas un atterrissage en douceur du marché de l’immobilier tant attendu, c’est le bon crash d’un A320 en pilotage automatique. Brutalement, l’idée qui était bonne de faire un découplage, devient tellement bonne, qu’il faut l’appliquer à tous les objets en tout lieu et toute condition, pour tout le monde, Ubi Et Orbi.
Et là, c’est parti, on définit un conteneur, on arrive même à utiliser les classe de réflexion et on finit même par ne plus utiliser PHP mais directement utiliser une configuration en XML, c’est certain, dans une démo pour un grand compte, cela doit faire joli. Mais chez moi, tous les signaux d’alerte passent au rouge, cela clignote de partout, cela fait peur à voir…
Sinon, ensuite la présentation redevient correcte (pour moi) avec l’explication du principe des signaux. Ce sont les signaux qui permettent à InDefero de faire la synchro de dépôts Subversion/Mercurial automatiquement pour vous. Les signaux c’est bien !
Conclusion, vous lisez jusqu’à la 47, puis vous passez directement à la 78. Maintenant vous savez aussi pourquoi Pluf est 5 fois plus performant que Symfony, le secret est d’ajouter un niveau de complexité uniquement si le besoin est là.
Par exemple, vous pouvez changer facilement de type de base de données ou de système de mise en cache, vous avez de nombreux paramètres de configuration pour vous permettre de plier le framework dans votre sens, mais ce n’est pas parce qu’une recette fonctionne pour un type d’objet qu’il faut l’appliquer partout.
Une petite analogie qui peut illustrer mes propos, quand vous faites un gâteau au chocolat, pour qu’il soit bien moelleux est fondant, vous montez des blancs en neige ou selon la recette vous battez vos œufs entiers jusqu’à ce que le mélange blanchisse. La conclusion symofniesque serait donc, vous voulez du fondant moelleux, il faut des œufs battus. Donc, dans ma soupe à la tomate, je vais mettre des œufs battus. Oups, erreur, application d’une recette à tous les problèmes sans distinction du contexte et du besoin réel, la soupe à la tomate, elle est onctueuse par l’ajout de crème et si vous faites vraiment bien les choses, par le mixage puis lyophilisation de vos tomates (congélation, évaporation) avant utilisation.
Si à chaque niveau de votre framework PHP, vous ajoutez un niveau d’abstraction pour vous permettre de changer les fonctionnalités, oui vous aurez un framework souple mais oui aussi vous devrez utiliser une ferme de serveurs comme Yahoo pour gérer vos bookmarks.
Une autre manière de voir les choses, si vous voulez partir de manière souple en vacances, vous pouvez partir avec un 33 tonnes et dès que les conditions météo changent, vous avez l’équipement exactement adapté aux conditions extérieures. Vous pouvez faire aussi ce que j’ai fait avec ma femme, partir 20 jours au Japon, sans valise, tout en bagage à main, quand il s’est mis à pleuvoir, pas besoin de faire tourner le 33T, juste de sauter au dessus de la flaque d’eau et d’aller sous un porche.
Quand je vois ce type de présentation, je reconnais la compétence certaine de programmeur et d’architecte logiciel de Fabien Potencier, mais ma formation n’est pas une formation d’ingénieur en logiciel, mais une formation en procédés industriels et la première qualité d’un procédé, après bien entendu la satisfaction des specs, c’est la simplicité. Quand vous avez une merde sur un FPSO, vous êtes content de garder la simplicité avec vous. Je conçois Pluf ainsi, InDefero aussi.
C’est très facile de rajouter des niveaux d’abstraction et de mise en cache etc…, c’est nettement plus dur de rester simple et efficace et de toujours restructurer votre code pour le rester. J’ai choisi le difficile chemin de la simplicité, chez moi le code n’est pas de la poésie en prose de Proust, mais plutôt de la musique de Miles Davis "Why play so many notes instead of just choosing the most beautiful?".
Comments from readers
desfrenes said:
Voilà une opinion rafraichissante que je partage tout à fait. C'est un peu ça l'agilité :-)
kbsali said:
Hmmm, ridicule! Si tu n'etais pas sur place, certaines choses t'auront echappe! Jamais il n'a ete question d'appliquer ca a tout un projet, il s'agissait plus d'une presentation pour expliquer et demontrer ce que c'est le decouplage et jusqu'ou on peut aller; mais Fabien a bien precise que ces methodes sont sans doute superflues dans beaucoup de cas, mais tres pratique dans beaucoup d'autres...
Et puis ton histoire de performance est tout aussi ridicule, encore 1 fois tout depend des contextes! Symfony sera surement bien plus adapte que ton Pluf pour le development de projets lourds, et sans doute moins pour d'autres plus legers.
Bref, c'est pas ce post qui va me donner envie d'aller fouiller ton "bijou" de framework!
Loïc said:
kbsali, le problème c'est que c'est une présentation mise en ligne directement sans contexte, elle a touché 20 personnes dans la salle et va rester en ligne pour plusieurs milliers et pour longtemps. Que le document mis en ligne soit issue d'une présentation, d'un workshop ou d'une soirée devant son écran, cela ne change pas le fait que c'est un document mis en ligne.
Alors effectivement, il a probablement dit "attention" dans son exposé, mais tous les exemples proposés sont des exemples en rapport avec un framework web, c'est bien là le problème et c'est pour cela que j'ai fait ce billet (on ne peut pas laisser de commentaires sur la présentation).
Quand on est une référence, car Symfony est une référence, on doit faire attention à ce qu'on met en ligne. PHP a un mal fou à sortir de l'image de langage pour faire du code sale avec des problèmes de sécurité à n'en plus finir. Être une référence et proposer des exemples qui vont à l'opposé de ce qui doit être fait en terme d'agilité et de simplicité, c'est aller d'un mal à l'autre.
Je répète donc, le problème n'est pas les concepts exposés, mais le fait que cela devient un document de référence avec des exemples qu'il ne faut pas suivre dans la pratique et qui sont bons que pour certains cas. Merci de bien lire le premier paragraphe de mon billet, je tape sur les exemples applicatifs et pas sur le concept !
Hugo said:
Je vais essayer de rebondir un peu sur le débat sans pour autant lancer un quelconque troll sur le sujet. Tout d'abord, il n'y avait pas que 20 personnes au workshop de Fabien mais déjà plus du double, voire le triple, car la petite salle était bondée.
Je ne partage pas tout à fait ton opinion au sujet de la présentation de Fabien. Ce que j'ai trouvé intéressant dans cette présentation, c'est de voir à quel point on peut découpler son application et rendre les objets les plus indépendants les uns des autres. Pour arriver à un tel résultat, cela nécessite un effort intellectuel particulier au développeur pour penser son application. L'objectif de sa session n'était pas de dire "faites absolument de l'injection de dépendances dans vos applis" mais de montrer ce que c'est et comment on peut y arriver. Libre ensuite aux développeurs de faire ce qu'ils veulent dans leurs applications.
Pour un framework comme l'est symfony, je pense que ce design pattern est particulièrement intéressant à exploiter dans le mesure où symfony se veut être capable de s'interfacer avec de nombreux cas de figure différents (système de stockage des sessions, SGBD...) sans avoir à trop impacter les modifications dans le code original du framework. L'injection de dépendances et le haut niveau d'abstraction des composants de symfony sont une des solutions mises en place pour y arriver. Comme Fabien l'a évoqué au cours de sa session, l'injection de dépendances sera davantage utilisée dans symfony 2.0 qui verra peut-être le jour dans le courant de l'année 2009, afin d'assurer un découplage plus important des composants du framework.
Enfin, je ne suis complètement pas d'accord avec toi concernant les performances et les tests de benchmarks. Ok Pluf est plus rapide à afficher un "hello world" que symfony. Et alors ? Ca ne reste qu'un simple hello world, et ce n'est en rien représentatif d'une application réelle. D'autre part, Pluf et symfony sont deux frameworks bien différents. Pluf n'intègre même pas la moitié des fonctionnalités qu'offre symfony par exemple. Donc forcément, symfony est plus lourd mais il a, selon moi, bien plus d'avantages à être utilisé que Pluf pour des gros projets. Personnellement, je travaille quotidiennement avec symfony et je peux t'assurer que je suis content :
- de ne pas à avoir à réinventer 10 000 fois la roue à chaque fois,
- d'avoir un excellent framework interne de tests unitaires et fonctionnels,
- d'avoir un système de gestion des formulaires puissant,
- de bénéficier d'un admin generator particulièrement utile,
- de pouvoir configurer et simuler aisément divers environnements en deux secondes et d'assurer les déploiements en production...
Bref, symfony offre un panel d'outils bien plus séduisant pour les professionnels que nul autre framework. Avant d'être performant, l'objectif de symfony est de faciliter et d'accélérer les développements dans le but de capitaliser un maximum d'argent sur un projet, tout en lui assurant une importante qualité.
La question des performances vient ensuite car symfony intègre une solution de cache des pages très poussée ainsi que la possibilité de d'utiliser des caches d'opcodes en supplément. Au final, les performances d'un site symfony en production avec un autre site réalisé avec un autre framework sont sensiblement les mêmes. Beaucoup de gros sites en production réalisés sous symfony assurent très bien en terme de performances, sans parler bien sûr de ceux de Yahoo! comme Del.icio.us ou autre Yahoo! Answers. D'ailleurs j'en profite pour signaler que l'un des principaux objectifs de symfony 2.0, est de rendre le framework beaucoup plus rapide et performant que les versions actuelles.
Au final, on aura capitaliser en développant sous symfony plutôt qu'avec un autre framework avec lequel on est obligé de redévelopper un système de contrôle d'accès ou bien d'intégrer soi même un système de cache des pages. Pour un professionnel, la question "monétisation" passe bien avant la notion du développement pur et dur.
++
Hugo.
Laurentj said:
Totalement d'accord avec toi Loïc. Dans Jelix il y aura de l'IoC pour ceux qui veulent utiliser ce concept pour leurs classes metiers/techniques. Mais le framework en lui même est suffisament extensible et souple pour ne pas avoir à rajouter cette couche d'IoC, qui apporte, quoi qu'on en dise, de la lourdeur en plus.
Pour ce qui est des slides de présentations sur le web, je me méfie toujours. Je me garde bien de critiquer une conférence sans l'avoir vu, les slides n'étant qu'un support, et sont rarement exhaustifs de ce qui se dit. D'ailleurs, je crois que parmi les conférences auxquelles j'ai assisté dans ma carrière, les meilleures étaient très souvent celles où les slides se résumaient à quelques illustrations et mots, et où on écoutait plus l'orateur (souvent très bon dans ce genre de cas) qu'on lisait l'écran. Et du coup, la simple lecture des slides publiés sur le web n'apprenait strictement rien. Bref, c'est un peu le cas extrême, mais du coup je sais aussi que des slides sur le web sont rarement très représentatifs de la conférence en elle-même. D'ailleurs Fabien au début de sa conf nous a bien averti qu'il avait fait ses slides un peu à l'arrache, traduit en plus de l'anglais.
NiKo said:
Le ServiceContainer de symfony 2 propose un lazy loading des services, c'est à dire qu'un service ne sera loadé que s'il est appellé une première fois. Par exemple, si un projet n'a pas besoin de la session, on ne la charge ni ne la configure pas par défaut. Ce n'est qu'au premier appel à $container->getService('user') que l'instanciation aura concrètement lieu.
En plus on peut configurer chaque service pour qu'il soit appellé en mode Factory ou Singleton, pour plus de souplesse.
Selon nos premiers tests, symfony 2 est 6 à 7 fois plus rapide sur le même périmètre fonctionnel que symfony 1.x, et je pense que c'est en partie grace au conteneur de services.
Maintenant, il est tellement à la mode de faire du symfony-bashing ces temps-ci, alors que le framework est de plus en plus adopté par de grands comptes et sites Web à (très) fort trafic (quelques grosses nouvelles à venir, d'ailleurs, mais sachons rester patients) que je ne peux me dire que tout cela n'est finalement que très bon signe :-)
Tiens, d'ailleurs je mets à peine le nez dans le code de Plouf que je trouve des appels à $_GLOBALS de partout. J'aurai presque aussi envie du coup de tirer des conclusions hâtives et de les publier sur mon blog, comme ça, pour rire et voir comment ça fait.
Mais j'ai vraiment autre chose de plus constructif à faire ;-)
nautilebleu said:
Utilisateur de symfony depuis 3 ans, je constate aussi que les derniers développements semblent avoir un peu perdu de vue le sens du mot simplicité. C'est d'ailleurs aussi ce que François Zaninotto reprochait avant de claquer la porte, si j'ai bien tout compris. Il semble que ce soit souvent un travers des développements PHP, peut être un petit complexe d'infériorité par rapport à Java. Dernièrement, j'ai évalué Magento (basé sur le Zend Framework) pour la boutique de ma société et ouch… c'est peut-être puissant mais c'est ultra lourd et alambiqué.
Eric said:
J'avais justement eu cette réaction quand j'ai lu les slides (mais justement, je n'ai pas assisté à la conférence).
C'est souvent tout une histoire entre l'architecture et l'agilité.
Java a souvent fait le pari de l'architecture. De l'injection de dépendance c'est courant, c'est même la structure de base. Le jour où quelque chose est à changer, le code Java est souvent bloqué, bourré de typage. Bref, c'est complexe. C'est plus simple de changer de configurations et se reposer sur une architecture complexe de classes abstraites et d'interfaces.
En PHP, Ruby et Python, on considère souvent que l'absence de typage statique permet de rester agile. On ne met pas de couches supplémentaires. Le jour où il faut changer quelque chose on change le code source. Le code source reste simple parce que justement on n'a pas multiplié les couches et les architectures. On veut utiliser un objet ? et bien utilisons le, sans abstraction "au cas où". Si besoin on change quelques lignes de code. Au pire on ajoute une abstraction, le jour où on en a besoin, pas avant.
Je me retrouve beaucoup dans cette seconde philosophie. Dans les slides j'ai eu l'impression que au moins le loader XML était de trop. Je suis capable d'initialiser mes objets directement, et de changer ce code source. Changer le code source m'apparait même plus simple que changer du XML.
Bref, la présentation est intéressante, parce que c'est un pattern utile, surtout dans un framework où parfois il vaut mieux éviter à certains de trop toucher au core.
Maintenant attention à ne l'employer que quand vous en avez besoin. Restez agile, le code sera plus simple, plus maintenable, plus évolutif, et plus agréable.
Loïc said:
NiKo, entre un $_GLOBALS bien utilisé, c'est à dire, utilisé pour une configuration "globale" et se farcir le pattern singleton avec des static pour faire propre, je préfère la rapidité et la flexibilité du $_GLOBALS.
Sinon, cela me ferait vraiment plaisir que tu publies sur ton blog ton analyse de Pluf, cela serait plus que bienvenu, surtout si tu peux nommer des points foireux.
gabriel said:
Tous les avis sont intéressants mais je pense le point de départ n'était pas le bon.
L'injection de dépendance est un résultat du concept d'inversion de contrôle appliqué aux dépendances entre classe. Ce qui a été présenté est une implémentation de l'injection de dépendance (pertinente ou pas, là n'est pas le problème).
A la source, l'inversion de contrôle est un concept qui, en résumé, préconise de fournir les éléments nécessaire à une unité de travail pour réaliser sa tache. Par exemple, tous les matins quand j'arrive dans mon entreprise, je trouve un ordinateur pour pouvoir travailler. l'entreprise me (l'unité de travail) fournit la machine (l'élément) pour assurer ma tâche (programmer). Cela parait normal pour tous mais ce n'est pas ce qu'on peut voir dans un programme. Une unité doit utiliser un base de données A, elle crée un instance elle même d'un driver pour utiliser la base de données A. Cela revient à arriver avec mon ordinateur personnel pour travailler dans mon entreprise ...
Le jour où l'unité doit travailler avec une base de données B, cela devient un problème.
C'est ici qu'intervient l'inversion de contrôle. L'unité doit travailler avec une base de données (A ou B), suivant le cas, le programme lui fournit un driver pour A ou pour B.
Cela ne résouds pas forcément le problème car les drivers A et B doivent publier la même interface ... Et là plusieurs pattern basé sur l'inversion de contrôle peuvent résoudre le problème ; il semble ici que faire de l'injection de dépendance par interface soit une bonne option. On construit une interface C fixant des régles de fonctionnement et l'unité attends quelque chose qui implémente C. Si A et B implémente C, cela assure à l'unité que l'implémentation du driver A et du driver B seront conforme à l'interface C.
Mais ce n'est qu'un parmis d'autres moyen de pratiquer de l'inversion de contrôle et ce qui a été décrit est UNE implémentation d'UN aspect de l'injection de dépendance visant à pratiquer de l'inversion de contrôle ET SURTOUT à résoudre un problème identifié, qui n'est pas celui que j'ai drécrit.
Définitivement, un "design pattern" ne s'impose pas de lui même par nature. Un problème identifié peut trouver une solution dans un design pattern qui réponds spécifiquement à son besoin. Autrement dit, on utilise un design pattern pour solutionner un problème identifié. On utilise pas un design pattern "comme çà".
L'injection de dépendance par interface est un design pattern de l'inversion de contrôle, l'utilisation d'un container de construction d'objet pour faire de l'injection de dépendance est un autre design pattern, le serviceLocator (sous la forme d'un Registry par exemple) en est un autre.
Voici une excellente lecture http://martinfowler.com/articles/injection.html dont l'auteur n'est plus à présenter.
En espérant avoir apporter un peu plus de lumière !
Loïc said:
Merci Gabriel pour cette belle explication.
> On construit une interface C fixant des régles de
> fonctionnement et l'unité attends quelque chose qui
> implémente C. Si A et B implémente C, cela assure à l'unité
> que l'implémentation du driver A et du driver B seront
> conforme à l'interface C.
Effectivement, c'est une bonne approche. Dans mon cas, pour des raisons de performances, je préfère avoir des interfaces implicites, c'est à dire que je ne définis pas d'interface en tant que telle mais mes classes en pratique disposent des bonnes méthodes.
Il ne faut pas oublier que le modèle d'exécution de PHP est tel, chaque interface que vous ajoutez dans votre code va réduire les performances car PHP va devoir faire des contrôles supplémentaires à chaque appel de votre page. Pourquoi contrôler à chaque requête que votre class Truc à bien toutes les méthodes avec les bonnes signatures de l'interface Bidule ?
gabriel said:
oui c'est aussi valable mais uniquement dans un environnement contrôlé (par exemple, c'est une consigne de développement, une coutume, un consensus accepté) mais si l'implémentation est fait par une personne tiers et l'utilisation en est faite par une autre, ca se complique. On a besoin dans ce cas de certitudes. C'est le cas par exemple, d'une partie du package org.xml de java. Il définit un ensemble d'interface qui sont ensuite implémenté par différents éditeurs.
Je dois analyser un fichier xml, la classe qui s'en occupe attends soit dans le constructeur, soit dans un setter, qu'on lui fournisse quelque chose qui implémente XMLReader. C'est un contrat explicite qui assure au développeur que le code ne compilera pas si l'implémentation n'est pas totalement réalisée.
Ce que je dit ici mais en lumière ce que je disais avant. deux contextes, deux problèmes différents, deux solutions adaptés. Cela confirme que le design pattern s'applique à un problème identifié.
Attention encore, de tout ce que je lis, il faudrait tout résoudre au premier jet !! Il faut envisager le contexte qui est changeant. Par exemple, j'ai une classe qui analyse un fichier xml, la lib xml est contrôlée, pas besoin d'aller faire de l'ioc. Demain mon appli est distribué avec des plugin, je peux alors envisager l'ioc plus en profondeur.
Certains diront pourquoi ne pas le faire dés le début ? Tout simplement parce qu'une application n'est pas figée. C'est la réponse à un problème dans on contexte donné. Fournir une usine à gaz (même si techniquement c'est très évolué), c'est bien mais il faut que cela reste adapté au contexte. Notez qu'il faut aussi savoir anticiper, se laisser des portes de sorties ...
Je pense profondément qu'on ne choisis pas la solution la meilleure mais la moyen pire, celle qui fait poids égal entre cout, qualité, délai ...
----
Concernant php, un cache d'opcode, permet de pallier à ce que tu décris. Même si je suis d'accord avec toi, je pense que ton argument n'est pas le meilleur dans ce cas.
Encore une fois, c'est le contexte qui commande. Pondre des interfaces à tout va, ça n'a pas de sens. Définir des interfaces pour des actions bien spécifique car notre application peut être étoffée par d'autres développeurs, ca en a déjà plus ( des plugins par exemples). Mais l'héritage de classe abstraite (ou Template) est aussi une autre solution applicable dans certains cas.
Si la conception est en accord avec le contexte, l'impact sur les performances est immédiat.
---
Mais on parle d'IOC, mais finalement en soit ce n'est qu'un concept. On pourrait parler aussi de séparations des préoccupations. j'ai pu constaté que bien souvent là où on met en oeuvre de l'ioc, les préoccupations sont liées.
Par exemple, j'ai une classe métier qui se charge de faire des opérations en bdd pour ses propres données. La préoccupation métier de ma classe est liée à la préoccupation technique de mon accès à la bdd. Je sors une interface mais finalement, mon premier problème est que ma classe métier fait des opération en bdd. Il faudrait avoir une unité qui s'occupe de faire du CRUD sur ma classe métiers.
Par exemple, au lieu de faire
$maClasse->upadate();
on fera
$monCRUD->update($maClasse);
L'avantage est :
1 - ma classe métier n'est plus dépendante
2 - ma classe métier est distribuable car elle n'est plus dépendante d'une implémentation ioc
3 - les préoccupations ne sont plus liées. Le metier est géré par la classe métier, l'accès à la base de données est gérée par une autre classe.
A partir de là, je m'ouvre la porte sur le portage simplifié vers une autre bdd, utiliser des mockObject pour les tests unitaires, etc ....
Notez que dans l'exemple que je viens de donner, on a un cas flagrant Inversion de contrôle sans aucun code ou processus particulier.
En séparant mes préoccupations, j'ai inversé le contrôle !! Le code en est la preuve.
Avant :
Mon objet appelle un driver bdd, lui fournit ses données à mettre à jour
Aprés:
une unité reçoit un objet à mettre à jour, il appelle un driver bdd et lui fournit les données à mettre à jour.
Le processus est presque que le même mais juste en séparant les préoccupations, j'ai inversé le contrôle et n'ai pas introduit du code en plus pour réduire les interdépendances !
neiluJ said:
Je suis d'accord avec toi: PHP ne doit pas devenir un fork étrange de Java. De toutes façons sans les threads, ce serait vraiment pour faire "genre" mais çà n'irait pas bien loin.
Je te rejoins également sur le fait que l'injection de dépendance c'est bien, mais les containers décrits en XML non merci. D'ailleurs, c'est ce qui m'empêche de sympathiser avec les productions (que je trouve de bonne facture) de Sensio comme Doctrine ou Symfony. Après tout, je suis pas développeur XML, non mais !
J'avoue quand même avoir du mal à comprendre pourquoi tant de gens se mobilisent à coups de benchmarks-parties pour savoir "qui va le plus vite" ... Les soucis de production se situent rarement au niveau du code mais plutôt du côté des environnements externes et des applications tierces. Avoir le plus joli temps de réponse sur une application web ne fera pas répondre les webservices plus vite. MySQL ? N'en parlons pas...
Les initiatives comme Pluf sont très proches de la philosophie "pure" de PHP, à savoir la simplicité. Maintenant le refactoring constant est une bonne chose mais cela s'intègre difficilement dans un process d'entreprise. Les namespaces et les approches objets sont un juste milieu que certains développeurs savent exploiter, d'autres en abusent.
Entre parenthèse, j'ai utilisé Pluf pour un petit projet. Très bon aperçu et clairement orienté "je-vais-droit-au-but". J'y reviendrais sans hésiter pour des projets perso que je serai seul à maintenir. Cela dit j'aurai beaucoup de mal à le vendre à mon travail pour nos futures versions.. et puis le meilleur framework, c'est le mien ;)
Fabien Potencier s'approprie les Design Patterns qui sont vieux comme le monde, mixe le tout avec un poil de marketing et ça fonctionne pour lui, tant mieux...
Les Rolling Stones ont bien fait leur pain avec Satisfaction, ce qui n'a pas du tout enrayé la carrière d'Otis Redding...