Pourquoi la philosophie d’Unix est-elle toujours importante?

Cet article explique l’importance de la philosophie d’Unix dans la conception logicielle. De nos jours, peu de concepteurs sont conscients de ces concepts et par conséquent beaucoup des logiciels actuels sont limités ou n’utilisent que très peu les possibilités offertes par l’effet de levier. Connaître et respecter les principes de la philosophie d’Unix c’est produire des logiciels plus puissants, plus précieux. Par Markus Schnalke.

1.0. Introduction

La philosophie d’Unix est l’esprit avec lequel a été conçu le système d’exploitation Unix et plus particulièrement sa boîte à outils internes. Ce n’est pas un corpus de règles fixes et définitives, mais un ensemble de bonnes pratiques qui indiquent comment écrire un logiciel qui s’intégrera parfaitement dans Unix. En fait, la philosophie d’Unix décrit ce que l’on retrouve couramment dans les logiciels de type Unix. Wikipedia nous en propose une définition précise [23] :

La philosophie d’Unix est un ensemble de normes et une approche du développement de logiciels basée sur l’expérience des principaux développeurs du système d’exploitation Unix.

Comme il n’existe pas une définition unique de la philosophie d’Unix, plusieurs personnes nous ont fait part de leur façon de la comprendre et de l’aborder. Voici les plus connus:

  • Doug McIlroy qui la résume par:

    Écrivez des programmes qui ne font qu’une seule chose et qui le font bien. [11]

  • Mike Gancarz dans son livre “The UNIX Philosophy” [8].
  • Eric S. Raymond dans son livre “The Art of UNIX Programming” [15].

Ces différents points de vue sur la philosophie d’Unix ont beaucoup en commun. Plus particulièrement, les concepts principaux sont similaires. La définition que nous en donne McIlroy peut être considérée comme le cœur, le noyau de la philosophie d’Unix, mais l’idée fondamentale qui reste derrière tout cela est “ce qui est petit est merveilleux“.

La philosophie Unix explique comment concevoir un bon logiciel pour Unix. De nombreux concepts décrits ici sont basés sur les spécificités d’Unix. Pour les systèmes d’exploitation ne proposant pas ces particularités, il impossible de concevoir des logiciels selon ces principes.

La philosophie Unix a bien une idée sur comment devrait se dérouler le développement logiciel, mais la majeure partie de la philosophie est tout à fait indépendante de la réalité du processus de développement. Néanmoins, certains découvriront que certaines méthodes de développement, au contraire d’autres, fonctionnent parfaitement avec les idées de la philosophie d’Unix et les utiliseront. Les livres de Kent Beck sur l’Extreme Programming sont de précieuses ressources complémentaires sur ce sujet.

Les questions de savoir comment écrire concrètement le code et à quoi il devrait ressembler sont en dehors du sujet de ce document. Le livre de Kernighan et Pike “The Practice of Programming” [10] traite de ce sujet et son point de vue correspond parfaitement à celui de cet article.

2.0. Importance de la conception logiciel en général

La conception logicielle consiste à prévoir la structure interne et les interfaces externes d’un logiciel. Cela n’a rien à voir avec son apparence. Si l’on compare un programme à une voiture, cela signifie que sa couleur n’a pas d’importance. La conception équivaudrait à sa taille, sa forme, l’emplacement des portes, le ratio passagers / espace, les commandes et les instruments disponibles, et ainsi de suite.

Après tout, pourquoi devrait-on passer par la conception logicielle ? Parce qu’il est généralement admis que même un mauvais plan vaut mieux que pas de plan du tout. Ne pas concevoir un logiciel équivaut à programmer sans un plan. Cela conduira inévitablement à d’horribles conséquences, horrible à utiliser et horrible à maintenir. Ces deux aspects constituent la partie visible. Souvent les bénéfices perdus sont invisibles et une bonne conception peut y remédier.

La conception d’un logiciel traite de propriétés qualitatives. Une bonne conception mène à une bonne qualité, et la qualité c’est important.

N’importe quelle voiture peut être capable de vous conduire d’un point A à un point B, mais il dépend de décisions pertinentes, prises lors de la conception du véhicule, pour que ce soit le bon choix pour transporter des passagers, aborder une zone montagneuse difficile, ou que la ballade soit amusante.

Les exigences d’un logiciel sont doubles: fonctionnelles et non fonctionnelles.

  • Les exigences fonctionnelles définissent directement les fonctionnalités du logiciel. Elles sont à l’origine de la décision d’écrire un logiciel. Quelqu’un a un problème et il a besoin d’un outil pour le résoudre. Être en mesure de résoudre ce problème est le principal objectif fonctionnel, c’est la motivation de tout effort de programmation. Les exigences fonctionnelles sont faciles à définir et à vérifier.
  • Les exigences non fonctionnelles appelées aussi exigences qualitatives. La qualité des logiciels s’exprime au travers de ces propriétés qui ne sont pas directement liées aux fonctions de base du logiciel. Les outils de mauvaise qualité peuvent résoudre les problèmes pour lesquels ils ont été écrits, mais ils génèrent des problèmes et des difficultés d’utilisation et d’évolution. Au premier abord, ces aspects qualitatifs sont souvent négligés, car ils sont souvent difficiles à définir clairement et donc à vérifier.

La qualité est difficilement prise en compte dans la version initiale d’un logiciel, mais elle a un impact considérable sur l’utilisation et la maintenance du logiciel par la suite.

Une néophyte peut voir le processus de développement logiciel comme construire simplement quelque chose. Mais l’expérience montre que développer une première version d’un logiciel ne représente qu’une petite partie de l’ensemble des tâches nécessaires.

Corriger les bugs, faire évoluer, refactoriser - les travaux de maintenance - prend très vite une grande partie du temps passé sur un projet de développement. Et bien sûr, il faut compter le temps passé à utiliser le logiciel. Ces activités sont fortement impactées par la qualité du logiciel. Pour cela, la qualité ne doit pas être négligée. Cependant, le problème avec la qualité, c’est que dès la première version, vous vous “cassez les dents” sur une mauvaise qualité alors que c’est le moment propice où il faut s’en préoccuper sérieusement.

