← all articles
// article

Pre-Commit Hooks That Don't Annoy Your Team

2026-01-15

Why Most Pre-Commit Hooks Feel Like a Toothache?

Pre-commit hooks that don't annoy your team are fast, selective, and respect developer flow. They should swiftly catch obvious, critical errors on *staged changes* before they even hit a shared branch, rather than becoming a slow, all-encompassing gatekeeper that nitpicks minor style differences or runs entire test suites that belong in Continuous Integration (CI).

For the uninitiated, a Git pre-commit hook is essentially a script that runs automatically on your local machine just before you finalize a git commit. Its purpose is noble: to ensure code quality, consistency, and prevent obvious blunders from ever polluting your shared repository. The problem? Often, these noble intentions pave the road to developer frustration, costing more in lost time and morale than they save.

What's the Point of a Git Pre-Commit Hook, Anyway?

Let's be clear: the fundamental idea behind pre-commit hooks is sound. Catching errors as early as possible is always cheaper. Imagine finding a syntax error in your JavaScript or a missing Python import right before you commit, versus discovering it when your CI pipeline fails, or worse, when a bug surfaces in production. The cost difference is staggering.

Think of it like a quick pat-down at the airport security vs. a full body scan. The pat-down is quick and catches obvious threats; the full scan takes longer and is for deeper analysis. Pre-commit is the pat-down.

The Cardinal Sins of Annoying Pre-Commit Hooks

If your team groans every time they type git commit, chances are your pre-commit setup is committing one or more of these cardinal sins:

1. They're Abysmally Slow

This is arguably the biggest offender. If a pre-commit hook takes more than a few seconds, it actively disrupts developer flow. Running an entire test suite, compiling complex assets, or performing exhaustive static analysis on every commit is a recipe for disaster. Developers will either disable them or learn to despise them, making the entire exercise pointless.

2. They're Too Noisy and Broad

Running linters or formatters on *every file in the entire repository* instead of just the *staged changes* is another common misstep. You've just fixed a single line in an old file, and suddenly the hook complains about formatting across 30 unrelated files. It's distracting, irrelevant, and often requires an unwanted cleanup of untouched code.

3. They're Too Strict and Non-Correcting

Enforcing subjective style rules (e.g., single vs. double quotes, maximum line length) without offering automatic fixes is pure friction. Developers spend precious time manually adjusting formatting that a machine could handle instantly. If a tool can fix it, it should fix it.

4. They're Inconsistent Across Environments

A hook that works on one developer's machine but not another's due to differing tool versions or environmental setups creates confusion and resentment. The setup needs to be easily installable and consistently runnable for everyone.

5. Poor or Vague Error Messages

When a hook fails, it needs to tell you *exactly* what went wrong and *where*. A cryptic error message or a stack trace that doesn't point to the problem just wastes time and leads to frustration.

The SISL.PL Approach: Crafting Developer-Friendly Guards

At SISL, we've learned through experience what makes a pre-commit setup a valuable ally rather than a burdensome chore. Our philosophy centers on speed, automation, and practical impact. Here’s how we approach it:

1. Speed is Non-Negotiable

A good pre-commit hook must execute in seconds, not minutes. This means:

2. Automate Fixes, Don't Just Flag

If a linter can automatically fix a style issue, it absolutely should. This is where tools like Prettier (JS/TS/CSS), ESLint --fix (JS/TS), Black (Python), isort (Python), php-cs-fixer (PHP) shine. When these tools run as pre-commit hooks, they format your staged files, re-stage the changes, and then allow the commit to proceed – all without you lifting a finger. It's magic, and it eliminates entire classes of bikeshedding discussions.

3. Focus on Impact, Not Perfection

Pre-commit hooks should catch critical issues that would genuinely break a build or introduce a major bug. They are not the place for every single stylistic nuance or complex business logic validation. For example:

As a boutique studio, SISL often sees teams get bogged down trying to make pre-commit hooks do too much. A lean, focused approach is always better.

4. Clear Feedback & Easy Bypassing (When Necessary)

When a hook fails, the message should be immediate and actionable. Point directly to the file and line number. Developers should also know that git commit --no-verify exists as an escape hatch for emergencies, though its use should be rare and perhaps trigger an internal review process.

5. Keep It Simple, Stupid (KISS)

Don't over-engineer. Start with a minimal set of highly effective hooks and expand only if a genuine, recurring problem emerges. For a typical Python project, a setup with pre-commit running black, isort, and ruff (a fast Python linter/formatter) on staged files is often more than enough. For JavaScript/TypeScript, Husky or Lefthook with eslint --fix and prettier --write covers most bases.

A Sample Setup for a Typical Project

Python Project Example (using pre-commit.com)

Your .pre-commit-config.yaml might look like this:

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
      - id: check-added-large-files
  - repo: https://github.com/psf/black
    rev: 24.3.0
    hooks:
      - id: black
  - repo: https://github.com/PyCQA/isort
    rev: 5.13.2
    hooks:
      - id: isort
        name: isort (python)
  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.3.5
    hooks:
      - id: ruff
        args: [ --fix ]
      - id: ruff-format

JavaScript/TypeScript Project Example (using Husky)

In your package.json:

"husky": {
  "hooks": {
    "pre-commit": "lint-staged"
  }
},
"lint-staged": {
  "*.{js,jsx,ts,tsx}": [
    "eslint --fix",
    "prettier --write",
    "git add"
  ],
  "*.{json,css,md}": [
    "prettier --write",
    "git add"
  ]
}

Beyond Pre-Commit: Where Do Other Checks Belong?

Understanding the limits of pre-commit hooks is as important as understanding their power. Here’s a quick guide:

The distinction is vital for developer morale and productivity. A developer shouldn't have to wait minutes for a pre-commit hook to pass before they can share their small, focused change. That's what your CI pipeline is for – the ultimate quality gate before deployment.

Conclusion

Smartly implemented pre-commit hooks are an investment in your team's sanity and your project's long-term health, not a tax on productivity. By focusing on speed, automation, and impact, you can transform them from annoying blockers into silent, efficient guardians of code quality. They reduce friction, maintain consistency, and free up your team to focus on what they do best: building great software.

If setting up and fine-tuning these developer safeguards feels like another chore you'd rather delegate, remember that expertise in this area is just a conversation away. You can always get in touch with us; we're quite good at making development smoother.

Got a similar problem?

Boutique web development studio from Poland — sites, WooCommerce / Magento stores, custom web apps and landings. See what we shipped.

See SISL portfolio →

Free technical audit of your site — in 24h

Core Web Vitals measured on real users, indexability, structured data, meta and internal linking. A written report with prioritised fixes, not a PDF from a generic tool. No cost, no call required.

Get the free audit →