18 de out. de 2025·7 min de leitura

Ganchos de pre-commit para repositórios herdados: guardrails simples

Configure ganchos de pre-commit em repositórios herdados com formatação, lint, varredura de segredos e testes rápidos para bloquear commits ruins antes do CI.

Ganchos de pre-commit para repositórios herdados: guardrails simples

Por que repositórios herdados continuam quebrando

Um repositório herdado é código que você não modelou. As regras são confusas, o estilo é misto e o clássico “funcionou na minha máquina” aparece porque ninguém sabe o que o repositório realmente espera.

Os problemas começam pequenos e viram falhas reais: um arquivo usa tabs, outro usa espaços; um conserto rápido adiciona uma dependência sem travar versões; um arquivo de configuração é editado à mão e se afasta do que a produção precisa. Até mudanças simples ficam arriscadas porque o repositório está cheio de suposições ocultas.

Confiar apenas em checagens no CI costuma ser tarde demais. O CI roda depois que o commit já foi compartilhado, as reviews já estão em andamento e pessoas já puxaram o estado quebrado. Quando o CI falha, você perde tempo rerodando pipelines, fazendo rebase e adivinhando qual mudança causou o problema. Com o tempo, revisores param de confiar nos sinais porque as falhas viram “normais”.

O dano real é como pequenos commits ruins se acumulam:

  • Uma pequena mudança de formatação cria diffs barulhentos, então um bug real passa despercebido.
  • Um problema de lint é deixado “para depois” e vira padrão.
  • Um teste não é executado localmente, então um build quebrado bloqueia todo mundo.
  • Um segredo é commitado e força uma correria para rotacionar chaves.

Ganchos de pre-commit ajudam porque movem as checagens mais comuns para o ponto mais cedo: antes de a mudança virar um problema compartilhado. Eles também criam uma base consistente sem longos debates sobre “a forma correta”.

Quando funciona, parece entediante (no bom sentido): menos builds quebrados, diffs menores e merges mais rápidos porque as reviews se concentram na lógica em vez do estilo.

O que os guardrails de pre-commit devem pegar

Repositórios herdados falham de maneiras previsíveis. O objetivo não é bloquear todo commit. É impedir os poucos erros de alto impacto que criam ruído, quebram builds ou vazam dados.

Comece pela deriva de formatação. Arquivos sem formato criam diffs enormes que escondem mudanças reais. Um conserto de uma linha não deveria vir com 400 linhas de mudanças de espaço em branco. Autoformatação no commit mantém as reviews legíveis e facilita merges.

Depois, pegue problemas de lint que indicam bugs reais, não opiniões de estilo: variáveis indefinidas, imports não usados, comparações suspeitas, awaits faltando ou padrões perigosos como SQL construído por strings. Se seu linter reclama principalmente de vírgulas, as pessoas aprendem a ignorá‑lo.

Segredos são o grande problema. Chaves entram quando alguém copia um .env para o repositório, cola um token num arquivo de config ou adiciona um log de depuração com credenciais. Uma varredura de segredos deve bloquear o commit antes que ele chegue a um remoto.

Por fim, rode testes rápidos que custem segundos, não minutos. O melhor hook é aquele que as pessoas mantêm habilitado. Um pequeno smoke test, um subconjunto rápido de unit tests ou uma checagem básica de tipos pega regressões óbvias cedo.

Um conjunto prático de coisas que valem bloquear:

  • Mudanças auto-formatadas (para manter diffs pequenos)
  • Erros de lint de alto sinal (prováveis bugs)
  • Segredos detectados (tokens, chaves de API, chaves privadas)
  • Testes rápidos ou checagens de tipos (feedback vermelho-verde rápido)
  • Checagens de sanidade (JSON/YAML válidos, consistência de lockfile)

Escolha um conjunto pequeno de ferramentas que caiba no seu repo

Quando você herda um repositório, o objetivo não é “cobertura máxima”. O objetivo é menos commits quebrados. Comece com três ou quatro checagens em que você confia, e só adicione mais depois que a equipe parar de brigar com a ferramenta.

Um bom conjunto inicial:

  • Um formatador (corrige automaticamente, sem debate)
  • Um linter (pega erros reais, não estilo)
  • Uma varredura de segredos (impede chaves e tokens vazarem)
  • Um teste rápido ou checagem de build (segundos, não minutos)