La conception logicielle a très peu à voir avec le fonctionnement de base d’un logiciel - cette exigence sera satisfaite de toute façon. La conception logicielle traite plus particulièrement des domaines de la qualité. Une bonne conception induit une bonne qualité, une mauvaise conception à une mauvaise qualité. Les fonctions principales d’un logiciel ne seront que très légèrement affectées par une qualité médiocre, mais une meilleure qualité peut offrir énormément de bénéfices, même à des endroits où l’on ne s’y attend pas. La première partie de la norme ISO/IEC9126-1 définit un modèle de qualité constitué de:

  • Capacité fonctionnelle (pertinence, exactitude, interopérabilité, sécurité)
  • Fiabilité (maturité, tolérance aux pannes, facilité de récupération)
  • Ergonomie (facilité de compréhension, facilité d’apprentissage, opérabilité, attractivité)
  • Efficacité (comportement dans le temps, utilisation des ressources)
  • Maintenabilité (facilité d’analyse, évolutivité, stabilité, testabilité)
  • Portabilité (adaptabilité, facilité d’installation, coexistence, interchangeabilité)

Une bonne conception du logiciel peut améliorer ces caractéristiques dans le logiciel; les logiciels mal conçus souffrent certainement de carences dans ces domaines. Un autre objectif de la conception logiciel est la cohérence. La cohérence facilite la compréhension, l’usage, et le travail. Une structure interne et des interfaces externes cohérentes sont les résultats d’une bonne conception.

Un logiciel devrait être bien conçu, car, durant sa vie, une bonne conception lui évitera de nombreux problèmes. Mais c’est aussi, parce qu’une bonne conception procure de nombreux bénéfices. Afin de rendre un logiciel plus performant, beaucoup d’efforts doivent être engagés en faveur d’une bonne conception. La philosophie d’Unix procure le moyen de concevoir des logiciels dans cette optique. Elle propose des recommandations pour atteindre qualité et valeur.

3.0. La philosophie d’Unix

Nous avons déjà présenté les origines de la Philosophie d’Unix. Ce chapitre aborde la philosophie, comme la présente Gancarz [8], ainsi que son application à partir d’exemples concrets.

3.1. Les pipes

Ndt: Comme dans “pipelines”. Les pipes sont aussi appelées des “tubes”.

Les exemples suivants démontrent de manière pratique la philosophie d’Unix. Cela suppose une connaissance dans l’utilisation du shell Unix.

Compter le nombre de fichiers présents dans le répertoire courant:

ls | wc -l

La commande ls liste tous les fichiers du répertoire courant, un par ligne, et wc -l compte le nombre de lignes.

Compter le nombre de fichiers qui ne contiennent pas “toto” dans leur nom:

ls | grep -v toto | wc -l

Ici, la liste des fichiers est filtrée par grep afin d’enlever les lignes qui contiennent “toto”. La suite est équivalente à l’exemple précédent.

Trouver les cinq plus grosses entrées présentes dans le répertoire courant:

du -s * | sort -nr | sed 5q

du -s * renvoie de manière récursive les tailles cumulées de tous les fichiers dans le répertoire courant - peu importe si ce sont des fichiers ou des répertoires réguliers. sort -nr trie la liste numériquement dans l’ordre inverse (descendant). Enfin, 5q sed termine après avoir imprimé la cinquième ligne.

Les lignes de commande présentées ici sont des exemples que les utilisateurs d’Unix emploient pour obtenir la sortie désirée. Il existe bien d’autres façons d’obtenir le même résultat, c’est le choix de l’utilisateur qui détermine la manière de faire.

Ces exemples montrent que, sur un système de type Unix, de nombreuses tâches sont réalisées en combinant plusieurs petits programmes. Ce lien entre les programmes est exprimé par l’opérateur pipe ‘|‘.

Les pipes, avec leur facilité d’utilisation et leur usage intensif, sont l’une des grandes réussites du système Unix. Les pipes étaient disponibles dans des systèmes d’exploitation plus anciens, mais jamais ils n’ont été conçus comme un élément central.

Quand, dans les années soixante-dix, Doug McIlroy a introduit les pipes dans le système Unix,

ce fut ce concept de notation pour relier plusieurs programmes simultanément qui transforma Unix d’un simple système d’échange de fichiers en un système totalement nouveau dans sa manière d’aborder l’informatique. [2]

Être capable d’écrire des pipes d’une manière aussi facile n’est pourtant pas suffisant, il en manque une partie. L’autre partie c’est la conception des programmes utilisés dans le pipe. Ils ont besoin d’interfaces leur permettant d’être utilisés dans cette situation.

3.2. La conception d’interface

Unix est, avant tout, simple - tout est fichier. Les fichiers sont des séquences d’octets, sans aucune structure spéciale. Les programmes devraient être des filtres, pour lire un flux d’octets depuis l’entrée standard (stdin) et écrire un flux d’octets vers la sortie standard (stdout). Si les fichiers sont des séquences d’octets, et les programmes sont des filtres sur les flux d’octets, alors c’est précisément une interface de données. À partir de là, il devient possible de combiner les programmes de n’importe quelle manière.

Même une poignée de petits programmes présente un grand nombre de combinaisons, donc un grand nombre des fonctions différentes. Il s’agit de l’effet levier! Si les programmes sont orthogonaux entre eux - dans le meilleur des cas - alors le nombre de fonctions différentes est maximal.

Les programmes peuvent aussi avoir une interface de commande séparée en plus de leur interface de données. L’interface de commande est couramment appelée l‘“interface utilisateur”, car elle est généralement conçue pour être utilisée par l’homme. La philosophie d’Unix décourage l’hypothèse de l’utilisateur humain. En effet, l’utilisation interactive de logiciel est lente dans l’usage, car, la plupart du temps, le programme attend l’entrée de l’utilisateur. Un logiciel interactif impose également à l’utilisateur d’être en face de l’ordinateur, lui demandant toute son attention.

Maintenant, revenons à l’idée de combiner plusieurs petits programmes pour réaliser une fonction spécifique: si ces outils simples sont tous interactifs, comment l’utilisateur peut-il les contrôler? Le problème n’est pas uniquement de contrôler plusieurs programmes à la fois et les exécuter en même temps, c’est qu’il est très inefficace d’avoir à contrôler chaque programme alors qu’ils sont destinés à agir de concert. Par conséquent, la philosophie d’Unix décourage la conception de programmes qui imposent l’utilisation interactive. Le comportement des programmes doit être défini lors de son invocation. Cela se fait en spécifiant des arguments à l’appel du programme (commutateurs de ligne de commande). Gancarz aborde ce sujet:

éviter les interfaces utilisateur captives [8, page 88 et suiv.].

L’utilisation non interactive est aussi avantageuse pour tester en cours de développement. Tester des programmes interactifs est beaucoup plus complexe que de tester leurs équivalents non interactifs.

3.3. L’approche boîte à outils

Une boîte à outils est un ensemble d’outils. Plutôt qu’un seul grand outil pour toutes les tâches, il y a plein de petits outils, un pour chaque tâche. Les tâches complexes sont résolues en combinant plusieurs petits outils simples.

La boîte à outils d’Unix est un ensemble de petits programmes, non interactifs (surtout) et ce comportant pour la plupart comme des filtres face à un flux d’octets. Ils sont, dans une large mesure, sans rapport avec leur fonction. Par extension, la boîte à outils d’Unix fournit un grand nombre de fonctions qui deviennent accessibles par une combinaison choisie de programmes.

Le développement de logiciels bénéficie aussi de ces petits programmes issus de la boîte à outils. Écrire de petits programmes est en général plus facile et moins sujet aux erreurs que d’écrire de larges programmes. Par conséquent, écrire un grand nombre de petits programmes est encore plus facile et moins sujet aux erreurs que d’écrire un programme important intégrant toutes les fonctionnalités. Si ces petits programmes peuvent être combinés, ils offrent même un nombre de fonctions bien plus important qu’un unique programme monolithique.

On obtient donc deux avantages à l’écriture de petits programmes combinables: ils sont plus faciles à écrire et offrent un ensemble plus important de fonctions grâce à la combinaison.

Il y a aussi deux inconvénients majeurs à l’approche boîte à outils. Tout d’abord, il faut se contenter d’une interface simple et standardisée. Si l’on désire quelque chose de plus “logique” qu’un flux d’octets, il sera nécessaire d’envisager une approche différente. Par ailleurs, une conception qui se satisfait uniquement d’un flux d’octets peut être inconcevable. En devenant de plus en plus familiers avec la “manière de penser d’Unix”, les développeurs trouveront plus souvent et plus facilement les modèles simples dans lesquels le flux d’octets sera une interface suffisante.

Le second inconvénient de l’approche boîte à outils concerne les utilisateurs. Une boîte à outils est souvent plus difficile à utiliser, car il est nécessaire de se familiariser avec chaque outil et être en mesure de choisir et d’utiliser le bon outil dans une situation donnée. Il est de plus nécessaire de savoir comment combiner ces outils de manière précise. Cette situation est semblable au couteau bien aiguisé - il s’agit d’un outil puissant dans la main d’un maître, mais n’a aucune valeur dans la main d’une personne inexpérimentée. Cependant, apprendre un par un à utiliser les petits outils de la boîte est souvent plus facile que d’appréhender un outil complexe. L’utilisateur aura déjà une connaissance de base pour un outil encore inconnu si tous les outils de la boîte possèdent le même fonctionnement logique. Il sera dès lors en mesure de transférer ses connaissances d’un outil à l’autre. Ce second inconvénient peut être supprimé dans une large mesure par l’ajout de conteneurs englobant les outils de base. Les utilisateurs débutants n’ont pas besoin d’apprendre plein d’outils si un expert enveloppe complètement les lignes de commande dans un script de haut niveau. Notez que le script conteneur appelle encore les petits outils; c’est juste comme une peau qui les entoure. Aucune complexité n’est ajoutée de cette façon, mais de nouveaux programmes peuvent être créés à partir d’autres existants. Cela avec très peu d’effort.

Un script pour trouver les cinq plus grandes entrées dans le répertoire courant pourrait ressembler à ceci:

#!/bin/sh
du -s * | sort -nr | sed 5q

Le script lui-même est un simple fichier texte qui appelle les commandes comme un utilisateur expert les taperait en direct. Il est sans doute utile de rendre ce programme souple vis à vis du nombre d’entrées qu’il imprime:

#!/bin/sh
num = 5
[ $# -eq 1 ] && num="$1"
du -sh * | sort -nr | sed "${num}q"

Ce script se comporte comme le précédent lorsqu’il est appelé sans argument, mais l’utilisateur peut aussi spécifier un argument numérique afin de définir le nombre de lignes à imprimer. On peut sûrement imaginer des versions encore plus souples; mais ils resteront dépendant de programmes externes qui effectueront le travail.

3.4. Un shell puissant

Le shell d’Unix fournit la possibilité de combiner de petits programmes dans de plus grands. Mais un shell puissant est très fonctionnel de bien d’autres manières, par exemple en étant “scriptable”. Les instructions de contrôle sont intégrées au shell et les fonctions sont des programmes standards du système. Comme les programmes sont déjà connus, il devient aisé d’apprendre à programmer dans le shell. Utiliser des programmes standards comme des fonctions dans un langage de programmation shell est uniquement possible parce que ces outils sont petits et combinables dans l’esprit boîte à outils.

Le shell d’Unix encourage l’écriture de petits scripts, en combinant les programmes disponibles, car c’est très facile à réaliser. Il s’agit d’un grand pas vers l’automatisation. C’est merveilleux si l’effort d’automatisation d’une tâche est égal à l’effort pour réaliser cette même tâche une seconde fois à la main. Si c’est le cas, alors l’utilisateur se fera un plaisir d’automatiser tout ce qu’il fait plus d’une fois.

Des petits programmes qui font du bon travail, avec entre eux des interfaces normalisées, un mécanisme pour combiner des pièces afin d’en créer de plus grandes, et un moyen facile d’automatiser les tâches, produira inévitablement un effet levier procurant plusieurs fois le bénéfice de l’investissement initial.

Le shell encourage également le prototypage rapide. De nombreux programmes bien connus ont commencé comme des scripts shell rapidement bidouillés, puis transformés par la suite en “réels” programmes écrits en C. Construire un prototype c’est d’abord un moyen d’éviter les problèmes les plus importants dans le développement d’applications. Fred Brooks explique dans “No Silver Bullet” [4]:

La seule partie difficile dans la construction d’un système logiciel est de décider exactement ce qu’il faut construire. Aucune autre partie du travail de conception n’est plus difficile que d’établir les exigences techniques détaillées […]. Aucune autre partie du travail ne compromet autant le système qui en résulte si elle est mal faite. Aucune autre partie n’est aussi difficile à corriger par la suite.

Écrire un prototype est une excellente méthode pour connaître les exigences et être confronté très tôt avec de réels problèmes [8, page 28 f.]. Prototyper est souvent considérée comme la première étape de la construction logicielle. Bien sûr, tout cela est très bien. Cependant, la philosophie d’Unix possède un point de vue complémentaire sur le prototypage: après avoir construit le prototype, on peut constater que le prototype est déjà assez bon. Par conséquent, aucune implémentation dans un langage de programmation plus sophistiqué ne semble être nécessaire, du moins pour le moment. Peut-être par la suite, il deviendra obligatoire de réécrire ce logiciel, mais pas maintenant.

En reportant la suite des travaux, on garde la souplesse nécessaire pour réagir à l’évolution des exigences. Les parties du logiciel qui ne sont pas écrites ne manqueront pas aux besoins. La citation classique de Gordon Bell est bien connue:

Les moins chers, les plus rapides, et les plus fiables des composants sont ceux qui ne sont pas là.

3.5. Moins bien, c’est mieux

Ndt: Worse is better.

La philosophie Unix vise 90% de la solution, aussi appelé l’approche “moins bien, c’est mieux

L’expérience issue de projets déjà réalisés nous montre:

  1. qu’il est presque impossible de définir les exigences définitivement et correctement du premier coup. C’est pourquoi il ne faut pas essayer; on échouera toute façon.
  2. Les exigences changent au cours du temps. Par conséquent, il est préférable de retarder les décisions de conception basées sur les exigences tant que c’est possible. Le logiciel doit être assez petit et flexible, aussi longtemps que possible afin de s’adapter à l’évolution des besoins. Les scripts shell, par exemple, sont plus facilement ajustables que les programmes en C.
  3. que le travail de maintenance est un travail difficile. Par conséquent, il faut conserver la quantité de code la plus petite possible, elle doit seulement couvrir les exigences actuelles. Les parties du logiciel qui seront écrites dans le futur n’ont pas besoin de maintenance avant ce moment-là.

Voir Brooks “The Mythical Man-Month” pour la référence [5, page 115 et suiv.]. Commencer avec un prototype dans un langage de script a plusieurs avantages:

  • Comme l’effort initial est faible, on va probablement commencer tout de suite.
  • Les besoins réels peuvent être identifiés rapidement, car les parties fonctionnelles seront disponibles au plus tôt.
  • Lorsque le logiciel est utilisable et précieux, il est utilisé, et donc testé. Cela garantit que les problèmes seront découverts dans les premiers temps du développement.
  • Le prototype pourrait être suffisant pour l’instant, par conséquent la poursuite du travail peut être retardée jusqu’à ce que l’on connaisse les besoins et les problèmes de façon plus fine.
  • Implémenter uniquement les parties réellement nécessaires à l’instant “t” implique moins de programmation et de maintenance.
  • Si la situation change de telle sorte que le logiciel devient inutile, alors le minimum d’effort aura été consacré au projet en comparaison à l’effort demandé pour une approche différente.

3.6. Croissance et survie des logiciels

Pour le moment, nous avons discuté d’écrire ou de construire un logiciel. Bien qu’“écrire” et “construire” ne soient que des verbes, ils impliquent une approche spécifique sur le processus du travail qu’ils décrivent. Un meilleur verbe serait “croitre”. Créer des logiciels dans le sens de la philosophie d’Unix est un processus incrémentiel. Il commence par un premier prototype, qui évolue à mesure que les besoins changent. Un script shell vite bidouillé peut devenir un grand programme sophistiqué réalisé par cette méthode. Sa vie commence avec le prototype initial et se termine lorsque le logiciel n’est plus utilisé du tout. Alors que tant qu’il dure, il sera étendu, réarrangé, reconstruit. Les logiciels qui grandissent correspondent à cette idée qu’

un logiciel n’est jamais fini. Il est seulement publié. [8, page 26]

Le logiciel peut être considéré comme contrôlé par des processus évolutifs. Un logiciel performant est un logiciel qui est utilisé par beaucoup et pour longtemps. Cela sous-entend que le logiciel est nécessaire, utile, et meilleur que les autres. Darwin décrit “la survie des plus aptes.” [7] Dans le cas des logiciels, le logiciel qui réussi est le plus adapté, celui qui survit. (Cela peut se situer au niveau d’une créature, ou au niveau d’une espèce.) L’adaptation du logiciel est affectée principalement par quatre propriétés: la portabilité du code, la portabilité des données, les possibilités d’utilisation, et la réutilisation des éléments.

Ndt: Dans l’écosystème du logiciel libre, je pense que c’est différent. Les logiciels survivent par la diversité des usages, donc des logiciels à pourvoir. Quand un développeur écrit un logiciel pour lui, il n’est pas en compétition. Le critère n’est pas la masse des utilisateurs. De plus les projets abandonnés peuvent même être réappropriés.

  1. La portabilité du code” signifie utiliser les langages de programmation de haut niveau, s’en tenir aux standards [10, chapitre 8], tout en évitant les optimisations qui introduisent des dépendances matérielles spécifiques. Le matériel a une durée de vie beaucoup plus courte que les logiciels. En rendant dépendant le logiciel d’un matériel spécifique, sa durée de vie devient limitée par celle de ce matériel. Au contraire, le logiciel devrait être facile à transposer - l’adaptation est la clé du succès.

  2. La portabilité des données” est mieux réalisée en évitant les formats binaires de stockage des données, puisque les formats binaires diffèrent beaucoup d’une machine à l’autre. Le format texte est préférable. Historiquement, ASCII est le jeu de caractères de choix; à l’avenir, UTF-8 semble être la meilleure piste à suivre. L’important est que format texte brut est un jeu de caractères très commun. En plus de permettre le transfert des données entre les machines, le format texte brut est synonyme de données lisibles, c’est-à-dire qui ont le grand avantage que les humains sont capables de les lire et les éditer directement avec un éditeur de texte et d’autres outils de la boîte à outils d’Unix [8, page 56 et suiv.].

  3. De nombreuses “possibilités d’utilisation” assurent une bonne adaptation et donc une bonne capacité de survie. Avec une mention spéciale lorsque le logiciel commence à être utilisé dans des domaines que les auteurs originaux n’avaient jamais imaginés. Un logiciel qui résout les problèmes d’une façon générique sera probablement utilisé pour beaucoup d’autres problèmes similaires. Être trop spécifiques restreint les possibilités d’utilisation. Les exigences changent avec le temps, de ce fait les cas d’utilisations changent ou même disparaissent. Comme pour illustrer ce point, Allman identifie la souplesse comme une des principales raisons du succès de sendmail [6]:

Deuxièmement, je me suis limité à la fonction de routage […]. Ce fut un départ à partir de la pensée dominante de l’époque […].
Troisièmement, le fichier de configuration de sendmail est suffisamment flexible pour s’adapter à l’évolution rapide du monde […].
Un logiciel performant s’adapte à l’évolution du monde.

  1. La “réutilisation des éléments” va encore plus loin. Le logiciel peut devenir obsolète et perdre complètement son champ d’action, mais ses éléments constitutifs peuvent être généralistes et suffisamment indépendants pour survivre à cette mort.

Si le logiciel est construit en combinant de petits programmes indépendants, ces pièces seront facilement disponibles pour une réutilisation. Qui se soucie d’un gros programme défaillant, si les parties de celui-ci réussissent à sa place?

3.7. En résumé

Ce chapitre vient d’expliquer les idées centrales de la philosophie d’Unix. Pour chacune de ses idées, les avantages qu’elles représentent ont été expliqués. La philosophie d’Unix est un ensemble de recommandations qui aident à la conception de logiciels a forte valeur. Du point de vue d’un développeur ou d’un concepteur de logiciels, la philosophie d’Unix fournit des réponses à de nombreux problèmes de conception. Les différentes idées incluses dans la philosophie d’Unix sont très liées et peuvent difficilement être appliquées de manière indépendante. Les messages les plus importants sont les suivants: “Ne compliquez pas les choses!“, “Faîte une chose et faîte la bien!“, et “Utilisez l’effet levier des logiciels!”

Ndt: Keep It Simple Stupid (KISS).

4.0. Etude de cas: MH

Le chapitre précédent a présenté et expliqué la philosophie d’Unix d’un point de vue général. Le sujet principal était les bonnes pratiques, les exemples de logiciels n’étant pas très nombreux. Dans ce chapitre et le suivant, des exemples concrets de logiciels guideront la discussion.

Cette première étude de cas porte un ‘Mail User Agents’ (MUA) MH (“Mail Handler”) et son descendant nmh (“new mail handler”) [13]. Les MUA permettent de lire, composer et organiser les emails, mais (dans l’idéal) ne permettent le transfert. Dans cet article, le mot MH désignera aussi nmh. Une distinction sera faîte si des différences entre MH et nmh sont nécessaires.

Ndt: MUA, client de messagerie. MH, client de courrier électronique.

4.1. Contexte historique

Le courrier électronique est disponible sous Unix depuis le tout début. Le premier MUA sur Unix était mail, déjà présent dans la première version [17, page 41 f.]. C’était un petit programme qui, en fonction des arguments donnés en ligne de commande [18], écrivait un fichier boîte aux lettres utilisateur ou ajoutait du texte à un fichier boîte aux lettres de quelqu’un d’autre. C’était un programme qui a fait un bon travail. Ce travail était la messagerie, qui était à cette époque très simple.

Par la suite, la messagerie est devenue plus puissante, et donc plus complexe. Le simple mail, qui ne savait rien des sujets, de la gestion indépendante des messages, et du stockage à long terme du courrier, n’était plus assez puissant.

En 1978, à Berkeley, Kurt Shoens à écrit Mail (avec un ‘M’ majuscule) pour fournir les fonctions supplémentaires de messagerie. Mail était encore un programme unique, mais était vaste et faisait plusieurs tâches.

5.0. Etude de cas: uzbl

Le chapitre précédent a abordé et expliqué la philosophie d’Unix à partir d’un point de vue général. La force motrice ce situe dans les recommandations; quelques renvois à des logiciels existants ont été donnés. Dans ce chapitre, un exemple concret de logiciel sera le moteur de la discussion.

Ce chapitre couvre uzbl, un projet récent. Uzbl est un navigateur qui respecte la philosophie d’Unix. Son nom vient du mot lolspeak “usable”, les deux se prononçant de la même façon.

Ndt: “usable” en Anglais équivalent à “utilisable” en Français.

5.1. Contexte historique

Uzbl a été lancé par Dieter Plaetinck en Avril 2009. L’idée est née dans une discussion du forum d’Archlinux [1]. Après une discussion sur les problèmes des principaux navigateurs web connus, Plaetinck (alias Dieter@be) est arrivé en proposant ce à quoi devait ressembler un navigateur Web meilleur. En réponse un autre membre a demandé si Plaetinck écrirait ce programme parce qu’il semblait fantastique, Plaetinck a répondu:

Peut-être, si je trouve le temps ;-).

