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.
O ponto não é falta de capacidade técnica. É que a ambiguidade só se manifesta quando o código já existe, e 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 verdade — Single 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.
Tratar a especificação como documentação escrita depois da entrega. Se ela não precede o código, ela não governa nada — apenas descreve.
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."
Um requisito não funcional sem número, janela de medição e instrumento não é requisito — é desejo. "Deve ser rápido" não é verificável; "p95 ≤ 200 ms medido em cache frio, com falha declarada acima disso" é.
SDD e TDD não competem
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 a verdade; o TDD a implementa. Um projeto pode ter TDD sem SDD — e nesse caso testa corretamente a coisa errada.
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ística | O que cobre |
|---|---|
| Adequação funcional | Correção, completude e pertinência das funções entregues. |
| Eficiência de desempenho | Tempo de resposta, uso de recursos e capacidade. |
| Compatibilidade | Coexistência e interoperabilidade com outros sistemas. |
| Interação (usabilidade) | Apreensibilidade, operabilidade e proteção contra erro do usuário. |
| Confiabilidade | Maturidade, disponibilidade, tolerância a falhas e recuperabilidade. |
| Segurança | Confidencialidade, integridade, não repúdio e autenticidade. |
| Manutenibilidade | Modularidade, reusabilidade, testabilidade e modificabilidade. |
| Flexibilidade | Adaptabilidade, escalabilidade e instalabilidade. |
| Proteção (safety) | Operação em estado seguro, alerta de risco e recuperação controlada. |
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. Declare explicitamente as descartadas — a omissão silenciosa é indistinguível 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.
A especificação não menciona linguagem, framework ou banco de dados. "Usar PostgreSQL" é decisão de tecnologia e pertence a um ADR, não a um requisito. 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. Captura o porquê da estrutura, não o como da implementação.
Estrutura
- Título — identificador e enunciado. ADR-001: processamento assíncrono de cotações via fila.
- Status — proposto, 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 — o que fica pior, não apenas o que melhora.
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.
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 escrito depois da decisão, para justificá-la, é ata, não raciocínio.
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.
| Artefato | Autoria | Responde a |
|---|---|---|
| 1. Problema | Produto e stakeholders | O 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. ADRs | Arquitetura e liderança técnica | Como a solução se estrutura. Padrões, decomposição, limites de módulo. Independente de tecnologia. |
| 3. Especificação | Arquitetura e time | Qual é o comportamento esperado. RF, RNF, contrato de API, modelo de dados, cenários. Ainda sem tecnologia. |
| 4. Escolha de tecnologia | Arquitetura | Com o quê será construído. Linguagem, banco, framework — registrado em ADR próprio e justificado contra a especificação. |
| 5. Plano de implementação | Time, com apoio de IA | Quais módulos realizam a especificação. Classes, funções, fluxo. |
Problema → ADR → especificação → tecnologia → implementação. A escolha de tecnologia entra em quarto lugar, não em primeiro. Quando ela antecede a especificação, o comportamento esperado passa a ser derivado da ferramenta disponível — e não o contrário.
7. Pipeline de elicitação
Da análise do negócio até a implementação, seis estágios:
- 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.
- Modelagem arquitetural (RM-ODP). Casos de uso, entidades e diagramas de sequência, organizados em pontos de vista.
- Esqueleto do software. Estrutura de diretórios, limites de módulo, composição de ambiente. Projeto vazio, mas estruturalmente completo.
- Documentos de especificação. Consolidação em Markdown e YAML, formatos legíveis por máquina — autoria integralmente humana.
- Testes que falham (TDD). Contratos comportamentais expressos como pré-condições, pós-condições e invariantes.
- 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.
A ordem dos pontos de vista espelha a sequência documental da seção 6: Technology é o último, não o primeiro.
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
Escrever cenários que descrevem cliques de interface. Gherkin fixa regra de negócio, não roteiro de navegação — um cenário que menciona botões quebra a cada mudança de layout, sem 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, não a imagem.
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
}
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
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
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. O que não produzem, a partir de instrução vaga, é aderência a requisitos não funcionais e 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.
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. Associar cada RNF a um instrumento é o que separa uma meta de uma intenção.
| Característica | Estratégia de verificação |
|---|---|
| Eficiência de desempenho | Perfilamento de tempo e de consumo de recursos sob carga representativa. |
| Confiabilidade | Injeção de falhas, teste de degradação e verificação do caminho de recuperação. |
| Segurança | Análise estática, varredura de segredos e auditoria de dependências. |
| Manutenibilidade | Complexidade 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.
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: compilar não é cumprir.
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.
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. As quatro metas não funcionais têm instrumento associado.
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.
- Escolha uma funcionalidade do projeto do seu grupo — por exemplo, cancelamento de pedido ou consulta de faturamento.
- 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.
- Registre em um ADR curto a principal decisão estrutural, incluindo a alternativa descartada e a consequência assumida.
- 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.
- Escreva os cenários Gherkin que fixam as regras. Confirme que todos falham antes de existir implementação.
- Passe a especificação e os testes como contexto para um modelo generativo e peça a implementação do endpoint.
- Execute análise estática, perfilamento de latência e um teste de falha para validar as metas não funcionais.
- Havendo desvio, devolva o resultado medido ao modelo e peça refinamento sem alterar a especificação original.
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.