Case as ferramentas com a linguagem que você realmente executa. Se você tem múltiplas linguagens, mantenha as checagens limitadas às pastas correspondentes para que um lado não deixe o outro lento.

Velocidade importa mais do que perfeição. Se um hook leva 30 a 60 segundos, as pessoas vão desabilitá‑lo. Prefira ferramentas previsíveis: mesma saída em toda máquina, poucos falsos positivos e correções claras.

Decida o que bloqueia um commit e o que apenas avisa. Bloqueio deve ser reservado para coisas que são quase sempre erradas (formatação, erros óbvios de lint, segredos). Avisos são melhores para regras “talvez” (limites de complexidade, regras de estilo estritas) até que o repositório estabilize.

Se você trabalha em um monorepo, uma regra simples ajuda: rode apenas o que mudou. Se você editar a pasta API, rode o lint da API e um teste rápido da API, não o repo inteiro.

Passo a passo: adicionar pre-commit a um repositório herdado

Crie uma branch base (ou tag) a partir do branch main atual. Rode então as checagens uma vez em todo o repositório para ver o raio de impacto. Essa primeira execução não é sobre perfeição. Ela mostra o que já está quebrado, o que é ruído de estilo e o que vai bloquear todo mundo no dia um.

A seguir, escolha como quer executar os hooks.

  • Um framework de pre-commit é mais fácil de compartilhar com a equipe e se comporta igual na maioria das máquinas.
  • Hooks nativos do git são simples, mas por padrão são locais, então as pessoas esquecem deles ou nunca os instalam.

Para a maioria das equipes, uma configuração compartilhada no repositório funciona melhor porque a configuração pode ser revisada como qualquer outra mudança.

Mantenha a configuração sem surpresas: um arquivo, nomes claros e apenas as checagens que você realmente quer aplicar. Instale hooks localmente e confirme que eles rodam fazendo uma mudança pequena e commitando.

Um rollout que evita drama:

  • Crie uma branch base e rode todos os hooks uma vez para registrar o que falha.
  • Adicione o arquivo de config ao repositório e mantenha o primeiro conjunto de hooks pequeno.
  • Instale os hooks e verifique que eles rodam em um commit real.
  • Comece rodando apenas nos arquivos modificados (ou em modo de aviso), depois mude para bloquear.
  • Adicione um hook novo por vez para que as falhas sejam fáceis de entender.

Aplicação gradual importa. Se seu primeiro commit bloquear em 700 mudanças de formatação, as pessoas vão burlar os hooks. Conserte primeiro os problemas de alto risco (segredos, erros óbvios de lint), e só depois trate a formatação.

Adicione formatação que acabe com debates de estilo

Fix critical issues in days
A maioria dos projetos FixMyMess é concluída em 48–72 horas com verificação especialista.

Formatação é a barreira mais fácil de adicionar e traz retorno rápido. Escolha um formatador por linguagem e trate‑o como a fonte única da verdade. Quando o repositório tem estilo consistente, as reviews podem focar na lógica, não se alguém usou tabs ou outro tipo de aspas.

Para velocidade, rode a formatação apenas nos arquivos staged. Isso mantém commits rápidos e evita o problema “toquei uma linha e 2.000 linhas mudaram”.

Uma boa base de formatação também evita diffs barulhentos que desperdiçam tempo:

  • Normalizar finais de linha (para evitar conflitos entre Windows e macOS)
  • Remover espaços em branco finais
  • Garantir que arquivos terminem com uma nova linha
  • Manter encoding consistente (geralmente UTF-8)

Cuidado com arquivos gerados e pastas de vendor. Normalmente você não quer um formatador reescrevendo outputs de build, lockfiles que não são seus ou código de terceiros copiado. Exclua-os explicitamente para que a formatação permaneça previsível:

# Example idea (not a full config)
exclude: "^(dist|build|vendor|\.next|coverage)/"

Por fim, concorde quando a formatação acontece. Se você formata ao salvar no editor, a formatação no commit deve bater com isso, não criar conflito. Uma regra simples funciona bem: formate ao salvar para conforto no dia a dia, e imponha formatação no commit para que o repositório fique limpo mesmo quando alguém usa outro editor.

Adicione lint que pega problemas reais

Linting é mais útil em repositórios herdados quando previne bugs reais, não quando começa discussões sobre vírgulas. Com ganchos de pre-commit, o objetivo é simples: impedir erros óbvios antes que cheguem a um branch e desperdicem tempo no CI.

