📝 Metodologia · Spec-Driven Development

Spec-Driven Development (SDD)

Este autoestudo examina por que contratos, schemas e regras de negócio devem ser especificados antes da implementação e de que modo a especificação disciplina o desenvolvimento de software assistido por Inteligência Artificial.

1. O que é Spec-Driven Development?

É comum iniciar a implementação antes de qualquer definição formal do comportamento esperado. Essa prática resulta em retrabalho, inconsistência de contratos de API e desalinhamento com as partes interessadas. O Spec-Driven Development (SDD) é uma metodologia de desenvolvimento de software na qual a especificação técnica constitui a única fonte de verdade (Single Source of Truth — SSoT).

Entende-se por Single Source of Truth (SSoT), em português fonte única de verdade, o princípio de arquitetura da informação segundo o qual cada informação relevante do sistema reside em um único artefato autoritativo, do qual todas as demais representações são derivadas. Artefato autoritativo é aquele que detém precedência formal em caso de conflito: quando duas versões da mesma informação divergem, a fonte única prevalece e as cópias devem ser reconciliadas com ela, jamais o inverso. No SDD, a especificação assume esse papel, de modo que código, testes e documentação permaneçam derivações verificáveis de um mesmo artefato, e não versões concorrentes sujeitas a divergência.

No SDD, antes de qualquer implementação, a equipe redige especificações que descrevem o comportamento planejado do sistema. Essas especificações contêm, no mínimo:

  • Contratos de API — rotas, métodos, payloads (o corpo de dados transmitido em uma requisição ou resposta) e códigos de status, tipicamente descritos em OpenAPI/Swagger.
  • Modelagem e esquemas de dados (tipos, chaves, restrições de integridade).
  • Regras de negócio explícitas e fluxos de exceção.
  • Metas de qualidade não funcional mensuráveis, não apenas requisitos funcionais.
Requisito funcional e requisito não funcional: entende-se por requisito funcional (RF) a especificação daquilo que o sistema deve fazer — as funções e os comportamentos observáveis que, a partir de determinadas entradas, produzem uma saída verificável (por exemplo, "o sistema deve permitir o registro de um usuário mediante nome e endereço de correio eletrônico"). O requisito não funcional (RNF) especifica como o sistema deve se comportar quanto a atributos de qualidade — desempenho, segurança, confiabilidade, manutenibilidade — como em "a operação de registro deve concluir em, no máximo, dois segundos sob carga nominal". A distinção permite associar cada função e cada atributo de qualidade a critérios de aceitação verificáveis e automatizáveis.
SDD vs. TDD (Test-Driven Development): o Test-Driven Development (TDD), ou desenvolvimento guiado por testes, é a prática em que o teste automatizado é escrito antes do código de produção que deve satisfazê-lo, organizada no ciclo red-green-refactor: na fase vermelha o teste falha porque a funcionalidade ainda não foi implementada; na fase verde implementa-se o mínimo necessário para que passe; na fase de refatoração aprimora-se a estrutura do código mantendo todos os testes verdes. Exige-se que o teste falhe primeiro para confirmar que ele exercita de fato o comportamento pretendido. O TDD fixa a correção lógica interna de um módulo; o SDD descreve as interfaces, os contratos e os limites do sistema antes disso. O SDD não substitui o TDD — ele o precede e o fundamenta: a especificação define o que deve ser verdadeiro, e o TDD codifica essa verdade em testes executáveis.

2. Qualidade Não Funcional como Parte da Especificação

Uma especificação tecnicamente completa não se limita a descrever o que o sistema faz. Ela define também como o sistema deve se comportar sob variação de carga, falha, uso indevido ou mudança de contexto — as chamadas propriedades não funcionais.

A ISO/IEC 25010:2023, publicada conjuntamente pela International Organization for Standardization (ISO) e pela International Electrotechnical Commission (IEC), é a norma internacional que define um modelo de qualidade de produto de software, sucedendo a ISO/IEC 9126. Seu propósito não é apenas catalogar características, mas fornecer vocabulário e taxonomia comuns que permitam especificar, medir e avaliar atributos de qualidade de forma objetiva, impedindo que requisitos não funcionais permaneçam implícitos ou sujeitos a interpretação subjetiva. O modelo organiza a qualidade em nove características: a adequação funcional trata da correção das funções entregues; as oito restantes tratam de qualidade não funcional e são particularmente relevantes quando parte do código é gerada por IA:

Característica Subcaracterísticas
Eficiência de desempenhoComportamento temporal, utilização de recursos, capacidade.
CompatibilidadeCoexistência e interoperabilidade com outros sistemas.
Capacidade de interaçãoApreensibilidade, operabilidade, proteção contra erro do usuário, inclusividade, assistência ao usuário.
ConfiabilidadeAusência de falhas (faultlessness), disponibilidade, tolerância a falhas, recuperabilidade.
SegurançaConfidencialidade, integridade, não repúdio, responsabilização, autenticidade.
ManutenibilidadeModularidade, reusabilidade, analisabilidade, modificabilidade, testabilidade.
FlexibilidadeAdaptabilidade, escalabilidade, instalabilidade, substituibilidade.
Segurança operacionalRestrição operacional, identificação de risco, fail-safe, alerta de perigo.

Cada subcaracterística decompõe-se em critérios mensuráveis. Um requisito enunciado como "a aplicação deve restabelecer o serviço em até trinta segundos após a queda da conexão de rede" corresponde à confiabilidade, na subcaracterística de recuperabilidade, e passa a ser verificável por critério objetivo em lugar de permanecer expectativa tácita.

Fail-safe: a expressão, do inglês "seguro em caso de falha", designa a propriedade de projeto segundo a qual um sistema, ao deparar-se com uma condição de erro que não é capaz de tratar, transita de forma determinística para um estado seguro e previamente definido, em vez de permanecer em operação incerta ou potencialmente danosa. Considere um módulo de autorização que consulta um serviço externo de permissões: diante da indisponibilidade desse serviço, um projeto fail-safe determina que o acesso seja negado por padrão (deny-by-default), preservando a segurança, em vez de concedido. O estado seguro de falha deve ser declarado explicitamente na especificação, como pós-condição ou comportamento esperado.
Meta mensurável, não intenção genérica: cada característica listada deve ser traduzida, na especificação, em um critério verificável. "O sistema deve ser seguro" não é uma especificação. "Nenhum segredo codificado diretamente no código-fonte; toda configuração sensível via variável de ambiente" é.

3. Os Pilares de uma Especificação Madura

Uma especificação madura no ecossistema de dados e software é composta por quatro pilares:

Contrato de API

Rotas, métodos HTTP, códigos de status e payload de requisição/resposta usando especificações de mercado como OpenAPI v3.

Schema de Dados

Definição explícita de tipos, chaves, índices e restrições de integridade no banco de dados e nos modelos de tráfego de dados.

Regras e Cenários

Critérios de aceitação escritos em linguagem compreensível por humanos e computadores (ex.: sintaxe Gherkin/BDD).

Metas de Qualidade

Critérios mensuráveis para cada característica não funcional da ISO/IEC 25010:2023 aplicável ao módulo especificado.

OpenAPI e Swagger. OpenAPI é uma especificação padronizada e legível por máquina, expressa em formato estruturado (YAML ou JSON), destinada a descrever de maneira formal APIs HTTP — tipicamente de estilo REST —, incluindo rotas, métodos HTTP, parâmetros, esquemas de payload e códigos de status de resposta. Swagger designa o conjunto de ferramentas associado a essa especificação, nome que historicamente identificava a própria especificação antes de esta ser doada à OpenAPI Initiative e renomeada; tais ferramentas geram documentação interativa, validadores e artefatos de código a partir do documento. A especificação constitui um contrato explícito entre o produtor e o consumidor da API, definindo com precisão a interface de cada operação — suas entradas, saídas e códigos de status —, e pode ser acordada antes da implementação. Um documento OpenAPI pode declarar, por exemplo, que a rota GET /usuarios/{id} recebe um identificador inteiro no caminho e retorna, com código 200, um objeto contendo os campos nome e e-mail, ou o código 404 quando o identificador não corresponde a nenhum registro.

