I was tired of catching trivial issues in PR reviews - trailing whitespace, missing newlines, formatting inconsistencies. Pre-commit hooks solved this by running checks before code even gets committed.
Installation Link to heading
Install it:
uv tool install pre-commit
My actual pre-commit config Link to heading
Here’s a solid starting configuration that balances thoroughness with speed:
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
args: ['--unsafe'] # Allow custom YAML tags
- id: check-added-large-files
args: ['--maxkb=500']
- id: check-merge-conflict
- id: detect-private-key
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.8.0
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
# I don't run mypy in pre-commit anymore - it's too slow
# Better to run it in CI where you can cache dependencies
Install the hooks:
pre-commit install
Running hooks Link to heading
Run on all files (useful after updating hook versions):
pre-commit run --all-files
Run on commits since branching from main:
pre-commit run --from-ref origin/main --to-ref HEAD
Hooks that slow things down Link to heading
I’ve learned the hard way which hooks kill productivity:
- mypy: Type checking can take 10-30 seconds on larger codebases. Run it in CI instead where you can cache the mypy cache properly.
- pylint: Similar story - comprehensive but slow. Ruff catches most of what you need instantly.
- anything that downloads dependencies: Some hooks try to install packages. Skip them.
My rule: if a hook takes more than 2 seconds, it probably shouldn’t be in pre-commit. Developer friction adds up fast.
Should hooks run in CI too? Link to heading
Yes, absolutely. Here’s my philosophy:
- Pre-commit: Fast checks (formatting, basic linting). Fails fast on developer machine.
- CI: Everything (including slow checks like mypy, test coverage). The source of truth.
The key insight: pre-commit can be skipped with git commit -n. CI cannot. Never rely on pre-commit as your only validation.
I run this in CI:
- name: Run pre-commit
run: |
uv tool install pre-commit
pre-commit run --all-files
This catches cases where someone skipped hooks locally.
Bypassing hooks Link to heading
Sometimes you need to skip hooks for a quick commit:
git commit -n -m "WIP"
Or skip specific ones:
SKIP=mypy git commit -m "Skip type checking"
Using --no-verify is fine for WIP commits on feature branches. Just make sure everything’s clean before merging.