jun. 2019
La dette technique semble être dans tout projet une problématique aussi cruciale qu’inévitable : trop de variables internes (deadlines, changement d’équipe, coût, …) aussi bien qu’externes (changement de scope, ajout de fonctionnalités, …) viennent chambouler et interagir avec le code, si bien que dans une intervention que j’avais pu faire à Paris Web 2017 j’avais parlé d’ “entropie croissante”. C’est un gros mot qui veut dire que sur le long terme la production est hautement imprévisible, désordonnée voire chaotique : on avance de rustine en rustine et la complexité augmente jusqu’à ce que cela devienne risqué d’ajouter ne serait-ce qu’une “dernière” rustine.
Dans ce dernier talk, je proposais un angle plus “UX design” pour tenter de répondre à cette entropie croissante, proposant de mettre en place 3 méthodes se concentrant sur l’implication et la collaboration de toute l’équipe dans un design system.
Avec ce nouveau talk, dont les diapos sont disponibles en fin d’article, je souhaite proposer une seconde approche plus “technique” sur le code front-end lui-même en illustrant comment on peut s’inspirer des deux paradigmes de programmation dominant actuellement (la programmation orientée objet et la programmation fonctionnelle) dans le but de produire un code qui soit plus lisible, plus flexible, et encore une fois, plus maintenable. Plus précisément : l’approche se concentre sur le CSS, que je divise donc dans mes projets en CSS objec et en CSS fonctionnel.
J’ai eu l’opportunité de publier un article complet reprenant mon talk lors des 24 jours de web 2018. Je reproduis ici tel quel l’article.
Dans un projet web, les prix sont bien souvent négociés en amont en fonction d’estimations, mais deviennent par la suite quasiment immuables. Il faut alors réussir à réaliser le projet en respectant les délais et un certain standard de qualité. Mais c’est souvent après les estimations que la machine s’emballe : il n’est pas rare d’avoir à ajouter à ces contraintes l’avis changeant du client, les limites techniques découvertes sur le tard, les variations des spécifications, … Le client souhaite ensuite de nouvelles fonctionnalités et exprime de nouveaux besoins. Les choses continuent de se gâter lorsqu’il s’agit de reprendre du code écrit plusieurs mois (années ?) auparavant, lorsque des nouvelles technologies et méthodes apparaissent, lorsque l’équipe technique elle-même évolue…
Ca devient très vite un enfer à gérer, que “certaines études américaines” ont pu qualifier d’”entropie croissante ”. C’est un gros mot qui veut dire ici que sur le long terme la production est hautement imprévisible, désordonnée voire chaotique : on avance de rustine en rustine et la complexité augmente jusqu’à ce que cela devienne risqué d’ajouter ne serait-ce qu’une “dernière” rustine. À ce stade là, on a cessé d’espérer tenir le niveau de qualité et l’on crée de la dette technique…
Pour répondre à cette dette technique, en particulier sur le code front-end, de nombreuses équipes mettent désormais en place une approche en styleguide et en composants. Cette méthode, notamment développée dans le désormais célèbre “atomic design”, permet d’appréhender globalement des interfaces en système : une collection cohérente de composants connectés entre eux, ainsi que des méthodes partagées au sein d’une équipe, organisées autour d’un artefact (le styleguide) dans l’objectif de répondre à une stratégie définie en amont (prototyper rapidement, redesigner un site existant, créer une plateforme de toutes pièces, …). L’approche est souvent bien pensée, méthodique et efficace, mais se retrouve très vite abandonnée une fois l’objectif atteint.
Or un design system n’offre toutes ses possibilités que sur le temps long : il doit vivre et a besoin pour cela d’amour, d’être intégré aux process, d’être un outil pour toute l’équipe. Sans ça, il devient ce que Jina Anne a pu appeler un “zombie styleguide” : c’est un système qui n’est pas pensé sur le temps long des process mais de manière statique et éphémère, quand l’entropie n’existe justement qu‘en croissance continuelle. Et quand on arrête de vouloir la contenir, alors cette entropie contre laquelle on luttait reprend ses droits sur le projet. Et le styleguide passe d’inutilisé à inutilisable, mi-vivant mi-mort : zombie.
Si le styleguide n’est pas pensé dynamiquement sur le temps long, il se fera rattraper par l’entropie de la production.
Le problème du “zombie styleguide” est avant tout une illustration, parmi d’autres, de la difficulté qui existe de penser, en amont de toute démarche de code, la collaboration et la scalabilité au sein d’une équipe. Confrontés avec mes collègues à ce problème, nous avons essayé d’y répondre sous l’angle des “process” et du manque d’adoption de la méthodologie au sein de l’équipe : comment “faire” équipe ? Comment aligner et impliquer tout une équipe dans un design system ? Comment choisir les méthodes qui favorisent la réflexion, la collaboration et donc la maintenabilité d’un système ?
Mais cette première approche reste incomplète si elle n’est pas envisagée ensuite sous son angle plus “technique” : comment proposer un outil qui soit à la fois garant de l’identité de marque tout en permettant la flexibilité nécessaire à son utilisation dans différents contextes ?
Ma première expérimentation technique est venue à la lecture d’un article de 2016 de Sarah Drasner qui, après avoir rappelé que coder c’est 30% de création et 70% de maintenance, proposait de regarder la maintenabilité du code front-end à travers la lentille des deux paradigmes de programmation dominants : la programmation orientée objet et la programmation fonctionnelle.
Avertissement: l’idée ici n’est pas de revenir précisément sur le débat entre OOP et FP mais plutôt de voir comment certains concepts de ces paradigmes de programmation peuvent s’appliquer, au moins sur le plan métaphorique, au CSS, qui n’est pas un langage de programmation.
La programmation orientée objet (OOP) est un paradigme de programmation qui se base sur la création d’objets réutilisables et la création de relations entre ces objets. Son objectif est d’isoler les différentes parties d’un programme dans différents scopes indépendants. Celui-ci est alors divisé en différentes abstractions, bien souvent des classes, qui tentent de définir l’essentiel d’une fonctionnalité par exemple, et en différentes instances qui sont des applications concrètes de ces classes.
Je me résume souvent la programmation orientée objet à 4 aspects :
L’approche “objet” en CSS est sans doute celle qui paraît la plus naturelle aujourd’hui. Dans les méthodologies de design UI et dans des outils de design comme Sketch, l’emphase est souvent mise sur le fait de diviser une interface en différents éléments (“symbols”). Beaucoup de métaphores alimentant ces approches sont des analogies avec des objets concrets (les Lego, les pièces d’une voiture dans le process industriel) ou moins concrets (les morphèmes d’un langage, les notes de musique, les atomes d’un organisme).
L’approche en composant dans le process industriel
On peut ainsi traduire le style d’une interface sous forme de classes regroupant des propriétés et d’instances appliquant concrètement ces classes. Par exemple pour une classe “card” et des objets “événement” et “article” on pourrait définir :
Le CSS semble by design proposer la même chose, avec un nom de classe qui encapsule les propriétés qui la définissent. Des préprocesseurs comme Sass favorisent d’ailleurs encore plus ce paradigme en permettant de bien faire la distinction entre une définition (la classe, au sens OOP du terme) et son utilisation (l’objet), là où justement le CSS amalgame ces deux concepts.
Nicole Sullivan a proposé il y a 10 ans maintenant d’appliquer la métaphore de l’OOP au CSS en proposant OOCSS (pour Object-Oriented CSS), dont les deux principes fondateurs reprennent les aspects mentionnés plus tôt.
L’avantage du CSS objet est ainsi que les objets sont naturellement des métaphores concrètes, ce qui facilite leur utilisation et la communication autour d’eux. Ils favorisent la décomposition d’une interface en différentes parties isolées, ce qui est exactement ce que l’on cherche à faire dans la méthodologie design system. Le CSS objet propose donc by design une modularité organisée et offre une sémantique lisible et compréhensible. En revanche, son inconvénient est qu’en divisant une interface en objets, il ne fait que déplacer le problème de flexibilité de l’interface vers ses objets : comment bien appréhender un objet en sachant qu’on ne pourra pas le modifier par la suite ? Comment savoir ce qu’un objet définit et quelles propriétés lui sont essentielles ? Que fait-on de ces propriétés (de positionnement notamment) qui “n’appartiennent” pas à la définition d’un objet ou de ces propriétés liés à un contexte particulier ?
La programmation fonctionnelle (FP) est un autre paradigme de programmation qui se base sur la déclaration d’un certain nombre de règles ou d’expressions qui décrivent le résultat que l’on attend. On déconstruit ainsi un programme, non plus en objets, mais en différentes expressions et fonctions qui décrivent tout ou partie du résultat. Une application peut ainsi être vue comme le résultat produit par l’évaluation mathématique de ces différentes expressions.
Comme pour OOP, je retiens surtout 3 concepts importants de la programmation fonctionnelle :
La plupart des frameworks CSS semblent proposer aujourd’hui des classes “utilitaires” qui permettent une approche plus déclarative du CSS. On peut ainsi exprimer le style d’une interface à l’aide de ces utilitaires, qui sont comme des appels de fonction retournant toujours les mêmes références, c’est-à-dire sous forme de déclarations immuables et transparentes référentiellement :
Plusieurs méthodes, souvent réunies sous la bannière de l’”Atomic CSS”, se fondent uniquement sur cette approche et se trouvent finalement assez proches des principes de la programmation fonctionnelle. C’est le cas par exemple du “utility-first CSS” proposé par l’équipe de Yahoo. Un exemple parmi d’autres, la “mega-bottombar” dans le code source de yahoo.com donne ceci :
En les reprenant point par point, on peut ainsi retrouver les différents aspects mentionnés auparavant de la programmation fonctionnelle :
Parce qu’il est déclaratif et non impératif, le CSS fonctionnel permet de contrôler le style là où le contenu est défini et donc d’ajuster les composants de manière contextuelle directement lors de leur utilisation. Il offre beaucoup de flexibilité là où le CSS objet demanderait d’étendre un objet ou de créer une nouvelle instance pour chaque contexte. Le CSS fonctionnel propose une approche flexible dans laquelle chaque composant peut “consommer” le style directement depuis le HTML comme une sorte d’API, facilitant grandement les phases de mise en page mais aussi de recettage qui demandent souvent à ce que les composants ne répondent pas uniquement à une charte graphique mais à des contextes techniques de positionnement, de breakpoint ou encore d’alignement. Surtout, elle apporte plus de sécurité : il paraît en effet plus sûr d’ajouter ou de retirer une classe sur un composant HTML spécifique que d’ajouter ou de retirer une propriété d’une classe qui s’applique à plusieurs éléments.
En apportant plus de lisibilité en déclarant directement sur le composant les références qu’il consomme, le CSS fonctionnel troque néanmoins de la sémantique contre plus de verbosité sur le HTML. Par ailleurs, si le CSS peut sembler naturellement orienté objet, il n’est en rien naturellement immuable ou transparent référentiellement : un nommage de classe n’oblige à rien, en tout cas n’interdit pas l’ajout d’effets de bord (par exemple une classe “bold” qui apporterait également une propriété de couleur) et, encore plus, la nature même de la cascade CSS permet la réassignation d’une classe et s’interdit donc une immuabilité “technique” par défaut (Harry Roberts propose d’ailleurs des ébauches de solutions techniques pour garantir de l’immuabilité dans le CSS). Cette dernière limitation technique n’est en revanche pas vraiment un inconvénient : le problème n’est pas réellement technique mais “humain”, c’est-à-dire de promouvoir dans des guidelines et dans l’architecture CSS cette approche fonctionnelle.
Le problème du “zombie styleguide” n’est globalement qu’une manière d’illustrer le problème plus général de la collaboration et de la maintenance d’un projet web et, comme on le précisait plus tôt, d’inscrire l’approche en design system dans un temps plus long, celui des process d’une équipe, celui dans lequel les méthodologies accompagnent le projet de sa conception à son amélioration bien après la livraison. Dans ce temps long, il semble nécessaire d’essayer d’identifier et de distinguer le plus tôt possible ce qui est voué à perdurer de ce qui a plutôt vocation au changement.
Nous avons toujours essayé, avec les développeurs et designers avec lesquels j’ai pu travailler, de traduire dans le code cette distinction essentielle, et nos échecs ont souvent été liés au fait de ne pas anticiper le changement là où il aurait pu être anticipé. À ce titre, la programmation orientée objet et la programmation fonctionnelle apportent avec leurs concepts une distinction qui, si elle peut être contestable et discutée, nous a en tout cas permis d’y voir plus clair dans notre manière de concevoir et construire notre design system. Surtout, en mettant des concepts (instances, pureté, immuabilité, …) sur des approches, elle aide à construire des guidelines et nous rappelle que l’on ne code pas uniquement pour la machine mais aussi pour les collègues développeurs qui liront le code et pour le développeur que l’on sera demain.
Le CSS objet nous permet alors de définir les briques sémantiques, lisibles et pérennes sur lesquelles se fondent tout le système, quand le CSS fonctionnel nous apporte des outils flexibles, compréhensibles et composables pour construire tout ce que l’on souhaite à partir de ces briques.
À l’heure actuelle, un de nos design systems pourrait ainsi ressembler schématiquement à ca :
Si l’on met de côté les bases (styles de base, reset, …) et les dépendances (principalement les design tokens, le design system tourne ainsi autour de trois “artefacts”.
Les composants bénéficient du CSS objet : ils contiennent une définition, des états et des relations (avec d’autres objets), ils sont ouvert à l’extension (les classes modifiers de BEM) ; ils sont agnostiques et abstraits, c’est-à-dire définissant uniquement une forme et indépendants de tout contenu ; ils sont réutilisables et disposent d’un nommage sémantique lisible.
Exemple d’une structure d’objet
Les utilitaires bénéficient du CSS fonctionnel : ils apportent aux objets des références abstraites qui ne font pas sens à être définies comme des propriétés d’un objet ou comme des entités indépendantes. Un exemple de cette approche est le système d’espacement, dont la flexibilité permet de travailler sereinement sur un design et dont l’immuabilité permet d’être “cache friendly”.
Aperçu simplifié du système d’espacement. Chaque référence est préfixée et définit une propriété (margin ou padding), une direction (top, right, …), une taille issue des design tokens (-s, -m, …) et éventuellement un breakpoint d’application (@main signifiant ici “à partir du principal breakpoint défini dans les design tokens”).
Enfin, le système de grille assez classique bénéficie de manière hybride à la fois du CSS objet (définissant des lignes, des colonnes et des types de grilles) et du CSS fonctionnel (apportant des références d’alignement par exemple).
Ce système repose donc, pour fonctionner dans le temps long, sur des guidelines fondées sur cette flexibilité et cette sémantique qu’apportent les deux approches CSS héritées des paradigmes de programmation. Mais ces langages de programmation n’ont-ils jamais eu à affronter la zombification de leurs outils ? Je me l’imagine oui. Et je me réconforte en me disant que nous ne sommes pas seuls, que nous pouvons apprendre les uns des autres, que nous pouvons collaborer ensemble pour la maintenabilité, c’est-à-dire la préservation de notre styleguide bien aimé, si souvent la victime de cette zombification qui dévore nos esprits, nos joies et notre temps.
Voici les slides utilisées pour le talk initial :