Capa do artigo: Intenção Antes do Código: Spec-Driven Development, Testes e IA em uma Pipeline de Engenharia

Intenção Antes do Código: Spec-Driven Development, Testes e IA em uma Pipeline de Engenharia

Introdução

Código sem testes não é confiável. Não importa o quão bem escrito ele esteja. Sem uma rede de segurança automatizada, qualquer mudança é uma operação de risco. Foi para resolver esse problema que Kent Beck sistematizou o desenvolvimento orientado a testes (TDD), invertendo a ordem convencional de trabalho. O engenheiro escreve primeiro um teste que descreve o comportamento desejado, depois escreve o código mínimo para fazê-lo passar, e então reorganiza a estrutura interna com os testes como proteção. Esse ciclo, conhecido como red-green-refactor, tornou-se um dos fundamentos da engenharia de software profissional.

À medida que as arquiteturas evoluíram de monólitos para sistemas distribuídos compostos por múltiplos serviços independentes, o problema mudou de forma. Dentro de cada serviço, a pirâmide de testes continua válida. Mas as fronteiras entre serviços introduziram uma classe diferente de falhas: chamadas de rede sujeitas a timeouts, contratos implícitos entre times e divergências de comportamento que testes de unidade não conseguem detectar. Especificações formais de interface e contratos executáveis entre serviços passaram a ser necessários onde antes não havia fronteira formal a testar.

Com a chegada de agentes de inteligência artificial ao fluxo de engenharia, uma terceira camada de complexidade se adicionou. Agentes são capazes de gerar código funcional a partir de descrições em linguagem natural, o que criou uma prática conhecida como vibe coding, em que o engenheiro descreve vagamente o que quer, itera sobre o código gerado e aceita o resultado que parece correto. O problema é que modelos de linguagem preenchem o que não foi dito com escolhas estatisticamente plausíveis. Casos de borda não descritos, regras de negócio ambíguas e comportamentos implícitos tornam-se decisões do modelo, não do engenheiro. O código gerado parece correto, criando uma ilusão de correção que torna esses problemas invisíveis até chegarem a produção.

Este texto propõe uma resposta a esse conjunto de problemas. Uma pipeline de engenharia shift left que começa com especificação estruturada, passa por cenários de comportamento executáveis e testes verificados, e só então entrega ao agente a tarefa de implementar. Uma abordagem onde a intenção precede o código e onde a automação opera dentro de fronteiras definidas por pessoas.


1. A Evolução do Desenvolvimento Orientado a Testes

O movimento Extreme Programming surgiu como resposta aos problemas de integração tardia e acúmulo de dívida técnica que caracterizavam os projetos de software dos anos noventa. Kent Beck propôs uma inversão da ordem convencional: antes de escrever código, o engenheiro escreve um teste que descreve o comportamento desejado. O código de produção mínimo para fazer o teste passar vem depois. Com os testes verdes, a estrutura interna pode ser reorganizada com segurança. Esse ciclo ficou conhecido como red-green-refactor.

O efeito mais relevante do TDD sobre a prática de engenharia não foi a cobertura de testes em si. Foi a mudança na forma como engenheiros pensam sobre o design antes de escrever o código. Um módulo que exige configuração complexa para ser testado expõe acoplamento implícito na interface. Um teste que precisa de muitos objetos auxiliares para rodar sugere que o módulo está assumindo responsabilidades demais. Escrever o teste primeiro torna essas considerações visíveis quando ainda é barato fazer mudanças.

A adoção foi gradual e frequentemente parcial. Muitas equipes adotaram testes automatizados sem adotar o ciclo em sua forma estrita, produzindo suítes escritas depois do código que refletiam os vieses de implementação do programador em vez de descrever o comportamento esperado. Mesmo nessa forma parcialmente adotada, os testes transformaram a forma como equipes de software trabalham. A integração contínua e o conceito de feedback loop rápido central ao XP influenciaram como pipelines de entrega contínua foram concebidos.

O contexto em que essas práticas operam, porém, se tornou mais complexo. O software dos anos noventa era predominantemente monolítico. As fronteiras entre componentes eram fronteiras de código, não de rede. Testar o comportamento de um sistema consistia em exercitar funções e classes dentro de um único processo. À medida que as arquiteturas se tornaram distribuídas, esse conjunto de práticas precisou ser estendido para cobrir também as fronteiras entre serviços.


2. O Aumento da Complexidade Sistêmica

A transição de arquiteturas monolíticas para sistemas distribuídos não foi uma escolha puramente técnica. Ocorreu devido às pressões de escala, autonomia de times e velocidade de entrega que os monólitos tornavam difíceis de sustentar. À medida que empresas como Amazon, Netflix, Google e Uber cresceram além da capacidade de um único banco de dados e um único processo para servir todo o tráfego, novas arquiteturas foram necessárias.

A decomposição de sistemas em serviços menores e independentes mudou a forma como sistemas grandes eram desenvolvidos e operados. Times diferentes podiam trabalhar em partes diferentes do sistema sem conflitos constantes em bases de código compartilhadas. Cada serviço podia ser escalado de maneira independente conforme os padrões de uso exigissem. Falhas em um serviço podiam ser isoladas para que o resto do sistema continuasse funcionando.

Esses ganhos vieram acompanhados de formas de complexidade que não existiam nos sistemas monolíticos. Quando dois módulos de um monólito se comunicam, a chamada acontece na mesma memória e tem custo previsível. Quando dois serviços se comunicam por rede, a comunicação pode falhar de dezenas de maneiras diferentes, cada uma com implicações distintas para o comportamento do sistema como um todo.

"Um sistema distribuído é aquele em que a falha de um computador do qual você nem sequer sabia a existência pode tornar o seu próprio computador inutilizável." — Leslie Lamport, 1987

Falhas parciais são uma propriedade inerente de sistemas distribuídos sem equivalente em sistemas monolíticos. Em um monólito, um componente falha e o processo inteiro pode terminar de maneira previsível. Em um sistema distribuído, um serviço pode responder lentamente sem falhar completamente, produzindo timeouts que se propagam pela cadeia de dependências e se manifestam como falhas em serviços que estão funcionando mas que dependem de um serviço degradado.

A consistência eventual, descrita formalmente por Werner Vogels e amplamente adotada pela equipe de engenharia da Amazon, descreve o comportamento de sistemas que permitem que réplicas diferentes de um mesmo dado temporariamente divirjam em favor de disponibilidade. Esse modelo é adequado para muitos casos de uso, mas torna muito mais difícil raciocinar sobre o comportamento observável do sistema em qualquer momento específico.

O diagrama abaixo representa um sistema composto por múltiplos serviços que se comunicam tanto por APIs síncronas quanto por um barramento de eventos assíncronos.

