Pre-commit hooks : 90% de bugs bloqués avant push
En octobre 2025, j'avais 4-6 bugs qui atteignaient la prod chaque mois sur mes projets. Aujourd'hui, ce chiffre est tombé à 0,5 — et la différence tient dans 9 pre-commit hooks bien choisis qui tournent en moins de 3 secondes à chaque commit. Voici la config complète, les mesures, et les 3 hooks que j'ai désactivés parce qu'ils ralentissaient sans bénéfice.
Le problème : les bugs qui passent les tests manuels
Mes bugs en prod venaient quasi tous de la même source : des checks que j'aurais pu faire automatiquement mais que je « savais » que le code était propre. L'erreur typique : un console.log laissé dans un handler de prod, une variable non utilisée, une migration DB non commitée, une clé API dans un fichier.
Les tests unitaires n'attrapent pas ça. Seule une automation qui bloque le commit peut.
La config complète (.pre-commit-config.yaml)
J'utilise le framework pre-commit (Python, cross-langage) parce qu'il gère mieux le cache et les hooks multi-langages que les alternatives natives.
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
args: ['--maxkb=500']
- id: check-merge-conflict
- id: detect-private-key
- repo: https://github.com/gitleaks/gitleaks
rev: v8.24.0
hooks:
- id: gitleaks
- repo: local
hooks:
- id: no-console-log
name: No console.log in production code
entry: bash -c 'grep -rn "console\.log\|console\.debug" src/ --include="*.ts" --include="*.tsx" && exit 1 || exit 0'
language: system
pass_filenames: false
- id: typecheck
name: TypeScript typecheck
entry: npx tsc --noEmit
language: system
pass_filenames: false
- id: lint
name: ESLint
entry: npx eslint --max-warnings 0
language: system
types: [javascript, typescript]
- id: test-affected
name: Run affected tests
entry: npx vitest related --run
language: system
types: [javascript, typescript]
pass_filenames: trueInstall : pip install pre-commit && pre-commit install.
Les 9 hooks — quoi, pourquoi, combien de bugs bloqués
1. `trailing-whitespace` + `end-of-file-fixer`
Cosmétique mais critique pour ne pas avoir des diffs pollués. 0 bug prod, mais énorme gain de lisibilité en code review.
2. `check-yaml` + `check-added-large-files`
Bloque un fichier YAML cassé ou un binaire > 500 KB accidentellement ajouté. 3 occurrences en 6 mois — j'avais committé par erreur un dump DB de 120 MB, ce hook m'a sauvé.
3. `check-merge-conflict`
Bloque les marqueurs <<<<<< oubliés. 2 fois en 6 mois — ça aurait dû être évident, ça ne l'était pas.
4. `detect-private-key` + `gitleaks`
Bloque les clés SSH, les tokens API, les mots de passe. 6 occurrences en 6 mois — le plus critique. Gitleaks a attrapé 4 fois des clés AWS et 2 fois des tokens Anthropic que j'allais pousser.
5. `no-console-log` (custom)
Cherche les console.log dans src/ et bloque si trouvés. 8 occurrences en 6 mois. Le hook qui m'évite le plus d'embarras.
6. `typecheck`
Lance tsc --noEmit sur tout le projet. 12 occurrences de type errors bloquées. Sans, j'aurais déployé du code qui compile pas (tsconfig.json avec ignoreBuildErrors).
7. `lint` avec `--max-warnings 0`
ESLint strict. 23 occurrences en 6 mois — unused variables, imports manquants, rules violations. Des trucs mineurs individuellement mais cumulativement ils dégradent la qualité.
8. `test-affected`
Lance seulement les tests qui touchent aux fichiers modifiés. 4 régressions bloquées en 6 mois. Important : utiliser vitest related pour ne pas lancer toute la suite à chaque commit (3 min vs 8 s).
Le bilan : 58 bugs bloqués en 6 mois
| Catégorie | Bugs bloqués | Criticité |
|---|---|---|
| Secrets (gitleaks + private-key) | 6 | 🔴 Critique |
| Type errors | 12 | 🟠 Élevée |
| Console.log en prod | 8 | 🟠 Élevée |
| ESLint rules | 23 | 🟡 Moyenne |
| Régressions tests | 4 | 🟠 Élevée |
| Fichiers larges | 3 | 🟡 Moyenne |
| Merge conflict | 2 | 🔴 Critique |
| Total | 58 |
58 bugs bloqués avant qu'ils atteignent main, dont 12 critiques qui auraient causé une indisponibilité ou une fuite de données. Le temps que j'aurais passé à les débugger en prod : estimation 30-50 heures. Le temps pour installer et configurer pre-commit : 30 minutes.
ROI : 60-100×.
Les 3 hooks désactivés
- 1.
prettier-check— remplacé parformat on savedans VS Code. Double job. - 2.
test-all— 3 minutes par commit, trop lent. Remplacé partest-affected. - 3.
docs-build— cassait sur des URLs externes flaky. Délégué au CI.
Les 2 règles qui rendent ça viable
Règle 1 : le hook doit durer moins de 5 secondes
Si un hook dépasse 5 secondes, tu finis par bypasser avec --no-verify par frustration. Tout doit être incrémental et caché. typecheck en plein : 8 secondes, trop. Solution : tsc --incremental + cache.
Règle 2 : le hook doit être actionnable
Un hook qui dit « X est cassé » sans dire comment le réparer, tu le désactives au bout de 2 semaines. Tous mes hooks retournent un message clair avec le fichier et la ligne concernés.
Ce qu'il faut retenir
- 1.9 pre-commit hooks ont bloqué 58 bugs en 6 mois, dont 12 critiques.
- 2.ROI 60-100× sur l'investissement d'installation (30 min).
- 3.Gitleaks + typecheck sont les 2 hooks avec le plus de valeur.
- 4.Règle 5 secondes — tout hook plus long va dans le CI.
- 5.Pre-commit framework (Python) est le meilleur cross-langage.
Pour le workflow complet Ralph Loop qui s'intègre avec ces hooks, j'ai écrit un livre dédié :
Soutenez mon travail sur Patreon
Accès anticipé aux articles, contenu exclusif, et la satisfaction de soutenir un auteur indépendant.
Rejoindre — à partir de 3€/mois