▶ Slides ← Módulo
Módulo 11 · Engenharia de Software · Computação 2

Spec-Driven Development

Aula 1 — Material de Leitura · Prof. Afonso Brandão · 04/08/2026

Sobre este encontro

Spec-Driven Development · 04/08/2026 · Prof. Afonso

Objetivo de aprendizagem

Ao final do encontro, o estudante deve ser capaz de escrever uma especificação executável com anatomia clara, rastreada em ADRs, que orienta a implementação, os testes e a geração de código assistida por modelos generativos.

Estratégia do encontro

Exposição em quatro blocos com construção incremental de uma especificação de referência; o encerramento é uma demonstração conduzida pelo professor sobre um sistema de biblioteca, e a aplicação ao projeto do grupo ocorre após a aula, conforme roteiro do material.

Estrutura do encontro

  1. Bloco 1 (30 min) — Por que SDD: problema, motivação, SSoT, requisito funcional e não funcional, qualidade ISO/IEC 25010
  2. Bloco 2 (35 min) — Anatomia da especificação: seis seções, ADR e rastreabilidade, pipeline de elicitação
  3. Bloco 3 (30 min) — Modelagem como código: OpenAPI e Gherkin; entidade-relacionamento, classes e sequência em Mermaid
  4. Bloco 4 (25 min) — Especificação e IA: verificação iterativa e demonstração ao vivo

1. Por que especificar antes

Iniciar a implementação sem uma definição formal do comportamento esperado produz um conjunto previsível de consequências: retrabalho, contratos de API inconsistentes entre times, desalinhamento com quem pediu o sistema, testes que cobrem o que foi construído em vez do que era exigido e dívida técnica acumulada por decisões nunca registradas.

Essas consequências decorrem da ambiguidade de requisito, que se manifesta apenas quando o código já existe; nesse momento, corrigi-la custa muito mais do que teria custado resolvê-la por escrito.

Spec-Driven Development (SDD) é a metodologia na qual a especificação técnica é a única fonte de verdadeSingle Source of Truth, SSoT. O código deriva da especificação, os testes a verificam, a documentação a reflete e a geração assistida por inteligência artificial opera dentro dos seus limites.

Erro comum

Tratar a especificação como documentação escrita depois da entrega. A especificação redigida após o código apenas descreve a implementação existente e deixa de orientá-la.

2. Vocabulário: SSoT, requisito funcional e não funcional

Single Source of Truth

Princípio de arquitetura da informação segundo o qual cada informação relevante reside em um único artefato autoritário, do qual todas as demais representações derivam. Quando duas versões divergem, a fonte única prevalece: as cópias são reconciliadas com ela, jamais o inverso.

Requisito funcional (RF)

Descreve o que o sistema deve fazer — funções e comportamentos observáveis. Exemplo: "O sistema deve permitir o registro de usuário a partir de nome e endereço de e-mail."

Requisito não funcional (RNF)

Descreve como o sistema deve se comportar — qualidade, desempenho, segurança, operação. Exemplo: "O registro deve concluir em no máximo 2 segundos sob carga nominal."

Critério de aceitação de um RNF

Um requisito não funcional exige número, janela de medição e instrumento. "Deve ser rápido" não é verificável; "p95 ≤ 200 ms medido em cache frio, com falha declarada acima disso" é verificável.

Relação entre SDD e TDD

TDD (Test-Driven Development) escreve testes antes do código e fixa a correção interna da implementação. SDD descreve interfaces, contratos e metas antes, e é o que fundamenta o TDD. A especificação define o comportamento esperado, e o TDD o implementa. Em projeto com TDD e sem especificação, os testes verificam a implementação contra o entendimento de quem os escreveu, que pode divergir do requisito.

3. Qualidade não funcional: ISO/IEC 25010:2023

A norma ISO/IEC 25010 define um modelo de qualidade de produto de software com nove características. Sua função em uma especificação é impedir que requisitos de qualidade permaneçam implícitos ou subjetivos: para cada característica pertinente ao componente, atribui-se uma métrica, um limite e um instrumento.