graph TD
    Client[Cliente HTTP] --> Gateway[API Gateway]
    Gateway --> OrderService[Serviço de Pedidos]
    Gateway --> UserService[Serviço de Usuários]
    OrderService --> PaymentService[Serviço de Pagamento]
    OrderService --> EventBus[Barramento de Eventos]
    EventBus --> InventoryService[Serviço de Estoque]
    EventBus --> NotificationService[Serviço de Notificações]
    OrderService --> OrderDB[(Banco de Dados de Pedidos)]
    PaymentService --> PaymentDB[(Banco de Dados de Pagamentos)]
    InventoryService --> InventoryDB[(Banco de Dados de Estoque)]

Garantir que esse sistema funciona como um todo não pode depender apenas de testes dentro de cada serviço isolado. Um teste que verifica em isolamento se o serviço de pedidos gera a requisição correta para o serviço de pagamento não detecta o que acontece quando o serviço de pagamento está temporariamente indisponível, quando a rede apresenta latência acima do esperado, ou quando o evento publicado no barramento não é consumido na ordem esperada pelo serviço de estoque.

Essa lacuna criou espaço para uma classe diferente de artefatos de engenharia. Se o comportamento de um serviço em relação aos serviços dos quais depende pode ser descrito formalmente, essa descrição pode ser usada para verificar, em qualquer ambiente, se o serviço está se comportando conforme o contrato. Essa ideia está na origem do que passou a ser chamado de desenvolvimento orientado a contratos e, mais amplamente, de engenharia guiada por especificação.


3. Especificações como Artefatos Executáveis de Engenharia

A palavra especificação tem história longa na engenharia de software. Nos modelos de desenvolvimento baseados em cascata, especificações eram documentos textuais que descreviam o que um sistema deveria fazer antes de qualquer código ser escrito. Esses documentos eram frequentemente volumosos, difíceis de manter atualizados e desconectados do código que eventualmente era produzido. A distância entre a especificação e a implementação tornava a verificação de conformidade um processo manual e sujeito a erros.

O surgimento de formatos padronizados para descrição de interfaces de serviços mudou essa situação. Uma especificação OpenAPI descreve de maneira estruturada e verificável quais endpoints uma API HTTP expõe, quais parâmetros cada endpoint aceita, quais respostas pode produzir e quais esquemas de dados estão envolvidos. Ela é um artefato processável por ferramentas que podem utilizá-la para múltiplos propósitos ao mesmo tempo.

O trecho abaixo apresenta um fragmento de especificação OpenAPI para um endpoint de criação de pedidos, com restrições de comportamento embutidas nos esquemas.

openapi: "3.1.0"
info:
  title: Order Service API
  version: "1.0.0"
paths:
  /orders:
    post:
      summary: Cria um novo pedido
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateOrderRequest"
      responses:
        "201":
          description: Pedido criado com sucesso
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Order"
        "400":
          description: Requisição inválida
components:
  schemas:
    CreateOrderRequest:
      type: object
      required: [customerId, items]
      properties:
        customerId:
          type: string
          format: uuid
        items:
          type: array
          minItems: 1
          items:
            $ref: "#/components/schemas/OrderItem"
    OrderItem:
      type: object
      required: [productId, quantity]
      properties:
        quantity:
          type: integer
          minimum: 1
    Order:
      type: object
      properties:
        id:
          type: string
          format: uuid
        status:
          type: string
          enum: [pending, confirmed, processing, shipped, delivered]
        createdAt:
          type: string
          format: date-time

A partir dessa especificação, ferramentas como Dredd ou Schemathesis podem executar testes automatizados contra uma implementação, verificando se ela se comporta conforme o contrato descrito. Bibliotecas como Ajv verificam requisições e respostas em tempo de execução usando os esquemas como referência. Swagger UI gera documentação interativa sem nenhum trabalho adicional.

Para sistemas baseados em comunicação assíncrona, o AsyncAPI desempenha papel análogo ao OpenAPI. Uma especificação AsyncAPI descreve quais eventos um serviço publica, quais eventos consome e os esquemas de dados de cada mensagem. Isso torna explícito o contrato entre produtores e consumidores de eventos, uma fronteira que em sistemas sem especificação formal frequentemente existe apenas de maneira implícita no código de ambos os lados.

O conceito de contract-first development descreve o fluxo de trabalho onde a especificação da interface é escrita e aprovada antes de qualquer implementação começar. Quando a especificação está estabilizada, times diferentes podem começar a trabalhar em paralelo. O time responsável pelo serviço provedor implementa o comportamento descrito. O time consumidor escreve o código que vai consumir a API usando mocks gerados automaticamente a partir da especificação. A dependência entre times é substituída por dependência sobre o contrato.

Há, porém, um limite importante nesse nível de especificação. Especificações formais de interfaces capturam o contrato de comunicação entre serviços, mas não capturam as regras de negócio que governam o comportamento interno de cada serviço. A lógica que determina como um pedido passa de pendente para confirmado, quais validações são aplicadas antes de processar um pagamento, ou como o sistema deve se comportar quando o inventário está insuficiente está além do que uma especificação OpenAPI pode descrever. Para esse nível de comportamento, outro mecanismo é necessário.

Essa lacuna ganhou peso específico com a popularização do vibe coding no desenvolvimento de sistemas corporativos. Sem especificação estruturada que documente a intenção antes do código existir, regras de negócio ficam implícitas no comportamento gerado, sessões com o agente não deixam artefato auditável e o único registro do que o sistema deve fazer passa a ser o próprio código. O código gerado parece correto o suficiente para reduzir sistematicamente o nível de revisão humana, fenômeno que a literatura de segurança descreve como viés de automação. Em sistemas de longa vida, onde times mudam e contexto se perde, essa deriva condiciona diretamente a capacidade do time de evoluir o sistema com segurança.


4. Spec-Driven Development — O Que É e de Onde Veio

O Spec-Driven Development opera em uma camada diferente do contract-first API development descrito na seção anterior. Enquanto uma especificação OpenAPI descreve a interface de comunicação entre serviços, o SDD descreve o comportamento completo de uma funcionalidade antes que qualquer código seja escrito, incluindo as regras de negócio, os casos de borda, as restrições do sistema e a intenção que justifica cada decisão.

Para entender de onde o SDD veio, é preciso entender o problema que ele resolve. Com a popularização de agentes de IA capazes de gerar código a partir de descrições em linguagem natural, uma prática passou a ser observada em times de desenvolvimento. Engenheiros descrevem vagamente o que querem para um agente e iteram sobre o código gerado sem uma especificação clara do comportamento esperado. Esse padrão ficou conhecido como vibe coding, termo que Andrej Karpathy introduziu em 2025 para descrever o desenvolvimento guiado por intuição e ajustes iterativos em vez de por especificação estruturada.

