Très récemment, j’ai visionné une présentation de George Fairbanks intitulée Building Theories is Building Value. Fairbanks est aussi l’auteur d’un livre de référence sur l’architecture logicielle: Just Enough Software Architecture: A Risk-Driven Approach (2010). C’est l’un des meilleurs livres que j’ai lu en la matière.
Au milieu de sa présentation, en plus d’être captivé, je n’ai pu m’empêcher de faire le parallèle avec un livre que j’ai lu il y a longtemps. Un livre sur le design, de Donald Norman: Design Of Everyday Things (1988). C’est aussi une référence en la matière. C’est ce genre de livre que l’on a beau lire une fois, comme la plupart des livres, mais qui laisse une trace forte dans notre mémoire, tellement les principes évoqués sont bien expliqués, évidents une fois qu’ils sont bien exprimés, et surtout utiles et applicables comme des outils dans de nombreuses situations. Le principe des principes, en somme. Si vous deviez choisir un livre parmi ceux évoqués dans cet article, c’est celui que je vous recommande le plus vivement. Même s’il ne s’agit pas de développement, je pense qu’il nous parle parce qu’on aime comprendre les choses, à la fois leur fonctionnement mais aussi les choix de leur design. N’avez-vous pas souvent pesté intérieurement face à des choses manifestement mal conçues, dans la vie de tous les jours ? Des portes à pousser au lieu de tirer, des caisses automatiques ridiculement mal faites dans les cinémas ou les grandes surfaces, etc. ? Si oui, ce livre vous procurera un certain plaisir. Norman a même réécrit une seconde édition en 2013 car les exemples de la première étaient quelque peu obsolètes (les principes expliqués en revanche non).
Pour en revenir à notre sujet, le propos de la présentation de Fairbanks était de dire: les modèles ont de la valeur, utilisez-les. Le choix du mot « théories » pour désigner les « modèles » peut surprendre, mais c’est bien de modèles dont il parle.
Avertissement: article long et ton provocateur
Cet article s’avère être beaucoup plus long que ce que j’avais en tête. Aussi, si toutefois vous choisissez de le lire, prévoyez une bonne tasse de café (ou de thé), et installez-vous confortablement. Il contient une bonne vingtaine de liens qui, pour beaucoup d’entre eux, sont vraiment de qualité et méritent d’être lu (ou regardés pour les vidéos).
De plus, je suis consciemment réducteur et provocateur sur certains aspects. Je réserve quelques notes pour la fin où j’éclaircirai mes positions sur ces aspects.
Entrée en matière par une petite digression sur l’agilité
Dans cette présentation de Fairbanks, l’agilité n’est jamais nommée mais elle est forcément présente à l’esprit comme, en apparence en tout cas, une méthode qui va à l’encontre de sa proposition. Je dis en apparence, pour dire telle qu’elle est généralement diffusée et appliquée.
Le propos simplifié de Fairbanks est de privilégier la réflexion avant le code ; tandis que l’agilité qui nous est vendue presque chaque jour ces dernières années (la méthode Scrum en particulier) privilégie quasiment l’inverse: la production de code avant la réflexion. « Code first, think later » est une expression que j’ai trouvé pour la première fois dans l’excellent livre Debugging .NET 2.0 Applications (2006) de John Robbins qui ne parlait pas spécialement d’agilité. Et je rassure le lecteur, John Robbins utilise cette formule comme une moquerie, pas comme une approche recommandée.
« Code first, think later » (on rencontre aussi « Code and Fix ») est un vieux rêve que nous vendent l’agilité, les ORM (Hibernate, Entity Framework…) et globalement divers frameworks censés décupler notre productivité en travaillant moins: utiliser SQL sans connaître SQL, développer sans vraiment avoir à spécifier. Evidemment cette formule (« Code first, think later ») est aussi moqueuse que l’approche antagoniste de l’agilité: Waterfall: elle non plus n’est jamais nommée de la sorte par ses adeptes mais présentée sous la forme d’un processus par étapes successives, chaque étape étant réalisée par un groupe de personnes différent: spécifications, design, implémentation, tests, et enfin maintenance (une méthode qui, j’en conviens, est pire que Scrum). Pour autant, l’une des approches de développement d’Entity Framework s’appelle Code First et vous engage clairement à ne pas créer de modèle de base de données pour plonger, tête la première, à taper votre code! Donc formulation moqueuse oui, mais révélatrice d’une certaine réalité quand même.
En tout cas, l’idée est de ne pas trop réfléchir car c’est dangereux pour le budget. Comprenez que vous êtes un peu lent quand vous voulez trop bien faire, c’est d’ailleurs la raison pour laquelle on vous demande de vous dépêcher en travaillant uniquement au pas de course, par sprints. De toutes façons, l’architecture émergera d’elle-même à la fin. On a d’ailleurs un concept pour ce modèle (si si): Emergent Design. En gros, on peut travailler en « mode automatique », et produire un résultat de qualité. Ou en tout cas un résultat. Enfin, quelque chose… C’est le plus important.
Evidemment, même avec l’approche « Code First », nous devons créer des modèles. Mais nous le faisons au dernier moment, au fil de l’eau, aboutissant à peu de cohérence dans le résultat final. Au bout du compte, le temps que nous passons à réaliser – quoiqu’il en soit – ce modèle, est du temps utilisé à mauvais escient car il n’est probablement pas inférieur au temps que nous aurions pu allouer en amont du développement proprement dit, sur un modèle qui n’aurait pas été parfait dès le départ, mais avec un bien meilleur retour sur investissement (le cher et fameux ROI). En définitive, nous sommes bien obligés de travailler avec des concepts car nous n’écrivons pas naturellement du code machine. La question est de savoir le faire au bon moment de manière volontaire, et donc de manière réfléchie.
Le modèle conceptuel
Je reviens sur le sujet qui nous occupe.
Aux deux tiers de sa présentation, Fairbanks s’intéresse à notre approche cognitive lorsque nous développons. Il parle d’un « alignement » nécessaire entre une représentation « interne » et « externe » d’un problème, pour que nous soyons efficace dans sa résolution (en sous-entendant que nous avons besoin de développer cette solution dans le monde extérieur, car nos capacités purement mentales sont limitées).
Dans son exemple d’un code source, la représentation interne est composée des instructions du code nécessaires au fonctionnement du programme, alors que le nommage des variables et l’organisation du code (éclatement en fonctions, utilisation de design patterns, etc.) est sa représentation externe. Le même code peut être exprimé avec des variables « x », « y », avec très peu de fonctions, sans concept visible, et fonctionner à l’identique d’un code organisé et « parlant », avec des variables nommées « customer », « product »… autrement dit avec des concepts visibles.
La représentation interne étant celle que nous ne maîtrisons pas (c’est le problème tel qu’il est), il met donc en avant l’importance de sa représentation externe.
L’expression du domaine métier dans le design, cher au Domain-Driven Design (DDD) décrit par Eric Evans, n’est rien de plus que rendre visible le modèle conceptuel de ce que réalise le code, si ce n’est qu’il faut utiliser les concepts connus par le métier (importance de partager les mêmes concepts pour que tout le monde se comprenne – l’ubiquitus language en DDD).
Et l’importance de sa visibilité
C’est cette relation entre représentation interne et externe qui m’a fait immédiatement penser aux travaux de Donald Norman sur le design. D’ailleurs, les termes de « visibilité » et « modèle conceptuel » sont plutôt ceux de Norman. Fairbanks parle de « théories » et de « représentations internes et externes » dans sa présentation (le chapitre 7 de son livre parle aussi bien du modèle conceptuel). Pour autant, nous allons voir qu’il s’agit des mêmes principes dans leur essence.
Design Of Everyday Things s’intéresse aussi au design par son approche psychologique, cognitive. Le livre répond au final à une question: qu’est ce qui rend un objet facile à utiliser ? (et à l’inverse, ce qui le rend difficile à utiliser).
Le livre est un recueil d’exemples et de cas pratiques mais la réponse à sa question est donnée dans les premières pages du livre, au premier chapitre:
- fournir un bon modèle conceptuel
- et rendre les choses visibles.
L’utilisateur de l’objet, lors de sa découverte, va s’en créer un modèle mental (conceptuel) à partir de ce qu’il observe. Il est important que le modèle qu’il se crée de l’objet soit aligné avec son fonctionnement. Sinon il aura du mal à l’utiliser et son expérience sera négative.
Pour Norman, la visibilité du modèle est rendue par trois moyens:
- affordance (remplacé par l’idée des « signifiants » dans la deuxième édition du livre)
- alignement (mapping en anglais)
- contrainte
Bien que ce ne soit pas directement lié, je note brièvement que même le principe du feedback, qui a été repris en agile, est un des fondamentaux décrits par Norman.
L’affordance: permet de…
Lorsque vous voyez quelque chose, une forme par exemple ou un matériaux particulier, et que son usage est évident, vous avez affaire à une affordance. Une chaise pour s’assoir, un tableau pour écrire, une fenêtre pour voir à travers, les trous des ciseaux pour y insérer vos doigts, un bouton pour appuyer dessus, une corde pour tirer dessus, etc. Devant la difficulté d’expliquer le concept d’affordance, Norman propose un autre terme dans la seconde édition de son livre: « signifiers » qu’on pourrait peut-être traduire par « signifiants », des signes, des indices, de comment utiliser un objet.
L’alignement (entre la représentation interne et externe)
Un bon alignement entre les représentations interne et externe permet un traitement optimal de l’information.
Pour exprimer cette idée, Norman prend l’exemple d’une poignée de porte: on ne sait jamais s’il faut pousser ou tirer. D’après Norman, l’affordance d’une poignée est de pouvoir tirer dessus. Si une porte doit être poussée, on devrait idéalement apposer un support plat ou une barre qui ne peut être que poussée (comme sur les issues de secours). S’il y a un mauvais mapping, un mauvais alignement, nous avons un conflit à résoudre, qui nécessite un effort particulier.
Fairbanks utilise un autre exemple connu pour illustrer la même idée: des noms de couleurs, dont la couleur du mot est en conflit avec le sens du mot. Il montre que notre esprit est moins performant parce qu’il doit résoudre un conflit dans ce qu’il voit: un mot et une couleur dont le sens sont différents. C’est l’effet de Stroop.
La contrainte
On utilise une contrainte quand on ne peut exprimer le meilleur modèle conceptuel qu’à partir des seuls affordances et alignements. Norman propose l’exemple des ciseaux: leur mouvement est contraint si bien que leur usage est évident (en plus des affordances des trous pour y insérer les doigts et des lames pour couper).
En programmation, les types de variable et le nombre d’arguments acceptés par une fonction sont un exemple de contrainte.
Théories, modèle mental, portes, logiciels, architecture: quel rapport ?
Fairbanks emploie le terme théorie, Norman parle de modèle conceptuel et de modèle mental. L’idée est la même. Mais l’important n’est pas tant le modèle que sa visibilité. C’est parce qu’il est visible qu’en tant qu’observateur, nous nous faisons un modèle mental de l’objet observé. C’est pour cette raison que l’event-driven design apporte normalement une de ses qualités (parmi d’autres) au design: les événements sont des objets qui transportent une intention (et non le résultat d’un changement d’état qui écraserait un précédent état). Les événements rendent clairement visibles les différents changements d’état possibles au sein du système.
Que nous parlions d’un objet tel qu’un smartphone, d’un ascenseur ou d’un logiciel informatique, seuls les acteurs changent: nous, développeurs, sommes les utilisateurs de notre propre code (et de celui d’autres développeurs). Les mêmes principes s’appliquent évidemment à l’interface utilisateur, pour les utilisateurs finaux, mais je m’intéresse dans cet article au code proprement dit.
Les critères d’un bon design sont variables, souvent exprimés sous forme d’un ou deux attributs qualitatifs à privilégier sur tous les autres. Le plus souvent dans un système informatique, l’un de ces principaux attributs est sa capacité à évoluer (adaptation au changement). C’est d’ailleurs le deuxième principe du manifeste Agile.
Pour y parvenir, nous avons besoin d’anticiper le résultat de nos actions sur le modèle dans sa forme actuelle. Autrement dit, le modèle doit être suffisamment bon pour servir de base à nos changements, et, encore plus important, suffisamment évocateur, visible, pour que ces changements soient faciles.
Sans visibilité sur nos changements, nous devons agir à tâtons, expérimenter et voir les réactions aux variations apportées, parfois presque de façon aveugle sur les pires projets (code spaghetti, Fairbanks utilise le terme « big ball of mude »).
Disons-le franchement: créer un bon modèle tel que le fonctionnement du système est évident n’est pas facile. En théorie, si on y arrive, il ne devrait pas y avoir besoin de documentation. En pratique, d’après mes expériences en tout cas, nous avons le plus souvent besoin de documentation annexe (du moins pour les projets d’envergure). Peut-être que moins il y en a, plus le code est parlant. Ou alors moins il y en a, plus les développeurs ont-ils été paresseux ? (non c’est parce qu’ils sont tellement doués que le code parle toujours de lui-même).
C’est d’ailleurs ce que Fairbanks (toujours lui) décrit dans son livre (chapitre 10) comme le model-code gap, qui désigne l’écart entre le code source et son modèle conceptuel. Cet écart désigne la part du design que nous n’arrivons pas à exprimer à partir du code seul. Autrement dit, pour reprendre les termes de sa présentation, c’est la distance entre la représentation interne et externe de la solution.
Pour conclure…
Le propos de Fairbanks est de remettre la réflexion au centre du développement
…plutôt que l’approche « code first think later », afin de rendre le fonctionnement du système à la fois efficace pour fournir une solution à un problème actuel, et facile à comprendre pour être aisément modifié. Le mot-clé est « facile »: les choses doivent être conçues pour être adaptées à êtres utilisées, que ce soit par les utilisateurs finaux pour l’expérience utilisateur (la fameuse UX), ou que ce soit par les ingénieurs du logiciel (pour cette douce activité qu’est la tierce maintenance applicative ou TMA).
Une dernière chose importante: un logiciel n’est jamais terminé tant qu’il est utilisé (ne jamais croire qu’il n’aura plus besoin d’être modifié). La vraie fin du projet, c’est l’arrêt du support du logiciel (et donc l’arrêt de son utilisation).
Grâce à l’utilisation volontaire de modèles
En informatique, les modèles sont autant le code lui-même (le nom des variables, des tables de bases de données, l’organisation en méthodes, l’emploi de design patterns…) que sa documentation lorsqu’elle apporte une valeur, des précisions pas toujours évidentes à voir dans le code, soit parce qu’il y en a une très grande quantité, soit parce qu’on a choisi un attribut qualitatif incompatible avec la meilleure lisibilité. Par exemple, le choix des performances entraîne souvent un code plus difficile à interpréter (Kevin Montrose, chez Stackoverflow, nous en a récemment donné un bel exemple, documenté par un article).
À propos de documentation d’architecture, Simon Brown a mis au point une méthode très intéressante et de plus en plus largement utilisée: le modèle C4. Vous pouvez en voir une présentation ici. Il évoque d’ailleurs le model-code gap de Fairbanks. Bien que toute la présentation soit intéressante, le modèle C4 est décrit entre les minutes 14 et 18. Brown fait une comparaison très parlante, je trouve, de sa méthode avec la cartographie: lorsque vous regardez au niveau du globe terrestre, des continents, des pays, vous ne vous intéressez pas au même niveau de détail que si vous regardiez un aspect particulier: les fleuves ou les routes d’un pays, ou l’altimétrie d’une zone par exemple. C’est exactement la même chose dans la documentation d’un système: on ne mélange pas des aspects techniques proches du code avec des aspects plus généraux comme les interactions entre plusieurs services d’un système.
Dans sa présentation, Fairbanks dit que nous ne somme pas des machines analytiques parfaites mais des « sacs chimiques » (bags of chemicals). Je vois en cela la raison pour laquelle ces principes (décrits par Norman) sont des fondamentaux, quel que soit le domaine (le design d’objets de la vie de tous les jours, le développement proprement dit…), car il s’agit avant tout de notre mode de fonctionnement, d’appréhension du monde qui nous entoure. C’est la raison pour laquelle ces principes sont des fondamentaux, quel que soit le domaine dont on parle. Et c’est ce qui les rend importants. Donc oui, modéliser et penser avant de coder est important, même si ce n’est pas la « mode » actuelle. Ce n’est pas parce qu’on a pendant longtemps abusé d’une méthode (big upfront design) qu’il faut faire exactement l’inverse.
Certes, le thème de la présentation de Fairbanks était bien différent de celui de Norman. Pour autant, ce parallèle m’est apparu comme un flash et j’ai trouvé le sujet intéressant à exprimer.
Post-scriptum
Note personnelle sur les ORM
On pourra trouver que je suis critique vis-à-vis des ORM et c’est le cas. C’est l’un de ces domaines de luttes très partisanes chez les développeurs. Pour moi, l’objectif d’un ORM est de s’abstraire de la connaissance du SQL. Quand je dis cela à des confrères du « camp » des ORM, on me répond que non, que les ORM permettent de faire des choses difficiles ou fastidieuses à faire autrement, notamment pour « aligner » le modèle « plat » d’une base de données avec le modèle objet d’un programme (le fameux « object-relational impedance mismatch »). Nous sommes d’accord sur le fait que vouloir faire entrer des triangles dans des carrés n’est pas une chose facile et il y a effectivement un beau challenge technique réalisé dans les ORM. Mais, si le choix du SQL est un bon choix, il suffit d’embrasser son modèle. Sinon, un meilleur choix est peut-être de s’orienter vers une solution noSql (à ma connaissance, il n’y a pas d’ORM noSql, ce qui me paraît logique mais je ne serai pas surpris que des gens assez créatifs parviennent à vendre ça un jour).
Note personnelle sur l’agilité
Je ne critique pas l’agilité en tant que telle, mais plus la façon dont le concept est marketé et interprété tel que ça arrange les uns et les autres. Je pense qu’on a « collé » ce terme initialement positif, porteur de pratiques de bon sens (mais d’aucune notion spécialement nouvelle), sur des interprétations et une mise en pratique bien différentes du sens original. L’une des lignes directrices du mouvement agile (tout comme l’approche DDD, dans une certaine mesure) était de rapprocher, de créer un lien direct entre les équipes de développement et les utilisateurs finaux (le client, le métier), afin de rendre plus transparent le processus de développement. Si la mise en oeuvre de Scrum consiste, comme dans beaucoup d’entreprises, à renommer le chef de projet “Product Owner”, à donner des rôles aux développeurs ( N développeurs et 1 Scrum Master), à suivre des cérémonies (daily meetings, poker & sprint plannings, retrospectives…), mais à rarement, voire jamais, rencontrer les utilisateurs finaux, cette organisation n’a d’agile que le nom qu’on a bien voulu lui donner.
Je sais que les fondateurs du manifeste Agile n’ont jamais privilégié l’approche « Code First, think later » ou « Code and Fix » tel qu’on l’entend dans le sens négatif de ces expressions. D’ailleurs je cite Robert C. Martin (l’un des auteurs du manifeste Agile), alias Uncle Bob, dans cette présentation:
When we were doing the Agile Manifesto, the thing we decried was big upfront design, we did not decry upfront design. […] [it] would probably be a good idea to know what the general shape of the application is.
Noter à ce propos que Fairbank réserve une section (5.5) dans son livre sur exactement le même conseil: Avoid Big Design Up Front (et cela sans parler d’agilité).
Je suis convaincu du bien fondé de la création de valeur par itérations, avec un feedback utilisateur rapide et répété jusqu’à la fin du projet. Si l’on oublie les règles de pratique extrêmiste, quasi-religieuse, des méthodes telles que Scrum, le fond de la méthode vise à mettre au premier plan la communication et les interactions directes entre les développeurs et les utilisateurs finaux, à privilégier la création de valeur par étapes, c’est-à-dire à prendre moins de risques: si le projet s’arrête ou change radicalement de direction pour une raison ou pour une autre, soit ce qui a été réalisé n’est pas perdu, soit ce qu’on accepte de perdre est un moindre mal (fail fast est un autre bon principe). Le rôle du Scrum Master a finalement les attributions d’un bon manager: protéger son équipe et lever les obstacles qu’elle rencontre afin que la force de travail puisse passer le plus de temps à réaliser, et perdre le moins de temps dans les défauts organisationnels de l’entreprise. De plus, il n’y a plus de hiérarchie (oui parce que la hiérarchie c’est trop has been, et puis comme ça on peut faire des équipes de juniors, c’est moins cher). Le rôle du Product Owner est de forcer le client final à s’impliquer dans le projet, au premier plan, et de le rendre disponible à plein temps auprès de l’équipe de développement. Tout cela devrait être du bon sens, mais on nous dit qu’en automatisant tout cela, tout se fera naturellement, avec des profils juniors à qui on aura donné la méthode pour réussir. Cela me semble illusoire.
Le lecteur qui n’est pas encore fatigué pourra lire ce travail de Martin Fowler, Craftmanship and the Crevasse, qui a le don du story telling et qui parle de cet affrontement (presque) entre le mouvement Agile et le plus récent mouvement Software Craftmanship. Je me reconnais personnellement davantage dans ce dernier.
L’idée originale de mon article n’était pas de parler de l’agilité mais de cette notion de modèle conceptuel, dans un texte condensé. Ce n’est pas une réussite, manifestement. Cependant, quand on parle de préparer, réfléchir, penser les developpements avant d’agir, ne pas parler de l’agilité (et de Scrum dans sa forme la plus connue actuellement) aurait été ignorer l’éléphant dans la pièce.