CaracterísticaO que cobre
Adequação funcionalCorreção, completude e pertinência das funções entregues.
Eficiência de desempenhoTempo de resposta, uso de recursos e capacidade.
CompatibilidadeCoexistência e interoperabilidade com outros sistemas.
Interação (usabilidade)Apreensibilidade, operabilidade e proteção contra erro do usuário.
ConfiabilidadeAusência de falhas, disponibilidade, tolerância a falhas e recuperabilidade.
SegurançaConfidencialidade, integridade, não repúdio e autenticidade.
ManutenibilidadeModularidade, reusabilidade, testabilidade e modificabilidade.
FlexibilidadeAdaptabilidade, escalabilidade e instalabilidade.
Proteção (safety)Restrição operacional, identificação de risco, falha segura, alerta de perigo e integração segura.
Aplicação seletiva

Nem toda característica se aplica a todo componente. Interação é irrelevante em um pipeline de dados sem interface; proteção é decisiva em sistema embarcado. As características descartadas devem ser declaradas explicitamente, porque a omissão não se distingue do esquecimento.

4. Anatomia de uma especificação

Uma especificação madura tem seis seções. A ordem importa: cada uma restringe a seguinte.

Seção 1 — Visão e escopo

Por que o sistema existe, quem são os usuários e qual problema resolve. Inclui o que está fora de escopo. Exemplo: "Sistema de cotação que reduz o tempo de resposta ao cliente de 24 h para menos de 5 min."

Seção 2 — Requisitos funcionais

Numerados (RF-001, RF-002) para que testes e código possam citá-los. Cada um declara descrição, pré-condições e pós-condições. Exemplo: "RF-001: usuário autenticado cria cotação. Pré: sessão válida. Pós: quoteId gerado e persistido."

Seção 3 — Requisitos não funcionais

Numerados (RNF-001), cada um com métrica, limite numérico e instrumento de verificação. Exemplo: "RNF-001: latência p95 ≤ 100 ms, medida no gateway, em janela de 30 dias."

Seção 4 — Contrato de API

Rotas, métodos, parâmetros, payloads, códigos de status e exemplos, expressos em OpenAPI 3.1. Deve incluir ao menos um caminho de erro por rota.

Seção 5 — Modelo de dados

Entidades, atributos, tipos, chaves e restrições de integridade — sem citar produto. Exemplo: Quote { id: UUID, chave primária; customerId: UUID, chave estrangeira; total: Decimal, não nulo; createdAt: Timestamp }.

Seção 6 — Cenários de teste

Critérios de aceite em Gherkin, um por regra de negócio. Cada cenário deve falhar antes de a implementação existir.

Separação entre especificação e tecnologia

A especificação não menciona linguagem, framework ou banco de dados. "Usar PostgreSQL" é decisão de tecnologia e pertence a um ADR. Misturar as duas coisas impede trocar a tecnologia sem reescrever o comportamento esperado.

5. ADR — Architecture Decision Record

Um ADR registra uma decisão arquitetural significativa: o contexto que a motivou, as alternativas consideradas, a escolha feita e as consequências assumidas. Registra o porquê da estrutura; o como da implementação permanece fora do registro.

Estrutura

  • Título — identificador e enunciado. ADR-001: processamento assíncrono de cotações via fila.
  • Statusproposto, aceito, substituído ou obsoleto.
  • Contexto — a restrição real que exige uma decisão.
  • Decisão — a escolha, em termos estruturais, sem produto.
  • Alternativas — ao menos uma opção descartada, com o motivo.
  • Consequências — os custos assumidos, além dos benefícios obtidos.

Exemplo mínimo

# ADR-001 — Processamento assíncrono de cotações
Status: aceito

Contexto:   RNF-002 exige resposta em menos de 2 s. O cálculo de
            desconto por volume percorre o catálogo e não cabe na
            janela síncrona.

Decisão:    A criação da cotação responde imediatamente com o
            identificador; o cálculo ocorre fora do ciclo de
            requisição, em fila de processamento.

Descartado: Cálculo síncrono com cache do catálogo — o cache
            invalidaria a cada alteração de preço, sem ganho
            estável.

Consequências: introduz um componente de fila e um estado
            intermediário "em processamento" visível ao cliente.
            Exige garantia de entrega e política de reprocessamento.
Por que o ADR vem antes da spec