A documentação do GitHub Spec Kit parte de um princípio simples. Sem especificação clara, o modelo preenche o que não foi dito com o que é estatisticamente mais provável no seu treinamento. Quando um engenheiro pede a um agente que construa um módulo de desconto sem especificar as regras de arredondamento, o comportamento esperado para carrinhos vazios, o tratamento de percentuais acima de 100% ou o que acontece quando o desconto excede o total, o agente preenche essas lacunas com as escolhas mais estatisticamente prováveis. Essas escolhas podem ser tecnicamente coerentes e ainda assim completamente erradas em relação ao que o sistema precisa fazer. A especificação não existe para limitar a IA. Ela existe para tornar a intenção do engenheiro explícita em uma forma que a IA possa usar como referência.

O GitHub Spec Kit, lançado pela Microsoft em 2025, formalizou um ciclo de SDD composto por quatro fases distintas. A fase specify captura o quê e o por quê, descrevendo o comportamento esperado da funcionalidade, o público que a usa, as restrições de negócio e os casos de borda relevantes. A fase plan transforma a especificação em decisões técnicas, escolhendo tecnologias, definindo arquitetura e identificando dependências. A fase tasks divide o plano em unidades implementáveis, cada uma pequena o suficiente para ser verificada de maneira independente. A fase implement é onde o agente executa cada tarefa usando a especificação como referência constante.

O princípio que diferencia SDD de documentação convencional é simples. Quando uma especificação falha, você corrige a especificação, não apenas o código. A especificação é a fonte de verdade, e mudanças nela propagam intenção para todas as gerações futuras de código. Isso inverte a dinâmica comum onde a documentação persiste desatualizada enquanto o código deriva silenciosamente da intenção original.

A ideia de tornar comportamento esperado explícito e verificável tem antecedentes importantes na engenharia de software. Bertrand Meyer, em "Object-Oriented Software Construction" publicado em 1988 e revisado em 1997, formalizou o conceito de Design by Contract, onde pré-condições, pós-condições e invariantes descrevem contratos formais que cada componente de software deve satisfazer.

Meyer defendia que cada módulo deveria declarar explicitamente o que exige de seus clientes (pré-condições), o que garante em resposta (pós-condições) e quais propriedades permanecem sempre verdadeiras sobre seu estado (invariantes). O programador e o cliente do módulo firmam um contrato com obrigações e benefícios para ambos os lados.

Uma linha de influência mais próxima vem do Behavior-Driven Development (BDD), desenvolvido por Dan North a partir de 2003. O BDD propôs que testes deveriam descrever comportamento em linguagem próxima do negócio, usando a estrutura "dado que... quando... então..." para tornar os cenários legíveis tanto por engenheiros quanto por stakeholders não técnicos. No contexto do desenvolvimento com agentes de IA, o BDD ganha um papel que vai além da documentação. Ele funciona como a camada que converte a spec em critérios de aceitação executáveis. A spec SDD descreve a intenção em linguagem estruturada, os cenários BDD traduzem essa intenção em comportamento verificável, e o agente implementa contra esses cenários. Quando um comportamento descrito na spec não consegue ser expresso como um cenário "dado que / quando / então" sem ambiguidade, isso é um sinal de que a spec precisa ser refinada antes da implementação começar.

A distinção de responsabilidades entre as três práticas é clara. O BDD valida o comportamento externo do sistema, o que ele faz do ponto de vista de quem o usa. O TDD valida a correção interna de cada módulo. Edge cases, estrutura de dados, contratos entre funções internas e regressão são o seu território. O SDD fornece o contexto de negócio que orienta tanto os cenários BDD quanto os testes de unidade. Nenhuma das três práticas substitui as outras. Cada uma responde a uma pergunta que as demais não cobrem.

O SDD pode ser entendido como uma instanciação prática das ideias de Meyer e North no contexto do desenvolvimento assistido por IA. Ao contrário dos formalismos de Meyer, o SDD não exige notação matemática nem ferramental especializado de verificação formal. A especificação pode ser escrita em linguagem natural estruturada, desde que seja explícita o suficiente para que tanto engenheiros quanto agentes de IA possam verificar conformidade.

Quatro camadas agora respondem a perguntas distintas nesse fluxo. O SDD pergunta o que o sistema deve fazer e por quê. O BDD pergunta como o sistema deve se comportar externamente, do ponto de vista de quem o usa. A especificação de interface (OpenAPI, AsyncAPI) pergunta como serviços se comunicam entre si. O TDD pergunta como sabemos que a implementação interna está correta. Cada camada cobre um nível de intenção que as outras não cobrem.


5. SDD na Prática — Da Descrição à Spec Verificada

Para tornar concreto o que significa desenvolver com SDD, este artigo apresenta um exemplo de ponta a ponta. A funcionalidade de aplicação de descontos em um carrinho de compras é simples o suficiente para caber em poucos testes, mas tem casos de borda suficientes para tornar visível o que uma especificação estruturada adiciona em relação a uma descrição vaga entregue diretamente a um agente de IA.

O fluxo começa com o engenheiro descrevendo a funcionalidade ao agente, que gera a spec no formato do GitHub Spec Kit. A spec resultante é revisada pelo time antes de qualquer cenário ou teste existir. Ela serve como contexto para gerações futuras do agente, como documento de revisão e como referência de auditoria após a implementação.

## Specify

Funcionalidade: Aplicação de descontos em carrinho de compras

Contexto de negócio:
O módulo de carrinho de compras precisa suportar a aplicação de descontos
sobre o valor total antes da finalização do pedido. Descontos são concedidos
por campanhas de marketing, cupons de fidelidade ou regras configuradas
pela equipe comercial.

Comportamento esperado:
- Descontos percentuais reduzem o total pelo percentual informado
- Descontos de valor fixo reduzem o total pelo valor absoluto informado
- O valor final nunca pode ser negativo
- A taxa percentual não pode exceder 100%
- Desconto fixo maior que o total resulta em valor zero, nunca negativo
- Descontos são calculados sobre o total bruto, antes de impostos e frete

Casos de borda:
- Carrinho vazio (total zero) com desconto aplicado
- Desconto percentual exato de 100%
- Desconto fixo exatamente igual ao total
- Valores fracionários com precisão de ponto flutuante

Restrições:
- Função pura e determinística: mesma entrada, mesma saída
- Sem efeitos colaterais externos
- Resultado com precisão de duas casas decimais

## Plan

Decisões técnicas:
- Implementar como função pura sem estado em TypeScript
- Interface tipada para distinguir desconto percentual de valor fixo
- Lançar erro explícito para percentual acima de 100%
- Usar Math.max para proteção contra resultado negativo

## Tasks

Tarefa 1: Implementar aplicarDesconto com suporte a desconto percentual
Tarefa 2: Adicionar suporte a desconto de valor fixo
Tarefa 3: Implementar proteção contra resultado negativo
Tarefa 4: Validar caso de percentual acima de 100%
Tarefa 5: Testes para todos os casos de borda da spec

Com a spec aprovada, o agente gera os cenários BDD que traduzem a intenção em comportamento verificável externamente. Cada cenário corresponde a um comportamento descrito na spec e pode ser revisado pelo time antes de qualquer código existir.

