Are Pre-commit Hooks Evil?

Tools come in all shapes and sizes - some better, some worse, some loved, others hated - but they’re always created with a specific purpose in mind. Today, let’s talk about one such tool: is the pre-commit hook really as bad as some people say?

Czy precommit hook to zło? PAGES.POST.COVER_THUMBNAIL.BY_WHOM undefined

From this article you will learn:

  • What is a pre-commit hook?

  • How much does it cost to run a CI process?

  • What is the cost of delayed feedback?

  • Why is --no-verify problematic?

There’s a very trendy term that describes how pleasant (or not) it is for developers to work with certain tools and libraries. That term is developer experience - the higher it is, the better. And when it’s bad… well, you get the idea. It’s important to note that satisfaction with a given tool is highly subjective. One developer might swear by Visual Studio Code and claim there’s nothing better, while another might insist that VS Code doesn’t even come close to JetBrains’ tools.

The question I want to raise today indirectly touches on developer experience. Are tools like pre-commit hooks - designed to improve code quality - actually harmful? I’m jumping into this topic because I know opinions are sharply divided. It’s a debate that polarizes the community almost as much as the infamous tabs vs. spaces argument. So let’s dig a bit deeper.

What Is a Pre-commit Hook?

Git - our trusty version control system - has one primary purpose: helping us maintain high code quality by managing iterations of our software. In my view, Git and quality are two words that naturally go hand in hand. Of course, Git is just a tool, and how we use it matters. Still, it’s only natural to treat Git as part of a quality-focused development environment.

So it’s no surprise that Git provides extra mechanisms meant to support that goal. A pre-commit hook is just a script that runs right before a commit is created. In other words, the git commit command will trigger it - if it’s been defined. Hooks have been part of Git from the very beginning. A pre-commit hook can perform any operation you want on your code: cleaning, formatting, testing - you name it. Sounds great, and in a perfect world, it probably would be.

What About CI/CD?

If we look at the types of tasks a pre-commit hook might handle - formatting, linting, testing - we’ll notice they’re often the same tasks that happen in CI/CD pipelines. So the question becomes: can we swap one mechanism for the other? Is it OK to replace one with the other?

CI/CD pipelines are meant to safely deliver applications to specific environments, prevent broken builds from being deployed, and catch issues before they go live. I’ve written about this before, in my post on the role of CI/CD in the development process.

Replacing CI/CD with pre-commit hooks is a bad idea. First and foremost, because the local environment where the pre-commit runs might differ significantly from production. CI/CD tools have built-in safety measures - for example, preventing skipped steps - which is crucial when pushing code to production.

Time, Time, Time

You’ll find plenty of criticism of pre-commit hooks online. But let’s start with the positives. One of the biggest advantages is early error detection - during development, even before you make a commit. Tools like linters, formatters, and tests can surface issues right away. Catching these early can save time and potentially eliminate the need for an extra commit with just stylistic fixes.

To me, that makes a lot of sense. Picture this: you’ve finished your work, made a commit, pushed your branch, and opened a pull request. Now your team starts reviewing the code, and the CI/CD pipeline kicks in.

A proper code review shouldn’t waste time on missing semicolons, stray spaces, or typos. It should focus on the implementation, logic, and performance. According to many sources, when our brains encounter simple errors, they get fixated on them - and overlook deeper problems. By eliminating small, stylistic issues before opening a PR, we give reviewers a better chance to focus on what matters.

Even if your team is mature enough to ignore those minor distractions and approves the PR, the CI might still fail on something silly - like that missing semicolon. Then someone has to re-review the PR after you push a fix. If we’re talking about wasting time, isn’t pushing a style-only fix after a PR’s been approved just as bad? Wouldn’t it be easier to catch that locally?

Resources

Another common argument is that pre-commit hooks eat up local resources and slow developers down - and since CI can do the job anyway, why bother? Fair enough, but let’s look at the actual costs.

In Poland, a mid-range hourly rate for developers is around 140 PLN net (based on B2B contracts, ranging from 100 to 180 PLN/hour). That’s about 22,400 PLN (or $5,600 USD) per month for a full-time developer.

Let’s say your typical project has five devs, each pushing four branches per week. That’s around 80 pull requests per month - and 80 CI pipeline runs. Assuming a single pipeline run takes five minutes, you’d be paying about $4/month for GitLab CI or AWS CodeBuild (excluding free tiers). Azure DevOps? Up to $40/month.