O ADR justifica a estrutura que aparecerá na especificação. Sem ele, a spec parece arbitrária a quem chega depois: "por que existe uma fila aqui?" passa a ter resposta rastreável — ADR-001. Um ADR redigido depois da decisão, apenas para justificá-la, registra o resultado sem documentar o raciocínio que o produziu.

6. A sequência documental

Antes de qualquer linha de código, e antes de qualquer escolha de tecnologia, cinco artefatos se sucedem. Cada um responde a uma pergunta distinta.

ArtefatoAutoriaResponde a
1. ProblemaProduto e stakeholdersO que não funciona hoje e por quê. Contexto de negócio, lacunas, restrições e métricas de sucesso. Não é técnico.
2. ADRsArquitetura e liderança técnicaComo a solução se estrutura. Padrões, decomposição, limites de módulo. Independente de tecnologia.
3. EspecificaçãoArquitetura e timeQual é o comportamento esperado. RF, RNF, contrato de API, modelo de dados, cenários. Ainda sem tecnologia.
4. Escolha de tecnologiaArquiteturaCom o quê será construído. Linguagem, banco, framework — registrado em ADR próprio e justificado contra a especificação.
5. Plano de implementaçãoTime, com apoio de IAQuais módulos realizam a especificação. Classes, funções, fluxo.
Invariante

Problema → ADR → especificação → tecnologia → implementação. A escolha de tecnologia ocupa a quarta posição. Quando antecede a especificação, o comportamento esperado passa a ser derivado da ferramenta disponível, o que inverte a relação de dependência entre ambos.

7. Pipeline de elicitação

Da análise do negócio até a implementação, seis estágios:

  1. Modelagem de processos (IDEF0). Análise as-is: o que a organização faz hoje, quais são os atores, os fluxos e as lacunas.
  2. Modelagem arquitetural (RM-ODP). Casos de uso, entidades e diagramas de sequência, organizados em pontos de vista.
  3. Esqueleto do software. Estrutura de diretórios e limites de módulo derivados dos ADRs estruturais, ainda sem linguagem, banco ou framework. Projeto vazio, mas estruturalmente completo.
  4. Documentos de especificação. Consolidação em Markdown e YAML, formatos legíveis por máquina — autoria integralmente humana.
  5. Testes que falham (TDD). Precedidos da escolha de tecnologia em ADR-Tech, que também define a composição do ambiente de execução. Contratos comportamentais expressos como pré-condições, pós-condições e invariantes.
  6. Implementação. Somente aqui o código é escrito, manualmente ou com apoio de IA, para fazer os testes passarem e atender às metas não funcionais.

RM-ODP e os cinco pontos de vista

O modelo ISO/IEC 10746 descreve sistemas distribuídos em cinco perspectivas complementares, o que evita misturar intenção de negócio, modelo de dados e detalhe tecnológico no mesmo documento:

  • Enterprise — propósito, papéis e políticas.
  • Information — semântica e estrutura da informação.
  • Computational — decomposição funcional e interfaces.
  • Engineering — distribuição, comunicação e infraestrutura.
  • Technology — escolhas concretas de produto.
Observação

A ordem dos pontos de vista espelha a sequência documental da seção 6: Technology ocupa a última posição.

8. Linguagens de contrato: OpenAPI e Gherkin

OpenAPI 3.1

Especificação padronizada, legível por máquina, expressa em YAML ou JSON. Descreve rotas, métodos, parâmetros, schemas e códigos de status, constituindo o contrato explícito entre quem produz e quem consome a API. A partir dela é possível gerar documentação navegável, validadores de requisição e esqueletos de cliente.

paths:
  /api/v1/quotes/{id}:
    get:
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string, format: uuid }
      responses:
        '200': { description: Cotação encontrada }
        '403': { description: Cotação pertence a outro cliente }
        '404': { description: Cotação inexistente }

Gherkin

Linguagem estruturada de cenários no formato Dado/Quando/Então, legível pelo negócio e interpretável por ferramenta de teste. Cada cenário corresponde a um critério de aceite verificável.

Cenário: desconto por volume
  Dado um pedido com 12 unidades do produto A
  Quando calculo o total da cotação
  Então o desconto aplicado é de 5%

Cenário: cliente inativo
  Dado um cliente com cadastro inativo
  Quando solicito uma cotação
  Então a resposta é 400 e nenhuma cotação é persistida
Erro comum