Feature: Aplicação de descontos no carrinho

  Scenario: Desconto percentual reduz o total
    Given um carrinho com total de R$ 100,00
    When um desconto de 10% é aplicado
    Then o valor final deve ser R$ 90,00

  Scenario: Desconto fixo maior que o total resulta em zero
    Given um carrinho com total de R$ 50,00
    When um desconto fixo de R$ 100,00 é aplicado
    Then o valor final deve ser R$ 0,00

  Scenario: Percentual acima de 100% é rejeitado
    Given um carrinho com total de R$ 100,00
    When um desconto percentual de 101% é solicitado
    Then o sistema deve retornar um erro de validação

  Scenario: Carrinho vazio com desconto resulta em zero
    Given um carrinho com total de R$ 0,00
    When qualquer desconto é aplicado
    Then o valor final deve ser R$ 0,00

Com os cenários BDD aprovados, o agente gera os testes de unidade que cobrem a correção interna de cada módulo. Nenhuma dessas etapas exige que o engenheiro produza os artefatos do zero. Spec, cenários BDD e testes de unidade podem todos ser gerados pelo agente a partir de descrições em linguagem natural. O que não pode ser delegado é a validação. Cada artefato gerado precisa ser revisado e aprovado pelo engenheiro antes de avançar para a próxima etapa. Somente depois que spec, cenários e testes estão validados é que o agente recebe a instrução de gerar o código. O ponto central é que esses artefatos são rastreáveis até regras explícitas na especificação, e a revisão do engenheiro tem uma referência objetiva em vez de depender de interpretação.

describe('aplicarDesconto', () => {
  describe('descontos percentuais', () => {
    it('aplica 10% de desconto sobre o total', () => {
      // Spec: descontos percentuais reduzem o total pelo percentual informado
      expect(aplicarDesconto(100, { tipo: 'percentual', valor: 10 }))
        .toBeCloseTo(90, 2);
    });

    it('aplica 100% de desconto resultando em zero', () => {
      // Spec: taxa percentual não pode exceder 100%
      expect(aplicarDesconto(100, { tipo: 'percentual', valor: 100 }))
        .toBeCloseTo(0, 2);
    });

    it('lança erro quando percentual excede 100%', () => {
      // Spec: a taxa percentual não pode exceder 100%
      expect(() =>
        aplicarDesconto(100, { tipo: 'percentual', valor: 101 })
      ).toThrow('Percentual de desconto não pode exceder 100%');
    });
  });

  describe('descontos de valor fixo', () => {
    it('aplica desconto de valor fixo sobre o total', () => {
      // Spec: descontos de valor fixo reduzem o total pelo valor absoluto
      expect(aplicarDesconto(100, { tipo: 'fixo', valor: 30 }))
        .toBeCloseTo(70, 2);
    });

    it('desconto fixo maior que o total resulta em zero', () => {
      // Spec: desconto fixo maior que total resulta em valor zero
      expect(aplicarDesconto(50, { tipo: 'fixo', valor: 100 }))
        .toBeCloseTo(0, 2);
    });

    it('desconto fixo exatamente igual ao total resulta em zero', () => {
      // Spec: desconto fixo exatamente igual ao total resulta em valor zero
      expect(aplicarDesconto(100, { tipo: 'fixo', valor: 100 }))
        .toBeCloseTo(0, 2);
    });
  });

  describe('casos de borda', () => {
    it('carrinho vazio com desconto resulta em zero', () => {
      // Spec: carrinho vazio (total zero)
      expect(aplicarDesconto(0, { tipo: 'percentual', valor: 50 }))
        .toBeCloseTo(0, 2);
    });

    it('mantém precisão com valores fracionários', () => {
      // Spec: valores fracionários com precisão de ponto flutuante
      expect(aplicarDesconto(99.99, { tipo: 'percentual', valor: 15 }))
        .toBeCloseTo(84.99, 2);
    });
  });
});

O diagrama abaixo representa o fluxo desde a especificação até a implementação verificada.

flowchart TD
    A[Engenheiro descreve funcionalidade ao agente] --> B[Agente gera Spec SDD]
    B --> C[Revisão da spec pelo time]
    C --> D{Spec aprovada?}
    D -->|Não| B
    D -->|Sim| E[Agente gera cenários BDD a partir da spec]
    E --> F[Engenheiro revisa cenários contra a spec]
    F --> G{Cenários capturam a intenção?}
    G -->|Não| E
    G -->|Sim| H[Agente gera testes de unidade]
    H --> I[Engenheiro valida cobertura dos testes]
    I --> J{Testes cobrem a spec?}
    J -->|Não| H
    J -->|Sim| K[Agente implementa código de produção]
    K --> L[Executa suíte completa - GREEN]
    L --> M[Refactor com segurança]
    M --> N[Implementação verificada e rastreável]

O que muda em relação ao ciclo sem SDD é visível no momento de revisão. O engenheiro que analisa os cenários BDD tem uma referência clara, a especificação. A pergunta passa a ser se cada cenário cobre um comportamento descrito na spec e se todos os comportamentos descritos têm pelo menos um cenário correspondente. Essa rastreabilidade entre spec, cenário e teste é o que transforma a revisão de uma atividade de interpretação em uma atividade de verificação.


6. SDD, Pact e a Fronteira entre Serviços

Quando a funcionalidade especificada envolve comunicação entre serviços, o SDD conecta naturalmente com o consumer-driven contract testing que o Pact implementa, no qual o comportamento esperado de uma interação deve estar explícito antes da implementação e a conformidade pode ser verificada de maneira automática.

O Pact funciona em dois momentos distintos. No lado do consumidor, os testes definem interações esperadas com o serviço provedor e verificam se o consumidor consegue gerar as requisições corretas e processar as respostas. Como resultado desses testes, um arquivo de contrato é gerado descrevendo exatamente o que o consumidor espera. No lado do provedor, esse contrato é usado para verificar se a implementação satisfaz as expectativas do consumidor, sem precisar de um ambiente de integração completo.

A conexão com o SDD acontece antes mesmo dos testes de contrato existirem. Uma spec SDD que descreve uma funcionalidade com integração entre serviços inclui na fase specify o comportamento esperado da comunicação, descrevendo quais dados são enviados, quais respostas são esperadas e quais estados do sistema existem antes e depois da interação. A partir dessa spec, um agente de IA pode gerar os testes de contrato do consumidor como uma das tarefas da pipeline SDD.

O exemplo abaixo mostra como um teste de contrato Pact para o serviço de pagamento poderia ser gerado a partir de uma spec SDD que descreve a integração entre o serviço de pedidos e o serviço de pagamento.

import { PactV3, MatchersV3 } from '@pact-foundation/pact';

const { like, string } = MatchersV3;

