Há alguns meses escrevi sobre vibe coding e como o que parece mágica na demo se transforma em dívida quando se trata de produção. Este artigo é a continuação natural dessa reflexão, pois desde então mudei bastante a minha forma de trabalhar com Claude Code, e a mudança tem nome: desenvolvimento orientado a especificações.
A ideia não é nova. Pensar antes de construir é a disciplina usual. O que mudou foi o contexto. Quando a programação era cara, o próprio atrito forçava você a pensar. Agora você pede ao Claude Code “faça-me um serviço de notificação” e em vinte minutos você tem duas mil linhas funcionando. Trabalhar pelo case feliz que o modelo imaginou, e não pelo que seu produto precisa. Já experimentei isso várias vezes: protótipos preciosos que tiveram que ser jogados fora porque a IA tomou vinte decisões arquitetônicas que ninguém pediu.
O ponto de viragem para mim foi parar de tratar Claude Code como um preenchimento automático gigante e começar a tratá-lo como ele é: um engenheiro muito rápido e literal que precisa de um briefing decente. Esse briefing é a especificação.
O que é uma especificação (e o que não é)
Uma especificação é um documento markdown que fica no repositório e define três coisas: o que é construído, com quais restrições e como saberemos se está certo. É o contrato entre você e o agente. Antes de solicitar o código, você redige o contrato. Posteriormente, o prompt é reduzido a algo tão simples como "implementar specs/notification-service.md, começar com o despachante".
É importante esclarecer o que não é, porque é aqui que tenho visto mais pessoas tropeçarem. Não é documentação escrita depois do fato para justificar o que já existe. Também não é um PRD de quarenta páginas: uma boa especificação para uma mídia especial ocupa entre 50 e 150 linhas. E não é um prompt longo. O prompt é efêmero, é perdido quando a sessão é encerrada. A especificação persiste, é versionada pelo PR e refinada entre as iterações, assim como o código.
Minha regra mental: se amanhã eu deletar toda a implementação, a especificação deverá me permitir regenerá-la sem perder nenhuma decisão importante. O código pode ser regenerado. O critério, não.
Definindo o contexto: regras e habilidades
Uma boa especificação não viaja sozinha. Para que o agente de IA entenda verdadeiramente o seu ecossistema de desenvolvimento, você precisa definir as regras (regras) globais do projeto e as habilidades ou scripts específicos que ele pode executar (habilidades). Isso é configurado no nível do repositório usando arquivos como AGENTS.md ou a pasta de customizações. Desta forma, Claude Code ou Antigravity conhece as restrições do seu projeto antes de processar o arquivo.
Se você quiser se aprofundar em como funciona esse sistema de regras, como carregá-las e como evitar que o agente aja por conta própria, recomendo a leitura do meu artigo anterior: Como aproveitar melhor o Google Antigravity (sem que o agente trabalhe contra você), onde detalho o uso de customizações, regras e fluxos de trabalho.
Os cinco blocos de uma especificação que funciona
Após meses de iteração no Venturest, minhas especificações convergiram para uma estrutura de cinco blocos.
- Contexto e objetivo: qual problema de negócio resolve, em no máximo dois parágrafos. O modelo precisa do porquê para resolver adequadamente as ambiguidades que inevitavelmente permanecerão.
- Escopo e não escopo: o não escopo é o bloco mais rentável de todo o documento. É onde você tira a liberdade do agente de “melhorar” coisas que ninguém pediu para ele fazer. Cada linha sem escopo contém centenas de linhas de código que você não precisará revisar ou excluir.
- Decisões técnicas fechadas: pilha, serviços, padrões, convenções de nomenclatura. Tudo o que já foi decidido vai para aqui, escrito como fato consumado. Se você escrever como uma sugestão, o modelo irá tratá-lo como negociável.
- Comportamento esperado: casos de uso, casos extremos e erros, em tabela ou formato Dado/Quando/Então. Esse bloco é o que Claude Code converte quase literalmente em testes, então o esforço é pago duas vezes.
- Critérios de Aceitação: a lista que permite responder sim ou não à pergunta "está terminado?" Se um critério não pode ser verificado, não é um critério, é uma intenção.
Exemplo real: serviço de notificação Venturest
Quando construímos nosso serviço de notificação multicanal, a primeira solicitação foi vaga e o resultado foi o esperado: um monólito acoplado ao backend. Na segunda vez que escrevi isto (versão resumida):
# SPEC: serviço de notificação
## Contexto
A Venturest precisa notificar eventos da plataforma (propostas, reuniões,
esclarecimentos, pagamentos) por email, push e in-app. Hoje os e-mails são enviados
inline do back-end, sem novas tentativas ou rastreabilidade.
## Escopo
- Atendimento desacoplado do backend, orientado a eventos.
- Canais: email (SES), push (SNS + FCM/APNs), in-app (GW WebSocket API).
## Fora do escopo
- SEM SMS. NÃO há preferências do usuário nesta fase. SEM painel de administração.
## Decisões técnicas (fechadas, não propõem alternativas)
- Fila: SQS FIFO como barramento de entrada. DLQ após 3 tentativas.
- Processamento: Lambda Node.js 20, tamanho de lote 10, 256 MB, tempo limite de 15s.
- Modelos: Guiadores no S3 (`templates/{type}/{channel}.html|txt`),
armazenado em cache na memória em partidas a quente. Atualize sem reimplantar.
- História: DynamoDB com TTL de 90 dias.
- Origens: back-end, EventBridge e webhooks externos são publicados no
mesma fila com um envelope comum.
## Comportamento esperado
| Caso | Resultado |
|---|---|
| Evento válido, canal de e-mail | Template de renderização + envio de SES + cadastro no DynamoDB |
| O modelo não existe no S3 | Log de erros + mensagem para DLQ, sem nova tentativa |
| Falha transitória do SES | Tente novamente via SQS (máx. 3) e depois DLQ |
| Evento duplicado (mesmo ID de desduplicação) | Descartado pelo FIFO, sem efeito |
## Critérios de aceitação
- [ ] Um evento postado na fila gera notificação em < 30 anos (pág. 95)
- [] Os 3 canais compartilham o mesmo envelope de entrada
- [ ] Teste a cobertura no despachante > 80%
- [] Alterar um modelo no S3 não requer reimplantaçãoCom esta especificação, Claude Code produziu a infraestrutura, o despachante e os testes em uma única sessão. E o mais importante: sem inventar um canal de SMS ou um CRUD de preferências que ninguém pediu. O bloco sem alcance fez seu trabalho.
Segundo exemplo: Reuniões, quando há um terceiro na equação
O segundo caso me parece ilustrativo por outro motivo: o recurso depende de um provedor externo. Construímos o Venturest Meetings, as videochamadas integradas no contexto de cada projeto, em daily.co. Quando você integra terceiros, a tarefa mais importante da especificação é definir a fronteira entre o seu produto e o fornecedor, porque essa é exatamente a área onde a IA improvisa com mais alegria.
# SPEC: Reuniões de risco (daily.co)
## Contexto
Clientes e parceiros de crescimento realizam reuniões de projeto no Zoom ou Meet,
fora da plataforma. O contexto se perde: o que é falado não está vinculado
ao projeto nem alimenta os Resumos de IA. Reuniões traz a videochamada
dentro do projeto.
## Escopo
- Crie e participe de videochamadas na visualização do projeto.
- Gravação e transcrição da sessão.
- Após a conclusão, resumo do pipeline: resumo, próximas etapas e alertas de escopo
vinculado ao projeto.
## Fora do escopo
- NÃO são permitidos webinars ou salas com mais de 10 participantes. SEM SDK móvel nativo
nesta fase. NÃO há chat próprio dentro da chamada (Esclarecimentos utilizados).
## Decisões técnicas (fechadas, não propõem alternativas)
- Provedor: daily.co com pré-construído incorporado (iframe). Não crie UI
própria chamada.
- As salas são criadas a partir do back-end por meio da API REST do daily.co, sempre
privado, associado a um `project_id`.
- Acesso somente com token de reunião assinado, gerado pelo nosso backend,
com vencimento. Nunca publique URLs de salas.
- Webhook `recording.ready` de daily.co aciona o pipeline
transcrição e resumo. A gravação é copiada para nosso S3;
Não dependemos da retenção de fornecedores.
- Rastreamento: evento `Reunião agendada` com `account_id`, `project_id`,
`duração_min`, `fonte`.
## Comportamento esperado
| Caso | Resultado |
|---|---|
| Usuário com acesso ao projeto | Token gerado, entre na sala |
| Usuário sem acesso ao projeto | 403, sem token, sem URL de sala |
| Token expirado | Acesso negado, opção de regeneração do projeto |
| Gravação de webhook.pronto | Copiar para S3 + transcrição + resumo no projeto |
| daily.co desativado ao criar espaço | Erro de usuário controlado, nova tentativa manual |
## Critérios de aceitação
- [] Nenhuma sala é acessível sem um token assinado pelo nosso backend
- [ ] Sala criada e usuário dentro de < 3s (pág. 95)
- [ ] O resumo aparece vinculado ao projeto após a sessão
- [ ] Queda do webhook não perde gravações (reconciliação diária)Veja a decisão de acesso. Sem especificações, o modelo provavelmente irá gerar salas com URLs públicas, porque é o caminho mais curto e é isso que mostram os exemplos na documentação de qualquer provedor. É o tipo de buraco que não aparece na demo e que aparece meses depois, quando alguém compartilha um link onde não deveria. A especificação torna essa decisão uma regra explícita antes que exista uma única linha de código.
Erros comuns ao escrever especificações
A primeira é escrever especificações muito abertas. “Use boas práticas” não é uma instrução, é uma oração. Cada decisão que você não toma, o modelo toma por você, e nem sempre na direção que mais lhe convém.
A segunda é não versionar as especificações. Quando o recurso evolui e as especificações ficam para trás, o agente trabalha contra um contrato obsoleto. A especificação fica no repositório e muda para PR, como qualquer outro arquivo.
A terceira é deixar indefinida a fronteira com terceiros. Quando o recurso integra um provedor externo, como acontece com daily.co em Meetings, tudo o que você não definir na especificação será resolvido pelo modelo copiando o quickstart da documentação do provedor. E o início rápido foi projetado para a demonstração, não para o seu modelo de permissões ou retenção de dados.
E a quarta, talvez a mais sutil, é confundir especificação com prompt. A dica é a conversa de hoje. A especificação é o documento que sobrevive à conversa. O prompt aponta, a especificação define.
reflexão de fundo
Há algo quase irônico em tudo isso. A IA devolveu-nos uma disciplina que a indústria vinha minando há anos. Escrever especificações era visto como uma burocracia de outra época, e agora acontece que é a alavanca que separa as equipes que geram código daquelas que entregam produtos.
Minha conclusão após esses meses é que o trabalho do engenheiro sênior não diminuiu, mas mudou. Menos tempo digitando implementação e mais tempo fazendo o trabalho que sempre foi a parte mais difícil: decidir o que construir, o que não construir e como saber se está certo. A IA não tira esse trabalho de você. Ele o coloca na sua frente, em markdown, antes da primeira linha do código.
E na sua equipe? Você escreve as especificações antes de abrir o Claude Code ou continua negociando com o modelo com base nas instruções? Estou interessado em saber como você está resolvendo isso.