Escrever cenários que descrevem cliques de interface. Gherkin fixa regra de negócio. Um cenário que menciona botões quebra a cada mudança de layout, ainda que nenhuma regra tenha mudado.

9. Modelagem como código

Entende-se por modelagem como código (modeling as code) a prática de descrever os modelos do sistema em notação textual — Mermaid, PlantUML ou Structurizr DSL — mantida no mesmo repositório da especificação. O diagrama é gerado a partir do texto por ferramenta; a fonte de verdade é o texto.

A consequência prática é dupla: o modelo passa a ser revisado por diff em pull request, como qualquer outro artefato, e a divergência entre o que foi modelado e o que foi implementado torna-se visível no histórico. Um diagrama exportado como imagem e anexado a um documento não oferece nenhuma dessas garantias.

Três modelos são exigidos em toda especificação: o modelo de dados, que fundamenta o schema de persistência; o modelo estático, que delimita responsabilidades entre classes; e o modelo dinâmico, que fixa a ordem das interações e os caminhos de erro. Os exemplos a seguir descrevem um sistema de biblioteca com três entidades persistidas e cinco classes de domínio.

9.1 Modelo de dados: entidade-relacionamento

O diagrama entidade-relacionamento declara entidades, atributos, chaves primárias e estrangeiras e cardinalidades. É o antecedente do schema: nenhuma tabela deve existir sem correspondência aqui.

