Perfect looking code with Husky
I work in a team with about 200 developers pushing commits to a repository. We have linting set up on the repo and there are unit tests in place as well but what I have found is that it can be pretty challenging to enforce proper formatting.
One option is to add a formatting step to the CI/CD which will automatically check and format the code changes. However, this quickly adds overhead and becomes expensive. Not only does it now use a resource which most likely costs money but the formatting is also checked too late in the process. It needs to be checked before it even gets pushed upstream.
This leads us to option two. Format your code before you push. While this option sounds perfect, it will soon prove impossible to police 200 developers and ensure everyone follows this rule.
This is where option 3 comes in.
Git hooks
Git has a way of firing off custom scripts before or during an important action. This is called hooks. There are many use-cases for hooks and many different hooks, including client-side hooks and server-side hooks.
For the purpose of this article, I will be focusing on client-side hooks.
Below are some examples of operations that one can hook into:
pre-commit- These hooks run first. Before you even type in the commit message. They can be used to check the snapshot to be committed and perform some actions, e.g. formatting, linting, running tests, etc.pre-push- Runs during a git push, after remote refs have been updated but before any objects were pushed.post-merge- Runs after a successful merge command. I am sure one can do some cool things here as well.
The hook that I am interested in is the pre-commit hook.
This hooks allows us to run some code before the user even commits, e.g. check and format the file. One can also lint the file and run some unit tests but I do write about whether you should down below.
Hooks are generated by git the moment you run git init and by default, they live in the .git/hooks directory.
.git/hooks
├── applypatch-msg.sample
├── commit-msg.sample
├── fsmonitor-watchman.sample
├── post-update.sample
├── pre-applypatch.sample
├── pre-commit.sample
├── pre-merge-commit.sample
├── prepare-commit-msg.sample
├── pre-push.sample
├── pre-rebase.sample
├── pre-receive.sample
├── push-to-checkout.sample
├── sendemail-validate.sample
└── update.sample
Git also generates a bunch of sample hooks which can be activated by renaming the file to remove the .sample.
Now, if this as good as it sounds, then the word Husky wouldn’t be in the title of this blog.
The problem with git hooks
As you can see above, git hooks are amazing and can really bring that extra piece of sparkle to a repository. The only catch is…
They aren’t tracked by git.
This means that sharing and managing git hooks can be a pain for teams, as this StackOverflow thread shows.
If the team decided that they want set up formatting to run before committing, then they will have to find a way to share that hook between each other and then each team member would have to add that hook to their .git/hooks folder.
Not the best developer experience.
Luckily, mans best friend comes to the rescue.
Husky
First of all, huskies are adorable and I think we should take a moment to appreciate how cute this one is.

Back to business, Husky is a tool built to “enhance your commits and more 🐶 woof”.
I got that directly from their website.
In a nutshell, husky let’s you define your git hooks in a file that is tracked by git, making it easy to share and manage hooks across the team for the project.
While researching for this article, I came across another tool called pre-commit which seems to be doing something similar, however, I have not worked with this yet so could be cool to check it out.
Getting started is easy well. Below is a quick example of adding husky to your project.
bun add --dev husky
bunx husky init
echo "npm run format" > .husky/pre-commit
After this, before you commit, git will run npm run format in your project.
And with this, we can now format the codebase before even committing ensuring that each change that goes into the repository looks amazing.
Should thou art
Formatting the code before committing makes sense for me because I do not want to waste pipeline runs on formatting code. But should one also lint and test code using pre-commit hooks?
In my opinion, no. And the reason is simple.
Sometimes, the branch one is working on might not be in a pull request state but one needs to push upstream for various reasons, e.g. getting the code to a teammate or you are using Windows and you machine might restart anytime now and wipe your entire hard-drive.
Quick side note, I don’t know if Windows does that because I have been a MacOS/Linux user since I started programming but I know Windows users complain a lot about the forced updates or something like that so this is the exact scenario I always imagine.
Either way, whatever the reason, being blocked by a pre-commit hook for linting failures at this state is premature, much like checking for formatting errors at pull request stage is too late.
Conclusion
Git has a feature called hooks which live in .git/hooks and they allow you to tap into some git operations to perform some custom actions. One of these hooks is the pre-commit hook which is a perfect place to format our code before committing.
Git doesn’t track its .git/hooks folder so managing and sharing hooks within a team can be a pain, that is why tools like Husky exist which make it much easier.
Lastly, while one can also lint code and run unit tests in the pre-commit hook, I personally wouldn’t. Linting and tests should pass in a pull request pipeline run while formatting should happen before code is committed.
Thats all for now.
Cheers!
