Les Grands Principes

Cet article est le troisième de la série « les Grand·e·s ». C'est plus un inventaire à la Prévert qu'une présentation complète de chaque principe. Ici encore, la liste n'est pas exhaustive. Je me contente de présenter ce qui a fonctionné pour moi.

J'ai choisi de diviser ces principes en quatre catégories :

  1. Conception
  2. Programmation
  3. Architecture
  4. Documentation

Les sujets les plus intéressants feront (peut-être) l'objet d'articles ultérieurs.

Attention, les sections sont sont dans l'ordre alphabétique. Pensez à utiliser la table des matières ci-dessus.

Behaviour Driven Design

La page Wikipédia du BDD le décrit comme :

La programmation pilotée par le comportement (en anglais behaviour-driven development ou BDD) est une méthode de programmation agile qui encourage […] les équipes à utiliser la conversation et les exemples concrets pour formaliser une compréhension commune de la façon dont l'application doit se comporter.

Techniquement, il y a deux types de BDD : le story BDD et le spec BDD.

Le story BDD consiste à écrire des spécifications en langage courant pour simplifier au maximum la communication avec les expert·e·s métier. Pour ce faire, on utilise des outils comme Behave (Python) ou Behat (PHP) qui sont des déclinaisons de Cucumber.
La syntaxe consacrée est GherkinG. On y associe souvent des outils pour tester les interfaces web comme Selenium. Mais c'est, à priori, une mauvais pratique !

Le spec BDD est plus technique et vise principalement les autres programm·euse·eur·s du projet. Le but est d'écrire des spécifications qui remplacent les tests unitaires. Elles décrivent le comportement des composants et produisent une documentation qui facilite la communication autour du code.

J'ai fait quelque tentatives de story BDD, principalement pour tester des interfaces web. Cela n'a pas été un grand succès, car le projet ne s'y prêtait pas. Mais j'espère pouvoir le mettre en pratique à l'avenir.

Par contre, j'utilise quotidiennement le spec BDD. Le logiciel que j'utilise pour écrire mes spécifications est Mamba. J'en suis très satisfait et j'aurai l'occasion d'y revenir plus longuement dans un article dédié.

Clean Architecture

La Clean Architecture a été théorisée en 2012 dans un article d'Uncle Bob. Il s'est inspiré d'idées provenant de l'Hexagonal Architecture (2005) et de l'Onion Architecture (2008).

L'article d'origine est très (trop) court, mais il a donné naissance, 5 ans plus tard, au livre Clean Architecture. D'autres auteurs ont aussi contribué à faire comprendre les concepts derrière cette architecture. Parmi eux, Plainionist dans sa série d'articles How to implement the Clean Architecture? et The Digital Cat Books dans le livre Clean Architectures in Python.

Les concepts qui sous-tendent cette architecture sont assez simples :

  • séparer le code en « couches » avec au centre le métier, juste au dessus les cas d'usage puis les interfaces et l'infrastructure ; et
  • ne jamais laisser une couche interne faire appel aux couches au dessus d'elle, mais toujours aller de l'extérieur (les détails d'implémentation) vers l'intérieur (les concepts métier).

Cela a l'air simple dit comme ça, mais il n'a fallu lire pas mal (beaucoup) d'articles afin de pouvoir implémenter une version qui soit fidèle à l'esprit Clean Architecture et qui me convienne. En malgré de nombreuses soirées de frustration, je pense avoir trouvé dans cette architecture une manière d'organiser le code d'une application qui me convienne vraiment.

Bien que ce ne soit pas le premier projet que j'organise de cette manière, Listsém est un bon exemple. Les différentes couches sont parfaitement visibles dans l'organisation des modules et, je l'espère, en parcourant les dossiers et les fichiers du projet, on comprend tout de suite de quoi celui-ci parle.

Code smells

Les code smells sont ces petites choses qui, dans un programme, nous font dire que quelque chose ne va pas. Cela peut-être :

  • deux bouts de code qui se ressemblent étrangement ;
  • des fonctions ou des méthodes très (trop) longues ;
  • des fonctions ou des méthodes qui ont beaucoup (trop) d'arguments ;
  • des commentaires qui expliquent ce que le code incompréhensible qui suit est censé faire ;
  • etc.

À chaque smell correspond un design pattern qui peut améliorer les choses.

Le site Refactoring Guru est une ressource précieuse qui permet d'identifier les problèmes et de trouver la solution la plus adaptée.

Command Query Responsibility Segregation

La « ségrégation des responsabilités entre commande et requête » CQRS consiste à diviser le modèle de l'application (ou d'un de ses sous-domaines, voir DDD) en deux : un modèle pour les commandes, qui peut changer l'état de l'application (write model) ; et un modèle pour les requêtes, qui peut consulter l'état de l'application.

