Pular para o conteúdo
IA

A IA acertou as regras. O bug era nosso.

Pedimos ao Claude Code um serviço de cupom sem dar nenhuma regra de negócio. Ele acertou as oito, passou em 30 testes — e um cupom de uso único foi usado duas vezes. O bug nasceu na nossa especificação.

Afonso Henrique8 min de leitura

O vídeo acima mostra o experimento do começo ao fim, com as saídas reais do terminal. Aqui está a versão em texto — e, no final, o passo a passo para montar o mesmo harness no seu projeto.

Trinta testes passando, tudo verde. E mesmo assim um cupom de desconto que cada cliente só podia usar uma vez foi usado duas vezes pelo mesmo cliente. Bastou aplicar o cupom duas vezes no mesmo instante.

O código foi escrito por uma IA. O bug não nasceu nela: nasceu numa regra que nós mesmos escrevemos. E quem avisou do problema foi a própria IA.

O experimento

Um serviço de cupom de desconto, em Java. O projeto já tinha as classes do pedido e do cupom, uma interface que registra quem já usou cada cupom e um método vazio para a IA implementar.

Entregamos isso ao Claude Code, com o modelo Opus, sem a minha configuração pessoal — sem as minhas instruções e sem os meus plugins. O prompt tinha duas frases:

Implemente o método aplicar da classe CupomService. Ele aplica um cupom de desconto a um pedido e devolve o desconto e o total.

Nenhuma regra de negócio. Antes do experimento, tínhamos escrito oito regras para esse serviço, e a IA não viu nenhuma delas.

Em pouco mais de um minuto, ela devolveu o método e doze testes escritos por ela mesma. No fim da resposta, uma frase que vale mais que o código:

O pedido não definia várias regras, então eu escolhi. Vale você revisar cada uma.

Vieram sete escolhas. O desconto vale sobre o frete? Ela decidiu que não. O valor mínimo do pedido inclui o frete? Também não. O cupom vale no último dia da validade? Vale.

Na sua empresa, quem decide se o cupom dá desconto no frete? Não é o modelo. É o time de produto, é o financeiro. Ela decidiu sozinha — e pelo menos avisou.

A aposta perdida

As oito regras, convencionais de propósito, viraram dezoito testes de aceite que a IA nunca viu. Rodando tudo junto, os doze dela e os dezoito nossos: trinta de trinta.

Ela não errou nenhuma das oito. Montamos o experimento apostando que ela ia errar, e quem perdeu a aposta fomos nós. Meu palpite é que essas regras são o senso comum de qualquer loja virtual, e que uma regra fora do comum ela provavelmente não adivinharia.

Mas o placar importa menos do que parece. Só sabemos que ela acertou porque a regra estava escrita e havia um teste para conferir. Sem isso, "a IA acertou" é só uma opinião.

O parágrafo que ninguém lê

Trinta testes verdes. Você aprovaria esse código?

Antes de responder, volte ao último parágrafo da resposta — daqueles que dá vontade de pular. Ela avisou que verificar se o cliente já usou o cupom (jaUsou) e registrar o uso (registrar) são duas chamadas separadas. Se duas requisições do mesmo cliente chegarem ao mesmo tempo, as duas podem passar pela verificação, e o cupom é usado duas vezes.

Isso tem nome: condição de corrida. Verificar e depois agir, em dois passos, sem nada que impeça outra requisição de entrar no meio. A primeira pergunta "já usou?" e ouve não. A segunda pergunta a mesma coisa antes de a primeira registrar, e também ouve não. As duas aplicam o desconto.

Escrevemos um teste para isso: duas requisições do mesmo cliente, com o mesmo cupom, soltas ao mesmo tempo, com um registro que simula a demora de um banco de dados. Uso duplo em cinquenta de cinquenta tentativas.

Para ser justo, esse cinquenta de cinquenta vem de como o teste foi montado. Sem a demora simulada, deu uso duplo em 611 de 2000 tentativas — cerca de 30%. O bug é real.

A reviravolta

A regra sete, escrita por nós antes do experimento, dizia: se o cliente já usou, recusa; se aplicou, registra. Verificar, e depois registrar. O bug estava na nossa especificação. Tanto que o gabarito que preparamos seguindo essa regra tem exatamente o mesmo bug.

A IA nunca viu esse gabarito. Mas a interface que entregamos tinha o mesmo desenho: uma chamada para verificar, outra para registrar. Ela implementou o contrato que recebeu e avisou que ele tinha um problema. O teste de concorrência só existe porque alguém leu aquele último parágrafo.

Por que ela não corrigiu

Se ela viu o problema, por que não corrigiu? Porque corrigir de verdade muda o contrato — a interface de registro, que outras partes do sistema podem usar. Ela sugeriu um caminho, registrar só se ainda não existir, numa operação única, e parou ali.