Heureusement, il trouva le temps. Un jour après, le premier prototype était sorti. Une semaine plus tard, uzbl avait son propre site Internet [20]. Un mois après le code initial a été présenté, une mailing liste a été mise en place pour coordonner et discuter de son développement, et un wiki a été ajouté afin de stocker la documentation et les scripts issus de la liste de diffusion et d’ailleurs. Durant sa première année d’existence, uzbl a été énormément développé dans plusieurs branches. Le travail de Plaetinck consista de plus en plus à fusionner le meilleur du code des différentes branches vers la branche principale, et appliquer des patchs [21]. Environ une fois par mois, Plaetinck publiait une nouvelle version. En septembre 2009, il présenta plusieurs forks d’uzbl [20 archives des actualités].

Ndt: Selon Wikipédia - Un fork, ou embranchement ou fourche, est un nouveau logiciel créé à partir du code source d’un logiciel existant.

Uzbl ouvra la voie à toute une famille de navigateurs web à la conception commune. En Juillet 2009, Linux Weekly News a publié une interview de Plaetinck à propos d’uzbl [21]. En Septembre 2009, le navigateur uzbl était sur Slashdot [19].

5.2. Différences avec les autres navigateurs Web

La plupart des navigateurs web sont monolithiques, mais uzbl est le frontal d’une boîte à outils. Aujourd’hui, uzbl est divisé en uzbl-core et uzbl-browser. Uzbl-core est, comme son nom l’indique, le cœur de uzbl. Il gère les commandes et les événements, sert d’interface aux autres programmes, et affiche les pages Web en utilisant webkit comme moteur de rendu. Uzbl-browser combine uzbl-core avec une sélection de scripts de gestion, une barre d’état, un gestionnaire d’événements, copier, coller, rechercher dans la page, zoomer, et bien d’autres fonctionnalités, pour former un navigateur web “complet”. Dans la suite du texte, le terme “uzbl” se réfère généralement à uzbl-browser, uzbl-core inclus