Gherkin e BDD. O Behavior-Driven Development (BDD), ou desenvolvimento orientado a comportamento, descreve o comportamento esperado de um sistema por meio de cenários concretos, formulados em linguagem compreensível tanto por equipes técnicas quanto por partes interessadas sem formação técnica. Gherkin é a linguagem estruturada que materializa esses cenários no formato Dado/Quando/Então (Given/When/Then), simultaneamente legível por humanos e interpretável por ferramentas de automação: "Dado" estabelece o contexto inicial, "Quando" identifica a ação ou o evento e "Então" define o resultado esperado. Cada cenário expressa um critério de aceitação verificável e versionado, mantendo a especificação como fonte de verdade continuamente confrontada com a implementação. Exemplo: "Dado que uma conta possui saldo de 100 unidades; Quando o titular solicita um saque de 30 unidades; Então o saldo resultante deve ser de 70 unidades e a operação deve ser registrada como concluída."

4. Da Análise de Negócio à Especificação: um Pipeline em Seis Estágios

A especificação não nasce pronta. Ela resulta de um processo estruturado de elicitação — a atividade sistemática de levantar, extrair e explicitar requisitos junto aos stakeholders (usuários, clientes, especialistas de domínio e demais partes interessadas), convertendo necessidades tácitas e frequentemente ambíguas em enunciados verificáveis. Constitui a etapa inicial da engenharia de requisitos e recorre a técnicas como entrevistas, questionários, observação direta, análise de documentos e prototipação. Quando organizada em etapas encadeadas de coleta, refinamento e validação, a elicitação assume a forma de um pipeline. Um pipeline consolidado segue seis estágios sequenciais, dos quais apenas o último admite participação de Inteligência Artificial:

  1. Modelagem de processos de negócio: análise as-is do domínio, usando técnicas de decomposição funcional (ex.: IDEF0) para identificar stakeholders, entradas, saídas, controles e mecanismos dos processos que o software deve apoiar. Este estágio responde a o que a organização faz e onde estão as lacunas, não qual deve ser a aparência do software.
  2. Modelagem arquitetural: casos de uso, modelos estáticos (entidades de domínio, esquemas de dados, decomposição em módulos) e modelos dinâmicos (diagramas de sequência, fluxos de eventos, transições de estado). Para sistemas com múltiplas perspectivas de interesse, frameworks como o RM-ODP organizam a arquitetura em viewpoints.
  3. Esqueleto do software: estrutura de diretórios, limites de módulo, arquivos de configuração, descritores de implantação (ex.: Docker Compose) e contratos de interface — um projeto vazio, porém estruturalmente completo.
  4. Documentos de especificação: consolidação das saídas anteriores em documentos legíveis por máquina (Markdown, YAML, JSON), incluindo metas mensuráveis para cada característica não funcional aplicável (Seção 2). A autoria destes documentos é inteiramente humana; a IA não participa deste estágio.
  5. Desenvolvimento guiado por testes (TDD): elaboração de casos de teste que consolidam os contratos comportamentais definidos nos estágios anteriores. Todos os testes devem falhar inicialmente (fase vermelha), confirmando que nenhuma lógica foi implementada e que os testes de fato verificam comportamento.
  6. Implementação: somente neste estágio o código é gerado — manualmente ou com apoio de IA —, com o objetivo de fazer os testes do estágio anterior passarem (fase verde) e, simultaneamente, satisfazer as metas de qualidade não funcional definidas no estágio 4.

Análise as-is. A expressão as-is, do inglês "como está", designa a representação do estado atual de um processo ou sistema, tal como opera antes de qualquer intervenção. A análise as-is documenta os fluxos, atores, regras e limitações vigentes, constituindo uma linha de base factual que se contrapõe ao estado futuro desejado, denominado to-be. Sua finalidade é assegurar que toda especificação de mudança parta de uma compreensão rigorosa daquilo que efetivamente existe, evitando suposições descoladas da realidade operacional. Ao modelar a emissão de segunda via de um documento, por exemplo, a análise as-is registra que a solicitação é hoje recebida por correio eletrônico, conferida manualmente e respondida em até dois dias úteis, sem automação — o estado atual, não a solução pretendida.

IDEF0 e a notação ICOM. IDEF0 (Integration DEFinition for Function Modeling) é uma técnica de modelagem funcional que representa as funções de um sistema como caixas conectadas por setas, suportando decomposição hierárquica de funções de alto nível em subfunções. Cada caixa é descrita pela notação ICOM, que distingue quatro elementos:
Elemento (posição)Significado
Input — entrada (esquerda)O que a função consome ou transforma.
Control — controle (topo)Condições, regras e restrições que governam a execução.
Output — saída (direita)O resultado produzido pela função.
Mechanism — mecanismo (base)Os recursos que realizam a função.

