# Qualité logicielle

En informatique et en particulier en génie logiciel, la qualité logicielle est une appréciation globale d'un logiciel, basée sur de nombreux indicateurs.
La complétude des fonctionnalités, la correction et précision des résultats, la fiabilité, la tolérance de pannes, la facilité et la flexibilité de son utilisation, la simplicité, l'extensibilité, la compatibilité et la portabilité, la facilité de correction et de transformation, la performance, la cohérence et l'intégrité des informations qu'il contient sont tous des facteurs de qualité.
Contrairement à un matériel, un logiciel est un produit qui n'a pas une fiabilité prédictible, de plus il ne s'use pas dans le temps. Donc une anomalie survient ou ne survient pas dans l'exécution du logiciel, l'anomalie est présente de manière latente et peut ne jamais survenir. La qualité d'un logiciel dépend entièrement de sa construction et des processus utilisés pour son développement, c'est par conséquent un sujet central en génie logiciel. Une appréciation globale de la qualité tient autant compte des facteurs extérieurs, directement observables par l'utilisateur, que des facteurs intérieurs, observables par les ingénieurs lors des revues de code ou des travaux de maintenance.
Les problèmes de qualité des logiciels, connus depuis les années 1960, sont par ailleurs à l'origine du génie logiciel, la science de la création de logiciels, y compris toutes les difficultés qui y sont liées - respects des coûts, des délais, du cahier des charges et du niveau de qualité.
Il existe plusieurs référentiels de certification du système de management de la qualité en entreprise, en matière d’ingénierie du logiciel comme TickIT.

## Indicateurs de qualité logicielle

La norme ISO 9126 (remplacée depuis 2011 par la norme 25010 ré-examinée en 2017) définit six groupes d'indicateurs de qualité des logiciels :
* la capacité fonctionnelle. c'est-à-dire la capacité qu'ont les fonctionnalités d'un logiciel à répondre aux exigences et besoins explicites ou implicites des usagers. En font partie la précision, l'interopérabilité, la conformité aux normes et la sécurité ;
* la facilité d'utilisation, qui porte sur l'effort nécessaire pour apprendre à manipuler le logiciel. En font partie la facilité de compréhension, d'apprentissage et d'exploitation et la robustesse - une utilisation incorrecte n'entraîne pas de dysfonctionnement ;
* la fiabilité, c'est-à-dire la capacité d'un logiciel de rendre des résultats corrects quelles que soient les conditions d'exploitation. En font partie la tolérance aux pannes - la capacité d'un logiciel de fonctionner même en étant handicapé par la panne d'un composant (logiciel ou matériel) ;
* la performance, c'est-à-dire le rapport entre la quantité de ressources utilisées (moyens matériels, temps, personnel), et la quantité de résultats délivrés. En font partie le temps de réponse, le débit et l'extensibilité - capacité à maintenir la performance même en cas d'utilisation intensive ;
* la maintenabilité, qui mesure l'effort nécessaire à corriger ou transformer le logiciel. En font partie l'extensibilité, c'est-à-dire le peu d'effort nécessaire pour y ajouter de nouvelles fonctions ;
* la portabilité, c'est-à-dire l'aptitude d'un logiciel à fonctionner dans un environnement matériel ou logiciel différent de son environnement initial. En font partie la facilité d'installation et de configuration dans le nouvel environnement.
Chaque caractéristique contient des sous-caractéristiques. Il y a 27 sous-caractéristiques.
Les différents indicateurs sont parfois conflictuels, ou au contraire complémentaires : une augmentation de la capacité fonctionnelle peut avoir un impact négatif sur la performance, la maintenabilité et la fiabilité. Tandis qu'une augmentation de la fiabilité, la maintenabilité ou de la disponibilité ont un impact positif sur l'utilisabilité. En outre, une augmentation de la maintenabilité peut avoir un impact négatif sur la performance.

## Crise du logiciel