Contrairement à la plupart des autres navigateurs Web, uzbl a principalement le rôle de médiateur entre différents outils qui ne font qu’une seule tâche. Uzbl écoute les commandes à partir d’un pipe nommé (FIFO), un socket Unix, sur l’entrée standard (stdin), et il écrit des événements dans un socket Unix et sur la sortie standard (stdout). Charger une page Web via une instance d’uzbl nécessite uniquement:

echo 'uri http://example.org' >/chemin/vers/uzbl-fifo

Le rendu de la page Web se fait par la libwebkit, autour de laquelle uzbl-core est construit.

Téléchargements, historique de navigation, signets, etc ne sont pas fournis par le noyau lui-même comme ils le sont dans d’autres navigateurs Web. Uzbl-browser fournit également des “scripts de gestion” qui encapsulent des applications externes afin de fournir la fonctionnalité voulue. Par exemple, wget est utilisé pour télécharger des fichiers et uzbl-browser inclut un script qui appelle wget avec les options appropriées dans un environnement défini.

Les navigateurs Web modernes sont fiers de posséder des addons, des plugins, des modules, etc. Des efforts pour atteindre des objectifs similaires. Mais au lieu d’utiliser des programmes existants externes, les navigateurs Web modernes internalisent ces fonctions.

5.3 Discussion autour de la conception

Cette section traite d’uzbl selon la philosophie d’Unix, décrite par Gancarz.

Faire que chaque programme fasse une chose bien. Uzbl essaie d’être un navigateur et rien d’autre. La définition généralement admise d’un navigateur Web est fortement influencée par l’implémentation des navigateurs Web existants. Mais un navigateur Web devrait être un programme servant à naviguer sur le Web, et rien d’autre de plus. C’est la seule chose qu’il doit faire.

Les navigateurs Web ne doivent pas, par exemple, gérer les téléchargements; c’est la tâche des gestionnaires de téléchargement. Un gestionnaire de téléchargement est principalement concerné par le téléchargement des fichiers. Les navigateurs Web modernes permettent la gestion du téléchargement uniquement en fonctionnalité secondaire. Comment pourraient-ils faire un meilleur travail que les programmes conçus spécifiquement pour ce travail? Et pourquoi voudrait-on moins bien que le meilleur des gestionnaires de téléchargement disponibles?

Le travail du navigateur Web est de permettre à l’utilisateur de naviguer sur le Web. Cela signifie, naviguer à travers les sites Web en suivant des liens. Interpréter les sources HTML est aussi un travail différent. Dans le cas d’uzbl, cela est délégué au moteur de rendu webkit. Gérer le contenu audio et vidéo, PostScript, PDF et d’autres fichiers n’est pas non plus le travail d’un navigateur Web. Ces contenus doivent être manipulés par des programmes externes qui ont été écrits pour traiter ces données. Uzbl s’efforce de fonctionner de cette façon. Rappelez-vous les mots Doug McIlroy:

Écrivez des programmes qui ne font qu’une seule chose et qui le font bien.
Écrivez des programmes qui travaillent ensemble.

Un principe sous-jacent qui est de permettre à l’utilisateur d’adapter son environnement s’applique ici très bien. Précédemment, la question, “Pourquoi voudrait on moins bien que le meilleur programme pour faire le boulot?” a été mis en avant. Mais en terme de préférences personnelles, il peut être plus important de se demander: “Pourquoi quelqu’un voudrait-il autre chose que son programme préféré pour faire le boulot?” Les utilisateurs veulent habituellement un programme pour un travail spécifique. Par conséquent, chaque fois que l’on souhaite télécharger quelque chose, le même gestionnaire de téléchargement doit être utilisé. Les utilisateurs plus avancés voudrez peut-être utiliser un gestionnaire de téléchargement dans une certaine situation et un autre dans une situation différente; ils devraient être en mesure de configurer ce fonctionnement. Avec uzbl, n’importe quel gestionnaire de téléchargement peut être utilisé. Pour passer de l’un à l’autre, changer une seule ligne dans un petit script de gestion suffit. Alternativement, il reste possible de demander d’utiliser tel gestionnaire de téléchargement en lisant un fichier global ou une variable d’environnement dans le script de gestion. Bien sûr, uzbl peut aussi utiliser un autre script de gestion. Cela nécessite simplement le changement d’une ligne dans le fichier de configuration de uzbl.

Uzbl n’a pas son propre gestionnaire de téléchargement et ne dépend d’aucun en particulier; de ce fait les capacités de navigation d’uzbl ne sont pas impactées par un mauvais gestionnaire de téléchargement. Les capacités de téléchargement d’uzbl sont aussi bonnes que le meilleur des gestionnaires de téléchargement disponibles sur le système. Bien sûr, cela vaut aussi pour tous les autres outils supplémentaires.

Utilisez l’effet levier des logiciels à votre avantage. Uzbl est conçu pour être étendu par des outils externes. Ces outils externes sont généralement contenus dans des petits scripts shell de gestion. Les scripts shell sont le ciment qui maintient ensemble les différents composants.

Le mécanisme d’historique de uzbl en est un exemple. Uzbl est configuré pour appeler un script afin d’ajouter une entrée dans l’histoire lorsque l’événement correspondant à une page entièrement chargée se produit. Le script qui ajoute l’entrée à l’histoire n’est que:

#!/bin/sh
file=/chemin/vers/uzbl-history
echo `date +'%Y-%m-%d %H:%M:%S'`" $$6 $$7" >> $file

$$6 et $$7 sont respectivement remplacés par l’URL et le titre de la page.

Pour charger une entrée, une touche est liée à l’exécution d’un script de chargement à partir de l’historique. Le script inverse l’historique pour avoir les nouvelles entrées d’abord, affiche dmenu pour permettre à l’utilisateur de sélectionner un élément, et ensuite écrit l’URL sélectionnée dans le pipe d’entrée de commande d’uzbl. Avec le contrôle d’erreur et le traitement des dossiers supprimés, voici à quoi ressemble le script:

