O que tenho aprendido ao construir um produto com Spec-Driven Development
Sumário
Há algumas semanas comecei a construir um projeto do zero. Microsserviços, pipeline de IA, autenticação, mensageria assíncrona — o tipo de projeto onde as decisões arquiteturais se acumulam rápido e o contexto se perde mais rápido ainda.
Desde o início decidi usar IA como par de desenvolvimento. E a primeira coisa que aprendi é que a IA, sem estrutura, acelera na direção errada com a mesma eficiência que acelera na certa.
O problema que ninguém fala
Quando você pede para um LLM implementar um endpoint, ele implementa. Muitas vezes bem. O problema aparece quando o modelo não sabe o que você decidiu na semana passada.
Não sabe que aquele campo precisa ser opcional porque o consumer vai buscar o dado via HTTP. Não sabe que você escolheu Fixed Window no rate limiting porque Sliding Window seria complexidade desnecessária para o MVP. Não sabe que você deliberadamente não quer ORM nenhum — nem Prisma, nem TypeORM.
Sem essas informações explícitas, a IA preenche os buracos com padrões genéricos. E padrões genéricos num projeto com decisões específicas geram débito técnico disfarçado de produtividade.
Coisas concretas que acontecem sem estrutura:
- Endpoint implementado com Prisma num projeto que usa
pgpuro - Consumer SQS escrito do zero quando existe um
SqsConsumercompartilhado no projeto - Erro de SNS silenciado quando o contrato dizia que SNS é operação primária e deve relançar a exceção
Tudo isso acontece quando o contexto não está codificado em lugar nenhum que a IA consiga ler antes de escrever código.
O que é Spec-Driven Development
A ideia é simples: antes de qualquer código, existe um spec aprovado.
O spec é um arquivo Markdown com uma estrutura fixa:
- Contexto — por que essa feature existe, que problema resolve
- Escopo — o que entra e, tão importante quanto, o que explicitamente não entra
- Contrato da API — request, response, erros esperados com exemplos reais
- Schema e queries — as consultas SQL que serão usadas
- Eventos publicados — se a feature publica mensagens para outros serviços, com o payload tipado
- Critérios de aceite — checklist binário do que significa “pronto”
- Notas de implementação — decisões técnicas, alternativas descartadas, armadilhas
Esse documento passa por três estados: draft → review → approved. Só depois de approved a implementação começa.
Por que a seção “O que não entra” importa tanto
Essa é a parte que mais subestimei no começo.
Num projeto real, scope creep não acontece porque alguém decidiu fazer mais. Acontece porque ninguém definiu os limites com precisão suficiente. Quando você escreve “O que não entra” no spec, você está tomando uma decisão explícita — não deixando em aberto para quem implementar resolver na hora.
Num dos specs do projeto, a seção de escopo negativo incluía:
Fora de escopo:
- Rate limiting por IP (responsabilidade do API Gateway / WAF)
- Sliding Window (Fixed Window é suficiente para o MVP)
- Persistência dos contadores no banco (Redis é o fast-path; banco é só auditoria)
Isso não é documentação do óbvio. É a eliminação de três decisões que alguém — humano ou IA — poderia tomar de forma diferente sem esse limite.
Essa prática se conecta diretamente com o que discuto em Construindo uma Arquitetura Robusta: decisões de arquitetura precisam ser explícitas e rastreáveis — não implícitas no código.
Como isso muda o trabalho com IA
Quando a IA tem acesso a um contrato preciso, o comportamento muda.
Um exemplo do projeto: o worker que extrai dados de currículos usando um modelo de linguagem (nesse caso, via Amazon Bedrock). Além do fluxo de processamento, o spec definia o schema de validação do output do modelo:
const ResumeExtractionSchema = z.object({
headline: z.string().nullable().optional(),
seniority_level: z.enum(['junior', 'mid', 'senior', 'staff', 'principal']).nullable().optional(),
years_experience: z.number().nullable().optional(),
skills: z.array(ResumeSkillSchema).default([]),
experiences: z.array(ResumeExperienceSchema).default([]),
});
A IA não inventou esse schema. Implementou esse. O critério de aceite “Bedrock retorna JSON válido → Zod valida → job marcado como completed” é binário: ou passa, ou não.
O spec também definia o comportamento de throttling — o que fazer quando a API do modelo retorna erro por sobrecarga:
Se ThrottlingException: aumentar o visibility timeout da mensagem na fila
e relançar o erro (não deletar a mensagem)
Sem essa linha, a implementação provável seria tratar ThrottlingException como qualquer outro erro — deletar a mensagem da fila e perder o processamento. Com a linha no spec, esse comportamento específico está no contrato.
O fluxo completo
Na prática, funciona assim:
- Issue criada no gerenciador de tarefas com número sequencial e uma descrição de alto nível
- Spec escrito seguindo o template, linkado à issue
- Review do spec antes do código existir — perguntas de arquitetura são resolvidas aqui
- Spec aprovado — status
approvedno documento - Implementação — a IA lê o spec e o contexto do projeto e implementa dentro desses limites
- Commit linkado à issue — rastreabilidade completa: spec → código → PR → issue
O step 3 é o mais importante. O review do spec é onde as decisões difíceis acontecem. O código é consequência.
Quando o spec de um endpoint de análise de vagas definia que profileId no payload do evento deveria ser opcional, com a nota:
O AI Worker será responsável por buscar o perfil internamente via HTTP —
padrão correto para o pipeline.
Essa decisão foi tomada durante o review, não durante a implementação. O código que veio depois não precisou deliberar sobre isso.
O que ainda não funciona bem
O spec resolve alinhamento, não qualidade de código. Um contrato preciso pode produzir código medíocre se a implementação não for revisada.
Escrever bons specs tem custo real. Preencher os critérios de aceite com precisão — torná-los binários, sem ambiguidade — exige pensar sobre a feature antes de tocá-la. Esse é exatamente o valor. Mas é trabalho.
Specs ficam desatualizados. Quando a implementação revela algo que o spec não previu, a decisão tomada no código precisa ser retroalimentada no documento. Isso não acontece automaticamente.
O que aprendi até aqui
Spec-Driven Development com IA não é sobre usar a IA para escrever specs. É sobre garantir que a IA — e qualquer pessoa no time — tenha o contrato certo antes de escrever código.
A velocidade da IA é real. O risco também: sem estrutura, ela produz código correto para o problema errado, ou para uma versão do problema que o time já descartou. O spec é o mecanismo que alinha velocidade com intenção.
O fluxo não é novo. É a disciplina básica de engenharia de software aplicada a um contexto onde o desenvolvedor escreve na velocidade de um modelo de linguagem. As ferramentas mudaram. A necessidade de pensar antes de construir, não.