To be fair, AWS and GitLab both offer free minutes - AWS gives you 100 minutes, though that likely won’t cover your team’s needs. Azure is pricier: $40/month if you’re not using a self-hosted agent.

The Cost of Delayed Feedback

Now, let’s consider how this affects developer time. Let’s assume that 50% of those pipelines catch issues that could’ve been caught earlier by pre-commit hooks. The feedback from a pipeline usually takes 5–10 minutes to reach the developer. Then they have to switch context, fix the issue, and push again. In the best-case scenario (no queueing), that might mean 15 extra minutes per error.

So we’re looking at 8 errors × 15 minutes = 120 minutes lost per dev, per month. That’s about 280 PLN (or $70 USD). Multiply by five devs, and you’re losing roughly $350/month — just because the feedback loop is too slow. Meanwhile, a simple local hook checking stylistic issues takes around three seconds. Three seconds that could save hundreds of dollars.

My Code, My Rules?

Another argument is about freedom and trust. Developers shouldn’t be forced to work a certain way. “True” seniors (sic!) will walk out the door the moment they’re forced to use pre-commit hooks. Honestly, that’s a bit much. Getting offended because someone asks you to use a tool that actually improves productivity and saves money? Sometimes it feels like developers take this sense of specialness too far. Sure, we have knowledge and experience - but so do doctors, taxi drivers, and personal trainers. Let’s not get too full of ourselves. We’re not better - just different.

--no-verify

A common counterargument is that pre-commit hooks can be skipped with the --no-verify flag. You just add it to your command, e.g., git commit -m "initial commit" --no-verify. Yes, that’s true. People can skip the checks. And sometimes, that’s valid - maybe there’s a bug that only shows up in your local environment. But deliberately bypassing agreed-upon rules? That’s plain irresponsible.

Redundancy

Running the same checks both locally and in CI can sometimes be a waste. Duplication for duplication’s sake isn’t efficient. I do agree with critics here - some tasks should be done only once, ideally in the CI. Reducing redundant steps can save time and resources. But duplication isn’t always bad. Sometimes, the same step serves different purposes in different contexts.

So, What Should We Do?

Scrapping CI and relying only on pre-commit hooks is a terrible idea. Sure, you get fast feedback - but you lose control over what ends up in your repository. Someone might’ve skipped the hooks entirely.

What if we did the opposite - ditch the hooks and run everything in CI? Sounds tempting at first. But then your cloud bills go up, and devs start ignoring coding standards. That’s a recipe for chaos - and not something every organization can accept, even if it means simpler local environments.

The best approach, in my opinion, is a hybrid one. Fast local feedback with pre-commit hooks, and more thorough checks (tests, builds) on CI. Sure, there’s still a risk of version mismatch - say, your linter config differs between local and CI - but overall, this gives you the best of both worlds. No drama, just thoughtful trade-offs.

Summary

Pre-commit hooks are great for fast, lightweight tasks like linting and formatting. CI is a better fit for integration tests, security scans, builds, and deployments. Each tool has its strengths and weaknesses. Knowing and understanding them helps you squeeze the most value out of both - for your team, your project, and your sanity.

Share this article:

Comments (0)

    No one has posted anything yet, but that means.... you may be the first.

You may be interested in

lorem

Monorepo vs Polirepo by Mateusz Jabłoński
Podcast
01 March 2023

Monorepo vs Polirepo

Programowanie to nie tylko podejmowanie decyzji, kiedy jaką funkcję napisać czy jak nazwać zmienną. Programowanie to proces, który trzeba zainicjować podejmując różne decyzje, które będą na niego rzutowały w następnych etapach. Jedna z nich to odpowiedź na pytanie: Monorepo czy Polirepo?

Posłuchaj
Delivery by cybrain
Article
05 September 2019

CI oraz CD w procesie deweloperskim

Automatyzacja procesów jest jednym z najistotniejszych elementów w procesie deweloperskim. Szczególny wpływ automatyzacji możemy zobaczyć, gdy rozmawiamy o jakości.

Read more

Zapisz się do newslettera

Bądź na bieżąco z nowymi materiałami, ćwiczeniami i ciekawostkami ze świata IT. Dołącz do mnie.