Reflexões sobre engenharia de harnesses (seja lá o que isso signifique)
Hoje os agentes escrevem 100% do meu código. Planejo algo, eles escrevem o código, revisamos, corrigimos o que aparecer e passamos para a próxima sessão. E repetimos.
Funciona. Mas pode virar bagunça bem rápido se não houver algo fora do modelo mantendo tudo junto.
Penso nesse algo como um harness. Construí-lo e mantê-lo é o que entendo por engenharia de harnesses.
Um harness é, literalmente, um conjunto de correias e equipamentos usado para controlar ou prender algo. Em inglês, “to harness” é aproveitar um recurso de forma eficaz.
O modelo é inteligente, mas, deixado sozinho, vai para todo lado. O harness é como você o orienta sem brigar com ele em cada tarefa.
Quando penso nisso, existem duas camadas.
O harness ao redor do modelo. Cursor, Codex, Claude Code, o que você usar. É o produto que envolve o LLM. Sessões, ferramentas, permissões, como o agente lê arquivos e executa comandos. Foi feito para todo mundo. Você escolhe um, ajusta configurações, talvez conecte alguns servidores MCP. Mas não está construindo isso. Você é usuário. Por definição, precisa funcionar para milhares de pessoas.
O harness ao redor do projeto. É o que você controla. O que constrói para si, para sua equipe e sua base de código. Scripts locais, organização dos testes, o comando que significa “concluído”, AGENTS.md, skills, documentos que apontam para arquivos reais. Ninguém vai escrever isso por você. É como o agente trabalha no seu mundo sem recomeçar do zero a cada sessão.
As duas camadas importam. Este artigo trata principalmente do que você controla: a camada que é sua, a parte que constrói para seu projeto e sua equipe.
O modelo prevê. O harness faz cumprir.
Mesmo prompt, outra execução, outra resposta. As duas podem parecer certas. Isso não é um bug. É assim que a previsão funciona.
- O modelo prevê o próximo token plausível. O resultado muda entre execuções.
- O harness determina o que conta como concluído. Mesmo repositório, mesmas verificações, mesmas regras.
Então o que é engenharia de harnesses?
Não é escolher Cursor em vez de Codex. E não é “escrever um prompt melhor”.
É a infraestrutura que você constrói para o agente não voar às cegas na sua base de código:
- Intenção clara. Como é, de fato, um trabalho concluído?
- Conhecimento no repositório, não no chat
- Verificações que comprovem o trabalho sem perguntar ao modelo “isso vai funcionar?”
- O hábito de transformar falhas repetidas em algo permanente
Quando a mesma coisa quebra duas vezes, “corrija, não cometa erros” pode funcionar de novo. A pergunta real é: o que falta e como tornamos isso óbvio e verificável?
Prompts são sugestões. Linters são lei.
O ciclo que eu realmente uso
No dia a dia é assim:
Planejar → Implementar → Verificar → Revisar
Se a revisão não estiver satisfeita, você volta a implementar. Verificar não é uma sensação. É npm run verify, ou a versão disso no seu repositório. Testes, lint, checagem de tipos. Coisas que você consegue executar.
| Etapa | Quem | O que acontece |
|---|---|---|
| Planejar | Você (talvez com o agente) | O que estamos fazendo, qual é o escopo, o que conta como concluído |
| Implementar | Agente | Código, testes, documentação. Esta é a parte incerta. |
| Verificar | Verificações + agente | Os mesmos comandos sempre. Passou ou não? |
| Revisar | Você e/ou outros agentes | Faz sentido de verdade? Voltar se não fizer. |
Quando a mesma coisa quebrar duas vezes, corrija o repositório
Existe um ciclo paralelo a tudo isso:
Erros / bloqueios / tarefas repetidas → registrar o aprendizado → incorporar às ferramentas
Você sabe que chegou a hora quando:
- Deixa o mesmo comentário de PR duas vezes
- O agente esbarra na mesma parede duas vezes
- Alguém diz “a gente sempre esquece de...”
- Passa localmente, falha no CI e você percebe que a verificação nunca esteve ali
- Um revisor continua corrigindo o mesmo padrão
Quando isso acontece, costumo seguir esta ordem:
- Documentação. Registrar o padrão.
- Testes. Cobrir o que quebrou.
- Lint. Impor a regra quando possível.
- Scripts. Transformar o fluxo em um comando que o agente consiga executar e verificar sozinho.
Toda vez que você explica a mesma coisa duas vezes, deve uma proteção à base de código.
O harness é a parte do sistema que lembra quando o modelo esquece.
Isso realimenta o planejamento e a verificação. Documentação melhor reduz palpites ruins antes de implementar. Verificações melhores detectam desvios antes da revisão. É assim que você obtém resultados confiáveis de algo que não é confiável por natureza.
Do que você realmente precisa
Dá para organizar isso de várias formas. Estas são as peças da camada do projeto que considero importantes:
Intenção e conhecimento. Se só existe no chat, se perde. A base de código é a memória. AGENTS.md deve ser um mapa, não um livro. Referências curtas a documentos reais. Notas de arquitetura que não sejam óbvias. Se estiver fazendo X, leia Y primeiro. Skills que tragam o contexto necessário para o trabalho, não a pasta inteira de documentação.
Verificações. Um comando que signifique concluído. Checagens rápidas no commit. A verificação completa antes do merge. O mesmo comando localmente e no CI. Você quer ver todas as falhas de uma vez, não uma por uma. É isso que o harness do modelo chama ao executar npm verify ou make validate. Você dá a ferramenta ao agente. O projeto define o que significa passar.
Regras. As regras que os agentes realmente vão encontrar antes de entregar. Linters que digam como corrigir, não só que você errou. Regras próprias quando as genéricas não bastarem. Minha regra: explicou duas vezes em revisão, codifique.
Camadas de testes. Uma pirâmide com regras claras sobre onde ficam os testes. Testes rápidos ao lado do código por padrão. Critério rigoroso para E2E completo. Desenvolvimento local que funcione offline para o agente não esperar algum serviço na nuvem iniciar.
Seu tempo é o gargalo. Deixe agentes cuidarem da preparação, das primeiras checagens e de boa parte da revisão mecânica. Você escolhe o que importa, o que significa concluído e as decisões que documentação e verificações ainda não conseguem tomar.
O harness do modelo fica mais inteligente a cada poucos meses. O harness do projeto é seu para continuar construindo. É essa parte que acumula valor.
Dê ferramentas reais aos seus agentes
Um harness de modelo consegue ler arquivos e executar comandos de shell. Não basta. Você precisa dar as ferramentas certas para a sua base de código.
Nada disso é novo. Linters, formatadores, verificadores de tipos, suítes de testes, um comando que diga que o build está bom. Equipes dependem dessas coisas há muito tempo.
O que mudou é quanto código se escreve. Um agente pode alterar uma quantidade enorme de arquivos em segundos. Aqueles linters e verificações que você talvez pulasse numa sexta à tarde? Não dá mais para se dar esse luxo. Muito mais código do que quando você digitava tudo. As mesmas ferramentas de sempre. Só que agora elas importam muito mais.
Quero dizer:
- Algo quebrou? O agente deve descobrir rápido, sem você explicar como executar testes.
- O código não segue seus padrões? Linters e verificadores de tipos devem dizer o que está errado, com detalhes suficientes para corrigir.
- Ambiente local bagunçado? Preparar deve ser um comando que o agente consiga executar, não uma conversa no Slack.
- As verificações falharam? A ferramenta deve dizer o que quebrou e como corrigir, não apenas que algo falhou. E depois o que executar (
make fix, repetir a verificação).
Tipos, lint e formato: devem funcionar no automático e dar respostas claras. Não “algo falhou em algum lugar”. Arquivo, linha, regra e, muitas vezes, uma dica sobre o que mudar. O agente lê essa saída como você lê erros do compilador. Você não quer que ele adivinhe se o código segue as regras da casa. Quer que a casa diga.
Essa é a camada de comunicação entre o harness do modelo e o do projeto. O harness do modelo executa o comando. Seu AGENTS.md diz quando. O Makefile, ou script do pacote, diz o que está dentro. O linter diz o que está errado. O agente corrige e executa de novo.
Documentação importa aqui também. Não uma wiki que ninguém atualiza. Diretrizes curtas que o agente realmente carrega: nomes, onde ficam testes, o que significa “concluído”, qual comando executar para cada parte do monorepo. Skills com contexto específico da tarefa para você não colar a mesma palestra em toda sessão.
Um exemplo concreto
É assim que algo desse tipo funciona na prática. Harmoney é um monorepo em que trabalho (mobile, backend, Supabase).
Preparação. make prepare-local-env cria os arquivos .env que faltam a partir de exemplos e preenche valores locais seguros. make check-local-env informa se você está pronto antes das verificações pesadas. Os agentes conseguem se virar em vez de pedir a décima segunda variável de ambiente.
Diretrizes. O AGENTS.md da raiz é o mapa: intenção do produto em VISION.md, limites em ARCHITECTURE.md, notas transversais em LEARNINGS.md. Um AGENTS.md por app em apps/mobile e apps/backend. Regras fixas que os agentes podem citar: todo texto visível ao usuário está em pt-BR, a identidade vem do JWT e não do corpo da requisição, bugs começam com um teste que falha. Verificações delimitadas: mexeu só no mobile? make check-mobile. Só no backend? make check-backend.
Camadas de testes. A pirâmide está documentada para os agentes não escolherem sempre a opção mais lenta.
- Testes unitários rápidos, ao lado do código. A maior parte da lógica dos handlers recebe um
*.test.tsao lado do arquivo testado.apps/backend/src/payments/handle-refund.tsao lado dehandle-refund.test.ts. Simule o banco, execute um fluxo e acrescente as asserções necessárias. Esse é o padrão. - Testes de banco quando Postgres for o foco. Políticas RLS, contratos RPC, comportamento de migrações? pgTAP em
supabase/tests/. Integração do backend com um banco local real?make db-test-ts. Nem toda mudança precisa disso. Só quando o banco for realmente o que você está comprovando. - Smoke tests de API, bem pequenos. Um pequeno percurso por script para autenticação e um webhook bem-sucedido. A regra é a mesma de uma boa suíte E2E: não adicione um caso sem precisar de fato do transporte HTTP real, de OAuth e da integração de sessão ao mesmo tempo. Dois percursos, não vinte.
- E2E completo de interface, último recurso. Web ou mobile, o mesmo critério: um ou dois percursos que provem que o login e uma tela crítica funcionam de ponta a ponta. Lentos, frágeis, caros de manter. A resposta padrão é não. Agentes começam por testes unitários ao lado do código.
Menos testes, mais completos, são melhores que cem testes de uma linha. Sem fixar strings exatas em descrições de ferramentas ou textos da interface. Offline por padrão. O harness diz qual camada usar para o agente não precisar adivinhar.
Feedback automático. Cada workspace executa formatação, lint e checagem de tipos como parte de check. Migrações SQL têm seus próprios scripts de lint. Controles de invariantes detectam erros arquiteturais estaticamente.
A verificação. make verify é a definição local de concluído: make check mais verificações de banco (pgTAP, paridade de contratos e testes de backend com banco). make fix entre workspaces quando a correção automática ajudar, depois execute novamente a verificação pertinente.
Quando algo falha. AGENTS.md deixa claro: se as verificações falharem, execute make fix e depois o alvo relevante. Sem adivinhar se precisava da verificação mobile ou da suíte completa de banco.
A estrutura é sempre a mesma:
- Orientar-se —
AGENTS.md+ o documento da tarefa - Implementar — o agente escreve código e testes ao lado dele
- Verificar — um comando autoritativo (
make verify) - Revisar — pessoas, agentes, preview quando ajudar
- Codificar — erro repetido vira documentação, teste ou regra de lint
O ganho não é memorizar seis jobs de CI. É ter uma resposta para “terminamos?” Execute a verificação. Verde localmente deve significar verde no CI. É o harness do projeto fazendo seu trabalho.
Não é mágica. Verificações de banco precisam do Docker rodando. Verificações delimitadas não executam a suíte inteira. A versão errada do Node ainda estraga sua tarde. Agentes pulam etapas se você permitir. O harness reduz a adivinhação. Não substitui seu julgamento.
No que eu realmente acredito
- Resultados probabilísticos são aceitáveis. Resultados probabilísticos sem proteção em produção não são.
- Boas verificações acumulam valor. O caos se desvia.
- Chat não é memória. O repositório é.
- Regras curtas e verificáveis vencem um documento de instruções de 50 páginas.
- O harness nunca está pronto. Você o constrói junto com o produto.
- O bom senso entra nas ferramentas para você não precisar estar em todo PR.
Engenharia de harnesses não substitui engenheiros. Só muda onde você investe seu tempo. Menos digitar cada linha. Mais projetar um ambiente em que agentes consigam fazer trabalho de verdade.