Stop running tests on precommit hook

Katya Pavlenko
4 min readDec 14, 2020

--

If you’re debating with someone about whether tests should run on a pre-commit hook and you’re AGAINST this approach, I’m on your side, and this is the article you can send to your opponent!

Feel free to throw in other time-consuming processes along with the tests: project builds, static analysis runs, checking on your parens. We’re talking about hooks that get automatically added via tools like Husky, pre-commit, and similar packages, which can’t just be turned off. Prettier is our friend because it’s fast and never fails — well, unless you’ve done something truly un-compilable. Linters without --fix fall into a gray area.

You shouldn’t run big scripts on pre-commit because:

It’s frustrating to wait

Seriously, it’s just frustrating.

It slows down development

Lets imagine a developer who wants to commit atomically and keep writing code without breaking his flow. But no, here he is, sitting and waiting, doing breathing exercises (which, by the way, could be a great idea for your next open-source package — showing them instead of a progress bar). Sometimes tests unexpectedly fail (that’s what they’re for), and the commit doesn’t happen at all. If you’re working on multiple projects at once, you might forget that the commit didn’t go through, confidently push your changes, and spend the next few hours driving your teammates crazy with, “No, no, I did push, you just need to refresh your caches.” I’ve done it more than once.

It also clashes with a workflow where you like to commit small bits of non-functional code often and then squash them into one clean commit later.

Sometimes it’s just plain useless

Do we really need to run all the tests when you’ve changed one insignificant file that doesn’t even have tests written for it? No. But how do you figure that out? It’s tricky. So, you end up running more code just to check if you need to run the other code.

People will just use —no-verify

You can be as right as you want, but what’s the point if your developer skips it? Skip it once — no big deal, skip it twice — it’s already a habit.

So, what do we do now?

All checks — run them on a remote machine via CI!
article about github, article about gitlab, documentation on bitbucket

It’s reliable, guaranteed, asynchronous, transparent for the whole team, and best of all, it’s the robots who do all the suffering.

But…
it’s convenient for me!

To start with: if you don’t plan to work in the repository with others, then do whatever you like :)
But if you do, it’s important not to disrupt the way everyone else works. The main thing is to ensure that the code in the master branch is green, and that reviewing pull requests is easy. As for how someone codes, whether they squash commits or not, or how they name their commits in their local branch — it’s all a matter of personal preference. If something unique works for you, add it to your local hooks, but don’t force it on others.

How do I guarantee that everything works in my branch?

You need to figure out when a branch needs to be green. Every single commit? Before you push? When you merge into master?
If it’s for every single commit, then either you’re a perfectionist (see point 1), or there’s something strange going on with your development process — leave a comment, I’d love to hear more! Otherwise, you should use CI (or in extreme cases, other hooks)

How do I make sure all tests are green in the pull request?

Run the tests remotely on CI for every push. Whether it’s GitHub, GitLab, or anything else, you can even block merges if tests fail. You do have a CI system, right? Just link it to your repository. Even if you use Mercurial, you can set up a CRON task in Jenkins/TeamCity/whatever to send notifications when it’s done. In 2020, it’s hard to imagine not having any remote machine to delegate the dirty work to.

What if I don’t make a pull request but just merge a branch into master locally?

  • Run the tests on CI for every push to master too (even with conflict-free merges, something can break integration)
  • Since we’re talking about hooks — trigger them on a pre-merge-commit.

What if CI time is expensive and we’re running out of quota?

  • Run tests on a pre-push hook. But don’t forget that anyone can still use --no-verify, and dirty code could sneak into master.
  • Optimize your tests to take less time—yes, that’s possible too.
  • You can also calculate the cost of developer time in dollars, compare it to the cost of CI minutes, also don’t forget to multiply that by human mistake and the burnout caused by frustration.

--

--

Katya Pavlenko
Katya Pavlenko

Written by Katya Pavlenko

frontend developer, love instant noodles and super simple explanations of complex things Github: https://github.com/cakeinpanic