# Step 03 — Article Generator Output (v18)
“`json
{
“article”: {
“title”: “Conditional Hooks: Seus Hooks Estão Disparando à Toa (E Custando Performance)”,
“slug”: “claude-code-conditional-hooks-if-performance-software-house”,
“meta_description”: “O campo if dos hooks do Claude Code filtra exatamente quando cada hook executa. Menos overhead, mais governança. Essencial para software houses com automação IA.”,
“category”: “Claude Code”,
“tags”: [“claude-code”, “ia”, “software-house”, “hooks”, “automacao”, “devops”, “performance”, “ci-cd”, “governanca”, “conditional-hooks”],
“feature_version”: “v2.1.85”,
“feature_name”: “Conditional Hooks if field”,
“cover_image_prompt”: “Editorial photo of a precision Swiss watchmaker’s workshop with magnifying loupe over intricate gears, one gear highlighted in electric blue glow representing the exact mechanism being triggered, dark moody lighting with selective focus, tech-precision aesthetic, 16:9 aspect ratio, premium quality”,
“sources”: [
“https://code.claude.com/docs/en/hooks-guide”,
“https://code.claude.com/docs/en/hooks”,
“https://code.claude.com/docs/en/changelog”,
“https://x.com/lydiahallie/status/2037573738670297583”,
“https://www.pixelmojo.io/blogs/claude-code-hooks-production-quality-ci-cd-patterns”,
“https://serenitiesai.com/articles/claude-code-hooks-guide-2026”
],
“body_markdown”: “BODY_BELOW”
}
}
“`
—
Você configurou hooks no Claude Code. Ótimo. Seu hook de segurança que bloqueia `rm -rf` está disparando toda vez que o Claude roda um `ls`. Seu hook de auditoria de git está spawnando um processo novo quando o Claude faz `npm test`. Seu hook de lint está executando quando o Claude lê um arquivo JSON.
Cada um desses processos desnecessários custa milissegundos. Multiplica por 10 hooks, multiplica por dezenas de tool calls por sessão, e você tem segundos de overhead puro — seu Claude Code fica lento sem motivo nenhum.
Na minha experiência com 300+ software houses, hooks são a feature mais subutilizada do Claude Code. Todo mundo configura o matcher genérico e acha que tá resolvido. Não tá. E desde a versão 2.1.85, existe uma solução elegante que quase ninguém conhece: o campo `if`.
## O que é o campo `if` nos hooks
Antes do `if`, hooks tinham apenas uma camada de filtro: o **matcher**. O matcher filtra por nome da ferramenta — “rode esse hook quando for Bash”, “rode quando for Edit ou Write”. Funciona, mas é grosseiro demais.
O `if` adiciona uma segunda camada de filtro que olha não só QUAL ferramenta, mas COM QUAIS ARGUMENTOS ela está sendo chamada. Usa a mesma [permission rule syntax](https://code.claude.com/docs/en/permissions) das permissões do Claude Code.
“`json
{
“hooks”: {
“PreToolUse”: [
{
“matcher”: “Bash”,
“hooks”: [
{
“type”: “command”,
“if”: “Bash(git *)”,
“command”: “.claude/hooks/check-git-policy.sh”
}
]
}
]
}
}
“`
Nesse exemplo, o hook de política de git **só spawna** quando o Claude executa um comando Bash que começa com `git`. Se o Claude rodar `npm test`, `ls -la`, `cat arquivo.txt` — nada acontece. Zero overhead.
A [Lydia Hallie destacou no X](https://x.com/lydiahallie/status/2037573738670297583): *”Claude Code now supports an `if` field in hooks. It uses permission rule syntax to filter when a hook runs, which is useful when you want a hook on some bash commands but not every single one!”*
Exatamente. Não é sobre ter hooks. É sobre ter hooks que sabem quando ficar quietos.
## Como funciona na prática: a filtragem em duas camadas
Pensa assim:
“`
Evento dispara (ex: PreToolUse)
│
├─ Camada 1: MATCHER
│ Pergunta: “A ferramenta é Bash?”
│ Se sim → continua
│ Se não → descarta (sem overhead)
│
└─ Camada 2: IF (novo!)
Pergunta: “O comando Bash começa com ‘rm’?”
Se sim → spawna o hook
Se não → descarta (sem overhead!)
“`
A mágica é que a condição `if` é avaliada **antes** de spawnar o processo. Não é o hook que decide se deve rodar — o Claude Code já sabe antes mesmo de criar o processo filho. Isso elimina o overhead na raiz.
### Patterns que você pode usar
A syntax é `NomeDaFerramenta(padrão)`:
“`
“Bash(git *)” → Comandos Bash que começam com “git”
“Bash(rm *)” → Comandos que começam com “rm”
“Bash(sudo *)” → Comandos com sudo
“Edit(*.ts)” → Edit em arquivos TypeScript
“Edit(*.tsx)” → Edit em arquivos TSX
“Bash(npm test)” → Exatamente “npm test”
“Bash(*docker*)” → Qualquer comando com “docker” no meio
“`
O `*` funciona como wildcard — qualquer sequência de caracteres.
## 4 casos de uso que toda software house deveria implementar
### 1. Bloquear comandos destrutivos (sem atrasar o resto)
Sem `if`:
“`json
{
“matcher”: “Bash”,
“hooks”: [{
“type”: “command”,
“command”: “.claude/hooks/block-destructive.sh”
}]
}
“`
Problema: `block-destructive.sh` spawna pra CADA comando Bash. Se o Claude roda 50 comandos numa sessão, são 50 processos — 49 desnecessários.
Com `if`:
“`json
{
“matcher”: “Bash”,
“hooks”: [
{
“type”: “command”,
“if”: “Bash(rm -rf *)”,
“command”: “.claude/hooks/block-rf.sh”
},
{
“type”: “command”,
“if”: “Bash(sudo *)”,
“command”: “.claude/hooks/block-sudo.sh”
}
]
}
“`
Agora: `block-rf.sh` só spawna quando alguém tenta `rm -rf`. `block-sudo.sh` só quando tem `sudo`. Os outros 48 comandos? Zero overhead.
### 2. Lint apenas em arquivos que importam
“`json
{
“hooks”: {
“PostToolUse”: [
{
“matcher”: “Edit|Write”,
“hooks”: [
{
“type”: “command”,
“if”: “Edit(*.ts)|Write(*.ts)”,
“command”: “jq -r ‘.tool_input.file_path’ | xargs npx eslint –fix”
}
]
}
]
}
}
“`
O linter só roda quando TypeScript é editado. Editou um `.json`? Criou um `.md`? Sem lint desnecessário.
### 3. Auditoria cirúrgica de operações git
“`json
{
“hooks”: {
“PostToolUse”: [
{
“matcher”: “Bash”,
“hooks”: [
{
“type”: “command”,
“if”: “Bash(git push *)”,
“command”: “.claude/hooks/audit-push.sh”
},
{
“type”: “command”,
“if”: “Bash(git commit *)”,
“command”: “.claude/hooks/audit-commit.sh”
}
]
}
]
}
}
“`
Você audita exatamente `push` e `commit` — as operações que mudam estado. `git status`, `git diff`, `git log`? Passam direto sem spawn.
### 4. Gate de permissão para operações de banco
“`json
{
“hooks”: {
“PreToolUse”: [
{
“matcher”: “Bash”,
“hooks”: [
{
“type”: “command”,
“if”: “Bash(*DROP *)”,
“command”: “.claude/hooks/block-drop.sh”
},
{
“type”: “command”,
“if”: “Bash(*DELETE FROM *)”,
“command”: “.claude/hooks/confirm-delete.sh”
}
]
}
]
}
}
“`
## A conta que ninguém faz (mas deveria)
Vamos fazer uma conta rápida. Digamos que sua SH tem 5 hooks configurados em `PreToolUse`, todos sem `if`. Cada hook leva 200ms para spawnar e executar (conservador — hooks com HTTP ou jq levam mais).
Numa sessão típica, o Claude faz ~80 tool calls. Cada tool call dispara todos os 5 hooks:
– **Sem `if`:** 80 × 5 × 200ms = **80 segundos** de overhead puro por sessão
– **Com `if`:** Digamos que só 10% dos tool calls são relevantes: 8 × 5 × 200ms = **8 segundos**
São **72 segundos** economizados por sessão. Se seu time de 10 devs roda 5 sessões por dia cada, são **60 minutos de overhead eliminados por dia**. Por mês? **~20 horas**.
E isso é só performance. O ganho de **clareza em logs** é igualmente valioso — quando um hook dispara, você sabe que era relevante, não ruído.
## O bug fix que confirma a adoção
Na versão 2.1.88 (um release depois), a Anthropic corrigiu um bug: *”Fixed hooks `if` condition filtering not matching compound commands (`ls && git push`) or commands with env-var prefixes (`FOO=bar git push`)”*.
Isso é significativo por dois motivos:
1. **A comunidade adotou rápido** — encontraram edge cases em uso real
2. **Compound commands são comuns** — `cd /app && git pull && npm install` é o tipo de pipeline que todo dev roda
Depois do fix, `if: “Bash(git *)”` também casa com `FOO=bar git push` e `ls && git push`. O matcher ficou robusto para uso em produção.
## Limitação importante: só funciona em tool events
O `if` só funciona em 4 tipos de evento:
– **PreToolUse** — antes da ferramenta executar
– **PostToolUse** — depois da ferramenta executar com sucesso
– **PostToolUseFailure** — depois da ferramenta falhar
– **PermissionRequest** — quando aparece diálogo de permissão
Se você colocar `if` em eventos como `SessionStart`, `Stop`, `Notification`, `CwdChanged` — **o hook nunca executa**. Faz sentido: esses eventos não têm “ferramenta” nem “argumentos” para filtrar.
## O ecossistema de hooks como camada de governança
Se você acompanha essa série, já falamos sobre [HTTP Hooks para controle centralizado](/blog/claude-code-http-hooks-controle-centralizado-software-house) e [StopFailure Hook como alarme de automação](/blog/claude-code-stopfailure-hook-alarme-automacao-ia). O `if` é a peça que faltava para tornar hooks viáveis em **escala**.
Sem filtro fino, uma SH com 15-20 hooks criava um gargalo de performance absurdo. Com `if`, você pode ter 50 hooks hiperespecíficos, cada um disparando cirurgicamente, com overhead total menor do que 5 hooks genéricos.
Isso muda a mentalidade: hooks deixam de ser “automação que atrasa” e viram “governança que acelera”. Você pode ter hooks para:
– Bloquear padrões perigosos em SQL
– Auditar pushes para branches protegidas
– Lint apenas no tipo de arquivo correto
– Validar formatação apenas em PRs para main
– Notificar apenas quando operações críticas acontecem
Tudo isso rodando **zero overhead** quando o Claude está fazendo tarefas cotidianas como ler arquivos ou rodar testes.
## O que eu penso
Hooks sempre foram a feature mais “enterprise” do Claude Code. Mas até a versão 2.1.85, implementar hooks seriamente era uma troca: mais controle em troca de mais lentidão. O `if` quebrou essa troca.
Na minha experiência mentorando software houses, os times que mais se beneficiam de IA são os que tratam IA como infraestrutura, não como brinquedo. E infraestrutura precisa ser rápida, precisa e observável. O `if` é o que transforma hooks de “scripts que rodam” em “políticas que governam”.
Se sua SH ainda usa hooks sem `if`, revise hoje. O ganho é imediato e o risco é zero — `if` é retrocompatível, hooks sem `if` continuam funcionando exatamente como antes.
## Como implementar agora
1. Abra seus settings: `.claude/settings.json` ou `~/.claude/settings.json`
2. Identifique hooks em `PreToolUse` e `PostToolUse` que hoje rodam pra tudo
3. Adicione `”if”: “NomeFerramenta(padrão)”` em cada handler
4. Teste com `/hooks` no Claude Code para verificar
5. Monitore com `Ctrl+O` (verbose mode) para ver quais hooks estão disparando
“`json
// Antes (spawna pra TODO comando Bash)
{ “type”: “command”, “command”: “meu-hook.sh” }
// Depois (spawna SÓ pra git)
{ “type”: “command”, “if”: “Bash(git *)”, “command”: “meu-hook.sh” }
“`
Uma linha de configuração. Impacto imediato na performance.
## Conclusão
O Claude Code não é uma ferramenta pronta — é uma plataforma que você configura. E hooks com `if` são a diferença entre configurar com martelo e configurar com bisturi.
Se você quer implementar esse nível de governança e performance na IA da sua software house, começa por aí. É uma linha de JSON que muda a equação de overhead de toda sua automação.
Sou Thulio, mentoro 300+ SHs desde 2016.