Un phénomène de baisse des prix du matériel informatique et d'augmentation des prix du logiciel, accompagné d'une baisse de la qualité des logiciels a été identifié à la fin des années 1960 et nommé la « crise du logiciel ». Cette crise s'apparente aujourd'hui à une maladie chronique de l'industrie du logiciel, dont les symptômes sont les suivants :
* les délais de livraison des logiciels sont rarement tenus, le dépassement de délai et de coût moyen est compris entre 50 et 70 % ;
* la qualité du logiciel correspond rarement aux attentes des acheteurs, le logiciel ne correspond pas aux besoins, il consomme plus de moyens informatiques que prévu, et tombe en panne ;
* les modifications effectuées après la livraison d'un logiciel coûtent cher, et sont à l'origine de nouveaux défauts. Les adaptations sont bien souvent une nécessité du fait de l'évolution des produits et des attentes des utilisateurs ;
* il est rarement possible de réutiliser un logiciel existant pour en faire un nouveau produit de remplacement ; l'amortissement du coût de développement initial est ainsi rendu impossible.
Auparavant minoritaire, le coût du logiciel en 1965 représentait 50 % du coût total d'un système informatique. En 1985 la part du logiciel est de 80 % et les coûts dus à la correction des défauts dans les logiciels (maintenance) représentent jusqu'à trois quarts du coût total d'acquisition, un excédent dû uniquement à la mauvaise qualité du logiciel lors de sa livraison.
Selon une étude réalisée en 1994 par le Standish Group, 53 % des logiciels créés sont une réussite mitigée : le logiciel est opérationnel, cependant le délai de livraison n'a pas été respecté, les budgets n'ont pas été tenus, et certaines fonctionnalités ne sont pas disponibles. Le dépassement des coûts est en moyenne de 90 %, et celui des délais de 120 %, et la qualité moyenne est estimée à 60 %.

## Raisons du manque de qualité des logiciels

Un logiciel étant un produit immatériel, les seules représentations observables du logiciel sont le code source, l'interface utilisateur et la documentation (spécification, cahiers de tests, manuels utilisateur, etc.). La quantité de code source (nombre de lignes) est rarement connue à l'avance, ce qui entraîne souvent une sous-estimation de la complexité du logiciel.
Pour chaque module d'un logiciel il existe de nombreuses conditions d'utilisation. La combinaison des différentes conditions d'utilisation des différents modules d'un logiciel amène une explosion combinatoire, et lors de son développement, un logiciel est rarement testé dans la totalité des conditions d'utilisation qu'il rencontrera durant son exploitation ; ceci pour des raisons pratiques (coût et durée des travaux).
Une autre raison est l'absence de lien entre un défaut mineur et majeur, et une modification mineure ou majeure. Et l'effort de correction d'un logiciel n'est pas proportionnel à l'importance du défaut constaté. Un défaut mineur peut entraîner un incident majeur, et nécessiter une correction mineure. Dans l'accident du vol 501 d'Ariane 5, une correction mineure aurait suffi à éviter la destruction de la fusée. De même une modification mineure d'un logiciel peut le mettre hors d'usage ; un phénomène largement exploité par les virus informatiques.
Pour être considéré comme produit de qualité par l'usager, un logiciel doit répondre aux besoins exprimés explicitement par l'usager aussi bien qu'aux besoins implicites (non exprimés). Or les vœux implicites évoluent avec le marché, et il arrive bien souvent que des vœux implicites des usagers ne soient pas connus des ingénieurs logiciels.

## Amélioration de la qualité