Ce principe est basé sur le constat qu'un seul modèle ne peut pas être optimisé pour plusieurs utilisations. Ceci est d'autant plus vrai pour le modèle de requête où plusieurs manières de regarder la même donnée peuvent être envisagées (une vue d'ensemble, une vue de détail, une vue agrégée).

La principale difficulté en CQRS est de synchroniser le write model avec le (ou les) read model. La manière la plus naturelle de le faire est l'event sourcing.

Diátixis

Diátixis est un cadre de travail pour la rédaction de documentation technique.

The Grand Unified Theory of Documentation

—David Laing

Cette méthode propose d'organiser la documentation en quatre catégories :

  • les tutoriels, orientés vers l'apprentissage ;
  • les guides pratiques, orientés vers les tâches à réaliser ;
  • les explications, orientées vers la compréhension ;
  • les références, orientées vers l'information.

J'ai commencé par utiliser cette méthodologie sur Listsém, un projet développé pour l'EHESS. Même si toutes les catégories ne sont pas (encore) utilisées, j'ai trouvé que travailler dans ce cadre permettait de clarifier le rôle et la place de chaque bout de documentation.

Domain Driven Design

La « conception pilotée par le domaine » (Domain Driven Design ou DDD) replace le domaine (ou « métier ») au centre du développement. Elle a été théorisée par Eric Evans dans le blue book puis rendue accessible à un plus large public par Vaugh Vernon dans le red book.

Le DDD introduit de nombreuses pratiques divisées en deux grandes catégories : stratégiques et tactiques.

Les pratiques « stratégiques » :

  • domain, une sphère d'activité ou de connaissance ;
  • subdomain (core, supporting ou generic), une division plus concrète du domaine ;
  • ubiquitous language, l'ensemble des termes utiliser dans le domaine ;
  • bounded context, une partie du domaine dans laquelle il n'y pas de conflit de vocabulaire ;
  • context map, la manière dont les sous-domaines interagissent.

Les pratiques « tactiques » :

  • value objects, un objet dont seule la valeur (pas l'identité) est importante ;
  • entities, un objet dont l'identité (pas les valeurs) est importante ;
  • aggregates, un ensemble d'entités et d'objets-valeurs dont l'état doit être cohérent ;
  • domain events, un objet qui décrit un évènement s'étant produit dans le domaine ;
  • etc.

Ces pratiques ont été résumées par Petter Holmström dans une série d'articles (partie 1 et partie 2)

Event Sourcing

L'Event Sourcing est le fait de « capturer tous les changements de l'état d'une application en tant qu'une séquence d'évènements » … et de reconstituer cet état à partir de ces évènements.

L'exemple le plus courant pour illustrer le concept est le livre de compte : chaque débit/crédit est enregistré et, en rejouant toutes les opérations, on peut calculer le solde du compte. Il n'est donc pas nécessaire de conserver le solde du compte (l'état), car il peut être recalculé à partir de l'historique des opérations.

L'event sourcing s'associe naturellement bien au CQRS. Les évènements sont générés par le write model et le read model est généré par des projecteurs qui « jouent » tous les évènements.

Attention le couple CQRS/ES augmente de manière drastique la complexité d'une application ! Mais le principal danger de la technique est qu'elle est hautement addictive ! Il faut donc résister à l'envie de la mettre en place pour toutes les applications et suivre les principes du DDD pour bien identifier les sous-domaines où l'appliquer.

Living Documentation

Le terme living documentation a été utilisé pour la première fois par Gojko Adzic dans son livre Specification by Example. Je l'ai personnellement découvert dans le livre Living Documentation de Cyrille Martraire .

L'idée est d'avoir une documentation qui évolue avec le code, afin que code et documentation soient toujours en phase.

La première approche consiste à écrire de la documentation exécutable qui pilote les tests. C'est alors l'outil d'intégration continue qui s'assurera que le code fait bien ce que la documentation dit. En Python, on peut utiliser l'outil Behave, mais n'importe quel outil comprenant la syntaxe Gherkin fera aussi bien l'affaire. Cette technique se prête parfaitement à l'écriture des spécifications des cas d'usage de la Clean Architecture, car les cas d'usage sont discutés et écrits en étroite collaboration avec les expert·e·s du métier.

La seconde approche est de générer la documentation à partir du code. Il est, par exemple, possible de générer un glossaire des termes métier à partir des noms de classes/fonctions et de leurs docstrings. Ceci n'est vraiment réalisable que si l'on suit l'approche DDD consistant à utiliser dans son code les termes métier. Si les classes sont nommées d'après les concepts du métier et si les noms des fonctions décrivent des activités métier alors le création d'un glossaire est très facile. Il est aussi plus facile de penser à maintenir à jour les docstrings que de la documentation technique, car elles se trouvent localisées juste à côté du code concerné.

Refactoring

Le refactoring, ou réusinage, est l'art d'améliorer un programme sans en altérer le comportement. Le réusinage peut porter sur l'amélioration de la conception, l'amélioration des performances, la préparation du code à l'arrivée de nouvelles fonctionnalités…

Il y a de nombreuses ressources sur le réusinage. Je conseille en particulier tout ce qui est fait/écrit/dit par Martin Fowler.

Le site Refactoring Guru est aussi une ressource précieuse.

SOLID

L'article de Wikipedia est un bon point d'entrée et présente les cinq principes de SOLID :

  • Responsabilité unique (Single responsibility principle) une classe, une fonction ou une méthode doit avoir une et une seule responsabilité
  • Ouvert/fermé (Open/closed principle) une entité applicative (classe, fonction, module ...) doit être fermée à la modification directe mais ouverte à l'extension
  • Substitution de Liskov (Liskov substitution principle) une instance de type T doit pouvoir être remplacée par une instance de type G, tel que G sous-type de T, sans que cela ne modifie la cohérence du programme
  • Ségrégation des interfaces (Interface segregation principle) préférer plusieurs interfaces spécifiques pour chaque client plutôt qu'une seule interface générale
  • Inversion des dépendances (Dependency inversion principle) il faut dépendre des abstractions, pas des implémentations

Il existe de très nombreuses ressources pour approfondir ces principes. Je suis récemment retombé sur un article d'Alex so yes qui est loin d'être le plus mauvais sur le sujet et qui a pour mérite de prendre des exemples que l'on rencontre dans le développement web.

Test Driven Design

Le Test Driven Design est un processus de programmation qui consiste à écrire les tests avant le code.

Il y a 3 principaux avantages :

  1. on est sûr que le code est testé ;
  2. on est incité à écrire du code plus testable, donc mieux conçu ; et
  3. on a réfléchi au code qu'on voulait écrire avant de l'écrire ! Ce qui ne peut pas faire de mal !

Bien sûr, ce n'est pas aussi facile que cela en a l'air, mais cela nécessite plus de rigueur et de volonté que de compétences techniques. Donc tout le monde peut le faire !

Attention tout de même, si vous ne voulez pas qu'écrire vos tests devienne un vrai calvaire, il faudra faire attention à certains points :

  1. bien faire la différence entre tous les types de tests (unitaires, fonctionnels, intégration) ;
  2. bien concevoir son code ; et
  3. ne pas re-tester les comportements de vos frameworks (Flask, SQLAlchemy…) qui sont souvent déjà bien testés.

Si vous vous retrouvez avec 42 tests d'intégration où un client de test fait des requêtes HTTP sur les URL de votre service web pour tester les différentes différentes combinaisons de valeurs pouvant être soumises via un formulaire HTML, c'est sûrement que quelque chose s'est mal passé ! 😅

Twelve-factor app

Le « manifeste » du Twelve-factor app n'est pas, à proprement parler, un grand principe. Il mérite cependant sa place ici, car il regroupe un ensemble de petits principes qui permettent d'écrire de meilleurs applications web.

Les 12 principes sont les suivants :

  1. Une base de code dans un système de gestion de version, plusieurs déploiements.
  2. Déclarer explicitement et isoler les dépendances.
  3. Mettre la configuration dans l'environnement.
  4. Traiter les services de support comme des ressources attachées.
  5. Séparer strictement les étapes de construction et d'exécution.
  6. Exécuter l'application en tant que un ou plusieurs services sans état.
  7. Exporter les services en les faisant écouter sur un port.
  8. Passer à l'échelle avec le modèle « processus ».
  9. Maximiser la résistance par un démarrage rapide et arrêt gracieux.
  10. Garder les environnements de test, de pré-prod et de prod les plus similaires possible.
  11. Traiter les journaux d'activité comme des flux.
  12. Lancer les tâches d'administration/gestion comme des processus « dédiés ».

La traduction étant de moi, je vois encourage, si vous en avez la possibilité, à aller lire le texte original.