Table des matières
Principes
Conception et mise en œuvre Ne se chevauchent pas
Les documents de planification qui n'ont plus rien de commun avec la mise en œuvre font plus de mal que de bien. C'est pourquoi il ne faut pas abandonner la planification, mais minimiser les chances d'incohérence.
Mutabilité | |
---|---|
Correction | |
Efficacité de la production | |
Amélioration continue | |
Équipe |
L'un des problèmes fondamentaux du développement de logiciels est que les implémentations ne reflètent pas la planification préalable. Les diagrammes de conception accrochés au mur n'ont presque plus rien à voir avec la réalité du code. La cause en est une violation du principe fondamental DRY : la conception et l'implémentation sont des répétitions de la même chose, la structure d'un logiciel. Étant donné que l'implémentation suit la conception et se taille la part du lion, les deux se détraquent rapidement si les modifications de la structure pendant l'implémentation ne sont pas toujours intégrées à la conception. Les diagrammes de conception ne sont alors plus d'aucune utilité après le début de l'implémentation.
Comment la situation peut-elle être améliorée ? Faut-il peut-être renoncer à la conception si, en fin de compte, la "vérité structurelle" réside dans la mise en œuvre ? Non, certainement pas. La conception est nécessaire. Sans planification, il n'y a pas d'objectif. Mais la conception et l'implémentation doivent être conformes au principe DRY. C'est pourquoi la conception et l'implémentation doivent se chevaucher le moins possible. Leur interface doit être mince. Si c'est le cas, ils ne représentent plus des répétitions, mais décrivent des choses différentes. Cela signifie que la conception/l'architecture ne s'occupe pas de l'implémentation et que l'implémentation ne s'occupe pas de l'architecture.
Et où se situe cette ligne de démarcation ? Au niveau de ce que l'on appelle les composants (voir ci-dessous les pratiques). Les architectes ne se préoccupent pas de la structure interne des composants. Pour eux, ce sont des boîtes noires dont la structure de classe n'est pas pertinente pour l'architecture. Inversement, pour un implémenteur de composants, l'architecture n'est pas pertinente. Ce qu'il doit implémenter résulte des contrats de composants que son composant importe et exporte. Il n'a pas besoin de connaître un contexte plus large.
La tâche de l'architecture est donc de décomposer le logiciel en composants, de définir leurs interdépendances et de décrire les prestations dans des contrats. Ces structures sont ensuite gérées uniquement par les architectes. Et la tâche de l'implémentation est de réaliser les composants définis par l'architecture. La manière dont ils le font n'est pas pertinente pour l'architecture. Leur structure interne est invisible pour l'architecture.
Implementation Reflects Design
La mise en œuvre, qui peut s'écarter à volonté de la planification, mène directement à l'impossibilité d'attente. La mise en œuvre nécessite donc un cadre physique défini par la planification.
Mutabilité | |
---|---|
Correction | |
Efficacité de la production | |
Amélioration continue | |
Équipe |
L'architecture et l'implémentation ne doivent pas se chevaucher afin de ne pas enfreindre le principe DRY. Cela permet d'éviter les incohérences qui peuvent survenir lorsqu'on modifie quelque chose d'un côté sans mettre à jour cette modification de l'autre côté.
Néanmoins, l'architecture donne des indications sur la mise en œuvre. Pas ses détails, mais sa forme fondamentale. L'architecture définit les éléments structurels et leurs relations au sein d'un système de code. L'implémentation n'existe donc pas indépendamment de l'architecture, même en l'absence de chevauchements, mais pour ainsi dire en son sein.
C'est précisément ce qui devrait se refléter dans l'implémentation. Cela facilite la compréhension et permet de s'assurer que l'implémentation suit effectivement l'architecture. Les éléments structurels définis par l'architecture à différents niveaux d'abstraction ne devraient donc pas être "mélangés" dans un grand "pot de code" (p. ex. une grande solution Visual Studio). Il est préférable de manifester les structures logiques de l'architecture de la manière la plus physique possible, afin d'augmenter la productivité et de faciliter les tests.
- Les structures prévues par l'architecture à différents niveaux d'abstraction devraient se refléter le plus largement possible dans l'organisation du code. Cela signifie d'une part que l'architecture utilise avant tout des unités de code physiques comme éléments structurels. D'autre part, ces éléments structurels doivent également être clairement visibles dans le code source ou dans l'organisation du code dans le référentiel.
- Lors du travail sur l'implémentation des éléments de structure et en particulier au sein des composants, il doit être impossible de modifier l'architecture "à la volée". Celui qui travaille dans ou sur un élément de structure, c'est-à-dire sur une partie, ne doit pas pouvoir modifier ad hoc la structure environnante, c'est-à-dire l'ensemble. Ce n'est que si cela est garanti que l'entropie d'un logiciel ne croît pas de manière incontrôlée. C'est important, car l'objectif principal de l'architecture est de minimiser l'entropie et donc la complexité du logiciel.
La planification est nécessaire. La mise en œuvre ne doit pas torpiller la planification. (Même si les connaissances acquises pendant la mise en œuvre peuvent naturellement avoir un impact sur la planification). C'est pourquoi la planification et la mise en œuvre doivent être dissociées. Et lorsque cela n'est pas possible, la planification devrait travailler avec des moyens de mise en œuvre et la mise en œuvre devrait refléter physiquement la planification.
You Ain't Gonna Need It (YAGNI)
Les choses dont personne n'a besoin n'ont aucune valeur. Ne perdez donc pas de temps avec elles.
Mutabilité | |
---|---|
Correction | |
Efficacité de la production | |
Amélioration continue | |
Développeur unique |
Le principe YAGNI (You Ain't Gonna Need It) est l'un des plus simples dans le développement de logiciels - et pourtant, c'est probablement le principe le plus souvent violé après le principe DRY. C'est pourquoi YAGNI n'est pas seulement au début du degré rougemais ici aussi, vers la fin du parcours, à travers le Système de valeurs.
Le principe YAGNI est dû au rapport particulier entre la précision des exigences et la matérialité du produit dans le développement de logiciels. Les exigences sont notoirement imprécises ou changeantes et le produit dans lequel elles doivent être mises en œuvre est immatériel. Par rapport à la construction de machines ou de bâtiments, le matériel est donc infiniment flexible et peut en principe s'adapter à pratiquement n'importe quelle exigence avec relativement peu d'efforts. Une grande volatilité ou imprécision s'accompagne donc d'une grande flexibilité. Dans un premier temps, cela semble idéal.
La pratique montre cependant que c'est précisément dans cette relation que se trouvent les germes de l'échec de nombreux projets. A court terme, les projets tentent de faire ce qu'il faut avec ce qui est le plus évident :
- Les exigences imprécises sont souvent compensées par des produits qui tentent de compenser l'imprécision. L'immatérialité des logiciels est utilisée pour implémenter de manière si large et flexible que même les exigences encore inconnues ou floues sont satisfaites quasiment par anticipation.
- Les exigences qui changent constamment sont mises à jour le plus rapidement possible dans le produit, car cela est possible grâce à son immatérialité.
Toutefois, à long terme, un tel comportement est contre-productif :
- L'obéissance anticipée entraîne une largeur et une flexibilité qui ne sont pas vraiment nécessaires. Elle réalise des fonctionnalités qui ne sont pas appliquées.
- Les modifications rapides apportées aux logiciels en raison de l'évolution des exigences entraînent une érosion de la qualité du code. Les logiciels sont certes immatériels et flexibles - mais toutes les structures logicielles ne sont pas évolutives ou même compréhensibles.
Des situations d'exigences peu claires et changeantes dans le contexte de la grande flexibilité fondamentale des logiciels conduisent rapidement à des dépenses inutiles et à un code friable. Un grand nombre de projets qui ont fait exploser leurs limites budgétaires et un nombre encore plus grand de projets qui sont devenus ingérables après quelques années seulement en témoignent de manière éloquente.
En tant que développeurs de logiciels professionnels, les CCD considèrent qu'il est de leur devoir de s'opposer chaque jour à une telle évolution. Compte tenu de la nature indéniable du logiciel - il est et reste immatériel -, l'approche consiste à traiter les exigences. C'est l'origine du principe YAGNI.
Le principe YAGNI est comme un couteau bien aiguisé : celui qui l'applique découpe un problème en petits cubes de ce qui est immédiatement nécessaire. Selon le principe YAGNI, seul ce qui est indubitablement et immédiatement utile est mis en œuvre. Tout le reste... eh bien, cela viendra plus tard. En ce sens, YAGNI va de pair avec la règle "Décider le plus tard possible" de l'approche de l'apprentissage. Lean Software Development.
Le principe de YAGNI est pertinent à tous les niveaux du développement logiciel et à toutes les étapes. Chaque fois que vous vous demandez "Devrais-je vraiment faire cet effort ?" ou "Avons-nous vraiment besoin de cela ?" - même si c'est de manière très timide et discrète au fond de votre tête -, c'est un cas d'application du principe YAGNI. Il dit : en cas de doute, décide de ne pas faire d'effort.
Cela semble facile, mais c'est difficile. D'où les fréquentes infractions. Il existe de nombreuses forces qui contredisent la décision de ne pas faire un effort. "Oh, ce n'est pas tant d'efforts que ça" ou "Si nous n'anticipons pas maintenant, nous ne pourrons pas faire autrement à l'avenir" ne sont que deux justifications évidentes pour les efforts, même si des doutes subsistent quant à leur utilité. Cela concerne les décisions architecturales (p. ex. faut-il déjà commencer par une architecture distribuée, même si la charge actuelle n'en aurait pas encore besoin ?) ainsi que les décisions locales (p. ex. faut-il déjà optimiser l'algorithme, même s'il ne pose pas encore de problèmes de performance pour le moment ?)
Le client ne paie que pour une utilité immédiate. Ce qu'il ne peut pas spécifier clairement aujourd'hui ne lui est pas utile. Vouloir le prévoir lors de la mise en œuvre implique donc des dépenses sans générer d'avantages. Lorsque le client saura plus tard plus précisément ce qu'il veut, c'est alors - et pas avant ! - il est temps de répondre à sa volonté. Mais chaque fois qu'un projet tente d'anticiper cette volonté, il risque d'être contredit par la réalité de la volonté du client demain. Une fonctionnalité - fonctionnelle ou non - qui est implémentée aujourd'hui sans exigence claire n'intéressera peut-être plus le client demain. Ou alors, elle ne sera plus aussi importante pour lui qu'une autre fonctionnalité.
Cela signifie pour le développement de logiciels
- Mettre en œuvre uniquement des exigences claires.
- Le client donne la priorité à ses exigences claires.
- Mettre en œuvre les exigences claires dans l'ordre de leur priorité.
- Mettre en place un processus de développement et une structure de code, à grande et à petite échelle, de manière à ce qu'il n'y ait pas de crainte à réaliser des exigences changeantes et nouvelles.
En tant que développeurs professionnels, les CCD communiquent cette approche sans équivoque au client. Ce faisant, ils
- prêts à fournir un service, car ils ne doivent pas refuser une demande claire au client
- responsables, car ils n'utilisent le budget que pour des avantages clairement formulés
- protecteur à l'égard du code, car il le préserve d'une surcharge de choses finalement inutiles
YAGNI n'est donc pas seulement un principe que chaque développeur devrait suivre, mais aussi un principe pour les projets et les équipes, donc au niveau de l'organisation. YAGNI doit toujours être pris en compte, tout comme DRY. En cas de doute, reporte la décision si possible. Sinon, décide de ne pas faire d'efforts. Cela permet de se détendre, d'alléger la charge de travail et de parvenir plus rapidement au succès.
Pratiques
Conception avant mise en œuvre
Une solution doit être conçue avant d'être mise en œuvre. Dans le cas contraire, il n'y a pas de réflexion cohérente sur la solution.
Mutabilité | |
---|---|
Correction | |
Efficacité de la production | |
Amélioration continue | |
Équipe |
La tâche d'un développeur consiste à traduire les exigences en code. Pour cela, il est nécessaire de développer une solution pour les exigences. Il faut réfléchir. Mais comment cela peut-il se faire dans de bonnes conditions si les développeurs sautent directement dans le code ?
Dans des cas triviaux, il peut être possible d'écrire directement du code. Cependant, même en passant directement au codage, on réfléchit à la solution. Toutefois, cela se fait plutôt inconsciemment, mais surtout pendant de la mise en œuvre. Le développeur réfléchit un peu, codifie, réfléchit, codifie, etc. Il manque ici une réflexion cohérente sur la solution, séparée de la mise en œuvre.
Au plus tard lorsqu'un groupe de développeurs souhaite travailler ensemble en tant qu'équipe, la conception doit être séparée dans le temps de la réalisation. Dans le cas contraire, il n'est pas possible de procéder de manière fluide en répartissant le travail.
La conception permet à l'équipe ou à un développeur individuel de réfléchir à des principes importants avant même de coder. Par exemple, il n'y a pas de méthodes ou de classes à responsabilités multiples, car la réflexion sur les principes de base est déjà présente au niveau de la conception. Principe de responsabilité unique (SRP) de la réflexion. L'équipe s'épargne ainsi le travail de refactorisation qui survient lorsque l'on codifie "à tout va".
Voir aussi https://flow-design.info.
Livraison continue (CD)
En tant que développeur Clean Code, je veux être sûr qu'un setup installe correctement le produit. Si je ne le découvre que chez le client, il sera trop tard.
Mutabilité | |
---|---|
Correction | |
Efficacité de la production | |
Amélioration continue | |
Équipe |
Sur le site degré vert nous avons mis en place le processus d'intégration continue pour le build et le test. Ainsi, le processus d'intégration continue veille à ce que les erreurs soient rapidement détectées pendant les phases de construction et de test. Si, par exemple, une modification du code entraîne l'impossibilité de traduire un autre composant, le processus d'intégration continue signale l'erreur peu de temps après la validation de la modification. Mais si, à la fin, on produit un programme d'installation qui ne peut pas être installé à cause d'erreurs, nous n'avons quand même pas atteint notre objectif : un logiciel qui fonctionne et qui peut être installé chez nos clients.
Par conséquent, nous devons également automatiser les phases d'installation et de déploiement afin de pouvoir les exécuter en appuyant sur un bouton. C'est la seule façon de s'assurer que nous produisons des logiciels installables. Et l'automatisation garantit que personne n'oublie une étape importante qui doit être exécutée "à pied". Ainsi, chaque membre de l'équipe peut produire et installer à tout moment l'état actuel du produit, prêt à être installé.
Voir aussi sous Outils.
Le livre le plus important dans ce contexte est sans doute Accélérer être.
Développement itératif
D'après von Clausewitz, aucun projet, aucune mise en œuvre ne survit au contact avec le client. Le développement de logiciels a donc tout intérêt à pouvoir corriger sa trajectoire.
Mutabilité | |
---|---|
Correction | |
Efficacité de la production | |
Amélioration continue | |
Équipe |
Bien entendu, le développement de logiciels passe toujours par la planification, l'implémentation et le test par le client. Il est toutefois erroné de penser qu'un projet peut se contenter d'une phase de planification, d'une phase d'implémentation et d'une phase de test client. Cela ne fonctionne - si tant est que cela fonctionne - que dans des scénarios triviaux, où toutes les exigences sont connues lors de la phase de planification. Dans les projets réels, chaque phase fournit des informations pour les phases précédentes. Le test client, en particulier, a des conséquences sur la planification et l'implémentation.
De telles connaissances ne peuvent toutefois avoir une influence sur un projet que si la procédure n'est pas linéaire. S'il n'est pas possible de revenir à une phase antérieure à partir d'une phase ultérieure, le feedback est inutile.
Pour pouvoir intégrer le feedback dans un produit logiciel, le processus de développement doit comporter des boucles. Il faut notamment passer de la phase de test du client à la planification. En d'autres termes, le développement de logiciels ne peut se faire que de manière itérative, c'est-à-dire en plusieurs passages, sur le catalogue d'exigences du client. Celui qui tente de livrer "en une seule fois" (big bang) va à l'encontre de ce constat. Le processus de développement logiciel doit plutôt être planifié de manière à ce qu'il "croque" les exigences par petites bouchées. Chacune de ces bouchées ne devrait pas être plus grande que le temps nécessaire pour passer de la planification au test client, soit plus de 2 à 4 semaines. Ce n'est qu'à cette condition que le feed-back du client sera suffisamment fréquent pour que l'on ne s'égare pas trop longtemps dans la mise en œuvre.
Le développement de logiciels est donc un processus d'apprentissage. Au cours de ce processus, l'équipe de projet apprend à connaître les exigences du client. Elle l'écoute, planifie, met en œuvre et livre une version du logiciel qui reflète la compréhension de ce qu'elle a entendu. Ensuite, l'équipe écoute à nouveau, planifie plus/nouveau en fonction des connaissances actuelles, etc. etc. toujours en cercle. Itération après itération. Parfois, quelque chose d'une itération précédente est affiné, parfois de nouvelles choses sont ajoutées.
Mais le développement d'un logiciel n'est pas le seul processus d'apprentissage. L'apprentissage devrait également avoir lieu au niveau de l'organisation. L'équipe ne doit pas seulement apprendre quelque chose sur le client, mais aussi sur elle-même. C'est pourquoi il devrait toujours y avoir des "points d'arrêt" au cours desquels l'équipe réfléchit à sa démarche. Les enseignements tirés de cette rétrospective sont ensuite intégrés dans la prochaine itération du développement organisationnel. Ici, le degré bleu fait suite au degré rouge, dont fait partie la réflexion personnelle quotidienne.
Bien sûr, chaque itération doit aussi avoir une fin. Et pour savoir si l'on a terminé, il faut définir clairement au préalable ce qui doit être atteint au cours de l'itération. La possibilité d'atteindre les objectifs ne peut toujours être qu'estimée, là aussi la réflexion aide à améliorer progressivement les estimations jusqu'à ce qu'elles soient suffisamment précises pour la planification. Mais quand l'objectif préalablement défini est-il atteint ? Qu'est-ce qui est fait ? L'objectif principal est de livrer des logiciels fonctionnels à nos clients. Par conséquent, cet objectif ne peut être atteint que si nous avons produit un logiciel prêt à être livré. Cela signifie notamment que le logiciel a été testé et qu'il peut être installé par configuration. L'intégration continue nous permet de garantir cela en permanence. Nous ne pouvons en aucun cas décider juste avant la fin d'une itération qu'un objectif est atteint alors que tous les tests ne sont pas encore terminés.
Voir aussi sous Outils.
Développement incrémental
Seul le travail par incréments permet au Product Owner de donner un feedback.
Mutabilité | |
---|---|
Correction | |
Efficacité de la production | |
Amélioration continue | |
Équipe |
Un incrément représente une coupe verticale des différents aspects d'un système logiciel. Un incrément est donc un morceau de logiciel exécutable. L'incrément peut être mis à la disposition d'un Product Owner sur une machine de test afin d'obtenir un feedback.
Un feedback régulier à intervalles rapprochés, à la fin de chaque itération, est la définition de l'agilité.
En revanche, si l'on procède horizontalement plutôt que verticalement, on obtient des modules qui ne sont pas exécutables de manière autonome. Un Product Owner ne peut pas donner de feedback sur de tels modules. Il n'est donc pas possible d'adopter une approche véritablement agile.
Orientation des composants
Les logiciels ont besoin de modules "boîte noire" qui peuvent être développés et testés en parallèle. Cela favorise l'évolutivité, la productivité et l'exactitude.
Mutabilité | |
---|---|
Correction | |
Efficacité de la production | |
Amélioration continue | |
Équipe |
Les principes du Système de mesure CCD ont jusqu'à présent surtout porté sur de petits extraits de code. Qu'est-ce qui devrait figurer dans une méthode, qu'est-ce qui devrait être réparti sur plusieurs ? Quelles méthodes une classe devrait-elle publier ? D'où devrait provenir un objet client pour un objet de service ? Jusqu'à présent, il s'agissait de principes pour le développement de logiciels à petite échelle.
Mais le système de valeurs CCD n'a-t-il rien à dire sur les structures plus grandes, sur le développement logiciel en général ? Qu'en est-il de l'architecture logicielle ? C'est précisément là qu'intervient le principe de l'orientation vers les composants. Jusqu'à présent, nous avons certes déjà utilisé le mot "composant", mais de manière plutôt laxiste et dans un sens familier. Mais à partir de maintenant, il faut Composant décrire quelque chose de très spécifique que nous considérons comme fondamental pour les logiciels évolutifs.
Tant que nous pensons que les logiciels ne sont constitués que de classes avec des méthodes, nous essayons de décrire les ordinateurs au niveau des transistors. Mais en fin de compte, cela ne fonctionne pas, car nous sommes étouffés par la richesse des détails. Même le fait de regrouper les classes en couches n'aide pas beaucoup. Nous avons plutôt besoin d'un outil de description pour les grandes structures logicielles. Mais ce n'est pas tout : l'outil de description devrait également être un outil d'implémentation - comme les classes - afin que le modèle, le plan, la description se reflète dans le code.
Les processus du système d'exploitation sont certes de tels moyens architecturaux, mais en fin de compte, ils sont eux aussi trop grands. Tant que l'EXE d'un processus d'une application se compose de plusieurs centaines ou milliers de classes, nous ne gagnons rien.
Le principe de l'orientation vers les composants apporte toutefois une aide. Il signifie qu'un processus d'application est d'abord constitué de composants et non de classes. Seuls les éléments constitutifs des composants sont des classes. Et qu'est-ce qu'un composant ? Il existe plusieurs définitions des composants :
- Les composants sont des unités fonctionnelles binaires. (Une classe, en revanche, est une unité fonctionnelle au niveau du code source).
- La performance des composants est décrite par un contrat séparé ( !). (En revanche, la description de la performance d'une classe se trouve en elle. C'est la somme de ses signatures de méthode).
Lors de la conception d'un logiciel, après avoir défini les processus, un CCD cherche donc d'abord les composants qui devraient constituer les processus. Il se demande quels sont les "blocs de services" qui constituent l'application ? Et le CCD considère ces blocs comme des boîtes noires en ce qui concerne leur structure en classes. Ces blocs sont des assemblages dont le service est bien défini, mais dont la structure est inconnue.
Un composant client C ne connaît donc rien de la structure de classe de son composant de service S. C ne connaît que le contrat de S, qui est indépendant de l'implémentation de S. Les contrats sont donc aux composants ce que les interfaces sont aux classes. Ce n'est pas un hasard si les contrats sont en grande partie, voire entièrement, constitués d'interfaces.
Les composants sont donc des éléments de planification et de mise en œuvre. Pour souligner cela, les composants sont mis en œuvre de manière physiquement indépendante les uns des autres ; un moyen éprouvé pour cela est l'utilisation de Établissements de composantsc'est-à-dire des solutions Visual Studio séparées pour chaque implémentation de composant. Cela favorise non seulement la concentration sur une tâche, car pendant le travail sur un composant dans l'IDE, on ne voit que son code. En outre, cela favorise également les tests unitaires cohérents avec l'utilisation de leurres, car le code source des autres composants n'est pas visible. En outre, une telle organisation du code augmente la productivité, car les composants peuvent être implémentés en parallèle grâce à leurs contrats séparés. Enfin, une isolation physique s'oppose à l'augmentation insidieuse de l'entropie dans le code. En effet, lorsque les liens entre les composants ne peuvent être établis que via un contrat, le couplage est lâche et contrôlé.
L'orientation vers les composants ne comprend donc pas seulement des unités de code binaires, plus grandes, avec des contrats séparés, mais aussi le développement des contrats avant leur mise en œuvre (La conception en premier lieu). En effet, dès que les contrats qu'un composant importe et exporte sont définis, le travail sur le composant peut commencer indépendamment de tous les autres.
Voir aussi sous Outils.
Pour la notion de composant, voir aussi ce Article de blog sur la hiérarchie des modules.
Test First
Le client est roi et détermine la forme d'un service. Les implémentations de services ne sont donc adaptées que si elles sont poussées par un client.
Mutabilité | |
---|---|
Correction | |
Efficacité de la production | |
Amélioration continue | |
Développeur unique |
Si l'orientation vers les composants exige de définir les contrats pour les composants indépendamment de leur mise en œuvre, la question se pose de savoir comment cela doit se faire. Par une discussion autour d'une table ronde ? C'est certainement une solution. Une meilleure solution consiste toutefois à ne pas concevoir les contrats sur un tableau noir, mais à les couler immédiatement dans le code. Les contrats de composants - ou, plus généralement, toute interface de code - servent en fin de compte d'API à d'autres codes. Il est donc cohérent et efficace de spécifier des interfaces à partir de ce code.
C'est la préoccupation de Test first. Test first est basé sur l'idée que les unités fonctionnelles (méthodes, classes, etc.) sont caractérisées par des relations client-service. Ces relations tournent autour de l'interface entre le client et le service. Et cette interface doit être déterminée par le client. En tant que client du service, le client est roi. C'est lui que le service doit servir et c'est donc à lui que l'interface du service doit s'adresser.
C'est pour cette raison que la définition des interfaces des unités de code d'un logiciel se fait de l'extérieur vers l'intérieur. À l'extérieur, à l'interface utilisateur, se trouve le client ultime, l'utilisateur. Il définit l'interface visuelle/haptique des unités de code de l'interface utilisateur. Celles-ci sont à leur tour les clients des couches de code sous-jacentes. Ils sont ensuite les clients des couches inférieures, etc. Les performances et les interfaces des couches de code les plus profondes ne peuvent donc être déterminées que si celles des couches supérieures le sont déjà, etc.
Cela va à l'encontre de l'approche fréquente de définition ascendante des unités de code. Les projets commencent volontiers par définir et mettre en œuvre une couche d'accès aux données. C'est compréhensible, car une telle fonctionnalité fondamentale semble être la condition préalable à tout le reste. Mais cette approche est problématique, comme le montrent de nombreux projets qui ont échoué :
- Celui qui spécifie et met en œuvre de bas en haut, de l'intérieur vers l'extérieur, ne propose que très tardivement une valeur au client. C'est pour le moins frustrant, voire contre-productif.
- Celui qui procède de bas en haut dans la spécification, spécifie sans exigences précises du client ultime, l'utilisateur. Ce qu'il spécifie risque donc d'être finalement trop général et donc peu maniable - ou tout simplement de ne pas être utilisé (une violation du principe YAGNI, voir ci-dessus et dans le degré rouge).
- Ceux qui implémentent de bas en haut risquent de ne pas vraiment découpler. En effet, si des couches inférieures sont nécessaires pour implémenter les couches supérieures, il est probable que l'on n'utilise pas de tests unitaires vraiment isolés avec des leurres, ni d'inversion du contrôle.
Les développeurs Clean Code évitent toutefois ces problèmes. Ils spécifient l'interface non seulement avant les implémentations (Contract-first, voir ci-dessus l'orientation vers les composants), mais aussi de l'extérieur vers l'intérieur et de manière très pratique par le biais du codage. Avec les moyens des tests automatisés, il est en effet très facile de définir les interfaces par petites étapes sous forme de tests.
Test first ajoute ainsi un côté sémantique aux contrats syntaxiques (par exemple les interfaces). En l'absence d'autres méthodes formelles pour spécifier la sémantique, les tests sont le seul moyen de formaliser les exigences. Celui qui veut attribuer à un développeur un composant à implémenter fait donc bien de ne pas seulement spécifier syntaxiquement sa "surface" (API), mais aussi le comportement souhaité sous forme de tests.
Cela présente de nombreux avantages :
- La forme d'une interface est directement pilotée par le client et donc d'une pertinence maximale. YAGNI n'a aucune chance.
- Les tests ne sont pas seulement des tests, mais aussi de la documentation de spécification. Les utilisateurs d'une interface et les implémenteurs peuvent les étudier de la même manière. Une documentation séparée est en grande partie superflue. Cela satisfait au principe DRY.
- Les spécifications ne sont pas seulement des textes passifs, mais du code exécutable. Une fois qu'une implémentation est disponible, elle peut être testée par rapport à ces tests. Les spécifications et les tests ne sont donc pas des phases successives qui prennent beaucoup de temps. Cela augmente la productivité. L'assurance qualité se trouve ainsi déjà en amont de l'implémentation.
Voir aussi sous Outils.
Poursuivre avec le degré blanc