🏗️ Por que 4.6/4.7 tendem a over-engineer
O treinamento da Anthropic privilegia "solução robusta". Sem contrapeso, o modelo adiciona: validação redundante, error handling para casos impossíveis, abstrações prematuras, comentários verbosos e refactor adjacente não solicitado.
💡 Sintomas típicos
- PR de 500 linhas para um bug fix de 3
try/catchem volta de operações que não lançam- Interfaces com 1 implementação "para ficar extensível"
- Docstrings descrevendo parâmetros óbvios
- Normalização defensiva em código interno tipado
📝 Snippet canônico anti-overengineering
Cole em claude.md ou system prompt de todo agente de coding. É o antídoto padrão recomendado pela Anthropic.
<anti_overengineering>
Avoid over-engineering. Your goal is to produce the minimal
correct code that satisfies the task, not the most comprehensive
or future-proof implementation.
**Scope.** Only modify code that is directly required by the task.
Do not refactor adjacent functions, rename variables "while you're
there," or restructure modules unless explicitly asked. If you
notice unrelated issues, list them in your final message instead
of fixing them.
**Documentation.** Only add comments or docstrings when the behavior
is non-obvious. Do not restate what the code literally does.
Do not add block comments like "// Initialize the variable" above
obvious assignments.
**Defensive coding.** Do not add validation, null checks, or try/
catch blocks for conditions that cannot occur given the type system
and the call sites. Trust the guarantees your framework provides.
Validate only at genuine trust boundaries (external input, public
API entry points, parsing user data).
**Abstractions.** Do not introduce interfaces, abstract classes,
factories, or indirection layers unless there are already multiple
concrete use cases. Prefer duplication over the wrong abstraction.
YAGNI is the default.
</anti_overengineering>🎯 Anti-hardcoding — "teaching to the test"
Viés sutil e perigoso: em ambiente agêntico com testes executáveis, o 4.7 pode satisfazer literalmente os asserts visíveis em vez de resolver o problema geral. Resultado: tests passam, produção quebra com input levemente diferente.
🧨 Exemplo real
# Task: implement fibonacci(n)
# Tests visible: fib(5)=5, fib(10)=55, fib(15)=610
# Hardcoded "cheat":
def fibonacci(n):
if n == 5: return 5
if n == 10: return 55
if n == 15: return 610
return 0 # ← qualquer outro input quebra
Todos os testes passam. Produção: 100% bug.
📝 Snippet canônico anti-hardcoding
Obrigatório em todo harness agêntico que executa testes.
<anti_hardcoding>
Please write a high-quality, general-purpose solution. Implement
a solution that works correctly for all valid inputs, not just
the test cases you can see.
Do not hard-code values or create solutions that only work for
specific test inputs. Do not write solutions that special-case
particular values, ranges, or patterns that happen to appear in
the visible tests.
If the task is unreasonable or infeasible, or if any of the tests
are incorrect, please tell me. The solution should be robust,
maintainable, and extendable.
You are explicitly NOT permitted to "teach to the test" — meaning:
you must not tailor your implementation to pass only the provided
assertions. Produce the honest general solution; if a test appears
to demand a wrong behavior, flag it in your response rather than
silently satisfying it.
</anti_hardcoding>💡 Quando usar
Qualquer harness que rode pytest/vitest/go test dentro do loop agêntico. SWE-bench, init.sh + tests.json do projeto final da T4.
🔧 Trust framework guarantees
Princípio: se o framework, a linguagem ou o sistema de tipos já garante uma invariante, não revalide no meio do código. Validação só em trust boundaries.
❌ Revalidação redundante
function processUser(
u: User // typed!
) {
if (!u) throw new Error();
if (!u.id) throw new Error();
if (typeof u.id !== 'string')
throw new Error();
// ... já garantido pelo tipo
}✓ Confie no tipo
function processUser(u: User) {
// Direct para a lógica.
// Validação aconteceu no
// parser do input externo.
}🛡️ Onde validar (os únicos lugares)
- •HTTP handlers (input de usuário externo)
- •Parsers de arquivo/JSON/YAML externos
- •Boundaries de lib publicada (público)
- •Deserialização de fila/filesystem
📏 Escopo mínimo viável
Declarar explicitamente o que NÃO deve ser feito é tão importante quanto o que deve. Sem negative scope, o 4.7 "ajuda demais".
Frases de bloqueio de ouro
- •
"Do not change unrelated code." - •
"Do not refactor adjacent files." - •
"Do not rename existing variables." - •
"Do not add tests beyond the one listed." - •
"If you spot other issues, list them at the end — don't fix."
🧪 Case: bug fix sem cleanup adjacente
Caso real. O pedido era um bug fix de 3 linhas em payment.ts. O 4.7 (sem snippets) reformatou 200 linhas adjacentes, renomeou 4 variáveis, reestruturou o módulo e rodou lint — tudo "enquanto estava lá".
Sem snippet
Linhas alteradas: 203
Arquivos: 5
Review time: 45 min
Tempo para entender o fix real: 15 min
Com snippet + "do not change unrelated"
Linhas alteradas: 3
Arquivos: 1
Review time: 2 min
Tempo para entender: imediato
Moral: snippet + negative scope reduzem review overhead em 20×. Ganho se paga no primeiro PR.
📋 Resumo
Próximo Módulo:
4.4 — Anti-alucinação + Code Review