Disclaimer
Este texto foi inicialmente concebido pela IA Generativa em função da transcrição de um vídeo do Dev Eficiente. Se preferir acompanhar por vídeo, é só dar o play.
Introdução
Depois de escrever quase 70 especificações para construir um projeto de ponta a ponta, quero compartilhar o que aprendi sobre Spec Driven Development. Esse tema tem ganhado tração por conta da capacidade de descrever o que você quer em linguagem natural e deixar um agente de código gerar o necessário para você.
O projeto em questão foi a plataforma Contrato Dev Eficiente, que conecta nossa comunidade de pessoas que estudam conosco com empresas que precisam de profissionais com vontade de aprender. Não foi um experimento pontual - foram muitas especificações, tanto para backend quanto para frontend, e o resultado está no ar sendo usado.
O contexto atual do Spec Driven Development
Recentemente, saiu um post no blog de Martin Fowler escrito por Birgitta Bockler, Distinguished Engineer e AI Assisted Delivery Expert na ThoughtWorks. Ela testou três ferramentas: Kiro (da Amazon), SpecKit e Tessel. O texto discute diferentes abordagens.
Eu olhei o Kiro quando a Amazon lançou e uma pessoa da comunidade Dev Eficiente me indicou o SpecKit. Confesso que quando olhei, não me interessou no sentido de ser o caminho mais promissor para mim. A maioria dessas ferramentas trabalha com a ideia de você escrever a feature inteira e deixar que a especificação seja fonte para quebra de tarefas, para que o LLM planeje as tarefas necessárias.
O que funcionou para mim não foi bem do jeito que está descrito nesses textos.
A evolução das minhas especificações
O começo: linguagem natural detalhada
Na minha primeira especificação, eu queria importar pessoas alunas da plataforma de cursos para o novo sistema. Era um código de importação e eu estava menos preocupado com design naquele momento.
O template inicial tinha:
- Objetivo
- Pacote/pasta onde o código deveria ser gerado
- Referências importantes (como links de documentação)
- Detalhes de implementação em linguagem natural
Eu dava detalhes bem granulares: "cria o controller que seja capaz de importar todos os membros que ainda não foram importados anteriormente", "quando fizer a primeira chamada, já verifica se existe o primeiro email", "a chamada deve ser feita pelo cliente X que deve vir do application properties e deve obrigatoriamente ser definida como variável de ambiente".
O problema? Percebi que estava investindo muito tempo escrevendo parágrafos em linguagem natural explicando o que eu queria.
O problema do código gerado por LLMs
A média do código que vem do LLM, para o tipo de design que eu queria construir, não era suficiente. Mesmo tendo um arquivo CLAUDE.md com guidelines, as decisões de design vinham com características que me incomodavam:
Código pouco coeso: O LLM é bom quando você quer generalização, mas quando você pensa em coesão e separação de responsabilidades, o código gerado geralmente não atende às expectativas.
Eu ficava olhando e pensando: "não é por esse caminho que eu quero". E aí comecei a refatorar bastante código, o que me fez questionar se essa era a melhor abordagem.
A virada: contratos de código ao invés de linguagem natural
A minha versão final das especificações é bem diferente. Ao invés de tentar descrever o código em linguagem natural, o que mais funcionou foi:
- Pensar nas classes e arquivos necessários para fazer determinado fluxo
- Investir tempo nos métodos públicos - as assinaturas, os parâmetros, o retorno
- Não pensar na implementação, apenas nos contratos
Por exemplo, numa especificação recente eu tinha:
Referências:
- Classe RecuperaAnaliseArquivoCurriculo (já declarada com métodos e parâmetros)
- ArquivoCurriculoRepository (estrutura já definida)
Eu declarava os métodos, os parâmetros, escrevia a classe que representava a resposta. Aí eu olhava e falava: "acho que o controller está bom, preciso desses parâmetros, com esses parâmetros eu consigo fazer o que quero fazer".
Exemplo prático: sistema de agentes
Numa especificação para criar agentes de análise, eu escrevi as classes antes:
// No arquivo prepara.ts
class AgenteAnalisadorDeFormacoes { }
class AgenteAnalisadorDeExperiencias { }
class AgenteAnalisadorDeHabilidadesTecnicas { }
class AnalisadorAgregado { } // junta tudo
class AgenteAnaliseCompleta { } // chama todos e retorna análise final
A explicação de alto nível foi apenas: "a partir de um ID de arquivo de currículo, precisamos analisar o currículo, criando um feedback e uma sugestão de versão atualizada".
As referências que eu quero que o agente use, e pronto.
O que delego vs. o que eu defino
A combinação que funcionou para mim foi:
Delego para o agente:
- Coisas que quero fazer e não sei direito como (detalhes de implementação de bibliotecas específicas)
- Coisas que simplesmente não ligo para o como
Por exemplo, numa especificação eu precisava:
- Código que utilizasse o Cloudflare R2 (S3 compatível) - não sabia usar a biblioteca
- Storage genérico para ter versão local e remota para desenvolvimento
- Usar Apache Tika para extrair texto de currículos (doc, pdf, texto)
Eu falei: "coda isso pra mim", porque não sabia os detalhes, mas sabia que precisava.
Eu defino:
- As interfaces (assinaturas dos métodos)
- Como uma coisa se conecta com a outra
- O fluxo de alto nível
Spec Driven Development no Frontend
O mesmo padrão funcionou no frontend. Uma especificação para tela de candidaturas ficou assim:
Objetivo: Possibilitar que o contato de empresa logada visualize
os candidatos para uma determinada vaga.
Referências:
- A tela de listagem de candidaturas é essa: [componente]
- O endpoint que deve ser acessado é esse: [endpoint]
- Retorno do endpoint: [exemplo JSON]
Explicação de alto nível:
- Essa tela deve ser só para pessoa logada
- Os links de GitHub e LinkedIn apontam para endereços internos
que retornam [formato X] - lidar com isso
- Fazer o link de visualizar candidaturas ir para essa nova tela
Eu entregava o endpoint, o retorno do endpoint, e pedia para codar a tela em função desse limite estabelecido.
A granularidade que funciona
Esse é um tema importante citado no texto da ThoughtWorks. Minha observação não indica que pegar uma ideia de feature e investir tempo para o LLM planejar as tarefas seja o jeito mais eficiente.
A granularidade que funciona para mim é a de Task.
Pensando nas nomenclaturas: se você tem Épico > História > Task (como no Jira), o que tem funcionado é pegar a Task e usar Spec Driven Development para ela. Não a feature inteira, não o épico - a task.
Sobre ferramentas específicas
Não usei nenhuma ferramenta dedicada a Spec Driven Development. Olhando as ferramentas, sinceramente não acho que precisa - mas não vou falar muito fortemente porque não usei a fundo. Os agentes de código que já existem (Claude Code, Codex, Gemini CLI) com um template minimamente razoável já fazem muito bem o trabalho.
Parei de pedir testes de início
Outra mudança: parei de pedir para escrever testes logo de cara. Prefiro primeiro ver o código gerado, e depois escrever os testes automatizados. Isso me deu mais controle sobre a qualidade final.
Além de código: documentação
A mesma ideia funcionou para construir documentações. Spec Driven Development para gerar documentação significa: delinear o que preciso na documentação, como escrevo, meus padrões de escrita, o template - e deixar o agente gerar.
Conclusão
O padrão que funcionou para mim em Spec Driven Development foi:
- Pensar na tarefa (não na feature inteira)
- Definir os contratos de código - classes, interfaces, assinaturas de métodos, parâmetros, retornos
- Escrever explicação de alto nível - apenas o suficiente para contextualizar
- Delegar detalhes de implementação que não sei ou não me importo como são feitos
- Limitar a autonomia do agente para o fluxo já delineado
Não foi tentar fazer tudo em linguagem natural. Foi uma união entre estrutura de código que limita a autonomia e contexto em linguagem natural para o que precisa de explicação.
É experiência de campo, de um projeto real que está no ar. Fiz muitas vezes - não foi uma nem duas - e esse é o setup que tem funcionado mais para mim quando penso em engenharia de contexto para agentes de código.
Dev + Eficiente
Desenvolva software de alta qualidade e domine Engenharia de IA com o Dev + Eficiente. Cursos práticos, acesso vitalício, comunidade ativa e acesso a vagas remotas exclusivas em diversas empresas de tecnologia. Sua jornada para se tornar um dev mais eficiente pode começar agora.
Top comments (1)
Aprendi que Spec Driven Development funciona melhor quando focamos em tarefas específicas, definimos contratos de código claros e delegamos apenas os detalhes de implementação que não dominamos, mantendo controle sobre coesão, fluxo e integrações — uma abordagem prática que une estrutura rígida e contexto mínimo em linguagem natural.