Na função "Aprovar crédito", a entrada é a solicitação do cliente, o controle é a política de risco vigente, a saída é a decisão de aprovação ou recusa, e o mecanismo é o analista apoiado pelo sistema de crédito. Distinguir Input de Control evidencia que a especificação atua como o controle que governa cada função, enquanto a análise as-is fornece a base factual sobre a qual os requisitos são formulados.

RM-ODP e seus cinco viewpoints. O RM-ODP (Reference Model of Open Distributed Processing), padronizado pela ISO/IEC 10746, é um arcabouço de referência para a especificação de sistemas distribuídos abertos. Descreve um mesmo sistema por cinco pontos de vista complementares, cada qual isolando um conjunto distinto de preocupações, o que impede a mistura, num único artefato, de intenção de negócio, modelo de dados, contrato funcional e detalhe tecnológico:
ViewpointPreocupação isolada
EnterprisePropósito, papéis, políticas e regras de negócio.
InformationSemântica e estrutura dos dados manipulados.
ComputationalDecomposição funcional em objetos e interfaces.
EngineeringMecanismos de distribuição e infraestrutura de comunicação.
TechnologyEscolhas concretas de tecnologia da implementação.

Em um sistema de reserva de salas, o Enterprise fixa que apenas usuários autenticados reservam e define a política de cancelamento; o Information modela as entidades Sala, Reserva e Usuário; o Computational decompõe o sistema em serviços com interfaces como "consultar disponibilidade" e "confirmar reserva"; o Engineering determina a comunicação por chamadas remotas com replicação de estado; e o Technology fixa o uso de contêineres e de um banco de dados relacional. Cada especificação torna-se rastreável e verificável em seu próprio nível de abstração.

Contratos comportamentais. Os contratos comportamentais mencionados no estágio 5 constituem a especificação formal das obrigações e garantias de uma operação ou módulo, conforme a abordagem de projeto por contrato (Design by Contract). Compõem-se de três cláusulas: a pré-condição, que estabelece o que deve ser verdadeiro antes da invocação e cuja satisfação é responsabilidade do chamador; a pós-condição, que descreve o que a operação garante ao terminar, desde que a pré-condição tenha sido satisfeita; e a invariante, propriedade que deve valer em todos os estados observáveis do objeto — após sua construção e entre as operações públicas —, podendo ser temporariamente violada durante a execução interna de uma operação. Em uma pilha de capacidade fixa, a inserção tem por pré-condição que a pilha não esteja cheia, por pós-condição que o elemento inserido ocupe o topo e o tamanho seja incrementado em uma unidade, e por invariante que o número de elementos permaneça entre zero e a capacidade máxima. Tais cláusulas tornam explícita a distribuição de responsabilidades entre chamador e operação chamada, delimitando de forma inequívoca a origem de uma falha.

Especificações legíveis por máquina. Os documentos do estágio 4 são redigidos em formato estruturado — tipicamente Markdown, YAML ou JSON —, cuja sintaxe permite processamento automático por ferramentas e por modelos de linguagem, em oposição ao texto livre em prosa. Ao adotar estrutura formal, com campos, chaves nomeadas, hierarquia e tipos declarados, tais documentos reduzem a ambiguidade interpretativa da linguagem natural e servem simultaneamente como comunicação inteligível para humanos e como entrada consumível por processos automatizados, habilitando validação, versionamento e geração de artefatos derivados.

Docker Compose e conteinerização. A conteinerização, referida no estágio 3, é a técnica de empacotar uma aplicação em conjunto com suas dependências, bibliotecas e configurações em uma unidade isolada denominada contêiner, a qual compartilha o núcleo (kernel) do sistema operacional hospedeiro e executa de maneira consistente na maioria dos ambientes compatíveis. O Docker Compose é uma ferramenta que descreve, por meio de um arquivo declarativo em YAML, a definição e a execução coordenada de múltiplos contêineres em um único host — por exemplo, uma aplicação e seu banco de dados —, especificando serviços, redes e volumes; a orquestração distribuída em escala pertence a ferramentas como Kubernetes ou Docker Swarm. Seu emprego favorece a reprodutibilidade: uma vez que o ambiente de execução passa a ser especificado explicitamente em código, a implantação torna-se mais previsível entre as máquinas de desenvolvimento, teste e produção. No SDD, o arquivo de composição atua como especificação executável do ambiente, convertendo a infraestrutura de conhecimento tácito em artefato versionado e verificável.

