A resposta da DW é ótima , mas eu gostaria de expandir um ponto. Uma especificação não é apenas uma referência na qual o código é verificado. Uma das razões para ter uma especificação formal é validá-la, provando algumas propriedades fundamentais. Obviamente, a especificação não pode ser completamente validada - a validação seria tão complexa quanto a própria especificação, portanto seria um processo sem fim. Mas a validação nos permite obter uma garantia mais forte em algumas propriedades críticas.
Por exemplo, suponha que você esteja projetando um piloto automático de carro. Isso é bastante complexo, envolvendo muitos parâmetros. As propriedades de correção do piloto automático incluem coisas como "o carro não bate contra a parede" e "o carro dirige para onde é solicitado". Uma propriedade como “o carro não bate contra a parede” é realmente muito importante, então gostaríamos de provar isso. Como o sistema opera no mundo físico, você precisará adicionar algumas restrições físicas; a propriedade real do sistema computacional será algo como "sob essas suposições relacionadas à ciência dos materiais e essas suposições relativas à percepção de obstáculos pelos sensores do carro, o carro não colidirá com a parede". Mas, mesmo assim, o resultado é uma propriedade relativamente simples que é claramente desejável.
Você poderia provar essa propriedade a partir do código? Por fim, é isso que está acontecendo, se você seguir uma abordagem totalmente formal¹. Mas o código tem muitas partes diferentes; os freios, as câmeras, o motor etc. são todos controlados de forma autônoma. Uma propriedade de correção dos freios seria algo como "se o sinal 'aplicar freios' estiver ativado, os freios serão aplicados". Uma propriedade de correção do motor seria "se o sinal da embreagem estiver desligado, o motor não estará dirigindo as rodas". É preciso uma visão de alto nível para reuni-los. Uma especificação cria uma camada intermediária na qual os diferentes componentes do sistema podem ser articulados juntos.
De fato, um sistema tão complexo como o piloto automático de um carro teria vários níveis de especificações com quantidades variadas de aprimoramentos. Uma abordagem de refinamento é frequentemente usada no design: comece com algumas propriedades de alto nível, como “o carro não bate contra a parede”, depois descubra que isso requer sensores e freios e elabore alguns requisitos básicos para os sensores, os freios e o software piloto, refine novamente esses requisitos básicos no design do componente (para o sensor, vou precisar de um radar, um DSP, uma biblioteca de processamento de imagens, etc.) etc. Em um processo formal de desenvolvimento, comprovadamente, cada nível de especificação atende aos requisitos definidos pelo nível acima, desde as propriedades de nível mais alto até o código.
É impossível ter certeza de que a especificação está correta. Por exemplo, se você errou na física, os freios podem não ser eficazes, mesmo que a matemática que relaciona o código de freio aos requisitos formais esteja correta. Não é bom provar que as quebras são eficazes com 500 kg de carga se você realmente tiver 5000 kg. Mas é mais fácil ver que 500 kg estão errados do que ver dentro do código dos freios que eles não serão bons o suficiente para os parâmetros físicos do carro.
¹ O oposto de uma abordagem totalmente formal é "Acho que isso funciona, mas não tenho certeza". Quando você está apostando sua vida, isso não parece tão bom.