Warming up the neural circuits...
By the end of this chapter you will:
CI catches many problems, but catching them at commit time is cheaper and faster.
Pre-commit hooks turn code quality checks into a local default behavior rather than optional discipline.
Mental model: commit-time automation is the first quality gate; CI is the second gate, not the first.
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.6.9
hooks:
- id: ruff
- id: ruff-formatThe .pre-commit-config.yaml file is your quality policy manifest.
pre-commit install
pre-commit run --all-filesHooks can run on commit, push, or manual all-file checks depending on configuration and workflow needs.
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: end-of-file-fixer
- id: trailing-whitespace
- id: check-merge-conflict
- id: check-yamlPair generic hygiene hooks with language-specific hooks like Ruff and mypy.
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.11.2
hooks:
- id: mypy
additional_dependencies: ["types-requests"]Keep mypy scope pragmatic early; widen coverage as annotations mature.
- repo: https://github.com/Yelp/detect-secrets
rev: v1.5.0
hooks:
- id: detect-secretsSecret scanning at commit time significantly reduces leak incidents and emergency key rotations.
Local (fast):
- ruff check
- ruff format
- basic hygiene hooks
CI (thorough):
- full mypy
- full pytest
- security/dependency scansKeep local hooks fast enough that developers do not bypass them.
Rollout pattern:
1) Baseline autofix PR
2) Enable minimal hook set
3) Document common failures and fixes
4) Add CI check for hook consistency
5) Tighten over timeChange management matters as much as tool choice in successful adoption.
If hooks are slow or noisy, teams will bypass them. Optimize for signal and speed first.
| Hook type | Local default | CI default | Reason |
|---|---|---|---|
| Formatting/lint | yes | yes | consistent style and bug-risk checks |
| Basic file hygiene | yes | yes | prevent trivial noisy failures |
| Type checks | scoped | full | keep local speed, preserve CI rigor |
| Full test suite | no | yes | expensive checks belong in CI gate |
| Security scans | optional | yes | stronger guarantees before merge |
| Mistake | Why it hurts | Better move |
|---|---|---|
pre-commit: command not found | Tool not installed in environment | Install with pipx/pip and re-run setup |
| Hooks run for minutes on every commit | Developer workflow friction | Reduce local scope and move heavy checks to CI |
Failed to load config YAML errors | Broken .pre-commit-config.yaml structure | Validate YAML and pin hook revisions |
| Secret leaks still happen | Secret hook missing or misconfigured | Add secret scanner hooks with baseline configuration |
| Different hook behavior per machine | Unpinned revisions or unstaged config changes | Pin rev versions and enforce CI consistency checks |
Python repo rootHooks execute successfully on all filesFunction with mismatched return annotationCommit blocked by mypy failureTest file with dummy credential patternHook rejection with secret warningExisting quality toolchainTwo-tier policy with clear command matrixPinned hook revisionsControlled version bump planBeginner:
"What is pre-commit and why is it useful?"
It runs automated checks before commits so many quality issues are prevented earlier than CI.
"Why should hook revisions be pinned?"
Pinned revisions ensure reproducible behavior across machines and time.
Senior:
"How would you enforce pre-commit in a team where many developers skip local setup?"
Keep local adoption easy but make CI enforce the same hook policy so merge gates remain consistent.
"How do you balance strictness and developer velocity in commit-time checks?"
Optimize local hooks for speed/high signal, push heavier checks to CI, and tighten policy incrementally.
Need early quality checks -> pre-commit hooks
Need reproducible behavior -> pin hook revisions
Need safe repos -> add secret scanning hooks
Need fast developer loop -> local-fast, CI-thorough split
Need sustainable adoption -> phased rollout and docsWhat is the biggest value of pre-commit hooks in day-to-day development?