The Hook (The "Byte-Sized" Intro)
A good hook takes 2 seconds and catches real issues. A bad hook takes 30 seconds, fails randomly, and makes developers hate hooks. The difference? Speed, clarity, and reliability. If your hook is slow, flaky, or gives cryptic errors, developers will --no-verify every commit — defeating the entire purpose.
📖 What are Hook Best Practices?
Guidelines for creating hooks that are fast, reliable, and helpful — encouraging adoption instead of avoidance.
Conceptual Clarity
The 7 hook rules:
| # | Rule | Why |
|---|---|---|
| 1 | Keep hooks fast | Slow hooks get bypassed |
| 2 | Only check staged files | Don't check unrelated files |
| 3 | Give clear error messages | Developers need to know what failed |
| 4 | Avoid flaky checks | Random failures erode trust |
| 5 | Allow escape hatch | Document --no-verify for emergencies |
| 6 | Match CI checks | Hooks should catch same issues as CI |
| 7 | Version hooks | Store in repo, review in PRs |
Speed targets:
| Hook | Target Time | If Slower |
|---|---|---|
pre-commit | < 5 seconds | Only lint staged files |
commit-msg | < 1 second | Regex only, no network |
pre-push | < 30 seconds | Acceptable for full tests |
Real-Life Analogy
Good hooks are like a quick security scan at the door — a 3-second bag check. Bad hooks are like a full-body MRI before entering every room. People stop going through the door.
Visual Architecture
Why It Matters
- Adoption: Fast, reliable hooks get used. Slow, flaky hooks get bypassed.
- Trust: Consistent behavior builds developer confidence.
- Efficiency: Hooks should complement CI, not duplicate it.
- Culture: Good hooks create a culture of quality.
Code
# ─── Good: fast, staged-only, clear messages ───
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/sh
# Only lint staged files (fast!)
FILES=$(git diff --cached --name-only --diff-filter=ACMR -- '*.js' '*.ts')
if [ -z "$FILES" ]; then
exit 0 # No staged JS/TS files, skip
fi
echo "$FILES" | xargs npx eslint
if [ $? -ne 0 ]; then
echo ""
echo "❌ ESLint found issues in staged files."
echo " Fix them or use 'git commit --no-verify' to skip."
exit 1
fi
echo "✅ Lint passed."
EOF
# ─── Bad: slow, checks everything, cryptic error ───
# DON'T DO THIS:
# npm run lint ← Lints ALL files, not just staged
# npm test ← Runs entire test suite on commit (too slow)
# exit 1 ← No error message explaining what failedKey Takeaways
- Fast: Pre-commit < 5 seconds. Only check staged files.
- Clear: Always explain what failed and how to fix it.
- Reliable: No flaky checks that fail randomly.
- Escapable: Document
--no-verifyfor legitimate emergencies.
Interview Prep
-
Q: What makes a Git hook "good"? A: Speed (pre-commit < 5 seconds), reliability (no random failures), clarity (descriptive error messages), and scope (only checking staged files, not the entire project).
-
Q: Why should hooks only check staged files? A: Checking all files is slow and may flag issues in files you haven't changed.
git diff --cached --name-onlygives you only the staged files, keeping the hook fast and relevant. -
Q: Should hooks be the same as CI checks? A: Hooks should catch the same types of issues (lint, format, tests) but at a lighter level — lint only staged files, not everything. CI is the definitive check; hooks are the fast first pass.