Escolha um linter que combine com sua linguagem principal e configure‑o para priorizar sinais de bug sobre gosto pessoal. Regras que geralmente valem a pena incluem:

  • Variáveis e imports não usados
  • Tratamento de erro faltando (promises não capturadas, valores de retorno ignorados)
  • Construção insegura de strings (fonte comum de injeção)
  • Comparações suspeitas e código inacessível
  • Erros de copiar/colar e lógica duplicada

Se seu repo usa tipagem, adicione uma checagem leve de tipos onde fizer sentido. Não comece checando tudo de uma vez. Mire nas partes que quebram produção com mais frequência (auth, pagamentos, acesso a dados) e expanda depois.

Código legado é o motivo pelo qual linting falha em projetos herdados. Não tente “consertar o mundo” só para introduzir um guardrail. Use ignores direcionados ou limite o lint aos arquivos modificados. Outra opção é uma baseline temporária para que o hook bloqueie apenas violações novas, e então você reduz os problemas antigos ao longo do tempo.

Deixe a saída amigável. Prefira configs que mostrem uma explicação curta e uma sugestão de correção, e habilite auto‑fix quando for seguro.

Mantenha execução previsível. Um hook que às vezes leva 5 segundos e às vezes 2 minutos será desabilitado. Mire em checagens rápidas e consistentes nos arquivos staged, e deixe análises mais pesadas para o CI.

Adicione varredura de segredos antes de qualquer coisa ir para o remoto

Repositórios herdados frequentemente escondem segredos à vista. Uma varredura rápida de segredos nos hooks de pre-commit detecta vazamentos óbvios (chaves de API, tokens de acesso, chaves privadas, segredos OAuth) antes que saiam do laptop.

Use um scanner que detecte padrões e bloqueie novos vazamentos. Muitas equipes escolhem ferramentas que falham o commit quando um novo segredo é introduzido, ao mesmo tempo em que permitem uma baseline revisada de achados conhecidos.

Faça a regra simples: bloqueie commits que adicionem segredos novos. Se o hook for acionado, o desenvolvedor remove o segredo ou marca como falso positivo revisado por um processo controlado (não desabilitando o hook).

Para exceções, prefira uma allowlist explícita e fácil de revisar. Mantenha a baseline no controle de versão e trate mudanças nela como significativas.

Segredos costumam entrar por alguns lugares previsíveis:

  • Arquivos .env e config locais
  • Arquivos de configuração de exemplo
  • Logs de depuração e dumps de erro
  • Fixtures de teste e respostas HTTP gravadas
  • Trechos copiados de dashboards de fornecedores

Se você descobrir que um segredo já foi commitado, trate‑o como comprometido, mesmo se o repositório for privado:

  • Rotacione ou revogue a chave/token
  • Remova do código e substitua por variável de ambiente
  • Verifique o histórico do git e purge se necessário
  • Procure onde foi usado (logs, deploys, CI)
  • Adicione uma checagem de regressão para que não aconteça de novo

Adicione testes rápidos que desenvolvedores realmente vão rodar

Clean up security issues fast
Remova segredos expostos e feche buracos comuns como injeção SQL antes do próximo deploy.

Os únicos testes que ajudam no momento do commit são os que as pessoas não pulam. Mire em um conjunto pequeno e confiável que termine em menos de 60 segundos em um laptop comum. Se demorar mais, os desenvolvedores vão burlar e seu guardrail vira ruído.

Comece com smoke tests que provem que o repositório ainda builda e os caminhos mais importantes ainda funcionam. Bons candidatos são testes que pegam quebras óbvias rapidamente, não cobertura completa.

O que rodar no pre-commit

Mantenha curto e ligado a falhas que você realmente viu:

  • Checagem de build (compilar, typecheck ou um bundle mínimo)
  • Sanidade de migração de banco (validar esquema ou aplicar em um DB descartável)
  • Um ou dois testes de API (endpoint de health, fluxo core create-read)
  • Smoke de auth (login funciona, rota protegida continua protegida)
  • Um subconjunto mínimo de unit tests marcado como “smoke” ou “fast”

Faça o comando funcionar em qualquer máquina com setup mínimo. Prefira um único ponto de entrada (por exemplo, uma tarefa make ou um script npm) que falhe com saída clara. Evite exigir serviços especiais que existam só no laptop de um desenvolvedor.

Onde ficam os testes mais pesados