describe('OrderService → PaymentService contract', () => {
  const provider = new PactV3({
    consumer: 'OrderService',
    provider: 'PaymentService',
    dir: './pacts',
  });

  it('processa pagamento para pedido válido', () => {
    // Spec SDD: quando um pedido confirmado é enviado para pagamento,
    // o serviço de pagamento deve retornar confirmação com transactionId
    return provider
      .given('serviço de pagamento disponível')
      .uponReceiving('requisição de pagamento para pedido válido')
      .withRequest({
        method: 'POST',
        path: '/payments',
        body: {
          orderId: like('order-uuid-123'),
          amount: like(150.00),
          currency: string('BRL'),
        },
      })
      .willRespondWith({
        status: 201,
        body: {
          transactionId: like('txn-uuid-456'),
          status: string('approved'),
          processedAt: like('2026-03-07T10:00:00Z'),
        },
      })
      .executeTest(async (mockServer) => {
        const client = new PaymentClient(mockServer.url);
        const result = await client.processPayment({
          orderId: 'order-uuid-123',
          amount: 150.00,
          currency: 'BRL',
        });
        expect(result.status).toBe('approved');
      });
  });
});

O diagrama abaixo representa o fluxo completo desde a spec SDD até a verificação de contrato entre os serviços.

flowchart TD
    A[Spec SDD descreve integração OrderService → PaymentService] --> B[Agente gera testes Pact do consumidor]
    B --> C[Testes geram arquivo de contrato JSON]
    C --> D[Contrato publicado no PactBroker]
    D --> E[PaymentService verifica conformidade com o contrato]
    E --> F{Verificação aprovada?}
    F -->|Não| G[PaymentService corrige implementação]
    G --> E
    F -->|Sim| H[Integração validada sem ambiente completo]

A integração de IA no processo de geração de contratos é uma direção que ferramentas como PactFlow têm explorado, reconhecendo que a manutenção de contratos em escala é um dos pontos de atrito que inibe adoção em organizações maiores. Gerar testes de contrato a partir de specs SDD reduz esse atrito sem eliminar a revisão humana do que foi gerado.

O que torna essa combinação coerente é que o Pact opera sobre o mesmo princípio que fundamenta o SDD. Ambos exigem que o comportamento esperado seja explicitado antes da implementação, e que conformidade possa ser verificada de maneira automática. A spec SDD que descreve a integração entre dois serviços é o insumo natural para os testes de contrato que verificam essa integração. A pipeline fecha um ciclo. A intenção documentada na spec se transforma em contratos executáveis que sobrevivem a mudanças de implementação em ambos os lados da fronteira.


7. A Pipeline Completa: SDD, BDD, TDD e IA em um Fluxo Coerente

Os elementos discutidos até aqui formam uma pipeline de engenharia quando combinados de forma planejada. O que torna essa pipeline coerente não é a quantidade de ferramentas, mas a clareza sobre o que cada etapa produz e o que a próxima etapa consome.

O pipeline começa com a spec SDD. Spec, cenários BDD e testes de unidade podem ser gerados pelo agente a partir de descrições em linguagem natural. O que é insubstituível é a validação humana em cada etapa. A spec captura intenção, e a aprovação de que ela descreve a intenção correta só pode vir do engenheiro ou do time que entende o problema. A spec aprovada alimenta a geração dos cenários BDD, que convertem a intenção em critérios de aceitação executáveis. Os cenários aprovados alimentam a geração dos testes de unidade, que cobrem a correção interna de cada módulo. Somente depois que spec, cenários e testes estão validados é que o agente implementa o código, operando dentro das fronteiras já definidas. Ao final, os testes de contrato Pact validam as fronteiras entre serviços.

flowchart TD
    A[Engenheiro descreve ao agente] --> B[Agente gera Spec SDD]
    B --> C[Revisão e aprovação da spec]
    C --> D[Agente gera cenários BDD]
    D --> E[Revisão e aprovação dos cenários]
    E --> F[Agente gera testes de unidade]
    F --> G[Revisão e aprovação dos testes]
    G --> H[Agente implementa código]
    H --> I[Agente gera testes Pact para integrações]
    I --> J[Contratos publicados no PactBroker]
    J --> K[Provedores verificam conformidade]
    K --> L[Pipeline de CI executa toda a suíte]
    L --> M[Deploy validado]

O esforço humano nessa pipeline é assimétrico porque os artefatos de validação têm pesos diferentes. Aprovar uma spec é confirmar que o que será construído resolve o problema certo. Aprovar os cenários BDD é confirmar que os critérios de aceitação cobrem os comportamentos que importam. Aprovar os testes de unidade é confirmar que as arestas internas do módulo estão endereçadas. Em cada um desses pontos, o engenheiro avalia intenção capturada em forma verificável, em atividades que exigem julgamento sobre o domínio e que nenhum agente consegue substituir. Spec, cenários, testes e código são gerados pelo agente. O agente implementa dentro das fronteiras que esses três artefatos delimitam em conjunto. A spec define o que o sistema deve fazer. Os cenários definem como o comportamento será verificado. Os testes definem o que constitui correção interna. Código que passa em toda a suíte é consequência direta de uma intenção documentada com precisão suficiente para ser testada.

Essa propriedade reposiciona os artefatos do processo de engenharia. Em uma pipeline com SDD, BDD e TDD, spec e cenários se tornam os documentos que o time mantém, versiona e revisa com rigor. O código gerado pelo agente que satisfaz toda a suíte expressa esses contratos em forma executável. O que o time audita quando algo muda são a spec e os cenários, o código segue. Sem uma referência estável de comportamento correto, engenheiro e agente iteram sem um critério objetivo para saber quando parar. Com spec e cenários aprovados como pontos fixos, o espaço onde o agente pode divergir está delimitado antes de qualquer implementação começar.


8. Os Riscos do Vibe Coding em Ambientes Corporativos

O risco do desenvolvimento sem especificação estruturada não aparece no momento em que o código é gerado. Ele se acumula no que o código não consegue documentar. A intenção que ficou implícita, os casos de borda que ninguém especificou e as decisões tomadas em sessões encerradas não têm representação em nenhum artefato.

O vibe coding não é um problema em contextos de vida curta. Prototipagem, experimentos e scripts internos descartáveis podem se beneficiar da velocidade que descrições informais oferecem. O risco emerge quando esse estilo se torna o padrão de desenvolvimento de sistemas de produção de longa vida. A pressão por velocidade e a capacidade dos modelos de produzir código plausível a partir de descrições vagas tornam esse padrão tentador. O código gerado parece correto, o que reduz sistematicamente o nível de revisão humana. Esse fenômeno é descrito na literatura de segurança e engenharia como viés de automação, a tendência de confiar em saídas automatizadas além do que a evidência disponível justifica. O problema vai além do código gerado no momento da sessão. O custo acumula no código que ninguém consegue entender, modificar ou auditar depois, porque a intenção que o gerou não foi documentada em nenhum artefato que sobreviva ao contexto da sessão original.

