Apprendre l’architecture logicielle
(matklad.github.io)- La conception logicielle s’apprend plus profondément dans de vrais projets, quand on porte une responsabilité et que les problèmes deviennent les siens, que dans des cours magistraux
- La loi de Conway est l’idée que le logiciel reproduit la structure sociale de l’organisation, et que la différence entre code scientifique et code industriel peut aussi venir des systèmes d’incitation
- rust-analyzer permet aux contributeurs à fort rendement de se concentrer plus facilement grâce à des builds rapides, la prise en charge de stable, l’absence de dépendances C et des tests qui s’exécutent en quelques secondes
- rust-analyzer protège les fonctionnalités indépendantes avec
catch_unwindet a abaissé les critères pour les PR, tout en appliquant des exigences de qualité bien plus strictes à la colonne vertébrale centrale - Une structure expérimentale peut devenir une réalité de long terme, et rust-analyzer est lui aussi passé d’un prototype d’architecture LSP à l’entretien d’un compilateur supplémentaire
La conception logicielle s’apprend surtout sur le terrain
- La conception logicielle s’apprend mieux en prenant des responsabilités dans un vrai projet et en résolvant directement les problèmes qu’au travers d’un enseignement formel
- Plus que lorsque l’auteur avait le rôle d’« architecte » dans des cours universitaires de conception et des projets de cursus, c’est dans son deuxième vrai projet, IntelliJ Rust, que l’apprentissage a vraiment commencé, lorsque les problèmes de conception sont devenus son affaire
- Il y a eu quelques erreurs dans IntelliJ Rust, mais rien de fatal, et cela a permis d’apprendre énormément au passage
- Le génie logiciel a aussi un côté suffisamment simple pour qu’une personne curieuse puisse l’apprendre en réfléchissant aux principes et en lisant divers textes
Structures d’incitation et loi de Conway
- La loi de Conway est l’idée que le logiciel reproduit la structure sociale de l’organisation qui le crée
- La différence entre logiciel industriel et code scientifique peut provenir moins de la connaissance nécessaire pour construire du logiciel que des structures d’incitation qui poussent les gens à en produire
- Une situation comme « un doctorant qui doit publier un article en trois mois » peut être un facteur majeur dans la forme que prend le code scientifique
- Il existe globalement deux manières de réagir à une structure d’incitation
-
Concevoir ou faire évoluer les incitations d’un projet
- Il est rare d’avoir l’occasion de concevoir ou d’ajuster la structure d’incitation d’un projet, mais lorsque cela arrive, l’impact peut être considérable
- L’essentiel de TIGER_STYLE ne réside pas dans la liste de règles elle-même, mais dans le contexte social qui fait de ces règles de bons choix
-
S’adapter aux contraintes si on ne peut pas les changer
- Les structures d’incitation sont rarement données comme on le souhaiterait, et si elles ne peuvent pas être modifiées, il faut s’y adapter
- Même dans les projets logiciels industriels, on a rarement « le temps de bien faire », et il faut faire du mieux possible dans les contraintes imposées
Comment rust-analyzer a aligné sa structure sur ses participants
- rust-analyzer est un projet qui a à la fois de la profondeur et de l’ampleur
- Sur le plan de la profondeur, sa nature de compilateur lui permet d’attirer des contributeurs excellents et très investis
- Sur le plan de l’ampleur, un IDE classique comporte de nombreuses fonctionnalités spécialisées, ce qui convient bien à des personnes qui apprennent Rust ou à des contributeurs occasionnels du week-end souhaitant passer une heure ou deux à corriger une gêne qui les touche
- Si rust-analyzer a tenu à ne pas exiger une build de
rustc, à se compiler sur stable, à ne pas avoir de dépendances C et à faire tourner toute la suite de tests en quelques secondes, c’était pour attirer des contributeurs à fort rendement - L’idée était de peaufiner le système de build pour que les gens puissent se concentrer sur le borrow checker sans se soucier du reste
- Pour attirer des contributeurs du week-end, l’intérieur de rust-analyzer a été découpé en plusieurs fonctionnalités indépendantes, chacune protégée à l’exécution par
catch_unwind - Le seuil d’acceptation des PR de fonctionnalité a été abaissé à « le happy path fonctionne et il y a des tests », en considérant acceptable que ce code plante
- Mais deux conditions étaient nécessaires
- Les problèmes de qualité devaient rester isolés à une fonctionnalité individuelle et ne pas se propager ailleurs
- Les plantages à l’exécution ne devaient pas être visibles par l’utilisateur ; pour cela, les fonctionnalités de rust-analyzer devaient fonctionner sur des snapshots immuables et ne pas pouvoir corrompre les données
- À l’inverse, des critères de qualité beaucoup plus stricts étaient appliqués à la colonne vertébrale centrale qui soutient les fonctionnalités
Le risque qu’une structure expérimentale devienne une réalité durable
- Quand on s’adapte à une structure d’incitation au lieu de la corriger, il faut garder à l’esprit que l’avenir est incertain et qu’il peut, en général, se concrétiser de la manière la plus inconfortable possible
- La motivation initiale de rust-analyzer était d’éviter d’écrire un compilateur parallèle de plus au sein d’IntelliJ Rust, et de valider au moyen d’un prototype une meilleure architecture pour le LSP afin de réinjecter ensuite ces apprentissages dans
rustc - C’est pourquoi le code, y compris son noyau, était très expérimental
- Au final, cela a conduit à maintenir un compilateur supplémentaire
- De manière similaire, le projet uutils a commencé comme une destination principale pour les personnes apprenant Rust, avant de devenir l’implémentation de coreutils dans Ubuntu
Ressources et livres à consulter
- Il n’existe pas de livre unique contenant la bonne réponse, et la pratique semble être un élément indispensable
- Boundaries by Gary Bernhardt
- Une ressource aux conseils concrets solides, qui a déclenché une exploration à un niveau plus élevé
- How to Test
- L’importance des tests a été comprise immédiatement, mais il a fallu longtemps pour admettre qu’une grande partie des conseils sur les tests largement cités n’étaient pas très pratiques, puis conceptualiser ce qui fonctionne réellement
- Le guide ∅MQ et les écrits de Pieter Hintjens
- Ce sont les ressources qui ont fait découvrir une manière de penser à la Conway’s Law
- L’architecture de développement des fonctionnalités de rust-analyzer est une application de l’optimistic merging
- Reflections on a decade of coding by Jamii
- Le texte traite de sujets très méta et est suffisamment excellent pour être placé en première position dans cette liste de liens
- Le blog de Ted Kaminski
- Sous la forme de notes pour un livre qui n’existe pas, c’est ce qui se rapproche le plus d’une théorie cohérente du développement logiciel
- Software Engineering at Google et The Philosophy of Software Design d’Ousterhout
- Ce sont de bons livres souvent recommandés, et en particulier Software Engineering at Google a aidé à organiser des catégories importantes autour des unit tests et integration tests
- Mais personnellement, ce n’étaient pas des livres révolutionnaires
1 commentaires
Commentaires Hacker News
En résumé façon aide-mémoire, une bonne conception doit reposer sur une idée unique qui imprègne l’ensemble et aller dans le sens de la surprise minimale
Si le système le permet, les gens finiront par l’utiliser ainsi, et une solution qui commence par « si seulement tout le monde faisait simplement X » n’est pas une solution
Il faut séparer la partie qui transforme les données de celle qui les utilise, les modèles de données vivent plus longtemps que le code, et le couplage est à la racine de nombreux problèmes
La gestion de versions est inévitable, l’état doit être rendu explicite, et chaque information doit avoir une source unique de vérité
Il faut passer plus de temps à nommer les choses, si c’est difficile à tester c’est que la conception est mauvaise, et les décisions non documentées finissent par se regretter
La communication a un coût, donc il faut la justifier avant de le payer, et le travail de l’ingénieur consiste à résoudre des problèmes avec des heuristiques dans un contexte d’information incomplète
Le texte lui-même n’était pas très cohérent du point de vue de l’architecture logicielle
La vue architecturale 4+1 reste, même sans UML, un bon cadre conceptuel pour penser la vue d’ensemble, et la série Pattern-Oriented Software Architecture organise bien les différentes architectures auxquelles les gens aboutissent
Grady Booch travaillait aussi à un handbook de software architecture, mais cela semble aujourd’hui presque à l’arrêt, et à l’époque la mailing list documentait les grandes architectures de systèmes d’entreprises ou de gros projets open source
En creusant ce type de ressources, on voit que les architectures sont construites selon des priorités différentes — échelle, sécurité, performance, interopérabilité, fail-safe — et que chaque objectif implique des compromis réalistes
Si c’est meilleur selon ce critère, alors même ce qui ressemble à une mauvaise conception peut en réalité être une bonne conception
Les interfaces doivent être faciles à utiliser correctement et difficiles à utiliser de travers, et il faut réfléchir à la manière dont quelqu’un qui ne connaît pas le projet va s’en servir
Le bon code doit être facile à écrire, le code suspect doit se remarquer, et les bugs doivent être déplacés le plus possible vers la gauche
Il vaut mieux éliminer une classe de bugs que corriger un seul bug, et comme une interface est plus difficile à changer qu’une implémentation, une implémentation laide est acceptable si l’interface est la bonne
Les commentaires et la documentation doivent expliquer pourquoi le code a cette forme, et même s’il semble exister une méthode plus simple, si certaines contraintes l’empêchent il faut le noter
Du point de vue des données, il ne faut pas se répéter : si le même fait est stocké à plusieurs endroits, cela finira par diverger et produire des bugs
Sortir du chemin balisé a un coût ; cela peut en valoir la peine, mais il ne faut pas sous-estimer ce coût
Une technologie ennuyeuse qui semble moins bonne est souvent une meilleure technologie, et il faut évaluer la valeur attendue non pas avec « est-ce que ça vaut le coup ? », mais avec « est-ce que ça vaut le coup par rapport aux autres choses qu’on pourrait faire ? »
Même si l’on se croit plus intelligent que les autres, certains problèmes ne se résolvent pas par la seule intelligence ; certains ne peuvent être découverts qu’une fois qu’ils explosent réellement, donc il faut apprendre des erreurs des autres
Les frictions sont des tueurs silencieux
Planifier est bien, mais parfois il faut aussi essayer directement, et tout coûte de l’argent
Concevoir sans tenir compte des coûts force plus tard à faire des choix difficiles
Même pour un projet solo, les contraintes du moteur servent de guide du type « ajoute cette nouvelle fonctionnalité bizarre découverte pendant les tests de cette façon »
Plus besoin de trimballer à chaque fois une énorme documentation du genre « pour créer un nouvel effet sonore exécuté plusieurs fois dans une transition d’état donnée, fais comme ceci »
Le concepteur système doit comprendre le secteur ; il n’a pas besoin d’adopter entièrement sa terminologie ni ses habitudes de modélisation, mais il doit comprendre pourquoi et selon quel point de vue les jeux de données sont regardés
Nous avons parfois simplifié volontairement la complexité du marché de la santé pour supprimer des surdéfinitions inutiles et proposer un modèle plus intégré, mais nous n’avons pu faire ces changements avec assurance que parce que nous comprenions suffisamment le domaine du problème
En particulier, les noms ne meurent presque jamais
Il arrive parfois qu’ils meurent, mais changer un nom demande un effort extrême ; il vaut donc vraiment la peine de consacrer beaucoup de temps à faire relire les propositions de nommage par des experts métier afin de vérifier qu’il ne manque rien
On peut parfois imposer certaines notions, mais les fonctions business comme les ventes ou le marketing continueront de réclamer la terminologie du secteur et pousseront à aligner le modèle sur la vision actuelle de l’industrie
Si l’on décide de rompre avec ce courant, cette rupture doit être intentionnelle et avoir un objectif clair
En logiciel, la propriété à mettre le plus en avant est la maintenabilité
Le coût de construction compte, bien sûr, mais le coût d’exploitation a un impact bien plus grand, car il inclut non seulement l’infrastructure, mais aussi l’accumulation des demandes de fonctionnalités, le refactoring du code et le maintien des versions de logiciels tiers
Les listes de recommandations sont souvent bonnes, comme A Philosophy of Software Design d’Ousterhout, mais dans l’ensemble on est plus proche de la théorie générale du développement logiciel que de la software architecture elle-même
Pour l’architecture, je recommanderais des classiques comme Software Architecture: Perspectives on an Emerging Discipline de Shaw/Garlan, ainsi que les écrits de Mary Shaw
Des articles plus récents qui explorent pourquoi le domaine de la software architecture n’a pas évolué comme prévu sont aussi intéressants, par exemple Myths and Mythconceptions: What Does It Mean to Be a Programming Language, Anyhow? ou Revisiting Abstractions for Software Architecture and Tools to Support Them
D’un point de vue pratique, il est utile de regarder pourquoi les Unix pipes and filters et REST ont réussi, et où et pourquoi ils échouent ; l’architecture hexagonale est aussi essentielle
Personnellement, j’aime aussi Beyond Procedure Calls as Component Glue: Connectors Deserve Metaclass Status, qui relie software architecture et protocoles méta-objets pour les envisager comme une nouvelle base pour les langages et la programmation
C’est une réponse au texte de Mary Shaw Procedure Calls Are the Assembly Language of Software Interconnection: Connectors Deserve First-Class Status, qui pose la question suivante : si les appels de procédure sont de l’assembleur, à quoi ressemblerait alors un langage de haut niveau ?
Peut-être que la software architecture a encore un avenir plus lumineux et plus pratique
Il suffit de quelques structures de données et types, avec un petit ensemble de fonctions de base, puis on les compose
Une chose que j’aime dans Lisp, c’est que les types plus complexes, en particulier ceux qui viennent d’une FFI, restent toujours opaques
J’aimerais voir une implémentation CLOS où définir une structure dans un langage proche du C donne automatiquement un ensemble standard de fonctions
La meilleure manière d’apprendre l’architecture n’est pas de construire un projet assez grand, mais d’en faire la maintenance
Et il faut le faire sur au moins deux ou trois projets
Si le projet est trop petit, n’importe quelle architecture fonctionne, et pour juger si un projet est « grand », il vaut mieux regarder le nombre de personnes qui ont travaillé dessus, ou mieux encore le nombre d’équipes, que le nombre de lignes de code
Il faut au moins deux projets différents pour pouvoir comparer, et j’ai vu des gens rester enfermés des décennies sur un seul projet sans connaître les approches modernes de résolution de problèmes
Mais en pratique, c’est souvent la personne qui a créé le projet qui est promue architecte, rarement celle qui en a assuré la maintenance
Chez Google c’est encore plus visible : il faut surtout lancer de nouveaux produits pour être promu, la maintenance n’est pas valorisée, et il est préférable de partir juste après un lancement si possible
Paradoxalement, la personne la mieux placée pour devenir architecte est peut-être le prestataire externe qu’on affecte à la maintenance des vieux projets internes dont personne ne veut
Ces gens doivent préserver l’architecture et passent d’un projet à l’autre, donc ils peuvent comparer
En revanche, s’ils facturent à l’heure, il existe aussi un risque qu’ils complexifient excessivement l’architecture pour pouvoir facturer davantage de temps
Dans ce contexte, je recommande vraiment Architecture of Open Source Applications
C’est une série de livres dont chaque chapitre est écrit par le mainteneur du projet concerné, ce qui permet d’apprendre l’architecture par l’exemple
On y découvre non seulement ce qu’est l’architecture, mais aussi les contraintes qui l’ont façonnée, souvent l’histoire du projet et l’évolution de sa vision
Comme souvent dans les ouvrages collectifs, tous les chapitres ne se valent pas et tous ne sont pas aussi intéressants, et ils ont tous vieilli, mais cela reste une lecture qui vaut le détour
http://aosabook.org/
J’aimerais passer plus de temps à acquérir un meilleur modèle mental du projet sur lequel je travaille, mais selon les projets, quand je commence à détester le langage de programmation ou certains choix d’architecture, ou des parties devenues si complexes qu’elles semblent ne plus mériter le temps qu’on y passe, ma motivation chute fortement
Cela dépend du projet, mais travailler comme « full-stack developer » donne parfois l’impression d’enlever tout le plaisir de la programmation
Je passe déjà 40 heures par semaine à regarder le projet le plus ennuyeux qu’on puisse imaginer
Il n’existe pas un seul projet sans défaut
Et si le langage de programmation est un problème à ce point, mieux vaut changer de bateau
Je pense que tout le monde devrait savoir travailler avec plusieurs langages, mais au final le choix t’appartient
Il faut vérifier si les décisions auxquelles on consacre le plus de temps sont vraiment les plus importantes
Des structures de données bien conçues ont bien plus d’impact sur la performance et la maintenabilité qu’un framework, un langage ou une plateforme
Personnellement, je travaille chaque jour avec mon TDAH, donc je dois continuer à pousser pour que les choses avancent, ce qui signifie sélectionner les décisions sans importance, les clôturer, puis me concentrer sur l’espace de problèmes restant qui exige une réflexion attentive
Des expressions comme « clean code » ou « beautiful code » n’aident pas beaucoup les juniors à apprendre les bonnes pratiques de software architecture
Quand un junior demande pourquoi on utilise un ORM et qu’un senior répond « parce que c’est plus propre », il ne lui reste qu’un gros point d’interrogation
Mieux vaut définir une liste claire d’objectifs
Maintenabilité, performance et scalabilité, efficacité, résilience, observabilité, testabilité et couverture de test, sécurité, facilité de lecture pour un nouveau développeur : ce sont des critères qu’il faut mettre en balance
Plus on ajoute de critères, plus il devient facile de faire de meilleurs choix quand on hésite, et cela a aussi du sens pour les personnes hors de l’équipe de développement, ce qui permet de s’accorder avec le client sur ce pour quoi il paie
Un projet passe l’essentiel de sa vie en mode maintenance, ce qui est une bonne chose car cela signifie que le projet a réussi ; on peut donc aussi définir la maintenabilité
Une bonne base consiste à pouvoir ajouter de nouvelles fonctionnalités sans casser l’architecture, voire sans casser la signature d’une seule méthode
Il faut être très prudent avec l’abstraction
La formule « les abstractions cachent souvent à quel point ce que l’on veut est simple » est juste, et les ORM en sont un bon exemple
Dans la plupart des cas, les données doivent être traitées comme des citoyens de première classe, et cela améliore aussi la collaboration avec les DBA
Il est aussi difficile d’éviter l’optimisation prématurée tout en réfléchissant à ce qui se passe en dehors du happy path, mais cela aide à éviter de foncer tête baissée dans l’implémentation d’une bonne idée sans en examiner les avantages et les inconvénients
Imaginer que la personne qui lira mon code passe une très mauvaise journée m’aide à le rendre plus agréable à lire
Cela concerne les commentaires disséminés, les variables locales qu’on pourrait omettre mais qu’on garde, les noms de variables, etc.
Les frameworks doivent être choisis avec soin : bons serviteurs, mauvais maîtres
La formule de l’article « ne sois pas un frameworker, sois un ingénieur » est juste
Je n’ai rien contre les opinions fortes en soi, et pour une bibliothèque ou un outil cela peut même être une excellente qualité
Cela signifie souvent que cette bibliothèque ou cet outil embarque une vraie expertise métier sur son domaine
Mais dans un framework, des opinions fortes dérivent facilement vers le syndrome « quand on a un marteau, tout ressemble à un clou »
Ce n’est pas toujours le cas, ni toujours un problème, mais lorsqu’on applique largement une orthodoxie issue d’un domaine précis, la justification qui valait dans ce domaine risque de se briser dans un contexte plus large
C’est pourquoi je préfère les frameworks modulaires où l’on peut brancher un autre ORM ou une autre couche d’intégration de persistance, remplacer le routeur, le validateur ou d’autres composants mal adaptés au problème
C’est particulièrement précieux quand l’écosystème du framework propose plusieurs alternatives relevant de paradigmes différents
On peut alors choisir un outil prêt à l’emploi qui convient presque, dont les défauts sont clairs, et qu’on peut compléter si nécessaire
Pour la maintenabilité, il est très important de lutter contre le syndrome NIH
C’est un risque permanent qui absorbe les ressources à une vitesse étonnante
En même temps, certains composants peuvent offrir de vrais bénéfices quand on les ajuste soi-même ou qu’on les maîtrise entièrement ; la difficulté consiste à déterminer lesquels relèvent de quel camp
Je suis profondément d’accord avec le fait de placer la maintenabilité tout en haut de la liste
Du point de vue de l’ingénierie système, la software architecture ressemble à la conception de la plomberie
C’est très important, mais on ne vit pas dans la plomberie, on vit dans la maison qui la contient
Si la plomberie ne tient pas compte du reste de la maison, la réparer peut coûter extrêmement cher
Bon article
« Apprendre la software architecture », c’est comprendre qu’il n’existe pas une seule bonne réponse
C’est à la fois un art et une science
Côté lecture, je recommande Simplify IT - The art and science towards simpler IT solution
https://nocomplexity.com/documents/reports/SimplifyIT.pdf
Les talks de Gary Bernhardt sont vraiment à part
Ils contiennent beaucoup d’idées qui mènent vers d’autres pistes intéressantes