Parou no lugar certo. Mudar contrato é decisão de engenharia, e essa decisão é sua.

Cuidado com a tentação de colocar uma trava no método. Passa no teste e quebra no dia em que a aplicação rodar em duas instâncias, cada uma com a sua trava. A garantia fica onde o dado mora: no banco, com uma restrição de unicidade, que recusa a segunda linha igual.

O harness

Segunda rodada, mesmo prompt, palavra por palavra. O que mudou foi o ambiente em volta dela — o harness. Quatro peças:

  • Contexto: um CLAUDE.md explicando o que é o sistema e que, aqui, erro é dinheiro perdido ou cliente cobrado errado.
  • Regras: as oito, com a regra sete corrigida. E uma instrução: se faltar regra, pergunte em vez de decidir sozinha.
  • Verificação: os dezoito testes de aceite e o de concorrência. Não termine com teste vermelho e não mexa nesses testes.
  • Limites: altere só o CupomService, sem tocar nas interfaces. Se precisar de mais, pare e explique.

Para não enganar ninguém: o contrato novo, registrar só se ainda não existir, foi decisão nossa e já estava pronto no projeto. O zero de uso duplo vem dele, não da IA. O trabalho dela era usar o contrato direito, registrando o uso por último, depois de todas as outras regras passarem.

Resultado: dezenove testes passando. Uso duplo em zero de cinquenta, e em zero de duas mil sem demora nenhuma. Conferimos por fora que nenhum outro arquivo foi tocado.

O que importa não é o verde

O resultado que mais me interessa é outro. Onde as regras não cobriam, ela seguiu a instrução: não escolheu sozinha, perguntou. O que fazer com valores nulos? E com subtotal negativo?

Numa execução anterior, com uma primeira versão do harness que tinha menos testes, ela fez uma pergunta melhor ainda: se o uso é registrado quando o desconto é aplicado e o cliente desiste da compra, o cupom fica queimado. Não deveria registrar só quando o pagamento é confirmado? Isso é pergunta de produto, e nenhum teste pegaria.

As duas execuções perguntaram coisas diferentes. Não dá para contar com a IA lembrar da pergunta certa toda vez — por isso o certo precisa estar escrito e testado. E enquanto ninguém responde, o código da segunda rodada aceita frete negativo e quebra quando falta um campo. Não decidir também é uma decisão.

O que sobra para você

Antes, o caminho era engenheiro → código. Agora é engenheiro → harness → agente → código.

O que sobra é escrever a regra — e às vezes errar nela, como nós erramos. É ler o que a IA avisa. É decidir o contrato. E é provar que está certo.

Código ficou barato. Julgamento ficou caro. É a mesma conclusão de Inteligência ficou abundante. Julgamento não., agora com saída de terminal para provar.

Passo a passo: um harness mínimo em meia hora

  1. Escreva as regras que a IA não tem como saber. Antes de pedir, crie um REGRAS.md com as regras de negócio da tarefa. Pronto quando cada regra cabe em uma frase.
  2. Transforme cada regra em teste de aceite, incluindo os limites: último dia, valor igual ao mínimo, zero, cem por cento. Pronto quando os testes existem e falham antes da implementação.
  3. Dê ao agente contexto, verificação e limites. No CLAUDE.md, ou no arquivo equivalente da sua ferramenta: o que é o sistema e quanto custa um erro; onde estão as regras, e "se faltar regra, pergunte"; o comando de teste, "não termine com teste vermelho" e "não altere estes testes"; o que ele pode alterar, e "se precisar de mais, pare e explique".
  4. Leia a resposta inteira. Cada "eu escolhi" vira pergunta para quem decide ou vira teste. Cada aviso vira teste. Pronto quando nenhuma escolha da IA ficou sem dono.
  5. Para tudo que verifica e depois grava, pergunte: e se rodar duas vezes ao mesmo tempo? Escreva um teste com duas chamadas simultâneas e coloque a garantia no banco, com restrição de unicidade. Pronto quando existe um teste de concorrência verde que falharia sem a garantia.

Um detalhe que tem tudo a ver com isso: desde a versão 2.1.277, o Claude Code lê um AGENTS.md quando o projeto não tem CLAUDE.md. O harness não pertence a uma ferramenta. O que importa é que ele esteja escrito e versionado junto com o código.

Pegue o último código que você aceitou de uma IA e liste cada decisão que ela tomou sem ninguém pedir. Escolha uma e transforme em teste. Depois faça a pergunta que derrubou o nosso cupom: e se isso rodar duas vezes ao mesmo tempo?

  • claude code
  • harness
  • testes
  • concorrência