Pesquisa acadêmica publicada no IEEE Symposium on Security and Privacy de 2022, conduzida por Hammond Pearce e colaboradores sob o título "Asleep at the Keyboard? Assessing the Security of GitHub Copilot's Code Contributions", analisou a geração de código em cenários baseados em vulnerabilidades conhecidas catalogadas como CWEs (Common Weakness Enumeration) e encontrou que o GitHub Copilot gerou código contendo vulnerabilidades em aproximadamente 40% dos cenários testados. A taxa variava conforme a complexidade do cenário e o domínio de segurança avaliado. O estudo foi conduzido com versões iniciais do Copilot, em 2021 e 2022. Pesquisas posteriores indicam melhora nos modelos mais recentes, mas o padrão de geração de código vulnerável em cenários específicos não desapareceu. Esses números reforçam a necessidade de mecanismos de verificação que detectem divergências entre intenção e implementação antes que o código chegue a produção.

O problema da deriva de especificação é igualmente relevante. Quando specs e testes não acompanham o código gerado, a base de código acumula comportamentos implícitos que nenhum documento descreve. Com o tempo, o único artefato que sabe o que o sistema faz é o próprio código. Novos membros do time precisam reconstruir a intenção a partir da implementação, uma atividade cara e sujeita a erros de interpretação. Quando esse processo se repete com geração assistida por IA, a deriva se acelera. O modelo aprende padrões do código existente e os reproduz, incluindo os comportamentos implícitos que ninguém intencionou explicitamente.

Os mecanismos probabilísticos dos modelos de linguagem representam um risco específico em ambientes corporativos. Um modelo que gera código diferente para a mesma descrição em diferentes sessões, ou que produz implementações funcionalmente distintas dependendo do contexto disponível, cria um ambiente onde a reprodutibilidade não pode ser assumida. Sem a spec como âncora de intenção, cada geração é um ponto de partida novo sem referência estável.

A spec SDD é o mecanismo que torna código gerado por IA auditável. Ela documenta a intenção que precede o código e permite que qualquer engenheiro, em qualquer momento, verifique se o que está no repositório corresponde ao que foi especificado. Sem a spec, o código gerado é um artefato sem proveniência de intenção. Com a spec, ele é um artefato que pode ser verificado, contestado e evoluído com base em critérios explícitos.

A perda de conhecimento institucional é acelerada em ambientes onde código gerado não tem spec correspondente. Na engenharia de software, esse processo é descrito como evaporação de conhecimento, a degradação gradual do entendimento coletivo sobre por que o sistema foi construído da maneira que foi. Artefatos como ADRs (Architecture Decision Records), especificações e testes que capturam comportamento esperado existem precisamente para conter essa degradação. Sessões de IA são efêmeras por natureza e não substituem esses artefatos. Quando um engenheiro sênior deixa o time, ele carrega consigo a compreensão de por que certas decisões foram tomadas. Em sistemas desenvolvidos com TDD e documentação adequada, parte desse conhecimento sobrevive nos testes e nas mensagens de commit. Em sistemas desenvolvidos por vibe coding sem spec correspondente, esse conhecimento não tem onde persistir. A spec SDD é o artefato que preserva o raciocínio de maneira acessível para quem vier depois.


9. Calibrando a Automação — SDD, BDD e TDD Sem Overhead

Uma objeção legítima ao modelo descrito até aqui é que a geração e revisão de specs estruturadas para cada funcionalidade pode se tornar uma burocracia que retarda a entrega sem agregar valor proporcional. Essa objeção merece ser tratada com seriedade, porque processos de documentação mal calibrados são um custo verificável em engenharia de software.

A calibração começa com uma distinção sobre o que uma spec SDD precisa conter. Ela não precisa ser um documento extenso com múltiplas seções e aprovações formais para cada funcionalidade. Uma spec eficaz captura o quê, o por quê, as restrições e os casos de borda. Para a maioria das funcionalidades, isso cabe em uma tela. A extensão da spec deve ser proporcional à complexidade do comportamento que descreve.

O SDD adiciona mais valor em contextos específicos. Funcionalidades com regras de negócio complexas, onde a lógica não é óbvia a partir do código, são candidatas naturais. A spec documenta o raciocínio que governa o comportamento e permite que qualquer engenheiro verifique se a implementação está correta sem precisar reconstruir esse raciocínio do zero. Integrações entre serviços que precisam ser validadas de maneira independente se beneficiam da spec como contexto para geração de contratos Pact. Código que múltiplas pessoas precisarão entender e modificar ao longo do tempo ganha com a spec como documento de referência que sobrevive a rotatividade de time.

Em contrapartida, nem todo código precisa de uma spec SDD completa. Protótipos e experimentos de curta duração que serão descartados, código de scaffolding e configuração que não carrega lógica de negócio, e funcionalidades triviais com comportamento completamente óbvio são casos onde a spec adicionaria overhead sem adicionar clareza. A decisão sobre quando uma spec é necessária pode ser guiada pela pergunta de se um engenheiro novo no time conseguiria entender o comportamento esperado apenas a partir do código e dos testes existentes.

A revisão humana dos testes gerados é o ponto de controle mais eficiente na pipeline. Revisar se um conjunto de testes captura corretamente a intenção de uma spec requer julgamento sobre comportamento esperado, não apenas sobre sintaxe de código. Essa é uma atividade de alta exigência de raciocínio e baixo volume de esforço para um engenheiro experiente. Em contraste, revisar cada linha de código de implementação exige esforço proporcional ao volume de código sem o mesmo retorno em termos de garantia sobre intenção.

Na prática, equipes que adotam esse modelo de maneira saudável tendem a distribuir responsabilidades de maneira clara. O desenvolvedor define o que o sistema deve fazer e como verificar se está correto antes de qualquer código existir, seguindo o ciclo descrito na seção 5. O agente executa dentro dessas fronteiras.

Esse modelo não elimina a necessidade do julgamento humano no centro do processo, e não deveria. Nenhum agente consegue decidir o que constitui cobertura suficiente para um comportamento de negócio, quais casos de borda são relevantes para o contexto da empresa, ou se um cenário BDD captura a intenção do stakeholder de maneira fiel. Essas decisões pertencem ao desenvolvedor. O agente pode sugerir casos adicionais, mas a aprovação de que a spec e os testes descrevem o comportamento correto é humana e intransferível.

Essa sequência não adiciona etapas ao processo. Ela substitui a fase de descoberta implícita, onde engenheiros descobrem comportamento esperado enquanto implementam, por uma fase de especificação explícita que acontece antes.

A organização das specs no repositório passa por duas decisões independentes. Quantos arquivos criar e onde armazená-los.

A spec de uma feature pode ser mantida em um único arquivo com as seções specify, plan e tasks juntas, ou dividida em arquivos separados. O arquivo único é o ponto de entrada natural para a maioria dos casos, já que as três seções são lidas em sequência e pertencem ao mesmo contexto de revisão. A separação faz sentido quando specify e plan têm revisores com perfis diferentes, como quando um product owner precisa aprovar a intenção de negócio antes das decisões técnicas serem tomadas, ou quando a feature é complexa o suficiente para os três documentos ultrapassarem o que permite leitura fluida em um único arquivo. O arquivo de tasks tende a ser efêmero. Criado durante o refinamento, consumido pelo agente na implementação e descartado quando a feature está entregue.