A ordenação é deliberada: a especificação precede o TDD, e o TDD precede a implementação. Cada estágio encerra decisões que o estágio seguinte não deve reabrir, garantindo que decisões arquiteturais e regras de negócio reflitam julgamento de engenharia deliberado, e não os comportamentos-padrão de um modelo generativo.

5. Spec-Driven Development e Inteligência Artificial

Com o avanço de assistentes de codificação por IA, o SDD tornou-se ainda mais relevante. Modelos de linguagem geram código sintaticamente correto com facilidade, mas o resultado frequentemente carece de alinhamento com requisitos não funcionais quando a instrução recebida é vaga ou informal.

Uma especificação bem estruturada cumpre três papéis simultâneos diante de um modelo generativo: restringe o espaço de soluções possíveis, evitando decisões arquiteturais improvisadas; codifica conhecimento de domínio que o modelo não possui; e define metas de qualidade verificáveis, conferíveis após a geração. Nenhum desses papéis é cumprido por um prompt informal.

Comportamentos que o modelo não infere: certas regras dependem de conhecimento de domínio que não é dedutível da observação de casos isolados e, por isso, devem constar explicitamente da especificação. É o caso do Processamento de Eventos Complexos (CEP, Complex Event Processing), paradigma que analisa fluxos contínuos de eventos e correlaciona múltiplos eventos ao longo do tempo — sequências ordenadas, ausência de eventos esperados, agregações em janelas temporais — para detectar situações que nenhum evento, considerado sozinho, é capaz de revelar. Um sistema que emite alerta quando três tentativas de autenticação falham para o mesmo usuário em menos de um minuto aplica CEP: o alerta não decorre de nenhuma falha isolada, e sim do padrão temporal formado pela sequência. Comportamentos desse tipo exigem que suas janelas, ordens e condições de ausência sejam definidas de forma explícita na especificação.
Como o SDD melhora o uso de IAs na programação:
  1. Redução de ambiguidade: a IA não precisa adivinhar tipos de dados ou nomenclatura de propriedades.
  2. Fundamentação dos testes: a especificação fornece a base sobre a qual a equipe redige cenários de teste unitário e de integração completos, que passam a delimitar o que a IA deve satisfazer na fase de implementação.
  3. Regeneração limpa: permite regerar código a partir de atualizações na especificação sem herdar decisões anteriores obsoletas.

6. Verificação e Refinamento Iterativo

Gerar código a partir de uma especificação não encerra o processo. O resultado deve ser verificado contra as metas definidas para cada característica não funcional, usando ferramental apropriado a cada uma:

Característica Estratégia de verificação
Eficiência de desempenhoPerfilamento de tempo de resposta.
CompatibilidadeTestes de integração e contrato.
ConfiabilidadeInjeção de falhas; teste de fallback.
SegurançaAnálise estática e auditoria de código.
ManutenibilidadeComplexidade ciclomática e taxa de duplicação.
FlexibilidadeTeste de variação de configuração.

Perfilamento (profiling). Entende-se por perfilamento a medição, em tempo de execução, do consumo de tempo de processamento e de recursos (memória, chamadas de sistema, operações de entrada e saída) atribuído a cada trecho de um programa. A técnica instrumenta ou amostra a execução para associar esses custos a funções, laços ou linhas específicas, revelando onde o programa concentra seu esforço. Distingue-se da análise estática por operar sobre a execução real, e não sobre o texto do código. Requisitos não funcionais de eficiência de desempenho só podem ser verificados objetivamente por meio dele, que converte as metas declaradas na especificação em medições comparáveis.