#!/bin/sh
file=/chemin/vers/uzbl-history
goto=`tac $file | dmenu | cut -d' ' -f 3`
echo "uri $goto" > $$4

$$4 est remplacé par le chemin du pipe d’entrée de commande de l’instance actuelle d’uzbl.

Évitez les interfaces utilisateurs captives. On pourrait dire que uzbl, dans une large mesure, est en fait une interface utilisateur captive. Mais la différence avec les autres navigateurs Web, c’est qu’uzbl n’est seulement que le frontend d’une interface utilisateur captive (et le noyau du back-end). De nombreuses parties du backend sont indépendantes d’uzbl. Pour certains programmes externes, des scripts de gestion sont distribués avec uzbl, mais des fonctionnalités supplémentaires peuvent toujours être ajoutés si nécessaire.

Le fronted est captif - c’est vrai. Cela est concevable pour la tâche naviguer sur le Web, car cette tâche concerne uniquement les humains. Des programmes automatisés explorent le Web, ce qui signifie, lire directement le code source, y compris la sémantique. La représentation graphique est juste là pour que les humains comprennent cette sémantique plus intuitivement.

Faites de chaque programme un filtre. Les navigateurs graphiques se situent pour la plupart à la fin de la chaîne de circulation de l’information. Ainsi, il est difficile de voir ce que les navigateurs Web graphiques doivent filtrer. Les navigateurs graphiques existent presque exclusivement pour être utilisé de manière interactive par les humains. Le seul cas dans lequel on voudrait automatiser la fonction de rendu serait de générer des images de pages Web.

Ce qui est petit est merveilleux n’est pas facile à appliquer à un navigateur web moderne, car la technologie web est très complexe; d’où la tâche d’interprétation très complexe. Malheureusement, les navigateurs web modernes “sont” composés de plusieurs milliers de lignes de code. Cependant, utiliser l’approche boîte à outils et les containers peuvent aider à scinder le navigateur en plusieurs petites pièces.

En mars 2010, uzbl-core se compose d’environ 3500 lignes de code C. La distribution comprend 3500 lignes de code Python et Shell en plus, correspondant aux scripts de gestion et aux plugins, comme celui qui fournit une interface modale. De plus, uzbl fait appel à des outils externes comme wget et socat. Jusque là, uzbl semble assez soigné et petit. La partie la plus affreuse de uzbl est le moteur de rendu, webkit. Webkit est composé d’environ 400.000 (!) lignes de code. Malheureusement, les petits moteurs de rendu ne sont plus réalisables à cause de la nature récente du web.

Construire un prototype dès que possible. Plaetinck a rendu son code public dès le début. Discussion et développement ont été, et sont toujours, ouverts à toutes personnes intéressées, et les versions de développement d’uzbl peuvent être obtenues très facilement à partir du dépôt de code. Durant la première année d’existence d’uzbl, une nouvelle version était publiée plus d’une fois par mois. Différents forks et branches ont émergé introduisant de nouvelles fonctionnalités qui ont ensuite été prises en compte pour une fusion dans la branche principale. Des expériences avec des prototypes ont influencé le développement. Actuellement, le développement est piloté par la communauté. Plaetinck affirma, trois mois après la naissance d’uzbl:

En ce moment je code difficilement pour uzbl. Je fusionne juste le code d’autres personnes, je réfléchis beaucoup, et je dirige les débats. [21]

5.4. Problèmes

Uzbl souffre d’être différent. C’est triste, mais les gens utilisent ce qu’ils connaissent. Heureusement, l’interface utilisateur d’uzbl peut être modifiée pour ressembler de manière très similaire aux navigateurs Web bien connus, cachant ses différences internes. Mais uzbl doit fournir cette ressemblance pour être accepté comme un navigateur “normal” par les utilisateurs “normaux”.

Le problème le plus important ici, c’est le web moderne. Le web moderne est tout simplement cassé. Il a un protocole serveur stateless, des technologies mal utilisées, et il est désespérément surchargé. Résultat, les moteurs de rendu “doivent” être composés de centaines de milliers de lignes de code. Ils doivent également combiner et intégrer de nombreuses technologies différentes pour rendre notre web moderne accessible. De plus, cela se concrétise par une tentative ratée de fournir une bonne ergonomie. Les convertisseurs de site web vers image sont presque impossibles à exécuter sans une intervention humaine en raison de l’état des sessions, des liens profonds, et des technologies “non automatisables”. Le web a été détourné afin de tenter de répondre à toutes sortes de désirs. Aujourd’hui les navigateurs web, et, finalement les utilisateurs, en souffrent.

5.5. Résumé

Uzbl est un navigateur qui souscrit à la philosophie d’Unix“, c’est comme cela qu’uzbl est considéré par ses auteurs. En effet, uzbl suit la philosophie d’Unix à bien des égards. Il se compose de parties indépendantes qui travaillent ensemble, tandis que son noyau est essentiellement un médiateur qui connecte les pièces ensemble. L’effet de levier logiciel est mis fortement à profit. Des outils externes sont utilisés, les tâches indépendantes sont séparées en composants indépendants et collés ensemble par de petits scripts de gestion. Depuis uzbl se compose à peu près d’un ensemble d’outils et d’un peu de colle, n’importe qui peut assembler les composants et le faire évoluer selon son désir. La flexibilité et la personnalisation sont des caractéristiques précieuses pour les utilisateurs avancés, mais il permet aussi aux utilisateurs débutants de le comprendre et de l’utiliser. Mais le problème principal pour uzbl est le web moderne, qui rend très difficile la conception d’un navigateur sain. En dépit de cette mauvaise situation, uzbl fait un assez bon travail.

6.0. Dernières réflexions

Cet article explique pourquoi une bonne conception est importante. Il a présenté la philosophie d’Unix comme un ensemble de directives qui favorisent une bonne conception pour créer des logiciels de bonne qualité. Ensuite, un logiciel concret qui a été conçu avec à l’esprit la philosophie d’Unix a été étudié. Tout au long de ce document, l’objectif était d’expliquer pourquoi quelque chose doit être conçu à la façon d’Unix. Des arguments ont été avancés pour justifier l’affirmation selon laquelle la philosophie d’Unix est une manière préférable de concevoir des logiciels. La philosophie d’Unix est proche de la vision du développeur de logiciels. Son principal objectif est de dompter la bête connue sous le nom de “complexité logiciel”. Par conséquent, il recherche en premier lieu la simplicité du logiciel.