Sobre localidade, a decisão depende da ferramenta principal do time. Para times que usam GitHub Copilot, a Microsoft publica o GitHub Spec Kit como referência oficial. O modelo usa arquivos .prompt.md dentro de .github/: .github/specify.prompt.md, .github/plan.prompt.md e .github/tasks.prompt.md. Esses arquivos ativam comportamento especial no Copilot quando referenciados com #file: no chat e seguem a convenção de prompts reutilizáveis da plataforma. Para times que não usam Copilot como ferramenta principal, não existe uma convenção de mercado consolidada. A padronização fica a critério da organização ou do time. A abordagem mais comum é co-localizar cada spec com o código que descreve: discount.spec.md na mesma pasta que discount.ts. Qualquer agente consegue encontrar o arquivo desde que o contexto arquitetural instrua essa busca. Essa limitação é inerente ao modelo centralizado. Specs que não ficam junto ao código não acompanham o módulo quando ele muda de localização, recriando a deriva que o SDD pretende evitar.

A estrutura abaixo representa o modelo agnóstico, com specs co-locadas ao código.

projeto/
├── src/
│   ├── cart/
│   │   └── discount/
│   │       ├── discount.spec.md          # Spec SDD — intenção, regras e casos de borda
│   │       ├── discount.feature          # Cenários BDD gerados a partir da spec
│   │       ├── discount.test.ts          # Testes de unidade gerados a partir dos cenários
│   │       └── discount.ts               # Implementação gerada pelo agente
│   └── orders/
│       ├── create-order/
│       │   ├── create-order.spec.md      # Spec inclui integração com PaymentService
│       │   ├── create-order.feature
│       │   ├── create-order.test.ts
│       │   └── create-order.ts
│       └── contracts/
│           └── order-payment.pact.test.ts  # Testes de contrato Pact gerados da spec
├── pacts/
│   └── orderservice-paymentservice.json    # Contrato gerado pelos testes Pact
├── .agent/
│   └── skills/
│       ├── cloud/
│       │   └── SKILL.md                    # Padrões de infraestrutura em nuvem
│       └── security/
│           └── SKILL.md                    # Políticas de segurança do domínio
├── .github/
│   └── copilot-instructions.md             # Contexto arquitetural para GitHub Copilot
└── CLAUDE.md                               # Contexto arquitetural para Claude

Cada pasta de funcionalidade contém os quatro artefatos do ciclo na ordem em que foram produzidos: spec, cenários, testes, implementação. Quem chega ao código pela primeira vez lê a spec antes de qualquer linha de TypeScript. Testes de contrato vivem em um subdiretório separado porque descrevem a fronteira entre serviços, não a lógica interna de uma funcionalidade. Os contratos gerados pelos testes ficam em pacts/ na raiz, onde o PactBroker ou a CI podem consumi-los diretamente.

Para times que adotam o fluxo nativo do GitHub Spec Kit, o modelo usa .prompt.md dentro de .github/. As specs ficam centralizadas e integram com o fluxo #file: do Copilot Chat, mas não acompanham o código quando módulos mudam de lugar.

projeto/
├── src/
│   ├── cart/
│   │   └── discount/
│   │       ├── discount.test.ts
│   │       └── discount.ts
│   └── orders/
│       └── create-order/
│           ├── create-order.test.ts
│           └── create-order.ts
├── .github/
│   ├── copilot-instructions.md             # Contexto arquitetural
│   ├── specify.prompt.md                   # Template da fase specify (reutilizável)
│   ├── plan.prompt.md                      # Template da fase plan
│   ├── tasks.prompt.md                     # Template da fase tasks
│   ├── discount-specify.md                 # Spec da feature de desconto
│   └── create-order-specify.md             # Spec da feature de criação de pedido
└── CLAUDE.md

Agentes não leem arquivos automaticamente. Para que a spec seja usada como contexto de implementação, ela precisa ser referenciada explicitamente no prompt ou o agente precisa ser instruído a procurá-la. O arquivo de contexto arquitetural é o lugar correto para essa instrução. Uma regra como "antes de implementar qualquer feature, verifique se existe um arquivo .spec.md no mesmo diretório e leia-o antes de gerar qualquer código" no CLAUDE.md ou .cursorrules garante que o agente execute essa busca. No modelo .github/, a referência explícita no prompt com #file:.github/discount-specify.md substitui essa busca automática. As duas abordagens produzem o mesmo efeito. A spec chega ao contexto do agente antes de qualquer código ser gerado.

Adotar SDD com agentes de IA envolve dois planos de configuração independentes. O primeiro configura o ambiente do agente com o contexto arquitetural do projeto e os skills de domínio que o agente carrega automaticamente quando relevante. Esse plano responde à pergunta de como qualquer código gerado deve ser estruturado. O segundo é a pipeline de engenharia por feature, composta por spec, cenários BDD, testes de unidade e contratos Pact. Esse plano responde à pergunta do que um módulo específico deve fazer. Os dois planos são complementares. Sem o ambiente configurado, o agente produz código correto para a feature mas incoerente com o projeto. Sem a pipeline, o agente produz código coerente com o projeto mas sem garantia de que implementa a intenção certa.

Plano de Ambiente (por projeto)
├── CLAUDE.md / .cursorrules     → como o agente deve trabalhar
└── .agent/skills/               → conhecimento por domínio (progressive disclosure)

Plano de Pipeline (por feature)
├── .spec.md                     → intenção e regras de negócio
├── .feature                     → comportamento verificável (BDD)
├── .test.ts                     → correção interna (TDD)
└── contrato Pact                → fronteira entre serviços

Output
└── .ts                          → implementação gerada

Agentes de código precisam de contexto arquitetural para gerar código coerente com os padrões do projeto. Esses arquivos ficam na raiz do projeto porque se aplicam a todas as gerações do agente, independente da funcionalidade. O GitHub Copilot usa .github/copilot-instructions.md, o Cursor usa .cursorrules, o Claude usa CLAUDE.md. Eles descrevem convenções de código, padrões arquiteturais, tecnologias preferidas e restrições de design que o agente deve seguir em todas as gerações. A spec descreve o comportamento de uma funcionalidade específica. O arquivo de contexto arquitetural descreve como toda funcionalidade deve ser estruturada. Sem esse contexto, o agente pode gerar código funcionalmente correto mas estruturalmente inconsistente com o restante do projeto.

Entre o contexto arquitetural do projeto e a spec de uma funcionalidade há um terceiro nível de contexto que times com domínios especializados precisam gerenciar. O formato Agent Skills, especificado pela Anthropic como padrão aberto, define esse nível. Um skill é uma pasta contendo um arquivo SKILL.md com metadados e instruções, mais opcionalmente scripts, referências e assets. O agente usa progressive disclosure para gerenciar contexto com eficiência: no início da sessão carrega apenas o name e o description de cada skill disponível, o suficiente para reconhecer quando um skill é relevante para a tarefa. Quando a tarefa corresponde à descrição, o agente carrega as instruções completas do SKILL.md. Esse comportamento é automático e nativo no Claude.