Injeção de falhas (fault injection). Constitui a técnica de teste que introduz deliberadamente condições anômalas em um sistema em execução — indisponibilidade de um serviço, latência excessiva, erro de rede —, a fim de observar seu comportamento sob adversidade. Exercita explicitamente as rotas de tratamento de erro e os mecanismos de contingência que os testes de caminho feliz não alcançam, evidenciando se o sistema degrada de forma controlada e retorna a um estado consistente após a remoção da falha. Em um serviço que consome uma API externa de câmbio, suspende-se temporariamente o contêiner dessa dependência durante os testes e verifica-se se o serviço aplica timeout, retorna valor em cache e registra o incidente, em vez de propagar a falha ao usuário.

Degradação graciosa (graceful degradation) e fallback. Degradação graciosa é a propriedade pela qual um sistema mantém operação parcial, com funcionalidade reduzida, quando um de seus componentes ou dependências falha, torna-se indisponível ou é submetido a sobrecarga, em vez de interromper por completo. O fallback é o comportamento alternativo acionado nesse cenário: um caminho substituto que preserva a continuidade do serviço, ainda que sob garantias menores. Um serviço de recomendação que consulta uma API externa de preferências pode, ao detectá-la indisponível, retornar uma lista padrão armazenada localmente — resultado menos personalizado, porém funcional. A especificação deve declarar de forma explícita o comportamento esperado diante da falha de cada dependência, convertendo a degradação graciosa em requisito verificável.

Análise estática de código. É o exame do código-fonte sem execução do programa, com o objetivo de detectar defeitos, vulnerabilidades e violações de padrão. A técnica inspeciona a estrutura textual e sintática — declarações, fluxos de controle, dependências, expressões — para identificar problemas como segredos codificados diretamente no fonte, uso de construções perigosas (por exemplo, a função eval()) e ausência de validação de entradas. Distingue-se da análise dinâmica, que avalia o comportamento durante a execução: a estática opera antes e independentemente da execução, alcançando caminhos que os testes dinâmicos podem não exercitar. Ambas são complementares. No SDD, a análise estática constitui um gate automatizado que verifica se o código respeita as restrições de segurança e de padrão fixadas na especificação.

Complexidade ciclomática. Métrica proposta por McCabe que quantifica o número de caminhos linearmente independentes no grafo de fluxo de controle de um módulo ou função. Cada estrutura de decisão — condicional, laço ou operador lógico de curto-circuito — introduz uma ramificação e incrementa o valor. Seu valor corresponde ao número de casos de teste de um conjunto-base de caminhos independentes: opera como limite superior para a cobertura de ramos e como limite inferior para a cobertura de todos os caminhos de execução possíveis. Uma função que apenas retorna a soma de dois parâmetros tem complexidade 1; ao acrescentar duas condicionais que tratam valores nulos e negativos, o número de caminhos independentes sobe para 3, exigindo três casos de teste para cobrir o conjunto-base. Valores elevados indicam lógica mais ramificada, o que eleva o esforço de teste e dificulta a compreensão e a alteração seguras — constituindo, assim, um proxy quantitativo de testabilidade e manutenibilidade verificável por análise estática.

Taxa de duplicação de código. Métrica que quantifica a proporção de trechos idênticos ou substancialmente semelhantes presentes em uma base de código, em relação ao seu volume total. É um indicador de manutenibilidade, comumente obtido por análise estática. Valores elevados sinalizam que uma mesma lógica se encontra replicada em múltiplos pontos, de modo que a correção de um defeito ou a alteração de uma regra deve ser propagada a todas as ocorrências, sob pena de inconsistência. Uma rotina de validação de e-mail copiada em cinco módulos exige, ao corrigir uma falha, a edição das cinco cópias; a omissão de qualquer uma mantém o defeito ativo. A especificação pode estabelecer um limiar máximo tolerado, assegurando que o código derivado dela preserve a manutenibilidade prescrita.

Desempenho e concorrência: operações assíncronas. A verificação de eficiência de desempenho frequentemente incide sobre operações assíncronas. Entende-se por operação assíncrona o modelo de execução no qual uma tarefa de entrada/saída — comunicação de rede, leitura ou escrita em disco — é iniciada sem que o fluxo de controle permaneça bloqueado à espera de sua conclusão. As palavras-chave async e await demarcam funções cuja execução pode ser suspensa no ponto de espera e retomada quando o resultado estiver disponível, liberando a thread para processar outras tarefas; a E/S não bloqueante, de forma complementar, designa chamadas de sistema que retornam de imediato e sinalizam a conclusão posteriormente. Um servidor que consulta um banco de dados com await passa a atender outras requisições enquanto aguarda a resposta. O caráter assíncrono de uma operação e seus requisitos de latência e concorrência devem constar do contrato, para que a verificação avalie o comportamento sob carga, e não apenas a correção do resultado.

