Qualité Developpement Henocque Esil Info.key
Transcription
Qualité Developpement Henocque Esil Info.key
Qualité en Développement par Laurent Henocque Enseignant Chercheur LSIS-Polytech Marseille/IRM http://laurent.henocque.com mis à jour en Novembre 2012 mardi 20 novembre 12 1 Licence Creative Commons Cette création est mise à disposition selon le Contrat Paternité-Partage des Conditions Initiales à l'Identique 2.0 France disponible en ligne http://creativecommons.org/licenses/by-sa/2.0/fr/ ou par courrier postal à Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. mardi 20 novembre 12 2 Objectifs • Ce cours a pour projet de donner les bases de techniques de travail reconnues comme nécessaires pendant tout le cycle de vie du logiciel, et • d'apprendre les méthodes qui permettent d'affronter systématiquement et de résoudre les problèmes. mardi 20 novembre 12 3 Plan • Concepts fondamentaux de qualité logicielle, • notamment les notions de contrats, d'invariants et une discussion sur la notion de test. • Comment l’activité de programmation peut être organisée, dans un objectif de qualité. • Considérations techniques sur la qualité. • Aspects humains (psychologiques notamment) mardi 20 novembre 12 4 Qualité en programmation Notions fondamentales mardi 20 novembre 12 5 Le contrat • Principe fondamental dans toutes les activités industrielles ou plus généralement économiques, le contrat est aussi un fondement de toutes les étapes de l'élaboration d'un logiciel, de sa spécification jusqu'à son abandon. • Bien sûr, en fonction du point auquel on se situe, la nature et les effets des différents contrats seront variables. mardi 20 novembre 12 6 Le contrat • Le contrat garantit une communication sans défaut, par un examen exhaustif de tous les cas nécessaires • un bon contrat lève toute ambiguïté entre ses parties. • Le contrat est un support fondamental de la simplicité des solutions mises en oeuvre pour le respecter. • En effet, il permet de travailler en monde clos, et de ne pas anticiper des évolutions improbables. mardi 20 novembre 12 7 Documents contractuels • Réponses aux questions suivantes : • "quoi" : quelles sont les fonctionnalités à implanter, • "comment ": comment accède t'on à ces fonctionnalités, • "quand" : quelles sont les limites hors desquelles ces fonctionnalités ne sont pas accessibles (pas implantées). • Ces contrats lient dans tous les cas des "clients" et des "fournisseurs". mardi 20 novembre 12 8 Trois niveaux de contrat en informatique • Le plus haut niveau permet la communication entre acheteur du logiciel et prestataire, c'est le cahier des charges (spécification). • Le niveau suivant lie les membres d'une équipe d'informaticiens: le document de conception. • Enfin, avec la granularité nécessaire, le dernier niveau lie les programmes (et programmeurs) entre eux : les invariants mardi 20 novembre 12 9 La spécification : contrat entre client et fournisseur • Le client admet que ses besoins y sont exprimés de façon correcte et complète, et donc accepte d'avance toute solution conforme. • définis par exemple au moyen d'UML: diagrammes de cas d'utilisation, et séquence système • Le prestataire sait de son côté que les solutions qu'il envisage sont réalisables (dans les délais, ceux ci pouvant apparaître comme un besoin dans la spécification). mardi 20 novembre 12 10 Vision du développement agile • Une partie importante des projets informatiques est aujourd'hui réalisée en développement agile • en itérant de façon très rapide des prototypes, destinés à recaler le cahier des charges selon un mécanisme de boucle de feedback • Chaque itération adresse les points identifiés comme présentant le plus fort risque pour la bonne fin du projet (technologiques notamment, mais pas seulement) • Cette approche est rendue nécessaire pour des projets difficiles ou impossibles à spécifier d'avance mardi 20 novembre 12 11 (Mots clé Scrum) • Product Backlog : la liste des 'user stories' (un besoin est formulé sur l'espace d'un post it • Sprint Backlog : les 'user stories' sélectionnées pour le sprint courant (d'après leurs story points, et leur valeur) • Sprint: un cycle • Daily Scrum: la réunion quotidienne où l'on répond aux trois questions • qu'ai je fait depuis hier • que dois-je faire aujourd'hui • quels points de blocages sont rencontrés? mardi 20 novembre 12 12 La conception : contrat liant les membres de l'équipe • La conception décrit de manière détaillée l'ensemble des solutions techniques devant être mises en oeuvre pour implanter ce qui a été spécifié. • Il est formellement impossible de faire plus, ou moins, que ce que la conception a décrit. mardi 20 novembre 12 13 Contrat entre programmes Chaque élément logiciel s'engage sur les points suivants : • quoi : quelle est la fonctionnalité implantée par ce programme • programme de test, manuel d'utilisation • comment : selon quelle interface de programmation (noms de fonction, types) • programme de test, charte, manuel de référence • quand : quelles sont les données pour lesquelles son fonctionnement est garanti • dans le code: invariants de classe, pré-conditions, manuel de référence, commentaires javadoc mardi 20 novembre 12 14 Que faire quand un appel contredit les pré-conditions? La communauté des développeurs est tiraillée entre deux approches contradictoires • La programmation défensive vise à permettre au programme de survivre aux appels de fonction sortant de son domaine de définition • La programmation 'offensive' (néologisme récent) vise à donner la possibilité au développeur • d'être informé au plus près de l'erreur ('fail fast') • d'être dans l'obligation absolue d'intervenir immédiatement (arrêt brutal du programme) mardi 20 novembre 12 15 Approche offensive avec ASSERT • Supposons qu'on implémente la division réelle y = f(x,z) = x/z • La valeur z=0 est hors du domaine de définition de la fonction. float divise(float x, float z) { assert (z!=0); return x/z; } • assert provoque l'arrêt du programme au plus tôt, et réalise donc 'fail fast' et l'obligation de correction immédiate mardi 20 novembre 12 16 ASSERT : une vision conditionnelle • Historiquement (ci dessous en C), assert est actif en mode debug, et retiré en production • Aujourd'hui assert reste souvent dans le code distribué (envoi de mail aux développeurs) mardi 20 novembre 12 17 A l’inverse: l’approche défensive • ‘Défensif’ signifie que le programme se ‘défend’ contre les erreurs d’utilisation et cherche à continuer autant que possible. • Cela possède plusieurs inconvénients mardi 20 novembre 12 18 Approche pseudo 'défensive' moderne: les Exceptions • Les langages de programmation modernes (C++, Java) permettent de gérer les exceptions • Il s'agit d'un mécanisme de contrôle particulier, mais la prise en charge de ces exceptions rentre dans le contrat au sens propre. • Les exceptions font partie de la fonctionnalité / des services offerts à l’utilisateur mardi 20 novembre 12 19 Le ‘Comment’ : Interfaces de Programmation Normalisées • On définit habituellement des chartes et guides de style pour les types et signatures de fonctions et méthodes. • les fonctions du module graphique débutent par "Gr", • les symboles globaux commencent par une majuscule, • les objets statiques sont en majuscules, • aucun nom de variable n’a moins de six caractères etc… • Vérifier ce 'comment' est réalisé par le compilateur mardi 20 novembre 12 20 Principe de l’Utilisateur Parfait Fondements de la programmation Offensive ou Fail Fast mardi 20 novembre 12 21 Principe de l'utilisateur parfait • Tout programme s'engage à se comporter comme un utilisateur parfait de ses ressources (les fonctions qu'il appelle), • c'est à dire à ne jamais appeler une fonction dans des conditions qui la mettent hors de son domaine de définition. • Tout programme qui fournit un service au système s'engage à fonctionner correctement lorsque ses éléments de calculs sont valides. mardi 20 novembre 12 22 Traduction technique • Aucune fonction ne doit s'attendre à gérer un cas ou un autre élément de programme fournirait des données incorrectes. • Aucune fonction ne doit tester les arguments de son calcul pour leur correction. mardi 20 novembre 12 23 Utilisation d'invariants • Un invariant est une propriété logique qui est toujours vraie pendant l'exécution d'une partie d'un programme, • soit un pré-requis ou une condition • (condition sur les données en entrée nécessaire pour le déroulement d’un programme), • soit implicite du fait de la nature du programme • (la définition et la vérification des invariants implicites est un outil de base dans la preuve formelle de programmes). mardi 20 novembre 12 24 Défensif ou offensif??? La discussion qui n'en finit pas mardi 20 novembre 12 25 Modèle de la Programmation Offensive ‘Assertive’ • Une fonction qui implante un algorithme ne teste jamais l'appartenance de ses arguments au domaine de définition. • Les cas d’erreur doivent être captés par des invariants, de manière à : • informer les développeurs au plus près de la source de l’erreur quand elle se produit • obliger à la correction de l'erreur • documenter le source mardi 20 novembre 12 26 Programmation Défensive • Le modèle dit de programmation ‘défensive’ vise à permettre la continuation d’un programme même en cas d’erreur. • cas rares : la fonction ne fait rien (e.g. chgt. de couleur non prise en charge en ihm) • vue moderne : on lance une exception • Une couche défensive peut toujours être ajoutée à une application Assertive. • L’inverse aussi, mais aboutit à du code inutile. mardi 20 novembre 12 27 Pour l'offensif • plus facilement débuggé • plus facilement maintenu • ne coûte rien en production • peut être complété par un code défensif • offre une garantie absolue de fonctionnement par la couverture des tests • offre une documentation vivante et testée: toujours correcte mardi 20 novembre 12 28 Et le défensif? • utile dans des situations très spécifiques sans danger (affichage d'une couleur inexistante...) • irréaliste dans le cas général • très souvent on ne sait pas comment poursuivre l'exécution • provoque des erreurs en cascade difficiles à remonter mardi 20 novembre 12 29 Défensif Dangereux • Ou l’on ne veut surtout pas que le programme s’arrête - combien de temps? mardi 20 novembre 12 30 Défensif Paresseux • Où la paresse l’emporte mardi 20 novembre 12 31 Défensif Moderne : Exceptions • Ou l’on se dit - lançons une exception, l’utilisateur/programmeur saura quoi en faire. mardi 20 novembre 12 32 Offensif • On ne teste pas les domaines. On les asserte mardi 20 novembre 12 33 Impact sur la performance • Dans de très nombreuses situations, les algorithmes garantissent que les conditions d'appel des fonctions sont satisfaites. mardi 20 novembre 12 34 Difference entre assert et exceptions • Une assertion signale un bug devant être pris en charge par un humain • Les assertions documentent des situations qui sont impossibles, et offrent un mécanisme qui prévient le développeur aussi tôt que possible • Une exception signale une erreur pouvant être prise en charge par un programme • Les exceptions offrent un flux de contrôle différent du flux standard, mais qui réalise une fonctionnalité utile mardi 20 novembre 12 35 Commentaire final • Les invariants de domaine fournissent une documentation vivante intégrée au programme utile pour la maintenance • Des tests défensifs inutiles dans un programme ne peuvent jamais être enlevés • Par contre si nécessaire, on peut toujours compléter les API avec des versions défensives mardi 20 novembre 12 36 Le Assert Historique Génèse de la programmation Offensive ou Fail Fast mardi 20 novembre 12 37 Le code C/C++ de la macro « assert » • En C/C++, l’outil de base pour la vérification d’invariants dans les programmes est la macro « assert » #ifndef NDEBUG #define assert (test) \ if(!(test)) { \ printf ("assertion failed %s line %d in file %s\n", #test, __LINE__, __FILE__ ); \ abort(); #else #define assert (test) /*ignore*/ #endif mardi 20 novembre 12 38 Exemple: definition de 1/x mardi 20 novembre 12 39 Test de 1/x en C/C++ mardi 20 novembre 12 40 Compiler Assert en C/C++ • Compiler avec les assertions: CC -o test test.cc • Compiler sans les assertions: CC -o test -DNDEBUG test.cc mardi 20 novembre 12 41 Comparaison: utiliser assert en Java • En Java, 'assert' est une instruction du langage, pas une primitive compilée conditionnellement • L'option 'java -ea' permet de lancer une instance de la JVM qui teste les assert présents dans le code • Par défaut, les assert sont ignorés (mais pas retirés: ils peuvent même être activés par programme - quoique techniquement délicat) mardi 20 novembre 12 42 Différents types d'invariants mardi 20 novembre 12 43 Invariants de compatibilité système • Ces invariants permettent de valider le respect par l'architecture matérielle et logicielle de conditions nécessaires au programme mardi 20 novembre 12 44 Invariants de compatibilité de bibliothèques • On vérifie que les types définis par deux bibliothèques distinctes sont compatibles mardi 20 novembre 12 45 Invariants d’étape dans les algorithmes • décrit une condition connue comme devant être vraie en un point d’un algorithme • Par exemple un pointeur non nul à la sortie d'une boucle mardi 20 novembre 12 46 Exemple // tab est un tableau d’entiers positifs int Max=-1; int MaxI=-1; for (int i=0; i<N;i++){ assert(tab[i]>=0); if (tab[i]>Max){Max=tab[i];MaxI=i;} } assert(Max>-1); assert(MaxI>=0); assert(MaxI<N); tab[MaxI]--; // toujours valide mardi 20 novembre 12 47 Variants de boucle • Un tel invariant s’appelle un « variant » parce qu’il décrit le fait que deux itérations successives d’une boucle modifient la valeur d’une variable de contrôle d’une manière telle que l’on sait que le programme va s’arrêter. mardi 20 novembre 12 48 Exemple de variant de boucle mardi 20 novembre 12 49 Invariants de classe • Ces invariants décrivent des conditions vraies de toutes les instances d’une classe à tout moment de leur existence compris entre • la fin de leur construction et • le début de leur destruction mardi 20 novembre 12 50 Exemple mardi 20 novembre 12 51 Organisation de l'activité mardi 20 novembre 12 52 Compiler et exécuter dès le début de son activité • Les outils de génération de code sont la première aide du programmeur. • compiler aussi tôt que possible, avec tous les fichiers include des bibliothèques que l'on va utiliser. • linker avec les bibliothèques que l'on doit utiliser par la suite. • exécuter le programme de façon à pouvoir le tester. mardi 20 novembre 12 53 Sauver le temps • Ne pas programmer de fonctions inutiles." • Communiquer l'information."" • Messageries, aide et manuel de référence en ligne, cvs. • Réduire le temps d'accès à l'information." • utilisation d'outils mardi 20 novembre 12 54 Sauver le temps • Documenter ses programmes. • Protéger ses programmes. • assert permet de presque totalement supprimer les recours au débugger. • Réduire la charge des machines. • dans des projets importants la progression vers le but s'accompagne d'une montée en charge du système. • prendre les décisions qui s'imposent pour que la compilation et les tests se fassent en un temps acceptable. mardi 20 novembre 12 55 Savoir automatiser • Bien utiliser les outils disponibles pour gagner du temps et des efforts rébarbatifs est fondamental dans l'activité de développement. • La mise en oeuvre d'un automatisme annexe doit prendre un temps négligeable au regard du problème posé (disons que cela se compte en minutes en général). mardi 20 novembre 12 56 Exemple: un shell pour l'appel récursif d'une commande dans les sous répertoires mardi 20 novembre 12 57 Avoir une démarche produit • documenter • tester • assurer la compatibilité ascendante de ses programmes • assurer la portabilité • permettre la ré-entrance • séparer les algorithmes et les interfaces homme machine mardi 20 novembre 12 58 Principes de documentation • Les invariants constituent le fondement de la documentation interne. • La structure logique d'un programme (tests et boucles) parle d'elle même au lecteur lui même programmeur. • Une documentation intégrée redondante est inutile. mardi 20 novembre 12 59 Principes de documentation • Quand une expression évaluée dans un test n'est pas suffisamment explicite pour le comprendre, un commentaire est nécessaire. • Lorsqu'un programme possède un effet de bord non évident, cela doit être documenté. • Un invariant qui ne peut raisonnablement pas être testé doit être mentionné en commentaire. mardi 20 novembre 12 60 La ligne de compilation avec tests unitaires Un exemple de makefile pour une bibliothèque d'outils - application au langage C++ mardi 20 novembre 12 61 mardi 20 novembre 12 62 mardi 20 novembre 12 63 mardi 20 novembre 12 64 mardi 20 novembre 12 65 mardi 20 novembre 12 66 Organiser L'accès à L'Information mardi 20 novembre 12 67 Structurer • • Décider que chaque classe (C++) est décrite dans un jeu de fichiers spécifiques, quelle qu'en soit la complexité. • avantages: la règle est facile à suivre • grouper par classes reflète la structure logique interne des classes dans les fichiers (c'est le choix du langage Java). Ou alors, grouper dans un fichier unique les programmes relevant de l'impression, ou de la trace, pour toutes les classes. • • avantages: les programmes de trace ont des traits communs faisant que leur présence groupée dans un fichier unique permet d'en programmer rapidement de nouveaux "par l'exemple". Quelle que soit l'approche, on aura dans certains cas besoin d'informations orthogonales. mardi 20 novembre 12 68 Accéder • L'organisation en répertoires n'offre pas tous les points de vue nécessaires pour disposer de toutes les informations utiles. • Il faut des outils pour trouver une information aussi rapidement que possible. • ex: consulter le source d'une méthode de sérialisation • ex: retrouver la déclaration d'une fonction • Sous Unix, les outils de base "grep", "sed" et "awk" sont précieux pour concevoir des programmes de recherche d'information dans des sources. Ils doivent être utilisés en l'absence de meilleur outil. • Les environnements de développement modernes (Visual, Eclipse) offrent de multiples possibilités mardi 20 novembre 12 69 Exemple: un shell d'extraction de source mardi 20 novembre 12 70 Ne pas dupliquer mardi 20 novembre 12 71 Classe, Fonction ou Macro? • Il y a des cas ou un fragment de code qui ne correspond pas à une unité identifiable du langage doive être répétée • Dans ce cas on doit utiliser le macro processeur mardi 20 novembre 12 72 Ex: décl/init de variable locale mardi 20 novembre 12 73 Ex : action locale répétée mardi 20 novembre 12 74 Ex : comparaisons en cascade mardi 20 novembre 12 75 Ex Comparaisons (2) mardi 20 novembre 12 76 Ne maintenir qu'un seul source mardi 20 novembre 12 77 Apports de la compilation conditionnelle • permettre la compilation sur toutes les plate formes, • ne jamais oublier de version, • réduire le nombre des fichiers source utilisés, • utilisation optimale des ressources de chaque environnement, • éviter l'oubli des conditions dans lesquelles la variabilité apparaît. mardi 20 novembre 12 78 Aspects techniques Distinguer les différents types de paramètres mardi 20 novembre 12 79 Paramètres machine • Les paramètres machine sont ceux qui sont liés à l'architecture physique de la machine. • mots de 32 bits ? • nombre de registres internes au processeur? Un "garbage collector " en dépendra certainement. • Le pré processeur C permet de prendre en compte des symboles définis automatiquement par le compilateur, ou explicitement par la commande de compilation, pour s'adapter aux variations d'architectures physique. • Le symbole correspondant s'appelle ARCH sous Unix. Sa valeur lors d'une compilation est connue. mardi 20 novembre 12 80 Exemple mardi 20 novembre 12 81 Paramètres système • Le système d'exploitation pose également des conditions sur l'exécution des programmes. • Par exemple, le nombre de fichiers qu'il est possible d'ouvrir simultanément est variable, comme le nombre de processus, ou de threads qu'il est possible d'exécuter à un moment donné. mardi 20 novembre 12 82 Paramètres de configuration • Paramètres transmis par l'environnement qui permettent l'exécution du programme mardi 20 novembre 12 83 Les variables d'environnement mardi 20 novembre 12 84 Paramètres d'exécution • Les données qui peuvent varier d'une exécution à une autre et qui ne dépendent ni de la machine, ni du système, ni de la configuration sont les paramètres d'exécution. mardi 20 novembre 12 85 Passage de ces paramètres mardi 20 novembre 12 86 Parser Argv mardi 20 novembre 12 87 Parser Argv (2) mardi 20 novembre 12 88 Parser Argv (3) mardi 20 novembre 12 89 Constantes • La bonne manière de déclarer les constantes est la dernière: mardi 20 novembre 12 90 Gestion de la mémoire mardi 20 novembre 12 91 Mémoire statique • espace associé au code du programme, et sous Unix il en est très proche physiquement. • réservé par la déclaration d'une variable globale mardi 20 novembre 12 92 Mémoire Statique mardi 20 novembre 12 93 Réentrance mardi 20 novembre 12 94 Réentrance en action mardi 20 novembre 12 95 Classe "Utility" mardi 20 novembre 12 96 Mémoire automatique • espace alloué sur la pile d'exécution mardi 20 novembre 12 97 Adresses invalides mardi 20 novembre 12 98 Mémoire dynamique • Espace total disponible pour le programme (hors pile d'exécution liée à la mémoire automatique). • Cet espace est égal lors du lancement du programme à celui strictement nécessaire au chargement dans la mémoire vive du code programme et des données statiques. • Un programme peut ensuite demander explicitement de la mémoire à l'aide de la fonction "sbrk". mardi 20 novembre 12 99 Panorama de la mémoire mardi 20 novembre 12 100 Conventions générales pour l'allocation de mémoire • En aucun cas une fonction ne peut renvoyer l'adresse d'un espace alloué automatiquement. mardi 20 novembre 12 101 Allocation de mémoire (2) • Lorsqu'une fonction retourne un pointeur, il faut savoir de façon non ambiguë (documentée) si le pointeur désigne une zone allouée par la fonction, ou bien une zone accessible par l'un de ses paramètres, et donc allouée antérieurement. • Il faut éviter d’avoir un mécanisme mixte : valeur allouée dans certains cas, non allouée dans d'autres. mardi 20 novembre 12 102 Allocation de mémoire (3) Lorsqu'une fonction prend un pointeur en paramètre, elle n'effectue jamais de duplication des données. • faisabilité : la fonction appelée ne dispose pas toujours des informations permettant une copie pertinente (copie profonde / de surface). • économie : de temps d'exécution/espace plus importants. • sécurité : moins de risque prolifération de zones non libérées La fonction appelante sait exactement dans quelles conditions la copie est nécessaire, en fonction de ses propres traitements, et peut la réaliser elle même avant l'appel. mardi 20 novembre 12 103 Distinguer les fonctions faisant des copies si nécessaire mardi 20 novembre 12 104 Aspects Humains mardi 20 novembre 12 105 Eviter de généraliser et d'abstraire • 1 le goût de résoudre un "problème" difficile, • 2 le goût d'une certaine esthétique des programmes, • 3 la croyance que l'on se protège d'avance contre des évolutions futures du besoin client. • 4 la croyance que le même programme puisse servir dans un autre projet • 5 l'impression d'améliorer la qualité du logiciel mardi 20 novembre 12 106 Savoir déboguer • les programmes corrects mais qui implantent un algorithme faux, • les programmes qui implanteraient un algorithme correct, s'ils ne contenaient des erreurs. • la combinaison des deux mardi 20 novembre 12 107 Aspects psychologiques du debug • décontraction (éviter le blocage)" • effort calme et patient de mémoire : • passer les symboles et les programmes en revue, en répétant mentalement leur rôle. • demander l'aide d'une personne extérieure : • lui expliquer le rôle des éléments du programme fautif et la logique générale du programme suffira souvent à lui permettre de trouver l'erreur. Souvent, le fait d'expliquer permet au programmeur de faire effectivement l'effort calme et patient en question ci dessus, et de trouver l'erreur lui même. mardi 20 novembre 12 108 Aspects statistiques • Lorsqu'un programme "plante", les instructions erronées sont en général très proches de l'endroit où le programme s'arrête effectivement. • L'utilisation des invariants a pour but de cerner le point d'erreur au plus près mardi 20 novembre 12 109 Isoler les conditions d'apparition du bug • Pour réduire les conditions d'erreur à leur plus simple expression il suffit en général de supprimer des portions du programme de façon itérative tant que l'erreur est maintenue. • Il est en général inutile de chercher la cause de l'erreur tant qu'on n'a pas procédé à cette réduction à la séquence minimale. mardi 20 novembre 12 110 savoir procéder par différences élémentaires • Après avoir isolé les conditions du bug, faire varier de manière indépendante tous les paramètres du programme, et considérer que ceux qui ne changent pas le comportement du programme ne sont pas concernés. mardi 20 novembre 12 111 Principes de Capitalisation Capitaliser=Réutiliser mardi 20 novembre 12 112 Spécifier • Les besoins auxquels répond un module (une gestion de liste par exemple) doivent être clairement définis. • Pour garantir leur réutilisation ils doivent être universels, donc indépendants de toute spécificité applicative. • Si de telles spécificités existent cependant, elles sont décrites à part en s'appuyant sur le noyau fondamental. mardi 20 novembre 12 113 Concevoir • Pour garantir la réutilisation, la conception doit prévoir une interface de portabilité (indépendance des architectures matérielle et système), et doit obéir aux différents principes généraux, dont celui de l'utilisateur parfait. mardi 20 novembre 12 114 Programmer • Les outils de base font l'objet de modules indépendants, groupés dans une ou plusieurs bibliothèques. • Ces bibliothèques sont accessibles à l'ensemble des membres de l'équipe, et font l'objet d'une rigoureuse gestion de versions. • Les programmes eux mêmes sont protégés des malversations par des contrôles d'invariants dans leur version de développement. • On ne doit jamais conduire un utilisateur (programmeur) à utiliser un débugger pour naviguer dans des sources inconnus à la recherche de l'origine d'une erreur. mardi 20 novembre 12 115 Documenter • De bons documents de spécification et de conception constituent la documentation de base. • Il faut les compléter par un "manuel de l'utilisateur" qui décrit notamment des cas d'utilisation détaillés et des exemples. • Un manuel de référence si possible généré automatiquement est nécessaire • Le code est documenté en interne par ses invariants mardi 20 novembre 12 116 Livrer • Un module doit être disponible aux développeurs sous deux versions. • La première, de développement, effectue tous les tests de domaines nécessaires par des contrôles d'invariants, et garantit de ne jamais "planter" sans dire pourquoi. • La seconde est une version de déploiement débarrassée des contrôles d'invariants mardi 20 novembre 12 117 Fin du chapitre • Questions? mardi 20 novembre 12 118