Spec Driven Development: o que é e por que adotar quando a IA escreve código com você
Você já recebeu uma tarefa assim: “implementa um sistema de notificações para os usuários”? Você abre o editor, começa a codar, cria uma tabela, um serviço, um endpoint. Dois dias depois, em uma reunião de revisão, descobre que o time de produto queria notificações por e-mail e push, com preferências configuráveis por usuário, rate limiting e um histórico de auditoria. Nada disso estava na frase original. Você reescreve metade do código, refaz a modelagem do banco e perde uma semana inteira revisando decisões que poderiam ter sido esclarecidas antes de a primeira linha de código existir.
Esse cenário se repete em praticamente toda equipe de desenvolvimento, independentemente da linguagem ou do domínio. E ele se agrava quando você passa a delegar parte da implementação para ferramentas de IA generativa. Um agente como o Claude Code ou o Cursor não tem contexto sobre as regras de negócio da sua empresa, sobre decisões de arquitetura tomadas há dois anos ou sobre o que “sistema de notificações” realmente significa para o seu time. Ele preenche as lacunas com suposições — e suposições erradas em escala geram retrabalho em escala.
O que é Spec Driven Development
Spec Driven Development (Desenvolvimento Orientado por Especificação, ou SDD) é uma abordagem em que a especificação — um documento estruturado descrevendo o que o software deve fazer, suas restrições, casos de borda e critérios de aceitação — é escrita e validada antes da implementação, e passa a ser o artefato central do processo, não um anexo esquecido em uma pasta do Confluence.
A ideia não é nova. Ela tem raízes em práticas como Design by Contract, proposto por Bertrand Meyer na linguagem Eiffel, e em métodos formais de especificação usados em sistemas críticos (aviação, saúde, sistemas financeiros). O que mudou é o contexto: com agentes de IA gerando código em minutos, a especificação deixou de ser burocracia e passou a ser a única forma confiável de guiar o que a IA produz.
💡 Dica do Mestre: Bertrand Meyer definiu o Design by Contract nos anos 1980 como forma de tornar explícitas as pré-condições, pós-condições e invariantes de cada componente de software — a ideia central do SDD moderno já estava lá. Veja mais em eiffel.org.
Spec Driven Development não é documentação tradicional
É importante diferenciar SDD de documentação de requisitos tradicional. Documentos de requisitos costumam ser escritos uma vez, aprovados em comitê e esquecidos assim que a implementação começa. No SDD, a especificação é:
- Viva: evolui junto com o entendimento do problema, não é um artefato congelado.
- Executável ou verificável: idealmente, você consegue derivar testes automatizados diretamente dela.
- Precisa o suficiente para eliminar ambiguidade, mas sem descrever a implementação técnica linha a linha.
- Usada como prompt estruturado quando você trabalha com IA generativa — é o contrato entre você e o agente.
Por que o SDD se tornou relevante agora
Antes da IA generativa, quem interpretava requisitos ambíguos era um desenvolvedor humano, com contexto acumulado sobre o projeto, memória das decisões passadas e capacidade de perguntar “isso inclui X?” no corredor. Um agente de IA não tem esse contexto implícito. Ele trabalha com o que está na janela de contexto — e se a especificação for vaga, o resultado será plausível, bem escrito, sintaticamente correto e, com frequência, errado em relação à intenção real.
Isso cria um efeito perverso: código gerado por IA “parece” pronto porque está bem formatado e sem erros de sintaxe, mas pode estar resolvendo o problema errado. Revisar esse tipo de erro é mais caro do que revisar um bug de sintaxe, porque exige reconstruir a intenção original — algo que a especificação deveria ter deixado claro desde o início.
💡 Dica do Mestre: “Se você não sabe para onde vai, qualquer caminho serve.” A frase, atribuída a Lewis Carroll em Alice no País das Maravilhas, resume bem o risco de gerar código sem especificação clara — o agente de IA vai produzir algo, mas não necessariamente o que você precisa.
Anatomia de uma boa especificação
Uma especificação eficaz para orientar desenvolvimento (humano ou assistido por IA) costuma conter, no mínimo:
- Contexto e objetivo: por que essa funcionalidade existe, qual problema de negócio resolve.
- Comportamento esperado: descrito de forma declarativa, preferencialmente com exemplos concretos de entrada e saída.
- Restrições e não-objetivos: o que explicitamente não deve ser feito nesta etapa.
- Critérios de aceitação: condições verificáveis que determinam se a implementação está correta.
- Casos de borda conhecidos: erros, limites, condições de concorrência, dados inválidos.
Exemplo prático: especificando uma função de validação de CPF
Vamos comparar uma instrução vaga com uma especificação estruturada, e ver o impacto no código gerado.
Instrução vaga: “cria uma função para validar CPF.”
Especificação estruturada (formato Given-When-Then, popularizado pelo BDD de Dan North):
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
## Especificação: Validação de CPF ### Objetivo Validar se uma string representa um CPF válido segundo o algoritmo oficial da Receita Federal, para uso em formulário de cadastro. ### Comportamento esperado - Given uma string com 11 dígitos numéricos e dígitos verificadores corretos When a função for chamada Then deve retornar true - Given uma string com todos os dígitos iguais (ex: "11111111111") When a função for chamada Then deve retornar false (CPF inválido por regra da Receita) - Given uma string com máscara (ex: "123.456.789-09") When a função for chamada Then deve normalizar e validar corretamente - Given uma string vazia, nula ou com menos de 11 dígitos When a função for chamada Then deve retornar false, sem lançar exceção ### Não-objetivos - Não valida se o CPF existe na base da Receita Federal (isso é outra função) - Não faz chamada de rede ### Critério de aceitação Todos os casos acima cobertos por testes automatizados passando. |
Agora observe a diferença ao usar essa especificação como prompt para um agente de IA em Python:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
def validar_cpf(cpf: str) -> bool: cpf = ''.join(filter(str.isdigit, cpf or '')) if len(cpf) != 11 or cpf == cpf[0] * 11: return False def calcular_digito(cpf_parcial: str, peso_inicial: int) -> int: soma = sum(int(d) * peso for d, peso in zip(cpf_parcial, range(peso_inicial, 1, -1))) resto = soma % 11 return 0 if resto < 2 else 11 - resto digito1 = calcular_digito(cpf[:9], 10) digito2 = calcular_digito(cpf[:9] + str(digito1), 11) return cpf[-2:] == f"{digito1}{digito2}" |
O código já nasce alinhado com os casos de borda documentados, porque eles estavam explícitos na especificação. Sem isso, é comum receber uma implementação que valida apenas o tamanho da string, ignorando o algoritmo de dígitos verificadores ou o caso de dígitos repetidos.
Escrevendo testes a partir da especificação
Uma das maiores vantagens do SDD é que a especificação, quando bem escrita, se traduz quase diretamente em testes automatizados — o que também serve como harness de verificação para código gerado por IA:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import pytest from validacao import validar_cpf def test_cpf_valido(): assert validar_cpf("52998224725") is True def test_cpf_todos_digitos_iguais(): assert validar_cpf("11111111111") is False def test_cpf_com_mascara(): assert validar_cpf("529.982.247-25") is True def test_cpf_vazio(): assert validar_cpf("") is False def test_cpf_none(): assert validar_cpf(None) is False |
Esse fluxo — especificação em linguagem estruturada, código gerado a partir dela, testes derivados dos mesmos critérios — é o núcleo prático do SDD aplicado ao desenvolvimento assistido por IA.
SDD na prática com ferramentas de IA generativa
Ferramentas como Claude Code, Cursor e GitHub Copilot Workspace têm incorporado, cada uma à sua maneira, fluxos que se aproximam do SDD: primeiro você descreve o que precisa em um arquivo de especificação (ou em um plano gerado pela própria ferramenta), revisa esse plano, e só depois autoriza a geração do código.
Isso é fundamentalmente diferente de simplesmente digitar um prompt e aceitar o primeiro resultado. O ganho está em transformar a etapa de “pensar sobre o problema” em algo explícito e revisável — por você e, se necessário, por outros membros do time — antes que qualquer código exista.
💡 Dica do Mestre: o GitHub mantém uma iniciativa chamada Spec Kit, com templates e ferramentas open source para praticar Spec Driven Development junto com agentes de IA. Vale explorar em github.com/github/spec-kit.
Exemplo em uma stack diferente: especificação para uma API REST em Node.js
SDD não é exclusivo de funções isoladas. Funciona igualmente bem para especificar endpoints antes de implementá-los:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
## Especificação: POST /api/pedidos ### Objetivo Criar um novo pedido para um cliente autenticado. ### Request - Body: { "clienteId": string, "itens": [{ "produtoId": string, "quantidade": number }] } - Header: Authorization Bearer token válido ### Comportamento esperado - Se o token for inválido, retornar 401 - Se "itens" estiver vazio, retornar 400 com mensagem "Pedido deve ter ao menos um item" - Se algum produtoId não existir, retornar 404 com o id inválido na mensagem - Se algum item tiver estoque insuficiente, retornar 409 (conflito) - Em caso de sucesso, retornar 201 com o pedido criado, incluindo id gerado ### Não-objetivos - Não processa pagamento nesta etapa (endpoint futuro) - Não calcula frete |
Com essa especificação em mãos, um agente de IA (ou um colega de equipe) tem contexto suficiente para implementar o endpoint em Express, Fastify, NestJS ou qualquer outro framework, sem depender de suposições sobre casos de erro que só apareceriam em produção.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
app.post('/api/pedidos', autenticar, async (req, res) => { const { clienteId, itens } = req.body; if (!itens || itens.length === 0) { return res.status(400).json({ erro: 'Pedido deve ter ao menos um item' }); } for (const item of itens) { const produto = await buscarProduto(item.produtoId); if (!produto) { return res.status(404).json({ erro: `Produto ${item.produtoId} não encontrado` }); } if (produto.estoque < item.quantidade) { return res.status(409).json({ erro: `Estoque insuficiente para ${item.produtoId}` }); } } const pedido = await criarPedido(clienteId, itens); return res.status(201).json(pedido); }); |
SDD também funciona fora do universo web
Vale reforçar que essa disciplina não é restrita a APIs ou aplicações web. Em sistemas desktop legados, como aplicações Delphi mantidas há anos em empresas de médio porte, especificar antes de alterar uma rotina crítica de faturamento é igualmente valioso — talvez ainda mais, dado o custo de regressões em sistemas que rodam há décadas sem cobertura de testes robusta. A especificação, nesses casos, funciona como uma rede de segurança documental que reduz a dependência de “conhecimento tribal” concentrado em poucas pessoas do time.
Riscos de exagerar no SDD
Como toda prática, o SDD tem limites. Especificar excessivamente cada detalhe de implementação recria os mesmos problemas dos documentos de requisitos burocráticos do passado: processos lentos, especificações que ficam desatualizadas em relação ao código e desenvolvedores que passam mais tempo escrevendo documentos do que resolvendo problemas.
O equilíbrio está em especificar o comportamento e as restrições, não a implementação técnica. A especificação deve responder “o que” e “por quê”, deixando o “como” para quem (ou o que) vai implementar — seja um desenvolvedor humano, seja um agente de IA.
Participe da Comunidade Dev’s AI
Se você quer discutir Spec Driven Development, ver exemplos reais de especificações usadas com Claude Code, Cursor e outros agentes, e trocar experiências com outros desenvolvedores que estão adaptando seus processos para a era da IA generativa, venha para a Comunidade Dev’s AI. É o espaço certo para compartilhar templates de especificação, discutir casos práticos e evoluir junto com quem enfrenta os mesmos desafios no dia a dia de desenvolvimento.
Conclusão
Spec Driven Development não é sobre burocratizar o desenvolvimento de software — é sobre reconhecer que, quanto mais delegamos a escrita de código para agentes de IA, mais crítico se torna comunicar intenção de forma precisa e verificável. Uma especificação bem escrita economiza retrabalho, reduz ambiguidade, serve como base para testes automatizados e se torna o contrato que orienta tanto humanos quanto máquinas na construção do software certo.
A próxima vez que você for delegar uma tarefa a um agente de IA — ou a um colega de equipe —, pare antes de escrever a primeira linha de código e escreva primeiro a especificação. O tempo investido nessa etapa costuma ser recuperado, com juros, na quantidade de retrabalho que você deixa de ter depois.