Quando a verificação aponta desvio — um teste que falha, uma meta de complexidade excedida, um segredo codificado diretamente no código —, o relatório de qualidade retorna à IA geradora junto às especificações originais, com instrução explícita sobre o que corrigir. O código é regenerado e reverificado. O ciclo se repete até que todas as metas sejam atendidas ou até que um número máximo de iterações seja atingido.

A verificação não constitui uma etapa opcional de revisão manual: é parte integrante do método. Sem verificação, a especificação permanece uma intenção não confirmada.

7. Exemplo Prático de uma Spec

Abaixo, um exemplo simplificado de especificação técnica em Markdown para um endpoint de criação de cotações em um microsserviço de faturamento, incluindo metas de qualidade não funcional.

Microsserviço: unidade de software autônoma, de responsabilidade única e implantável de forma independente, que compõe, com outras unidades semelhantes, uma aplicação distribuída. Cada microsserviço encapsula uma capacidade de negócio delimitada, expõe suas funcionalidades por interfaces bem definidas — tipicamente APIs sobre HTTP — e comunica-se com os demais por contratos explícitos, sem compartilhamento de estado interno. Um sistema de comércio eletrônico pode ser decomposto em catálogo, carrinho e pagamento, cada um com sua própria base de dados e API, de modo que uma falha no pagamento não interrompa a consulta ao catálogo. Por delimitarem suas fronteiras mediante contratos de interface explícitos, os microsserviços são especialmente aderentes ao SDD.
Endpoint: ponto de contato exposto por uma API, identificado de forma unívoca pela combinação de uma rota — o caminho do recurso na URI — e de um método HTTP (GET, POST, PUT, DELETE). Cada endpoint associa essa identificação a uma operação determinada sobre um recurso e define o formato esperado da requisição e da resposta. Uma mesma rota compõe endpoints distintos quando combinada a métodos diferentes: POST /cotacoes cria uma cotação a partir dos dados enviados no corpo da requisição, ao passo que GET /cotacoes recupera as cotações existentes.
# Especificação: Endpoint de Cotação (Billing Service)

## Informações Gerais
*   **Rota:** `/api/v1/quotes`
*   **Método:** `POST`
*   **Content-Type:** `application/json`
*   **Cabeçalho `Idempotency-Key`:** UUID único da requisição, obrigatório. Requisições repetidas com a mesma chave não geram cotações duplicadas.

## Payload de Requisição
```json
{
  "customerId": "uuid (obrigatório)",
  "items": [
    {
      "productId": "string (obrigatório)",
      "quantity": "integer (obrigatório, mínimo 1)",
      "priceUnit": "float (obrigatório, positivo)"
    }
  ]
}
```

## Regras de Negócio e Validações
1.  **Validação de Cliente:** o `customerId` deve ser um UUID válido e pertencer a um cliente ativo no banco de dados. Caso contrário, retornar `400 Bad Request` com o erro correspondente.
2.  **Cálculo do Total:** o microsserviço deve calcular o valor total da cotação multiplicando `quantity` * `priceUnit` para cada item e somando os resultados.
3.  **Desconto por Quantidade:** se a quantidade de qualquer item for maior ou igual a 10, deve ser aplicado um desconto de 5% sobre o preço unitário desse item.

## Respostas Esperadas
*   `201 Created`: cotação gerada com sucesso. Retorna o ID da cotação criada e o total final.
*   `400 Bad Request`: payload inválido ou falha em regra de negócio.
*   `500 Internal Server Error`: erro inesperado do servidor.