Se uma suíte de testes leva minutos, coloque‑a fora do pre-commit:

  • Pre-commit: apenas smoke tests rápidos
  • No push ou PR: unit tests e integration tests completos
  • Noturno: end-to-end lentos, testes de carga, auditorias de dependências

Para manter testes determinísticos, reduza dependências de rede instáveis. Simule APIs externas, congele tempo onde precisar e use fixtures locais. Se precisar atingir um serviço, torne opcional e pule por padrão no pre-commit.

Exemplo: estabilizando um repositório protótipo gerado por IA

Imagine um repositório que começou como um protótipo rápido de uma ferramenta de geração de código por IA. Funciona bem em demos, mas a produção continua quebrando. Auth às vezes retorna ao login, pastas estão uma bagunça e cada mudança arrisca abrir um novo buraco.

É aqui que ganchos de pre-commit brilham: adicione guardrails pequenos que impedem os piores erros cedo, sem iniciar uma grande reescrita.

Um rollout simples em três commits mantém a confiança alta:

  • Primeiro commit: adicione um formatador e um linter básico com padrões seguros.
  • Segundo commit: adicione varredura de segredos, remova quaisquer .env commitados com cuidado, substitua por um arquivo de exemplo e rotacione chaves vazadas.
  • Terceiro commit: adicione um smoke test que reflita a última queda (por exemplo, um fluxo de auth que deve produzir uma sessão válida).

Ao explicar essas mudanças para um fundador ou cliente não técnico, foque nos resultados:

  • “Isso bloqueia vazamentos acidentais de senhas e chaves de API antes de o código sair do laptop.”
  • “Isso pega erros óbvios antes que desperdicem tempo no CI ou durante o deploy.”
  • “Esse teste rápido previne a queda específica que vocês acabaram de pagar.”
  • “Nada disso muda funcionalidades. Só torna mudanças futuras mais seguras.”

Armadilhas comuns e como evitá‑las

Find what keeps breaking builds
Não tem certeza por que o CI está sempre vermelho? Vamos apontar as causas reais.

Repositórios herdados já têm atrito suficiente. A forma mais rápida de fazer pre-commit falhar é fazer parecer uma punição. Bons guardrails são silenciosos na maior parte do tempo e barulhentos apenas quando capturam algo que te custaria tempo depois.

Mantenha hooks rápidos e previsíveis

A maior armadilha é tornar hooks tão estritos que ninguém consegue commitar. Se levar mais de um minuto, as pessoas vão usar flags de bypass e param de confiar na configuração.

Regras práticas:

  • Rode apenas checagens rápidas localmente (formatação, lint, segredos e um pequeno smoke test).
  • Não rode a suíte completa de testes em todo commit.
  • Corrija apenas arquivos staged. Auto‑fix em arquivos unstaged cria diffs surpreendentes.
  • Comece com avisos para regras barulhentas, depois aperte quando o repositório estiver mais limpo.
  • Faça a saída clara: uma mensagem de erro deve dizer o que fazer a seguir.

Se seu formatador ou linter costuma tocar arquivos que não faziam parte do commit, mude para modo staged-only (ou uma ferramenta que suporte isso) para que o hook nunca edite trabalho em andamento.

Faça o bypass uma escolha consciente

Outro modo comum de falhar é permitir que as pessoas ignorem falhas de hook sem plano. Às vezes pular é válido (hotfix, ferramenta upstream quebrada, demo urgente), mas deve ser uma exceção deliberada.

Defina expectativas no repositório:

  • Documente quando o bypass é permitido e qual acompanhamento é necessário.
  • Se alguém burlar, o CI ainda deve pegar a mesma classe de problemas.

Fique de olho em problemas de paridade com o CI. Se desenvolvedores rodam uma versão do linter localmente e o CI roda outra, você terá commits “funciona na minha máquina”. Trave versões de ferramentas e mantenha hooks locais alinhados com o CI para que as falhas sejam consistentes.

Checklist rápido e próximos passos

Se você está adicionando ganchos de pre-commit a um repositório herdado, mantenha o primeiro passo pequeno e confiável. Você está tentando pegar problemas óbvios cedo, sem transformar commits em punição.

Uma baseline que você pode configurar em uma tarde:

  • Auto‑format no commit
  • Lint apenas nos arquivos que você tocou (rápido, regras de alto sinal)
  • Varredura de segredos (chaves, tokens, chaves privadas, commits acidentais de .env)
  • Rode um comando de teste rápido (smoke test ou um conjunto focado de unit tests)
  • Trave versões das ferramentas

