La réponse de DW est excellente , mais je voudrais développer un point. Une spécification n'est pas seulement une référence par rapport à laquelle le code est vérifié. L'une des raisons d'avoir une spécification formelle est de la valider en prouvant certaines propriétés fondamentales. Bien sûr, la spécification ne peut pas être complètement validée - la validation serait aussi complexe que la spécification elle-même, ce serait donc un processus sans fin. Mais la validation nous permet d'obtenir une garantie plus forte sur certaines propriétés critiques.
Par exemple, supposons que vous conceviez un pilote automatique de voiture. C'est une chose assez complexe impliquant beaucoup de paramètres. Les propriétés de correction du pilote automatique incluent des choses comme «la voiture ne va pas s'écraser contre un mur» et «la voiture va conduire là où on lui dit d'aller». Une propriété comme «la voiture ne va pas s'écraser contre un mur» est vraiment très importante, nous aimerions donc le prouver. Étant donné que le système fonctionne dans le monde physique, vous devrez ajouter des contraintes physiques; la propriété réelle du système de calcul sera quelque chose comme "sous ces hypothèses concernant la science des matériaux, et ces hypothèses concernant la perception des obstacles par les capteurs de la voiture, la voiture ne va pas s'écraser contre un mur". Mais même ainsi, le résultat est une propriété relativement simple qui est clairement souhaitable.
Pourriez-vous prouver cette propriété à partir du code? En fin de compte, c'est ce qui se passe, si vous suivez une approche entièrement formelle¹. Mais le code a beaucoup de parties différentes; les freins, les caméras, le moteur, etc. sont tous contrôlés de manière autonome. Une propriété de correction des freins serait quelque chose comme «si le signal« appliquer les freins »est activé, alors les freins sont appliqués». Une propriété de correction du moteur serait «si le signal d'embrayage est coupé, alors le moteur ne conduit pas les roues». Il faut une vue de très haut niveau pour les rassembler. Une spécification crée une couche intermédiaire où les différents composants du système peuvent être articulés ensemble.
En fait, un système aussi complexe qu'un pilote automatique de voiture aurait plusieurs niveaux de spécifications avec des quantités variables de raffinements. Une approche de raffinement est souvent utilisée dans la conception: commencez par certaines propriétés de haut niveau comme «la voiture ne va pas s'écraser contre un mur», puis déterminez que cela nécessite des capteurs et des freins et définissez certaines exigences de base pour les capteurs, les freins et le logiciel pilote, puis affiner à nouveau ces exigences de base dans la conception du composant (pour le capteur, je vais avoir besoin d'un radar, d'un DSP, d'une bibliothèque de traitement d'images,…), etc. Dans un processus de développement formel, il est prouvé que chaque niveau de spécification répond aux exigences définies par le niveau supérieur, depuis les propriétés de plus haut niveau jusqu'au code.
Il est impossible d'être sûr que la spécification est correcte. Par exemple, si vous vous trompez de physique, les freins peuvent ne pas être efficaces même si les calculs reliant le code de freinage aux exigences formelles sont corrects. Il ne sert à rien de prouver que les pauses sont efficaces avec 500 kg de charge si vous en avez réellement 5000 kg. Mais il est plus facile de voir que 500 kg est faux que de voir à l'intérieur du code des freins qu'ils ne seront pas assez bons pour les paramètres physiques de la voiture.
¹ Le contraire d'une approche entièrement formelle est «Je suppose que cela fonctionne, mais je ne peux pas en être sûr». Lorsque vous pariez votre vie dessus, cela ne semble pas si bien.