## Metas de Qualidade Não Funcional
*   **Segurança:** nenhum segredo codificado diretamente; validação de `customerId` obrigatória antes de qualquer cálculo.
*   **Manutenibilidade:** lógica de desconto isolada em função própria, testável isoladamente do cálculo do total.
*   **Confiabilidade:** falha de acesso ao banco de dados deve retornar `500` com mensagem genérica, sem expor stack trace ao cliente.
*   **Confiabilidade (idempotência):** a submissão repetida de uma mesma cotação, identificada pelo cabeçalho `Idempotency-Key`, não deve gerar registros duplicados.
Payload, Content-Type e UUID. O payload (carga útil) é o corpo de dados efetivamente transmitido em uma requisição ou resposta HTTP, distinto dos metadados de transporte contidos nos cabeçalhos e na linha inicial da mensagem. No exemplo, o objeto JSON com customerId e items é o payload; o cabeçalho Content-Type: application/json apenas descreve o formato desse corpo e não integra a carga útil, tal como a linha inicial POST /api/v1/quotes é metadado de transporte. O UUID (Universally Unique Identifier, Identificador Único Universal) é um valor de 128 bits, comumente representado por 32 dígitos hexadecimais em cinco grupos separados por hifens no formato 8-4-4-4-12 (por exemplo, 550e8400-e29b-41d4-a716-446655440000). Destina-se a identificar entidades sem depender de uma autoridade central, pois a probabilidade de colisão entre valores gerados de forma independente é desprezível. A validação do formato do customerId permite rejeitar entradas malformadas já na fronteira do sistema.
Idempotência e a operação upsert. Idempotência é a propriedade de uma operação cujo efeito sobre o estado do sistema é idêntico quando aplicada uma ou múltiplas vezes: repetições sucessivas não produzem resultado distinto do da primeira execução. A operação upsert (contração de update-or-insert) atualiza o registro quando este já existe e o insere quando ausente, com base em uma chave de identificação, sendo comumente empregada para tornar escritas idempotentes. Sua idempotência, contudo, não é automática: verifica-se quando a atualização sobrescreve o registro com valores determinados pela entrada, independentes do estado previamente armazenado, ao passo que atualizações relativas ao valor corrente — incrementos, acumulações — não são idempotentes. Ao processar o mesmo lote de cotações duas vezes, uma inserção simples geraria registros duplicados; um upsert, ao detectar a chave já cadastrada, sobrescreve os dados existentes, de modo que o estado final é o mesmo em ambas as execuções. No SDD, a idempotência deve constar como critério explícito no contrato da operação, para que o comportamento sob reexecução seja verificável.
Controller. Quando o exemplo acima é implementado, o Controller é o componente da camada de entrada que, no padrão arquitetural MVC (Model-View-Controller), recebe as requisições externas, valida e interpreta os dados de entrada, delega o processamento às camadas de domínio ou de serviço e devolve a resposta apropriada. Constitui o ponto de articulação entre a interface e a lógica de negócio, sem conter, ele próprio, as regras de negócio: isola a mecânica de protocolo — rotas, verbos HTTP, formatos de carga e códigos de status — do núcleo funcional. No SDD, o Controller é a camada onde o contrato formal da interface (por exemplo, uma definição OpenAPI) se traduz diretamente em código executável, tornando verificável a conformidade entre o que foi especificado e o que foi implementado.

8. Desafio Prático de Autoestudo

Esta seção propõe um exercício de aplicação do Spec-Driven Development.

Instruções para o Desafio:
  1. Escolha uma funcionalidade do projeto do seu grupo (ex.: cancelamento de pedido, consulta de faturamento, cadastro de produto).
  2. Escreva uma especificação Markdown (como no exemplo da Seção 7) detalhando rota, payload, validações, regras de negócio e ao menos três metas de qualidade não funcional (Seção 2). Traduza cada meta em critério verificável, e não em intenção genérica.
  3. Antes de gerar qualquer código, escreva os casos de teste que fixam essas regras. Confirme que todos falham (fase vermelha do TDD).
  4. Utilize a especificação e os testes como contexto para uma IA de código (ex.: Gemini ou Claude) gerar a implementação do Controller.
  5. Execute os testes e verifique se as metas de qualidade não funcional foram atendidas, empregando o ferramental apropriado a cada característica (Seção 6): perfilamento, análise estática, injeção de falhas, complexidade ciclomática.
  6. Caso algo falhe, retorne o resultado da verificação à IA e peça o refinamento, sem alterar a especificação original.
  7. Analise: o código gerado com os testes escritos previamente foi mais aderente ao esperado do que quando você programa sem essa disciplina prévia?