Era 23h43 de uma quinta-feira e eu estava olhando para 340 linhas de código React que o GPT-4 tinha gerado com total confiança. Limpo. Bem comentado. Completamente quebrado em produção. O custom hook estava gerenciando state de um jeito que causava silent re-render loops, aqueles que não jogam erros — eles só discretamente destroem sua performance até um cliente te ligar numa sexta de manhã perguntando por que a página de checkout dela leva nove segundos para carregar.
Aquela noite me ensinou mais sobre engenharia de prompts do que qualquer tutorial do YouTube ou thread do Twitter jamais ensinaria. E tive muitas outras noites assim desde então.
Estou construindo na web há nove anos. Na Seahawk Media fizemos deploy de bem mais de 12.000 sites — WordPress, builds headless, apps React sob medida, lojas WooCommerce movimentando volume sério de transações. Assistentes de codificação por IA entraram adequadamente no meu workflow por volta do início de 2023, e oscilei entre achar que são milagrosos e querer jogar meu laptop no Tâmisa.Seahawk Media we've shipped well over 12,000 sites — WordPress, headless builds, bespoke React apps, WooCommerce stores handling serious transaction volume. AI coding assistants entered my workflow properly around early 2023, and I've swung between thinking they're miraculous and wanting to throw my laptop into the Thames.
Aqui está o que realmente aprendi. Do jeito difícil.
---
O Modelo Não Conhece Sua Base de Código. Você Tem que Contar a Ele.
Isso parece óbvio. Não é, não na prática.
O maior erro que vejo desenvolvedores cometerem — inclusive eu mesmo pelos primeiros seis meses — é tratar um LLM como um engenheiro sênior que já leu todo seu código. Você pede "escreva uma função para lidar com autenticação de usuário" e ele escreve algo tecnicamente correto no vácuo. Mas seu projeto usa Supabase, não Firebase. Seus tokens vivem em cookies httpOnly, não em localStorage. Seu formato de erro é { status, message, data }, não o que quer que o modelo tenha adotado por padrão.{ status, message, data }, not whatever the model defaulted to.
O modelo não está errado. Ele só não te conhece.
Dê a Ele um Preâmbulo de Projeto, Toda Maldita Vez
Agora começo cada sessão de codificação significativa com o que chamo de "bloco de contexto." Leva cerca de 90 segundos para escrever. Parece algo assim:
- Stack: Next.js 14 (App Router), TypeScript, Supabase, Tailwind CSS 3.4
- State: Zustand, sem Redux em lugar nenhum
- Auth: Supabase Auth com cookies httpOnly via middleware
- Error shape: { success: boolean, error?: string, data?: unknown }
{ success: boolean, error?: string, data?: unknown } - Convenção de estilo: utility-first, sem arquivos CSS customizados a menos que absolutamente necessário
Cole isso antes de qualquer solicitação não trivial. Faço isso no Cursor mantendo um arquivo _context.md na raiz do projeto. Dois toques e pronto. A qualidade do output melhora notavelmente — menos suposições, menos coisas que preciso descartar.Cursor by keeping a _context.md file in the project root. Two keystrokes to paste. The output quality jumps noticeably — fewer assumptions, fewer things I have to rip out.
---
Especificidade é o Jogo Todo
Lá em 2022, antes de usar IA pesadamente, um cliente me entregou um brief que tinha literalmente duas frases: "Construa um sistema de agendamento. Faça bem feito." Passamos três semanas indo e vindo sobre o escopo. Essa experiência ficou comigo e molda diretamente como escrevo prompts agora.
Prompt vago → código vago. Toda vez.
"Escreva uma função que busca pedidos" vai te dar algo. "Escreva uma função async TypeScript chamada fetchOrdersByUser que aceita um userId: string, consulta a tabela orders no Supabase onde user_id corresponde e status não é cancelled, ordena resultados por created_at descendente e retorna Order[] ou lança um erro tipado" vai te dar algo que você realmente consegue colocar em produção.fetchOrdersByUser that accepts a userId: string, queries the orders table in Supabase where user_id matches and status is not cancelled, orders results by created_at descending, and returns Order[] or throws a typed error" will get you something you can actually ship.
A diferença não é a capacidade do modelo. É a especificidade do prompt.
O que Incluir em um Prompt de Código
- Nome e assinatura da função — não deixe o modelo inventar convenções de nomenclatura — don't let the model invent naming conventions
- Tipos de entrada e tipos de saída — genéricos TypeScript se relevante — TypeScript generics if relevant
- A fonte de dados — qual tabela, qual endpoint de API, qual camada de cache — which table, which API endpoint, which cache layer
- Casos extremos que você já conhece — "tratar o caso onde o array está vazio" — "handle the case where the array is empty"
- O que NÃO fazer — "não use useEffect para isso, use uma server action" — "don't use useEffect for this, use a server action"
Esse último importa mais do que as pessoas percebem. Dizer ao modelo o que evitar economiza tempo enorme. Comecei a manter uma pequena nota de "anti-patterns" por projeto — coisas como "sem client components a menos que interação do usuário exija" — e incluo linhas relevantes em prompts para esse projeto.
---
Encadeie Seus Prompts. Não Peça por Tudo de Uma Vez.
Seahawk tinha um cliente fintech no final de 2023 — não vou dizer quem — onde estávamos construindo um fluxo KYC de múltiplas etapas. Coisa complexa. Upload de documento, integração de verificação de vivacidade, polling de status. Cometi o erro no início de pedir ao GPT-4 para "construir o componente de fluxo KYC completo." Produziu 600 linhas de lixo heróico. Lógica emaranhada, responsabilidades misturadas, nenhuma separação real entre estado de UI e lógica de negócio.
Então descartei e comecei de novo com uma cadeia.
Primeiro prompt: "Projete a máquina de estado para um fluxo KYC de 4 etapas. Etapas: identidade, upload de documento, verificação de vivacidade, revisão. Me dê apenas o tipo de estado e as transições, nenhuma UI.""Design the state machine for a 4-step KYC flow. Steps: identity, document upload, liveness, review. Give me the state type and transitions only, no UI."
Segundo prompt: "Dado esta máquina de estados [cole], escreva a store Zustand.""Given this state machine [paste], write the Zustand store."
Terceiro prompt: "Dada esta store [cole], escreva o componente StepIdentity. Apenas este step.""Given this store [paste], write the StepIdentity component. Just this step."
A saída da abordagem encadeada foi utilizável. Não perfeita — ainda reescrevi cerca de 30% — mas utilizável. A abordagem monolítica não me deu nada.
A própria orientação da Anthropic sobre prompting fala sobre quebrar tarefas complexas em subtarefas, e honestamente, isso se alinha exatamente com o que descobri por tentativa e erro. Quebre o problema antes de quebrar sua base de código. talks about breaking complex tasks into subtasks, and honestly, this aligns exactly with what I found through trial and error. Break the problem down before you break your codebase.
---
Faça Ele Argumentar Consigo Mesmo
Aqui está um que encontrei completamente por acaso. Eu estava revisando uma função utilitária gerada e em vez de apenas executá-la, adicionei um prompt de acompanhamento: "Quais são os possíveis bugs ou casos extremos no código que você acabou de escrever?""What are the potential bugs or edge cases in the code you just wrote?"
O modelo encontrou três problemas que não havia considerado. Um deles era um problema genuíno — uma race condition em um loop assíncrono que seria um pesadelo para debugar em produção.
Agora faço isso rotineiramente. Escreva o código, depois peça para criticá-lo. Depois peça para corrigir a crítica. Parece ligeiramente absurdo — pedir ao modelo para revisar seu próprio trabalho — mas consistentemente surfam coisas que só teria capturado após uma sessão de debug dolorosa.
Você pode ir além. Depois de obter uma função funcional, tente: "Reescreva isso com foco em performance" ou "Como isso se comportaria sob alta concorrência?" As respostas nem sempre são aplicáveis, mas cerca de 40% das vezes surfam algo que vale a pena agir."Rewrite this with a focus on performance" or "How would this behave under high concurrency?" The answers aren't always applicable, but about 40% of the time they surface something worth acting on.
---
O Frame "Role + Constraint"
Existe um padrão de prompt que uso constantemente agora e gostaria de ter descoberto no primeiro ano. Funciona assim: "Você é um [tipo específico de engenheiro]. Sua restrição é [regra rígida]. Agora [tarefa].""You are a [specific type of engineer]. Your constraint is [hard rule]. Now [task]."
Exemplo: "Você é um engenheiro backend que se importa profundamente com a eficiência de consultas de banco de dados. Sua restrição é que você não pode buscar mais do que o necessário para este render — sem over-fetching. Escreva uma query Supabase para o painel de administração que retorna contagem de pedidos, receita total e os cinco pedidos mais recentes.""You are a backend engineer who cares deeply about database query efficiency. Your constraint is that you cannot fetch more than what's needed for this render — no over-fetching. Write a Supabase query for the admin dashboard that returns order count, total revenue, and the five most recent orders."
Esse enquadramento faz duas coisas. Alinha a "persona" do modelo com o que realmente preciso. E a restrição atua como um guarda-chuva — algo que o modelo explicitamente verifica contra si mesmo conforme gera.
As melhores práticas de prompting da OpenAI descrevem uma ideia similar sobre dar ao modelo uma persona com instruções explícitas. Vale a pena ler se você ainda não leu, embora eu diga que a parte de constraint é subenfattizada na documentação deles. describe a similar idea around giving the model a persona with explicit instructions. Worth reading if you haven't, though I'd say the constraint piece is underemphasised in their docs.
Compare a saída desse prompt enquadrado com "escreva uma query Supabase para o painel de administração." Dia e noite. Genuinamente.
---
Quando Parar de Fazer Prompts e Apenas Escrever o Código
Esta é a parte que ninguém quer dizer em voz alta.
Ferramentas de IA para código são brilhantes em: boilerplate, operações CRUD, funções utilitárias, escrever testes para código que você já escreveu, traduzir entre formatos (esquema JSON para tipo TypeScript, SQL para query Supabase, etc.), e primeiras versões de coisas que você vai modificar pesadamente.
Elas são genuinamente fracas em: entender a arquitetura real do seu app, saber qual trade-off importa para sua escala específica, escrever qualquer coisa que toque uma interação stateful complicada sem orientação pesada, e qualquer coisa onde a spec é fundamentalmente ambígua.
Tenho uma regra pessoal agora: se enviei mais de quatro prompts de acompanhamento tentando acertar um pedaço de código, fecho o chat e escrevo eu mesmo. O custo de tempo do debug de prompts pode exceder o custo de tempo de simplesmente escrever, especialmente para qualquer coisa com menos de uns 50 linhas.
O Stack Overflow Developer Survey 2024 descobriu que 76% dos desenvolvedores estão usando ou planejam usar ferramentas de IA — mas os mesmos dados mostraram confiança relativamente baixa na precisão. Essa lacuna entre uso e confiança é exatamente onde vive uma boa engenharia de prompts.Stack Overflow Developer Survey 2024 found that 76% of developers are using or planning to use AI tools — but the same data showed relatively low trust in accuracy. That gap between usage and trust is exactly where good prompt engineering lives.
---
Versione Seus Prompts Como Você Versiona Seu Código
No ano passado comecei a manter uma pasta prompts/ em projetos onde a assistência de IA é significativa. Arquivos Markdown. Um por área de feature major. Quando um prompt produz output particularmente bom, eu salvo. Quando encontro uma versão melhor, atualizo o arquivo.prompts/ folder in projects where AI assistance is significant. Markdown files. One per major feature area. When a prompt produces particularly good output, I save it. When I find a better version, I update the file.
Parece obsessivo. Me salvou provavelmente seis horas no último projeto grande sozinho — uma build headless WooCommerce para um varejista migrando de Shopify. Reusei um prompt de product query (com edições menores) em quatro componentes diferentes em vez de re-engenheirar o contexto do zero cada vez.
Git-rastreie. Sério. Qualidade de prompt é reproduzível se você tratar prompts como artefatos em vez de inputs descartáveis. Os prompt templates do LangChain formalizam essa ideia em um contexto de framework, mas você não precisa de framework algum — uma pasta de arquivos Markdown é suficiente para a maioria dos workflows de agency.LangChain's prompt templates formalise this idea in a framework context, but you don't need any framework — a folder of Markdown files is enough for most agency workflows.
---
FAQ
Engenharia de prompts é realmente uma habilidade transferível ou apenas específica do modelo?
Principalmente transferível. Os princípios fundamentais — especificidade, definição de contexto, encadeamento, loops de crítica — se aplicam em GPT-4, Claude 3.5 Sonnet, Gemini, o que vier depois. A sintaxe varia um pouco e alguns modelos respondem melhor a certos formatos, mas a lógica subjacente se mantém. Descobri que Claude responde bem a restrições explícitas; GPT-4 responde bem a exemplos. Diferenças pequenas, mesmos fundamentos.syntax varies a bit and some models respond better to certain framing, but the underlying logic holds. I've found Claude tends to respond well to explicit constraints; GPT-4 responds well to examples. Minor differences, same fundamentals.
Como você lida com código gerado por IA em code review?
Igual a qualquer outro código. Se vai para produção, passa por review. Ponto final. Parei de sinalizar "isso foi gerado por IA" nos PRs na Seahawk porque virou uma pista falsa — reviewers examinavam diferente, às vezes injustamente, às vezes insuficientemente. O código se sustenta ou cai por seus próprios méritos. O que eu sinalizo: qualquer seção em que a lógica não é óbvia e eu não adicionei comentários inline explicando o raciocínio.
Você usa system prompts ou apenas chat prompts?
Ambos. No Cursor, eu confio no arquivo .cursorrules para instruções persistentes em nível de projeto — coisas que eu teria que colar toda vez caso contrário. Para tarefas pontuais na web UI do ChatGPT ou Claude, tudo fica no chat. A abordagem com .cursorrules reduziu significativamente a repetição e o modelo fica mais consistente ao longo de uma sessão longa..cursorrules file for persistent project-level instructions — things I'd otherwise paste every time. For one-off tasks in the ChatGPT or Claude web UI, it's all in the chat. The .cursorrules approach has meaningfully reduced repetition and the model stays more consistent across a long session.
Qual é sua opinião honesta sobre IA substituindo desenvolvedores?
Está substituindo certas tarefas, não desenvolvedores. As decisões de julgamento — o que construir, como arquitetar, qual trade-off se encaixa na situação real deste cliente — essas estão longe de serem automatizadas. Se algo, os desenvolvedores que estão sendo pressionados são aqueles que eram puramente orientados à execução, não orientados a design ou arquitetura. Aguçe a camada de julgamento. Essa é a parte defensável.tasks, not developers. The judgment calls — what to build, how to architect it, which trade-off fits this client's actual situation — those are nowhere near automated. If anything, the developers getting squeezed are the ones who were purely execution-oriented, not design or architecture-oriented. Sharpen the judgment layer. That's the defensible bit.
---
Nove anos construindo coisas na web, e a lição fundamental se repete constantemente: a qualidade do seu output é determinada pela qualidade dos seus inputs. Isso era verdade quando eram briefings de clientes. É verdade agora com prompts.the quality of your output is determined by the quality of your inputs. That was true when it was client briefs. It's true now with prompts.
O modelo é um desenvolvedor júnior rápido, ocasionalmente brilhante, frequentemente excessivamente confiante. Gerencie-o de acordo.