Um skill como cloud/ não descreve o que uma feature específica deve fazer nem as convenções gerais do projeto. O seu SKILL.md documenta padrões de infraestrutura em nuvem, políticas de retry, limites de throttling, critérios de custo de egress e taxonomia de erros que qualquer feature operando nesse domínio deve respeitar. A distinção em relação à spec é de escopo. A spec descreve a intenção de uma funcionalidade. O skill descreve como qualquer funcionalidade nesse domínio deve ser construída.

Aplicado à spec de processamento de pagamentos do exemplo anterior, o skill cloud/ altera o que o agente produz mesmo que a spec não mude. A spec define os cenários BDD, as regras de negócio e os casos de borda. O skill garante que a implementação inclua circuit breaker para chamadas externas, que timeouts sigam a política definida pelo time de plataforma e que erros de throttling sejam tratados com backoff exponencial. Sem o skill, o agente pode gerar uma implementação que satisfaz todos os cenários da spec e ainda assim ignora restrições que qualquer engenheiro do time pressuporia como óbvias.

Times com múltiplos domínios especializados, como cloud, segurança, banco de dados e eventos assíncronos, mantêm um skill para cada um. O agente seleciona automaticamente os skills relevantes para cada tarefa sem necessidade de instrução manual. O ecossistema Antigravity Awesome Skills disponibiliza uma biblioteca de skills pré-construídos compatíveis com Claude Code, Cursor, GitHub Copilot e outros agentes, tornando-se uma referência de mercado para skills portáteis entre ferramentas.

O versionamento das specs é parte integrante do modelo. Specs armazenadas junto ao código, no mesmo repositório e sujeitas ao mesmo processo de revisão via pull request, garantem que spec e implementação evoluam juntas. Quando uma mudança de comportamento é necessária, o pull request inclui a atualização da spec, a atualização dos testes e a mudança na implementação. Reviewers verificam a coerência entre as três camadas. Esse padrão evita a deriva onde o código evolui mas as specs permanecem descrevendo comportamento antigo.

Isso garante coerência entre spec e código, mas não diz nada sobre a proporção de funcionalidades que têm spec correspondente. A saúde da pipeline pode ser acompanhada por métricas objetivas, ainda que o ferramental para automatizá-las esteja em evolução junto com as próprias práticas de SDD. A cobertura de especificação mede a proporção de funcionalidades com spec correspondente. A rastreabilidade de testes mede quantos testes têm referência explícita a um item da spec. O tempo médio entre a aprovação da spec e a implementação verificada mede a velocidade do ciclo. Times que acompanham essas métricas conseguem identificar onde o processo está sendo seguido de maneira superficial antes que a acumulação de specs desatualizadas se torne um problema de manutenção.


10. Implicações para Arquitetos e Staff Engineers

As práticas descritas ao longo deste texto alteram de maneira concreta as responsabilidades de engenheiros que tomam decisões estruturais em plataformas de grande escala. Quando specs SDD se tornam artefatos de primeira classe no processo de engenharia, a qualidade dessas specs passa a ter impacto direto sobre o que a automação pode produzir e sobre a capacidade do time de evoluir o sistema com segurança ao longo do tempo.

A qualidade de uma spec é um fator de alto impacto arquitetural. Uma spec que captura o comportamento correto, as restrições relevantes e os casos de borda importantes governa o que o agente de IA pode e não pode fazer. Uma spec vaga ou incompleta produz código que satisfaz a letra mas não o espírito da intenção. Staff engineers e arquitetos que compreendem essa dinâmica passam a exercer influência sobre qualidade não apenas revisando pull requests, mas definindo padrões de como specs devem ser estruturadas, quais informações são obrigatórias e como casos de borda devem ser documentados.

A qualidade das especificações tem o mesmo tipo de importância arquitetural que a qualidade das interfaces em design orientado a objetos. Uma spec que mistura responsabilidades, que omite restrições de negócio ou que não descreve casos de borda relevantes cria os mesmos problemas de longo prazo que interfaces mal projetadas criam em código orientado a objetos. A diferença é que os problemas de uma spec mal definida se materializam no comportamento do código gerado por IA, que é mais difícil de rastrear até a causa original do que um bug convencional.

A questão da governança técnica em times que adotam esse modelo merece atenção específica. Onde specs são armazenadas, como evoluem quando o comportamento muda, como garantir que specs e código permaneçam sincronizados, e quem tem autoridade para aprovar mudanças em specs que afetam múltiplos times são perguntas de governança com implicações técnicas diretas. Times que armazenam specs próximas do código de cada serviço têm padrões diferentes de coordenação do que times que mantêm specs em repositórios centralizados. A escolha depende das dinâmicas de organização do time e dos padrões de mudança da plataforma.

O papel do revisor humano experiente se desloca da geração de feedback para a validação de julgamento. A pergunta deixa de ser se o código está sintaticamente correto e passa a ser se a spec captura a intenção correta, e se os testes gerados cobrem os comportamentos que importam. Esse deslocamento amplia o impacto que um engenheiro experiente pode ter sobre a qualidade de uma plataforma, porque julgamento sobre intenção e comportamento é muito mais escasso do que a capacidade de revisar código linha a linha.

"Qualquer tolo pode escrever código que um computador entenda. Bons programadores escrevem código que humanos entendam." — Martin Fowler, Refactoring: Improving the Design of Existing Code, 1999

O argumento central deste texto pode ser encerrado com a mesma premissa com que começou. Código ainda é feito para ser lido por humanos. Legibilidade, rastreabilidade de intenção e capacidade de auditoria são propriedades que código corporativo precisa ter independentemente de como foi gerado. Essa afirmação não contradiz o uso de IA na geração de código. Ela define a condição que esse uso precisa satisfazer. Código gerado por IA que ninguém consegue entender, auditar ou modificar com segurança acumula passivo técnico até que o custo de compreendê-lo supere o custo de reescrevê-lo.

SDD, BDD, TDD e IA respondem a perguntas distintas que um fluxo de engenharia precisa responder. O SDD pergunta o que o sistema deve fazer e por quê. O BDD pergunta como o sistema deve se comportar externamente, do ponto de vista de quem o usa. O TDD pergunta como sabemos que a implementação interna está correta. A IA executa dentro das fronteiras que essas três camadas definem. Engenheiros experientes são os que definem essas fronteiras com precisão suficiente para que a automação produza código que não apenas funciona, mas que carrega intenção documentada, pode ser verificado contra uma referência explícita e pode ser evoluído por pessoas que não estavam presentes quando foi gerado. Essa é a diferença entre usar IA como amplificador de engenharia e usá-la como substituto para ela.