Développement de logiciels à long terme, par Bert Hubert
Récemment, le Conseil électoral néerlandais (où je suis également conseiller à temps très partiel) m'a invité à faire une présentation sur son logiciel open source de tabulation des votes Abacus.
De nombreux logiciels sont désormais fournis en tant que service et sont généralement déployés en continu (CD, continuous deployment - déployement continu), entourés de suffisamment de tests automatisés (CI, continuous integration - intégration continue) pour que nous puissions être raisonnablement sûrs qu'une nouvelle révision est susceptible de fonctionner au moins dans une certaine mesure.
En revanche, il existe encore un vaste monde où les gens n'apprécient pas ces changements continus combinés à une probabilité assez élevée de fonctionnement. Les logiciels qui contrôlent les centrales (nucléaires), les élections, les stimulateurs cardiaques, les avions, les ponts, les machines lourdes. En général, des choses qui peuvent vous tuer si elles font la mauvaise chose, ou peut-être simplement si elles ne fonctionnent pas.
Ces domaines apprécient que votre logiciel reste en place pendant des décennies, avec des changements bien décrits et annoncés à l'avance. Des notes de version qui vont au-delà de « diverses corrections de bogues et améliorations ». Des logiciels qui peuvent ne pas subir de changements pendant quelques années, après quoi une nouvelle version majeure est planifiée, et des choses doivent encore être construites à partir des sources.
Il s'agit bien sûr d'un discours farfelu, mais certains d'entre nous l'apprécient !
Développement de logiciels à long terme : se préoccuper (suffisamment) de l'avenir
La présentation comprenait une partie générique sur le développement logiciel à long terme, et une critique spécifique de leur développement logiciel jusqu'à présent.
Pour la partie générique, j'ai demandé à mes amis de Mastodon d'intervenir :
Mastodon a répondu à l'appel, et la majeure partie de ce billet a été rédigée à partir des commentaires d'aimables Mastodoniens.
Rien de ce qui suit n'est particulièrement révolutionnaire. Cependant, la force de certaines recommandations est remarquable et pourrait vous faire réfléchir.
Dépendances
À un niveau extrêmement élevé, nous pouvons considérer les logiciels de la manière suivante. Il existe un monde extérieur que nous ne contrôlons pas, par exemple les logiciels clients (comme les navigateurs). Il s'agit de réalités avec lesquelles nous devons composer. Au sein de notre propre logiciel, nous avons certains choix de base très fondamentaux, comme les langages de programmation à utiliser. Changer de langage de programmation nécessite en effet une réécriture de l'ensemble de la pile, on ne le fait donc pas à la légère.
Un peu plus haut, nous trouvons souvent des cadres qui s'associent étroitement à notre base de code. Pensez aux frameworks web/JavaScript, aux mappeurs relationnels d'objets (ORM), à des choses comme Spring Framework, Ruby on Rails, React, Rust Axum, etc. Bien que vous puissiez remplacer une telle dépendance, il s'agit d'un travail (très) lourd. Elle touche une grande partie de votre code.
Ensuite, presque tout dépendra d'une sorte de base de données. Il existe de nombreuses bases de données, mais la plupart d'entre elles fonctionnent plus ou moins de la même manière. Si vous avez construit sur MySQL et que vous vous ravisez et passez à autre chose, vous aurez un vrai travail à faire, mais c'est surtout dans les détails et les spécificités.
Enfin, les « aides » sont pour la plupart interchangeables. Il s'agit de bibliothèques qui remplissent une fonction pour vous ici et là, mais qui sont surtout des solutions « ponctuelles ». Vous pourriez arracher et remplacer une telle dépendance sans même avoir à en informer vos utilisateurs.
Il ressort clairement de ce qui précède que si vous pensez à long terme, les dépendances sont extrêmement importantes. Vous en dépendez. Cependant, comme nous ne pouvons pas consacrer une attention maximale à tout, nous devrions penser beaucoup plus au bas de la pyramide qu'à son sommet. Cette réflexion est d'autant plus nécessaire qu'à l'échelle de plusieurs années, vos dépendances et le monde extérieur ne restent pas inactifs :
L'un des principaux retours après mon post sur Mastodon a été de limiter sévèrement vos dépendances, parce qu'elles peuvent toutes, avec le temps :
- s'éloigner, entraînant des ajustements dans votre code ou, pire, des changements silencieux de comportement
- passer à de nouvelles versions majeures avec des changements sémantiques, nécessitant des réécritures de votre part
- être abandonnés ou simplement disparaître, ou commencer à se dégrader
- sont détournés par des acteurs (de l'État-nation) (pensez à npm, pypi, etc.)
- commencent à être monétisées par le nouveau propriétaire VC
- développent des exigences de dépendance conflictuelles qui leur sont propres
(J'ai personnellement été brûlé sur Python par le dernier point où l'une des dépendances nécessitait la version 3.14 ou moins du module tel et tel, et une autre dépendance nécessitait la version 3.15 ou plus).
Il ne s'agit pas d'un appel à se passer des dépendances, bien sûr ! Elles sont inévitables pour faire avancer les choses. Mais si l'on extrapole sur une période de 10 ans, nous devons porter un regard extrêmement critique sur le code sur lequel nous allons nous appuyer :
- La technologie semble-t-elle bonne si l'on regarde le code source ? Et le pouvez-vous ?
- Qui d'autre utilise cette dépendance ?
- Qui l'écrit ? Pourquoi ?
- Quels sont leurs objectifs ?
- Sont-ils financés ? Par qui ?
- La maintenance est-elle assurée ? Des versions de sécurité sont-elles prévues ?
- Existe-t-il une communauté qui pourrait prendre en charge la maintenance ?
- Pourrais-je prendre en charge la maintenance si nécessaire ?
- Devrais-je les financer/soutenir pour m'assurer qu'ils restent en place ?
- Et qu'en est-il de leurs dépendances ?
- Quels sont les antécédents en matière de sécurité ?
Si cela ressemble à un gros travail, vous avez raison. Il faut facilement des heures ou des jours par dépendance pour déterminer ce qu'il en est. C'est d'ailleurs la raison pour laquelle je pense que l'ajout de dépendances à un projet devrait être un travail techniquement difficile. Cela vous donne une pause naturelle pour réfléchir si c'est une bonne idée !
Ce qui pourrait ne pas être une bonne idée, c'est d'avoir 1600 dépendances en 2024, des dépendances qui changent déjà à un rythme si rapide que votre base de code est en fait une cible mouvante. Pour être honnête, vous ne savez pas ce que vous expédiez si votre code s'appuie sur plus d'un millier de dépendances, dont la plupart ne sont pas directement connues.
Dépendances d'exécution
Jusqu'à présent, nous avons parlé des dépendances de compilation. Cependant, de nos jours, de nombreux projets ont également des dépendances d'exécution, comme par exemple S3 ou Google Firebase. Certaines de ces dépendances sont informellement bien standardisées (comme S3), mais d'autres sont en fait un blocage. En bref, si vous planifiez sur un horizon de 10 ans, vous devez avoir une liste extrêmement courte ou vide de services tiers dont vous dépendez. En 2034, il sera extrêmement coûteux de trouver un équivalent exact de ce sur quoi vous vous basez aujourd'hui.
Ceci est particulièrement pertinent pour le développement de logiciels « cloud native » qui utilisent de nombreux services tiers complexes ou de niveau supérieur.
Il convient également de noter les dépendances de services au moment de la construction. Si « npm install » (ou équivalent) ne fonctionne plus pour une raison quelconque, pouvez-vous même construire votre logiciel ?
TESTS TESTS TESTS
Ai-je bien dit tests ? Tout le monde a été clair sur ce point. Il faut faire autant de tests que possible. Certains ont fait valoir que tous les tests n'avaient pas la même valeur, ce qui est évidemment vrai. Mais vous ne regretterez presque jamais un test. Les tests sont toujours une bonne idée, surtout si vous avez de nombreuses dépendances qui changent et dérivent constamment. Les tests n'empêcheront pas que cela se produise, mais ils vous aideront à démarrer plus tôt pour vous adapter à la nouvelle situation.
Les tests vous apporteront également un soutien (mental) lors de la refonte ou de la suppression de dépendances. De plus, après un hiatus de 3 ans dans le développement, les tests sont un excellent moyen de rétablir que vous avez toujours un système fonctionnel, même lorsque vous utilisez des compilateurs, des moteurs d'exécution, des systèmes d'exploitation, etc. plus récents. Écrire plus de tests.
Complexité
Ce n'est pas nouveau, mais il faut le répéter : la complexité est le fléau du développement logiciel. La complexité peut tuer le meilleur programmeur et même la meilleure équipe. C'est l'ennemi ultime. Pourtant, en raison de l'entropie et du comportement humain, la complexité augmentera toujours, à moins que vous n'agissiez consciemment.
Bien qu'il s'agisse d'un sujet bien connu, je pense que ce graphique est quelque peu nouveau. Mais en toute honnêteté, je suis probablement en train de canaliser quelque chose que j'ai lu plus tôt :
Si vous avez une quantité limitée de code à traiter, ce code peut être assez complexe par nature. Au fur et à mesure que la quantité de code augmente, il doit être de plus en plus simple si vous voulez toujours le maîtriser. Tant que votre code se trouve dans le triangle vert et que votre équipe reste compétente, tout va bien. Mais vous ne pouvez pas remodeler arbitrairement la zone verte :
Même si vous embauchez plus de personnes et/ou des personnes plus intelligentes, il y a une limite stricte à la complexité que nous pouvons gérer. Même dans les bons jours. Et une fois que votre code s'est échappé de la zone verte, vous êtes fichu. Et comme le montre la flèche, le mouvement naturel de votre code est « vers le haut et vers la droite ». Les gens exigent davantage de fonctionnalités, les programmeurs aiment optimiser les choses même lorsque ce n'est pas nécessaire, et même les corrections de bogues nécessaires ajoutent souvent beaucoup de nouveau code (au lieu de faire le travail le plus effrayant pour défaire la complexité sous-jacente qui a probablement conduit au bogue).
Au fil du temps, la complexité ne fait que s'accroître. Une fonction appelée CreateFile qui, la plupart du temps, ne crée pas réellement un fichier, implique une charge cognitive. Il est extrêmement important de réduire le nombre de fonctions mal nommées ou d'API contre-intuitives. Les appels et les méthodes au comportement étrange offrent chaque jour de nouvelles occasions de vous faire trébucher.
Le message du terrain était donc sans équivoque : remaniez tôt et souvent, supprimez le code inutile ou dupliqué, prenez le temps de simplifier. Sinon, vous finirez inévitablement par vous retrouver dans un marécage impossible à maintenir au cours de votre projet logiciel de plus de 10 ans. Et notez que cela sera plus facile à faire si vous avez investi dans des tonnes de tests.
Écrire un code simple ennuyeux. Encore plus simple que cela. Encore plus ennuyeux.
Écrivez un code super ennuyeux. Écrire un code naïf mais évident. "L'optimisation prématurée est la racine de tous les maux." Si c'était trop simple, vous pourrez toujours le rendre plus complexe plus tard. Et ce moment n'arrivera peut-être jamais. N'écrivez pas de code intelligent tant que vous n'y êtes pas obligé. Vous ne regretterez jamais d'avoir écrit un code simple.Envoyé par Brian Kernighan
Soyez particulièrement attentif aux codes/fonctionnalités très performants qui ne fonctionnent que si vous les « tenez bien ». Je suis un grand fan de LMDB, par exemple, mais PowerDNS a connu quelques difficultés avant que nous puissions bénéficier de manière fiable de sa vitesse et de ses capacités impressionnantes. De même, j'ai utilisé RapidJSON, une bibliothèque JSON accélérée par SIMD, mais j'ai fini par trouver les bords trop tranchants. Trop de jonglage de tronçonneuses.
Ce qui est effrayant, c'est que de telles technologies peuvent vous séduire - « Je peux faire face à ces contraintes et récolter les formidables bénéfices en termes de performances ». C'est peut-être vrai pour les bons jours de cette année. Mais vous, ou votre remplaçant, risquez d'avoir des difficultés dans cinq ans. Cela vaut également pour les langages de programmation complexes.
Mais sérieusement, restez simple. Encore plus simple que cela. Vraiment.
Développement de logiciels basés sur LinkedIn
Quelle technologie allons-nous utiliser ? En théorie, nous devrions suivre la liste de contrôle des dépendances que nous avons dressée plus haut pour le déterminer. En réalité, il s'agit souvent d'une décision presque inconsciente où l'on essaie quelque chose qui semble attrayant, et si cela fonctionne, on l'adopte.
Cet attrait peut provenir d'articles publiés sur LinkedIn par des leaders d'opinion et des influenceurs réputés. Il peut également provenir des utilisateurs de Hacker News qui s'extasient sur le dernier framework glorieux pour mettre fin à tous les framework.
Il se peut que ces technologies soient à la hauteur de l'engouement qu'elles suscitent. Mais il est probablement préférable de reconnaître que les nouvelles technologies n'ont pas encore fait la preuve de leur longévité. Il vaut mieux les utiliser à des fins expérimentales et les tenir à l'écart du projet de logiciel décennal pour l'instant. Selon l'effet Lindy, la longévité de nombreuses choses est proportionnelle à leur âge actuel.
Journalisation, télémétrie, performance
L'un des problèmes des logiciels qui ne sont pas mis à jour/déployés en continu pour des millions d'utilisateurs est que vous n'obtenez pas un retour d'information instantané quelques instants après avoir cassé le site web. Curieusement, même dans ce cas, il peut s'écouler un certain temps avant que le message n'atteigne les personnes capables de le réparer.
Le retour d'information de Mastodon était de s'assurer que votre logiciel enregistre très bien ses performances, ses échecs et ses activités. Et ce, dès les premières versions. Au fil des ans, ces données s'avéreront inestimables pour résoudre des bogues peu courants dans le cadre de déploiements qui n'ont lieu qu'une fois tous les quelques mois.
Une fois, j'ai accidentellement déployé une interface utilisateur pour des clients qui ne devaient lister que quelques éléments. L'utilisateur ne m'a jamais dit qu'il avait créé 3 000 dossiers, mais il m'a dit que rien ne fonctionnait plus. Une meilleure journalisation/télémétrie (des performances) m'aurait épargné des mois de souffrance.
Documentation
Nous savons tous que nous devrions documenter davantage, ce n'est pas nouveau. Mais les commentaires que j'ai reçus étaient très clairs sur le fait qu'il y a des choses spécifiques que nous devons documenter. De nos jours, il est facile de produire une documentation d'API très attrayante. Mais cela ne vous dit pas POURQUOI les choses sont ainsi. Quelle est l'idée qui sous-tend le fonctionnement du système ? Y a-t-il une philosophie ? Y a-t-il une raison spécifique pour laquelle nous faisons ces choses non évidentes ? Pourquoi la solution est-elle divisée comme elle l'est ?
Cela va au-delà de la rédaction de documents d'architecture, bien qu'il faille absolument les rédiger. Mais vous pouvez aussi penser à des articles de blog (internes) rédigés par des développeurs ou à des entretiens avec des équipes. Expliquez pourquoi les choses se sont passées ainsi.
Dans 7 ans, avec une équipe en grande partie nouvelle, les connaissances contenues dans ces documents n'ont pas de prix. « Oui, le système est à fil unique, et laissez-moi vous dire pourquoi. »
Plus précisément, j'ai reçu des commentaires selon lesquels, malgré l'engouement pour l'absence de commentaires dans un bon code, le code a bel et bien besoin de commentaires. Surtout pourquoi une fonction est comme ça. Un autre commentaire était de travailler sur les messages de validation, ce que j'aimerais soutenir, mais aussi de s'assurer que les gens peuvent facilement voir ces messages de validation.
Personnellement, il y a des jours où mon cerveau n'est pas prêt à écrire plus de code, mais je pourrais certainement passer cette journée à écrire des commentaires et des notes utiles. Ce serait une bonne idée de prévoir du temps pour cela pour les équipes également.
L'équipe
Certains des commentaires ont été formulés par des personnes dont les logiciels soutiennent une mission de 80 ans ( !). De nos jours, il est courant que les équipes se renouvellent assez rapidement. Dans de nombreux endroits, une durée de 3 ans est une durée de vie assez longue. Bien qu'il soit possible de contrecarrer cette rotation par une bonne documentation et d'excellents tests, il s'agit d'un travail difficile. L'un des moyens les plus simples d'assurer la longévité d'un logiciel consiste à garder les employés pendant une décennie. Cela signifie qu'il faut les embaucher en tant qu'employés et prendre soin de vos programmeurs. C'est fou, non ?
Dans certains endroits, le développement de logiciels est effectué par des consultants, qui partent après avoir introduit leur code dans vos systèmes. Cette pratique est généralement considérée comme une très mauvaise idée si l'on veut obtenir une qualité logicielle durable pendant dix ans.
Soyez open source
Ce n'est pas toujours, ni même souvent, quelque chose que vous pouvez faire. Mais exposer votre code à des regards extérieurs est un excellent moyen de rester honnête. Une anecdote amusante est que les entreprises ou les gouvernements disent souvent qu'ils ont besoin de mois ou d'années pour préparer (NETTOYER) le code pour l'open sourcing. En effet, à l'intérieur, les gens s'autorisent des codes bien pires que ceux qu'ils préféreraient partager avec le monde extérieur. Le code open source est souvent soumis à des normes plus strictes, et c'est un excellent moyen de rester sur la bonne voie.
Si vous pouvez vous en sortir, bien sûr.
Vérification des dépendances (bien-être)
J'ai écrit plus haut comment les dépendances peuvent évoluer pour dériver ou s'éloigner de ce que nous attendons d'elles. Si vous ne faites rien, vous le découvrirez à cause de bogues, d'échecs de construction ou d'autres déceptions. L'une des recommandations de Mastodon est de programmer périodiquement une vérification de l'état de santé de vos dépendances. Cela peut également apporter de bonnes surprises. Il est tout à fait possible que l'une de vos dépendances ait acquis de nouvelles capacités, par exemple, ce qui pourrait permettre de simplifier votre code. Ou de supprimer une autre dépendance.
Si vous consacrez du temps à cette maintenance, vous découvrirez les problèmes au moment de votre choix. Ou, comme le disent les mécaniciens, prévoyez du temps pour la maintenance, ou votre équipement la programmera pour vous.
Quelques livres
Comme nous l'avons indiqué, il s'agit là d'un terrain bien connu, et voici quelques livres qui ont fait le tour de la question :
- The Practice of Programming, Brian W. Kernighan, Rob Pike. Plein de choses qui devraient être évidentes, mais qui, dans la pratique, ne le sont pas encore assez. Même s'il s'agit principalement de C, les leçons sont universelles.
- Le mythique mois-homme, Fred Brooks. Appelé « La Bible du Génie Logiciel », parce que « tout le monde le cite, quelques personnes le lisent, et quelques personnes s'en inspirent ».
- Une philosophie de la conception de logiciels, John Ousterhout - un point de vue moderne par un professionnel chevronné.
- Large Scale C++ Software Design, John Lakos - Je ne recommanderais pas particulièrement l'achat de ce livre car une grande partie de ce qu'il décrit est assez spécifique à l'ancien style C++. Cependant, si vous tombez sur cet ouvrage, ses histoires sur le développement de logiciels à très grande échelle sont illustratives de l'art. Il contient des mots précieux sur la façon dont les dépendances circulaires apparaissent et à quel point elles craignent.
Les utilisateurs de Hacker News cpeterso, rramadass et cratermoon ont également suggéré ce qui suit (merci !):
- Kill It with Fire : Manage Aging Computer Systems (and Future Proof Modern Ones), par Marianne Bellotti, qui a géré la maintenance et la modernisation d'importants systèmes patrimoniaux au sein de l'United States Digital Service.
- Les lois de Lehman sur l'évolution des logiciels
- « Laws of Software Evolution Revisited », Lehman, M. M. In Software Process Technology, édité par Carlo Montangero, 108-24. Berlin, Heidelberg : Springer Berlin Heidelberg, 1996.
- « On Understanding Laws, Evolution, and Conservation in the Large-Program Life Cycle », Lehman, M.M. Journal of Systems and Software 1 (1979) : 213–21.
- « Programs, Life Cycles, and Laws of Software Evolution ». Proceedings of the IEEE 68, no. 9 (septembre 1980) : 1060–76.
- « Metrics and Laws of Software Evolution-the Nineties View », Lehman, M.M., J.F. Ramil, P.D. Wernick, D.E. Perry et W.M. Turski. In Proceedings Fourth International Software Metrics Symposium, 20-32, 1997.
- Étude des lois de l'évolution des logiciels dans un projet FLOSS de longue durée, Jesus M Gonzalez-Barahona, Gregorio Robles, Israel Herraiz, Felipe Ortega
Enfin
Voici, dans leur grande majorité, les recommandations pour le développement de logiciels à long terme :
- Rester simple. Plus simple que cela. Oui, encore plus simple. Vous pourrez toujours ajouter de la complexité plus tard si nécessaire !
- La simplicité exige des remaniements périodiques et la suppression de code.
- Réfléchissez bien à vos dépendances. Moins il y en a, mieux c'est. Examinez-les et vérifiez-les. Et si vous constatez que vous ne pouvez pas auditer 1600 dépendances, repensez votre plan. N'optez pas pour des choix de développement basés sur LinkedIn.
- Revenez régulièrement sur vos dépendances pour voir comment elles se comportent
- Tester, tester, tester ! Cela vous aidera à repérer les dépendances changeantes à temps, et vous donnera la confiance nécessaire pour effectuer le remaniement dont vous avez besoin pour garder les choses simples.
- Documentez tout, pas seulement le code, mais aussi la philosophie, l'idée, le « pourquoi ».
- Visez une équipe stable. Faites appel à des employés qui s'investissent à long terme dans votre projet.
- Si vous pouvez vous en sortir, envisagez d'opter pour l'open source.
- La journalisation et la télémétrie (des performances) vous sauveront la mise au fil des ans.
Rien de tout cela n'est nouveau, mais la véhémence avec laquelle les développeurs chevronnés nous exhortent à réfléchir sérieusement à ces recommandations devrait nous faire réfléchir.
Enfin, je tiens à remercier les nombreux utilisateurs de Mastodon qui nous ont fait part de leurs expériences sur une décennie. Votre aide a été très appréciée ! En outre, le matériel a été considérablement amélioré par les questions et les réflexions de mes collègues du Conseil électoral néerlandais.
Source : "On Long Term Software Development"
Et vous ?
Pensez-vous que ces recommandations sont crédibles ou pertinentes ?
Quel est votre avis sur le sujet ?
Voir aussi :
Le Manifeste anti-héritage : Écrire du code qui dure, par Mensur Durakovic
Le pouvoir des dix règles pour le développement de codes critiques pour la sécurité, par Gerard J. Holzmann du Laboratoire pour les logiciels fiables NASA/JPL
Comment je livre des projets informatiques dans de grandes entreprises technologiques, par Sean Goedecke
Le jour où j'ai commencé à croire aux tests unitaires, par Benjamin Richner
Partager