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:

  1. Pre-commit: Fast checks (formatting, basic linting). Fails fast on developer machine.
  2. 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.

Resources Link to heading