Como Escrever Prompts que Geram Código Melhor
Você abre o chat da IA, digita “cria uma função para validar CPF” e recebe um código que compila, mas que não trata máscaras, não considera CPFs com todos os dígitos iguais, não segue o padrão de nomenclatura do seu projeto e usa uma biblioteca que a sua equipe não aprovou. Você corrige, pede de novo, a IA erra em outro ponto. Cinco interações depois, você já teria escrito a função sozinho — e ainda ficou com a sensação de que a IA “não presta”.
Esse cenário se repete com uma frequência incômoda em times que adotaram ferramentas como Claude, ChatGPT ou assistentes integrados ao editor, como o Cursor e o GitHub Copilot. O problema quase nunca está no modelo. Está no prompt. Um prompt vago produz um código genérico; um prompt bem construído produz um código específico, alinhado ao seu contexto e, na maioria das vezes, correto de primeira.
Neste artigo vou detalhar a estrutura que uso profissionalmente para escrever prompts que geram código de qualidade — com exemplos reais em diferentes linguagens, incluindo os erros mais comuns que fazem desenvolvedores desistirem da IA antes de aprender a conversar com ela corretamente.
O problema real: prompt vago, código genérico
Modelos de linguagem não leem sua mente. Eles preveem a continuação mais provável do texto que você forneceu. Se o texto é vago, a continuação mais provável é… genérica. É a mesma lógica de perguntar a um consultor “me faz um orçamento” sem dizer o que precisa orçar: ele vai te entregar algo plausível, mas raramente útil.
Compare estes dois prompts:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
Prompt vago: "cria uma função para validar email" Prompt específico: "Escreva uma função em TypeScript chamada validateEmail que: - Recebe uma string e retorna um boolean - Usa regex compatível com RFC 5322 simplificado - Rejeita strings vazias ou com espaços - Não usa bibliotecas externas - Inclui JSDoc explicando o comportamento - Segue o padrão de nomenclatura camelCase do projeto" |
O segundo prompt elimina praticamente toda ambiguidade. A IA não precisa “adivinhar” se você quer suporte a domínios internacionais, se pode usar uma lib, ou qual convenção de nomes seguir. Cada decisão que você toma antecipadamente é uma iteração de correção que você economiza depois.
💡 Dica do Mestre: o conceito de “prompt engineering” ganhou formalização acadêmica com o paper “Pre-train, Prompt, and Predict” (Liu et al., 2021), que documenta como pequenas mudanças na formulação de um prompt alteram drasticamente a qualidade da saída de modelos de linguagem.
A anatomia de um prompt de código eficaz
Depois de escrever centenas de prompts para geração de código em projetos reais, identifiquei cinco elementos que, quando presentes, aumentam consistentemente a qualidade da resposta. Vou detalhar cada um.
1. Contexto: onde esse código vai viver
A IA não sabe se você está construindo um microsserviço em produção ou um script descartável. Informe a linguagem, o framework, a versão e, se relevante, o ambiente de execução.
|
1 2 3 4 5 |
Contexto: Projeto Node.js 20 com Express 4, usando TypeScript strict mode. O código roda em um servidor serverless (AWS Lambda) com timeout de 10s. |
Esse tipo de informação evita que a IA sugira, por exemplo, um loop bloqueante síncrono em um ambiente serverless com timeout curto, ou que use uma API do Node 22 em um projeto travado na versão 20.
2. Restrições explícitas: o que NÃO fazer
Dizer o que você quer é importante, mas dizer o que você não quer costuma ser ainda mais eficaz, porque elimina os “caminhos padrão” que o modelo tende a seguir por estatística.
|
1 2 3 4 5 6 7 8 |
Restrições: - Não use bibliotecas externas além do que já está no package.json - Não use "any" em nenhum lugar do código TypeScript - Não crie classes; prefira funções puras - Não faça chamadas de rede dentro da função de validação |
3. Exemplos (few-shot): mostre o padrão que você quer seguir
Se seu time tem um padrão de código estabelecido, mostrar um exemplo real vale mais do que qualquer descrição textual. Essa técnica é chamada de few-shot prompting e é uma das mais eficazes para manter consistência de estilo.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
Aqui está um exemplo de como estruturamos nossos serviços: class UserService { constructor(private readonly repository: UserRepository) {} async findById(id: string): Promise<User | null> { if (!id) throw new InvalidArgumentError('id é obrigatório'); return this.repository.findById(id); } } Agora crie um OrderService seguindo exatamente esse padrão, com os métodos findById e listByUserId. |
O resultado tende a manter a mesma injeção de dependência via construtor, o mesmo tratamento de erro e a mesma nomenclatura — porque você não descreveu o padrão, você mostrou o padrão.
4. Formato de saída: peça exatamente o que você vai usar
Se você vai colar o código direto no projeto, diga isso. Se quer apenas o trecho da função sem explicações longas, diga isso também. Prompts que não especificam o formato geram respostas com texto explicativo excessivo, que exige trabalho manual de extração.
|
1 2 3 4 5 6 7 8 |
Formato de resposta: - Apenas o código, sem explicações antes ou depois - Comentários apenas onde a lógica não for óbvia - Se houver mais de um arquivo, use blocos separados com o nome do arquivo como cabeçalho |
5. Critério de aceitação: como saber se está correto
Esse é o elemento mais negligenciado e, na minha experiência, o que mais reduz retrabalho. Descreva os casos de teste ou o comportamento esperado nas bordas (edge cases). Isso obriga o modelo a considerar cenários que normalmente ficariam de fora de uma resposta genérica.
|
1 2 3 4 5 6 7 8 |
Critérios de aceitação: - validateEmail("") deve retornar false - validateEmail("a@b.co") deve retornar true - validateEmail("usuario@dominio") deve retornar false (sem TLD) - validateEmail(" a@b.com ") deve retornar false (espaços nas pontas) |
Prompt completo na prática
Juntando os cinco elementos, um prompt real para o exemplo de validação de e-mail ficaria assim:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
Contexto: Projeto TypeScript 5.4 sem dependências externas para validação, executado no navegador (não pode usar módulos Node). Tarefa: Escreva uma função chamada validateEmail que recebe uma string e retorna um boolean. Restrições: - Não use bibliotecas externas - Não aceite espaços nas extremidades da string - Não considere domínios sem TLD como válidos Critérios de aceitação: - validateEmail("") -> false - validateEmail("a@b.co") -> true - validateEmail("usuario@dominio") -> false - validateEmail(" a@b.com ") -> false Formato de resposta: apenas o código TypeScript, com JSDoc na função. |
Testei esse exato prompt no Claude e a resposta já veio com os casos de borda tratados corretamente, sem necessidade de correção — porque cada decisão ambígua já havia sido eliminada antes da geração.
Aplicando o mesmo raciocínio em outras linguagens
A estrutura funciona independentemente da linguagem. Veja um exemplo em Python para geração de uma função de retry:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
Contexto: script Python 3.12 para chamadas HTTP usando a biblioteca requests, já presente no requirements.txt. Tarefa: crie uma função retry_request que tenta uma requisição HTTP até 3 vezes com backoff exponencial. Restrições: - Não use bibliotecas de retry externas (tenacity, backoff etc.) - Deve lançar a última exceção capturada se todas as tentativas falharem - Deve logar cada tentativa usando o módulo logging padrão Critérios de aceitação: - Se a primeira tentativa funcionar, não deve haver espera - O tempo de espera deve dobrar a cada tentativa (1s, 2s, 4s) - Deve funcionar com qualquer função que receba **kwargs Formato: apenas a função, com type hints e docstring no formato Google. |
E, para times que trabalham com Delphi — cenário comum em sistemas legados de médio e grande porte no Brasil — o mesmo princípio se aplica ao gerar uma classe ou unit:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
Contexto: projeto Delphi 12 (Object Pascal), usando o padrão de nomenclatura com prefixo T para classes e I para interfaces, seguindo Clean Code. Tarefa: crie uma classe TCpfValidator com um método público function IsValid(const ACpf: string): Boolean. Restrições: - Não usar unit de terceiros - Remover máscara antes de validar (pontos e hífen) - Rejeitar CPFs com todos os dígitos iguais Critérios de aceitação: - IsValid('') -> False - IsValid('111.111.111-11') -> False - IsValid('529.982.247-25') -> True Formato: apenas a classe completa, com interface e implementation. |
Notou o padrão? A estrutura do prompt não muda com a linguagem. O que muda é o vocabulário técnico específico de cada ecossistema — e isso você domina melhor do que qualquer modelo, porque é você quem conhece o seu projeto.
Erros comuns que arruínam bons prompts
Pedir “o código completo” sem definir escopo
Prompts como “crie um sistema de login completo” geram respostas superficiais em várias frentes ao mesmo tempo, em vez de uma solução sólida em uma frente específica. Prefira decompor: primeiro o modelo de dados, depois a validação, depois a persistência, depois a rota HTTP — cada etapa com seu próprio prompt, revisado antes de avançar para a próxima.
Não informar o que já existe no projeto
Se você já tem uma classe de erro customizada, um logger configurado ou um padrão de resposta de API, diga isso no prompt. Sem essa informação, a IA cria suas próprias abstrações — que depois você precisa substituir manualmente pelas do projeto.
Aceitar a primeira resposta sem revisão crítica
Um prompt bem escrito reduz a chance de erro, mas não elimina a necessidade de revisão. Trate a saída da IA como o trabalho de um desenvolvedor júnior talentoso: provavelmente está no caminho certo, mas merece um code review antes de ir para produção.
💡 Dica do Mestre: a Anthropic publicou um guia oficial de boas práticas para prompts técnicos em docs.anthropic.com, com técnicas específicas para geração de código, incluindo o uso de tags XML para separar contexto, instruções e exemplos — uma prática que reduz ambiguidade em prompts longos.
Ignorar o histórico da conversa
Em ferramentas com contexto contínuo, como o Claude Code ou o Cursor, cada correção que você faz vira contexto para a próxima resposta. Se você corrigir um erro de estilo manualmente sem explicar por que corrigiu, a IA vai repetir o mesmo erro na próxima geração. Sempre que corrigir algo, explique o motivo — isso ensina o modelo dentro daquela sessão.
Um checklist rápido antes de enviar o prompt
- Informei a linguagem, versão e ambiente de execução?
- Deixei claro o que não deve ser usado (bibliotecas, padrões, abordagens)?
- Mostrei um exemplo do padrão de código que quero seguir?
- Descrevi os casos de borda que o código precisa tratar?
- Especifiquei o formato da resposta que espero receber?
Se você responder “sim” para a maioria desses pontos, a probabilidade de precisar de múltiplas correções cai de forma expressiva — e o tempo que você economiza é justamente aquele que normalmente se perde em rodadas de ajuste.
Participe da Comunidade Dev’s AI
Escrever bons prompts é uma habilidade que se desenvolve com prática e, principalmente, com troca de experiências reais entre quem já passou pelos mesmos erros. Na Comunidade Dev’s AI discutimos prompts, arquiteturas de agentes, casos de uso reais e trocamos templates testados em projetos de produção — em diferentes linguagens e contextos. Se você quer parar de reinventar a roda a cada nova conversa com a IA, esse é o lugar certo para aprender com quem já testou o que funciona e o que não funciona.
Conclusão
A diferença entre um desenvolvedor frustrado com a IA e um desenvolvedor produtivo com ela raramente está na ferramenta escolhida. Está na clareza da comunicação. Um prompt vago devolve um código genérico; um prompt estruturado — com contexto, restrições, exemplos, critérios de aceitação e formato definido — devolve um código que já nasce próximo do que você realmente precisa.
Isso não elimina a necessidade de revisão técnica, nem transforma a IA em um substituto do julgamento de engenharia. Mas transforma a interação de uma sequência frustrante de tentativa e erro em um processo previsível, no qual você investe alguns segundos extras de escrita para economizar minutos — às vezes horas — de correção posterior. No fim, prompt engineering para código não é mágica: é engenharia de requisitos aplicada a uma conversa.