Il pourrait sembler aux utilisateurs que la convivialité est un objectif mineur. En réalité, la Philosophie d’Unix voit l’ergonomie pas forcément comme intuitive, mais elle fournira un moyen cohérent d’accéder à la puissance énorme de l’effet de levier logiciel. Être capable de résoudre un problème spécifique concret devient de moins en moins important à mesure qu’il existe aujourd’hui des logiciels pour presque toutes les tâches possibles. Mais la qualité des logiciels compte. Il est important que nous ayons de bons logiciels.

Mais pourquoi la philosophie d’Unix?

Le plus grand problème du développement de logiciels est la complexité. C’est la seule partie du travail que les ordinateurs ne peuvent pas prendre en charge. La philosophie d’Unix lutte contre cette complexité, car elle est sa principale ennemie. Par ailleurs, l’avantage le plus significatif d’un logiciel est sa capacité d’effet levier. Les logiciels actuels ne parviennent toujours pas à faire un meilleur usage de cette faculté. La philosophie d’Unix se focalise sur la mise à profit de cette formidable possibilité.

A propos de ce document

Auteur: Markus Schnalke
http://marmaro.de/

Traduction & adaptation: F.
http://www.f.0x2501.org

Relecture & correction: Elie Gavoty

Je tiens à remercier vivement Markus pour la qualité de son texte. Merci pour m’avoir autorisé à le traduire; longtemps j’ai voulu écrire un article sur la philosophie d’Unix mais définitivement c’est cet article que j’aurais aimé écrire. Je vous encourage à le lire dans sa version anglaise. Vous y trouverez une étude supplémentaire, qui n’est pas incluse ici, sur mh un MUA. Je me suis limité à la deuxième étude sur uzbl.

Ce document a été publié le 16/04/2010 pour le séminaire “Analyse logicielle” de l’Université d’Ulm.
Document d’origine en version anglaise (PDF): http://marmaro.de/docs/studium/unix-phil/unix-phil.pdf
Page d’origine: http://marmaro.de/docs/studium/unix-phil/

Traduction sous licence CC BY-SA: http://creativecommons.org/licenses/by-sa/3.0/fr/

Références

  1. Arch Linux Forums, Thread “Arch Philosophy/Structure Applied to a Browser”, Spring 2009.
    En ligne: http://bbs.archlinux.org/viewtopic.php?id=67463

  2. Jason Aughenbaugh, Jonathan Jessup, and Nicholas Spicher, “Building Unix,” in Unix: An Oral History.
    En ligne: http://www.princeton.edu/~hos/frs122/unixhist/finalhis.htm

  3. Morris I. Bolsky and David G. Korn, The KornShell: command and programming language, p. 254–290, Prentice Hall, 1989. ISBN: 0-13-516972-0

  4. Frederick P. Brooks, Jr., “No Silver Bullet: Essence and Accidents of Software Engineering,” in Information Processing 1986, the Proceedings of the IFIP Tenth World Computing Conference, p. 1069–1076, Elsevier Science B.V., Amsterdam, The Netherlands, 1986.

  5. Frederick P. Brooks, Jr., The Mythical Man-Month: Essays on Software Engineering, Anniversary Edition, Addison Wesley Longman, Inc., 1995. ISBN: 0-201-83595-9

  6. Bryan Costales and Eric Allman, sendmail, p. xix, O’Reilly, 2003. ISBN: 1-56592-839-3

  7. Charles Darwin, On the Origin of Species, John Murray, London, 1859.
    En ligne: http://en.wikisource.org/wiki/On_the_Origin_of_Species_(1859)

  8. Mike Gancarz, The UNIX Philosophy, Digital Press, 1995. ISBN: 1-55558-123-4

  9. ISO Standard 9126: Software Engineering – Product Quality, part 1, International Organization for Standardization, Geneve, 2001.

  10. Brian W. Kernighan and Rob Pike, The Practice of Programming, Addison-Wesley, 1999. ISBN: 0-201-61586-X

  11. Michael S. Mahoney, The UNIX Oral History Project, Bell Laboratories.
    En ligne: http://www.princeton.edu/˜hos/Mahoney/expotape.htm

  12. MH/nmh workers, MH/nmh Documentation, pp. mh-profile(5), mh-sequence(5). Distributed with nmh-1.3.
    En ligne en différentes versions:
    http://linux.die.net/man/5/mh-profile
    http://linux.die.net/man/5/mh-sequence

  13. Website of nmh.
    En ligne: http://nmh.nongnu.org

  14. Jerry Peek, MH & xmh: Email for Users & Programmers, p. Appendix B, O’Reilly, 1995.
    En ligne: http://rand-mh.sourceforge.net/book/

  15. Eric S. Raymond, The Art of UNIX Programming, Addison-Wesley, 2003.
    En ligne: http://www.faqs.org/docs/artu/

  16. Gunnar Ritter, mail, Mail, mailx, nail—history notes, 2007-01-28.
    En ligne: http://heirloom.sourceforge.net/mailx_history.html

  17. Peter H. Salus, A Quarter Century of UNIX, Addison-Wesley, 1994. ISBN: 0-201-54777-5

  18. Ken Thompson and Dennis M. Ritchie, Unix Programmer’s Manual, First Edition, p. mail(1), 1971-11-03.
    En ligne: http://cm.bell-labs.com/cm/cs/who/dmr/pdfs/man12.pdf

  19. DigDuality (posted by timothy), Meet Uzbl – a Web Browser With the Unix Philosophy, Slashdot, 2009-09-05.
    En ligne: http://linux.slashdot.org/story/09/09/05/2142235

  20. Website of uzbl.
    En ligne: http://uzbl.org

  21. Koen Vervloesem, Uzbl: a browser following the UNIX philosophy, LWN.net, 2009-07-15.
    En ligne: http://lwn.net/Articles/341245/

  22. Willis H. Ware, RAND and the Information Evolution: A History in Essays and Vignettes, p. 128–137, The RAND Corporation, 2008. ISBN: 978-0-8330-4513-3.
    En ligne: http://www.rand.org/pubs/corporate_pubs/CP537/

  23. Wikipedia, The Free Encyclopedia, Unix philosophy, Version of 2010-03-21.
    En ligne: http://en.wikipedia.org/w/index.php?title=Unix_philosophy&oldid=351189719