Faça rollout em etapas para que a equipe não resista à mudança:

  1. Baseline: rode hooks apenas em arquivos novos ou alterados
  2. Aviso: faça hooks falharem localmente, mas não bloqueie o CI ainda
  3. Aplicar: bloqueie commits e faça o CI falhar quando hooks falharem

Depois de uma semana, você deve ver menos commits “corrigir formatação”, menos comentários em PRs sobre estilo, menos falhas no CI causadas por problemas fáceis de pegar e reviews mais rápidas porque as pessoas focam na lógica em vez do ruído.

Saiba quando parar de remendar. Se todo hook revela problemas mais profundos (testes instáveis, auth quebrando aleatoriamente, configs vazando, módulos emaranhados), guardrails não vão consertar a base. É hora de trabalho focado de reparo: reestruturar, endurecer a segurança e chegar a um ponto em que testes rápidos realmente signifiquem algo.

Se o repositório começou como um protótipo gerado por IA e continua quebrando em produção, FixMyMess (fixmymess.ai) foi feito para essa situação exata: diagnosticar a base de código, corrigir lógica e problemas de segurança e prepará‑la para deploy. Uma auditoria rápida também ajuda a decidir quais guardrails aplicar primeiro para não desacelerar a equipe.

Perguntas Frequentes

Why use pre-commit hooks if we already have CI?

CI detecta problemas depois que o commit ruim já foi compartilhado. Ganchos de pre-commit evitam os erros mais comuns antes que cheguem ao seu branch, aos colegas ou ao pipeline, reduzindo builds quebrados e tempo perdido reexecutando CI.

What are the first guardrails I should add to an inherited repo?

Comece pequeno: um formatador, um linter com alto sinal, uma varredura de segredos e um teste ou checagem de tipos rápida. Esse conjunto evita diffs ruidosos, bugs óbvios, credenciais vazadas e regressões simples sem transformar commits em uma tarefa lenta.

How fast should pre-commit hooks be?

Aponte para menos de 60 segundos em um laptop normal e prefira checagens que rodem apenas em arquivos staged ou modificados. Se os hooks parecerem lentos ou imprevisíveis, as pessoas vão ignorá-los e você perde o benefício.

How do I avoid a huge “format everything” diff?

Formate apenas o que você está commitando, não o repositório inteiro. Isso mantém os diffs pequenos, evita mudanças surpresa em arquivos não relacionados e facilita mesclar um guardrail em uma base de código bagunçada sem iniciar uma guerra de formatação.

How can I enforce linting without breaking the whole repo?

Execute uma execução inicial para ver o que já falha e configure os hooks para bloquear apenas novas violações no começo. Assim você continua entregando enquanto limpa os problemas legados gradualmente, em vez de tentar consertar o repositório inteiro em um PR doloroso.

What should we do when a hook finds a secret?

Faça da varredura de segredos um bloqueio rígido para novos vazamentos e trate qualquer segredo comprometido como tal: remova do código, rotacione ou revogue, e mantenha o scanner ativo para que o mesmo erro não entre novamente.

What tests belong in pre-commit vs CI?

Comece com um smoke test simples que reflita falhas reais que você já viu, como um fluxo de autenticação que precisa gerar uma sessão válida, ou um build mínimo que deve ter sucesso. Testes mais pesados ficam no CI para manter commits locais rápidos e confiáveis.

How do pre-commit hooks work in a monorepo?

Restrinja as checagens às pastas que você tocou para que editar a API não dispare um build completo do front-end e vice-versa. O padrão deve ser “execute o que mudou”; caso contrário, monorepos rapidamente tornam os hooks lentos demais.

When is it okay to bypass hooks?

Permita o bypass apenas para emergências reais e faça com que seja uma escolha deliberada com acompanhamento claro. Mesmo quando alguém pula localmente, o CI ainda deve rodar as mesmas classes de checagens para que exceções não se tornem comportamento normal.

When should we stop adding guardrails and get outside help?

Se toda mudança continua quebrando autenticação, expondo configs ou revelando problemas estruturais, os hooks não vão consertar a fundação — eles só vão mostrar a dor mais cedo. Se seu repositório veio de ferramentas de IA e precisa ficar pronto para produção, FixMyMess pode rodar uma auditoria gratuita, diagnosticar, reparar lógica, endurecer segurança, refatorar e preparar o deploy, com a maioria dos projetos finalizados em 48–72 horas.