#
Pre-Commit or CI/CD
At work, we're transitioning from a proprietary SDLC to a more open-source process. This shift has prompted questions that have made me re-evaluate aspects of the development process I had long taken for granted.
One recurring question is whether linting tools should run as part of the continuous integration/continuous deployment (CI/CD) pipeline, as pre-commit hooks, or both. Let's explore the pros and cons.
#
Pre-Commit Hooks
Why push code that won't pass the linter? With CI/CD, developers get feedback only after they've pushed their changes. Pre-commit hooks provide feedback to the developer as soon as possible.
#
CI/CD
Pre-commit hooks must be installed in every development environment for every new joiner. The main benefit of running linters as part of the CI/CD pipeline is uniform enforcement. A linter in the CI/CD pipeline cannot be forgotten.
#
Pre-Commit Hooks Would Be Great, If Everyone Used Them
In pre-commit utopia, everyone runs pre-commit hooks, making CI/CD linting redundant. However, if even one contributor doesn't use pre-commit hooks, the hooks cause issues for everyone else. They can fail on unrelated changes, or include unrelated formatting changes in the same commit.
This reality becomes a self-fulfilling cycle: pre-commit hooks aren't ubiquitous, so they cause pain, and we avoid using them. But they'll never be ubiquitous because skipping them is so easy; by forgetting to install, disabling them, or bypassing with git commit --no-verify
.
#
Speed & Time
Pre-commit hooks run synchronously on the developer's machine while the developer waits for feedback.
Pre-commit hooks run frequently. I create small frequent commits, using tools like git commit --fixup
and git absorb. Even fixing a commit message with git commit --amend
will run hooks.
Pre-commit hooks must be blazingly fast.
How long before developers start skipping? Not very long. If git commit
is taking over 1 second, I'll Ctrl+C it and add --no-verify
. After skipping hooks a few times, I disable them. I've never worked in a project where I kept them on for long, and it would be more accurate to say "I don't use pre-commit hooks."
#
Conclusion
When the pre-commit framework was announced in 2021, the top comments were now-familiar rebuttals.
Whatever test runs during pre-commit must also run during normal CI/CD run... It must run during normal CI/CD because pre-commit hooks can be skipped. So now I have two different black calls: in the pre-commit hook and in the CI/CD. And they must be of the same version. Ad infinitum for all other tests. This is the reason I don't use pre-commit framework. It leads to "double accounting".
and:
The CI check is the important one: It's what ensures that bad formatting doesn't make it into master. The pre-commit is just a convenience for the developer. It gives faster feedback - immediately when trying to commit, instead of a couple of minutes later. And if a developer on my team wants to disable it, it doesn't affect others.
Fans of pre-commit hooks describe a framework that's new but growing into an industry standard.
I think I'm writing to dispel that notion. pre-commit.com is new, but pre-commit hooks are not. Pre-commit suffer from the tragedy of the commons; they will never become ubiquitous, and you shouldn't feel guilty about turning them off.