spec-driven

A especificação que o modelo não consegue ignorar

Prompts orientam. DSLs constrangem. Quando você codifica a spec como uma linguagem de domínio, o modelo não adivinha mais: ele traduz. A contenção não vem do tamanho do prompt, vem da forma do artefato.

Alexandre Izefler
spec-drivenai-nativeengenharia

A especificação que o modelo não consegue ignorar

Já escrevi aqui que o gargalo de desenvolvimento com IA não está mais na execução. Está na especificação. Mas tem uma pergunta prática que fica sem resposta: como você faz a especificação funcionar de verdade quando o modelo entra em campo?

Escrever a spec em linguagem natural não resolve. O modelo lê, "entende" e gera. Às vezes certo, às vezes não. A ambiguidade da linguagem natural vai para dentro da saída. Você passa a vida revisando o que o modelo interpretou diferente do que você quis dizer, e raramente a culpa está no prompt em si. Está no espaço que o prompt deixou aberto.

O Unmesh Joshi, Distinguished Engineer na ThoughtWorks, publicou um artigo recente no site do Martin Fowler sobre exatamente isso. O título é direto: DSLs Enable Reliable Use of LLMs. A tese é mais simples do que parece: quando você codifica a especificação como uma linguagem de domínio, o modelo para de adivinhar e começa a traduzir. A diferença entre esses dois verbos é enorme.

Prompt orienta. DSL constrange.

Um prompt guia a distribuição de probabilidade do modelo. Ele aponta uma direção, mas o modelo ainda tem um espaço de saída vasto para navegar. Você diz "crie um componente de login com validação de senha" e o modelo gera algo. Ele segue a direção, mas a variação entre execuções é alta. Dois devs com o mesmo prompt chegam a resultados diferentes. O mesmo dev na semana seguinte também.

Uma DSL faz outra coisa. Ela define o espaço de saída. O modelo não gera código livre: gera tokens que precisam conformar com a gramática da linguagem. Se o vocabulário da DSL define OrderLine, PaymentGateway e RetryPolicy, o modelo não inventa OrderItem nem TransactionHandler. Ele trabalha dentro do que o domínio nomeia.

Joshi dá um exemplo concreto que gosto. Ele construiu uma DSL em YAML que aceita especificações de apresentações usando notação PlantUML e gera slides passo a passo. O modelo não "entende apresentações" de forma genérica. Ele traduz YAML que respeita um schema para o código que monta os slides. A contenção está na estrutura do artefato, não no comprimento do prompt.

O design acontece antes, no modelo de domínio

A observação que mais ficou comigo no artigo do Joshi é essa: "A especificação é no máximo uma hipótese inicial. As verdadeiras restrições são descobertas iterativamente, na implementação."

Não existe spec completa antes de começar a construir. O design emerge quando você implementa, porque é aí que as decisões concretas aparecem. Construir uma DSL força esse exercício mais cedo: você precisa nomear as entidades do domínio, definir o vocabulário, tomar decisões de abstração antes de escrever o primeiro prompt.

Esse custo parece friction. É o custo que vale pagar.

Quando você nomeia OrderLine, você está dizendo que esse conceito existe, que tem identidade própria, que outras partes do sistema vão se referir a ele com esse nome. O modelo aprendeu com código onde conceitos parecidos aparecem. Quando você usa o nome que faz sentido no seu domínio, ele tem onde se ancorar. Sem esse vocabulário, ele inventa. Com ele, ele traduz.

Joshi chama isso de "abstrações nomeadas como vocabulário de ancoragem". Eu chamaria de o custo do design que você ia pagar de qualquer forma, só que mais tarde e com mais dor.

As duas fases que a maioria está pulando

O Joshi descreve o processo em duas fases, e é exatamente aí onde as equipes travam.

A primeira fase é usar o LLM como parceiro de brainstorming para projetar a própria DSL. Não para gerar código de produção: para explorar o espaço de abstrações, testar nomes, identificar o que precisa existir no domínio. O modelo como ferramenta de design, não de execução.

A segunda fase é usar o LLM como interface de linguagem natural para a DSL já construída. Você descreve o que quer em linguagem natural, o modelo traduz para a DSL, a DSL executa. A saída é controlada, repetível, verificável.

A maioria das equipes pula a primeira fase completamente e vai direto para a segunda, sem ter o artefato que torna a segunda confiável. Daí a surpresa quando o modelo entrega coisas diferentes para prompts parecidos. O problema não é o modelo. É a ausência do que deveria constrater o modelo.

O que fica quando o modelo muda

Tem um ponto que o Joshi coloca no final que conversa direto com o que venho defendendo aqui: o ativo duradouro não é o prompt. É a DSL e o modelo semântico. Quando você troca de fornecedor de LLM, o prompt talvez precise de ajuste. A DSL permanece. Quando você integra um novo engenheiro ao time, ele aprende o vocabulário do domínio codificado na DSL, não o conjunto de prompts que alguém foi acumulando.

Isso muda a equação de investimento. O esforço que vai para design de abstrações e construção do vocabulário de domínio acumula e resiste ao tempo. O esforço que vai para refinamento de prompt é volátil. Você refina, muda de modelo, refina de novo.

Já escrevi que o gargalo virou intenção e especificação. O que Joshi aponta é a forma concreta que essa especificação precisa ter para ser durável: uma linguagem que o domínio controla e que o modelo não consegue ignorar.

A diferença entre orientar o modelo e constratar o modelo é a diferença entre ter um bom prompt e ter uma DSL. Ambos funcionam no piloto. Mas só um escala.

Referências