# modelos/dados.mmd
erDiagram
  LIVRO  ||--o{ EMPRESTIMO : origina
  LEITOR ||--o{ EMPRESTIMO : realiza
  LIVRO {
    uuid id PK
    text isbn UK
    text titulo
    int exemplares
  }
  LEITOR {
    uuid id PK
    text email UK
    bool ativo
  }
  EMPRESTIMO {
    uuid id PK
    uuid livro_id FK
    uuid leitor_id FK
    date prevista
    date efetiva
  }
erDiagram LIVRO ||--o{ EMPRESTIMO : origina LEITOR ||--o{ EMPRESTIMO : realiza LIVRO { uuid id PK text isbn UK text titulo int exemplares } LEITOR { uuid id PK text email UK bool ativo } EMPRESTIMO { uuid id PK uuid livro_id FK uuid leitor_id FK date prevista date efetiva }
Decisão que o diagrama torna explícita

A cardinalidade ||--o{ fixa que um livro origina zero ou muitos empréstimos. O campo efetiva nulo identifica empréstimo em aberto: trata-se de decisão de modelagem que deve constar da especificação, sob pena de ser reinventada de forma divergente por cada implementação.

9.2 Modelo estático: diagrama de classes

O diagrama de classes descreve a estrutura do domínio em memória: atributos, operações e dependências entre classes. Nem toda classe corresponde a uma tabela — a distinção entre estrutura persistida e estrutura de comportamento é uma das razões de existir o modelo estático.

# modelos/estatico.mmd
classDiagram
  direction LR
  class Livro {
    +UUID id
    +String isbn
    +int exemplares
    +disponivel() bool
  }
  class Leitor {
    +UUID id
    +bool ativo
  }
  class Emprestimo {
    +Date prevista
    +Date efetiva
    +emAtraso(hoje) bool
  }
  class PoliticaEmprestimo {
    +int prazoDias
    +int limitePorLeitor
    +validar(leitor, livro)
  }
  class ServicoEmprestimo {
    +registrar(leitor, livro)
    +devolver(emprestimo)
  }
  ServicoEmprestimo --> PoliticaEmprestimo
  ServicoEmprestimo --> Emprestimo
  Emprestimo --> Livro
  Emprestimo --> Leitor
classDiagram direction LR class Livro { +UUID id +String isbn +int exemplares +disponivel() bool } class Leitor { +UUID id +bool ativo } class Emprestimo { +Date prevista +Date efetiva +emAtraso(hoje) bool } class PoliticaEmprestimo { +int prazoDias +int limitePorLeitor +validar(leitor, livro) } class ServicoEmprestimo { +registrar(leitor, livro) +devolver(emprestimo) } ServicoEmprestimo --> PoliticaEmprestimo ServicoEmprestimo --> Emprestimo Emprestimo --> Livro Emprestimo --> Leitor

Das cinco classes, três correspondem às entidades persistidas e duas existem apenas em memória. PoliticaEmprestimo concentra as regras de prazo e limite, que mudam por decisão institucional e não devem exigir alteração de esquema; ServicoEmprestimo orquestra a operação sem conhecer detalhes de persistência.

9.3 Modelo dinâmico: diagrama de sequência

O diagrama de sequência fixa a ordem das mensagens trocadas em uma operação e, sobretudo, os caminhos de erro. O bloco alt representa a bifurcação entre recusa e aprovação; cada ramo corresponde a um cenário Gherkin e a um código de status declarado no contrato OpenAPI.

# modelos/dinamico.mmd
sequenceDiagram
  actor A as Atendente
  participant API as POST /emprestimos
  participant S as ServicoEmprestimo
  participant P as PoliticaEmprestimo
  participant R as Repositorio
  A->>API: leitor_id, livro_id
  API->>S: registrar(leitor, livro)
  S->>P: validar(leitor, livro)
  alt limite excedido ou sem exemplar
    P-->>S: recusa(motivo)
    S-->>API: 422 motivo
  else apto
    P-->>S: aprovado(prazo 14d)
    S->>R: salvar(emprestimo)
    R-->>S: emprestimo
    S-->>API: 201 emprestimo
  end
sequenceDiagram actor A as Atendente participant API as POST /emprestimos participant S as ServicoEmprestimo participant P as PoliticaEmprestimo participant R as Repositorio A->>API: leitor_id, livro_id API->>S: registrar(leitor, livro) S->>P: validar(leitor, livro) alt limite excedido ou sem exemplar P-->>S: recusa(motivo) S-->>API: 422 motivo else apto P-->>S: aprovado(prazo 14d) S->>R: salvar(emprestimo) R-->>S: emprestimo S-->>API: 201 emprestimo end
Erro comum

Modelar apenas o caminho de sucesso. A ausência do ramo de recusa no modelo dinâmico produz implementação sem tratamento de erro e contrato sem código de falha, defeito que só se manifesta em produção.

10. Especificação e inteligência artificial

Modelos generativos produzem código sintaticamente correto com facilidade. A partir de instrução vaga, porém, não asseguram aderência a requisitos não funcionais nem a conhecimento de domínio ausente do enunciado. Diante disso, a especificação cumpre três papéis:

  • Restringe — limita o espaço de soluções e impede decisões arquiteturais improvisadas pelo modelo.
  • Codifica — carrega conhecimento de domínio que o modelo não possui internamente.
  • Verifica — define metas mensuráveis aplicadas depois da geração.

Comportamentos que o modelo não infere

Há classes de requisito que nunca emergem de uma instrução em linguagem livre. O processamento de eventos complexos é o caso típico: análise de fluxos contínuos que correlacionam múltiplos eventos ao longo do tempo — sequências, ausência de evento, agregações em janela. Um requisito como "alertar após três falhas de autenticação em menos de um minuto" depende de janela, ordem e critério de reinício que precisam estar declarados na especificação.

Método de trabalho

Passe a especificação e os testes como contexto, em vez de descrever a tarefa em linguagem livre. Diante de um desvio, peça correção contra o desvio medido — sem reabrir a especificação. Limite o número de iterações e escale quando o limite for atingido.

11. Verificação e refinamento iterativo

Cada característica de qualidade tem estratégia própria de verificação. A associação de cada RNF a um instrumento converte a intenção em meta verificável.

CaracterísticaEstratégia de verificação
Eficiência de desempenhoPerfilamento de tempo e de consumo de recursos sob carga representativa.
ConfiabilidadeInjeção de falhas, teste de degradação e verificação do caminho de recuperação.
SegurançaAnálise estática, varredura de segredos e auditoria de dependências.
ManutenibilidadeComplexidade ciclomática, acoplamento e taxa de duplicação.

O ciclo

O código é gerado, verificado contra as metas e, havendo desvio, o relatório retorna ao modelo acompanhado da especificação original e da instrução de correção. Segue-se nova geração e nova verificação, até que todas as metas sejam atendidas ou até o limite de iterações definido — momento em que o caso é escalado e o trade-off, registrado.

Verificação obrigatória

Sem instrumento de medida, a especificação permanece uma intenção não confirmada. Código que passa nos testes funcionais e viola a meta de latência não atende ao requisito.

12. Exemplo completo: endpoint de cotação

Especificação reduzida, mas completa nas seis seções, do endpoint usado como referência na aula.

# POST /api/v1/quotes — Criar cotação

## Visão
Permitir que o cliente obtenha o preço de um conjunto de itens
sem intervenção humana, reduzindo o tempo de resposta comercial.

## Requisitos funcionais
RF-001  Criar cotação a partir de customerId e lista de itens.
RF-002  Aplicar desconto de 5% quando a quantidade do item for ≥ 10.
RF-003  Rejeitar cotação de cliente inativo.

## Requisitos não funcionais
RNF-001 Latência p95 ≤ 100 ms, medida no gateway.
RNF-002 Idempotência garantida pelo cabeçalho Idempotency-Key.
RNF-003 Erro de banco resulta em 500 genérico, sem stack trace.
RNF-004 Lógica de desconto isolada em função própria e testável.

## Contrato
POST /api/v1/quotes
Cabeçalho: Idempotency-Key (UUID, obrigatório)
Corpo: { customerId: uuid,
         items: [ { productId: uuid, quantity: int ≥ 1,
                    priceUnit: decimal > 0 } ] }
201 → { quoteId: uuid, total: decimal }
400 → payload inválido ou cliente inativo
500 → erro interno

## Modelo de dados
Quote     { id UUID PK, customerId UUID FK, total Decimal NOT NULL,
            createdAt Timestamp NOT NULL }
QuoteItem { quoteId UUID FK, productId UUID, quantity Int,
            priceUnit Decimal, PK (quoteId, productId) }

## Cenários
Dado 12 unidades a 10,00, Quando crio a cotação,
  Então o total é 114,00 e o desconto aplicado é de 5%.
Dado a mesma Idempotency-Key de uma cotação existente,
  Quando repito a requisição, Então retorna o mesmo quoteId
  e nenhuma cotação nova é criada.
O que observar no exemplo

Nenhuma linha menciona linguagem, framework ou banco. O total de 114,00 é verificável por cálculo — 12 × 10,00 = 120,00, menos 5% — e o cenário falha até existir implementação. RNF-001 declara o ponto de medição; na especificação completa, cada meta não funcional recebe métrica, limite e instrumento equivalentes.

13. Aplicação após a aula

A aula reserva o encerramento para a demonstração conduzida pelo professor sobre o sistema de biblioteca. A aplicação do método pelo estudante ocorre após a aula, sobre o projeto do próprio grupo, no roteiro abaixo.

  1. Escolha uma funcionalidade do projeto do seu grupo — por exemplo, cancelamento de pedido ou consulta de faturamento.
  2. Escreva a especificação em Markdown com as seis seções: visão, RF numerados, RNF numerados, contrato, modelo de dados e cenários. No mínimo três metas não funcionais verificáveis, cada uma com métrica, limite e instrumento.
  3. Registre em um ADR curto a principal decisão estrutural, incluindo a alternativa descartada e a consequência assumida.
  4. Produza os três modelos em Mermaid, versionados junto da especificação: entidade-relacionamento, diagrama de classes e diagrama de sequência da operação principal, este último com o caminho de erro explícito.
  5. Escreva os cenários Gherkin que fixam as regras. Confirme que todos falham antes de existir implementação.
  6. Passe a especificação e os testes como contexto para um modelo generativo e peça a implementação do endpoint.
  7. Execute análise estática, perfilamento de latência e um teste de falha para validar as metas não funcionais.
  8. Havendo desvio, devolva o resultado medido ao modelo e peça refinamento sem alterar a especificação original.
Questão de fechamento

O código gerado a partir de especificação e testes escritos previamente ficou mais aderente ao esperado do que o produzido sem essa disciplina prévia? Onde exatamente a diferença apareceu — no comportamento funcional ou nas metas de qualidade?

14. Referências

  • ISO/IEC 25010:2023 — Systems and software Quality Requirements and Evaluation: product quality model.
  • ISO/IEC 10746 — Reference Model for Open Distributed Processing (RM-ODP).
  • OpenAPI Specification 3.1 — OpenAPI Initiative.
  • Cucumber — Gherkin Reference.
  • MADR — Markdown Any Decision Records.
  • Nygard, M. — Documenting Architecture Decisions.