En génie logiciel, la factorisation des données et du code constituent le moyen universel d'obtention de la qualité. La factorisation des données aboutit au modèle objet (avec usage de l'héritage) dont le correspondant systématique relationnel est idéal lorsqu'il est normalisé (formes normales de Codd). En effet, lorsque les structures de données sont ainsi normalisées, elles deviennent non redondantes, donc minimales en taille, et n'engendrant aucun problème d'incohérence dès lors que l'intégrité découlant de leur type et l'intégrité référentielle sont assurées, contraintes auxquelles il faut ajouter des "règles métiers" consistant en contraintes logiques faisant intervenir plusieurs champs/attributs. Ce travail au niveau des données permet en soi la réduction du code de traitement exploitant ces données. La performance n'en souffre pas si les requêtes sont bien organisées.
Concernant le code, la factorisation permet de n'écrire qu'une fois des instructions similaires, par un usage raisonné des variables intermédiaires et locales, des boucles, des fonctions et des procédures. Cela permet la réduction maximale de la taille du code source (sans perte normalement de lisibilité), et aboutit à ce que les modifications soient le plus locales possibles (donc plus rapides, et plus fiables en termes de non régression). Un gain complémentaire de réduction du code source est apporté par le polymorphisme et la liaison dynamique (qui éliminent les « procédures aiguillage ») en programmation objet (celles-ci sont générées par le compilateur au lieu de devoir être écrites explicitement par le programmeur). Ces factorisations font émerger les bonnes abstractions, la bonne structuration, et permettent le meilleur contrôle possible de l'intégrité des données et de la bonne exécution des traitements, dont les fonctions, appelées en plusieurs points du code source peuvent devenir de fait des services réutilisés, autant que les données qui, étant partagées, sont réutilisées sans duplication. Ce travail permet à une application d'être "modulaire". Ces modules sont les services. Leur interface étant claire et sans effet de bord, le traitement qu'ils réalisent devient caché (boîte noire) pour ses modules clients. Le couplage entre un module et ses modules appelants devient le plus faible possible, du fait que seules les valeurs de paramètres variant d'un appel à l'autre sont passées en argument, les variables invariantes entre appels devant idéalement constituer des attributs d'une classe porteuse, la fonction/module en devenant une méthode (dans une programmation objet aboutie). En ce sens, on peut parler de couplage faible, et l'on peut substituer une implémentation à une autre. Il est faux en général que l'on obtienne ainsi la minimisation de l'impact de la défaillance d'un module sur les autres modules et sur le fonctionnement de l'application. La fiabilité y gagne cependant en ce qu'en ayant factorisé, la maintenance du module deviendra un sujet critique à traiter et donc objet d'attentions, la modification restant par ailleurs locale donc plus facilement adressable sans générer de régressions. Pour les modules les plus critiques, leur disponibilité sans faille peut reposer sur une stratégie de redondance d'instanciation physique, ou au contraire le maintien d'une unicité mais sur base d'un service matériel géré de manière autonome par une équipe spécialisée. Dans le corps humain, la stratégie adoptée pour les fonctions de l'ouïe, de la vue, de la préhension et du filtrage du sang est la redondance matérielle, alors que celle prise pour l'estomac, le foie, le cœur, le cerveau, la digestion, qui sont tout aussi critiques, est l'unicité du service, mais avec une forte autonomie de gestion.
En jargon de programmation, le syndrome du plat de spaghettis désigne un logiciel de mauvaise qualité au couplage trop fort et au code source difficile à lire, dans lequel toute modification même mineure demande un intense travail de programmation. Le langage de programmation BASIC utilisant la fonction GOTO est couramment pointé comme l'origine de la "mauvaise éducation" des programmeurs formés dans les années 80.
L'abstraction vise à diminuer la complexité globale du logiciel en diminuant le nombre de modules et en assurant l'essentiel. Elle peut également apporter une uniformité du logiciel qui augmente son utilisabilité en facilitant son apprentissage et son utilisation.
La dissimulation vise à séparer complètement les détails techniques du logiciel de ses fonctionnalités selon le principe de la boîte noire, en vue d'améliorer sa maintenabilité, sa portabilité et son interopérabilité.
La structuration des instructions et des données rend clairement visibles dans le code source les grandes lignes de l'organisation des instructions et des informations manipulées, ce qui améliore sa maintenabilité et facilite la détection des bugs.
De nombreux langages de programmation soutiennent, voire imposent l'écriture de code source selon les principes de structuration, de modularité et de dissimulation. C'est le cas des langages de programmation structurée et de programmation orientée objet.