Jab, jab, jab, lefthook.yml
Look, I get it, you're a consummate professional. You just joined a team that's sponsored by Nike but your bread is buttered by Adidas. Thing is, it's a team sport: on this team when you're out there on the field/pitch/court, please wear the Nikes. That's just how it goes. Best we can do? Commit to our team Nikes but tape over the swooshes. Just don't tell 'em I said so, deal? 🤝
It's no different on a tech team. Sure, you're as flawless as flawless gets with the code; a demigod on the command-line; a virtuoso with the vim motions. You don't make mistakes! You don't understand why you have to spend a couple moments letting some git hooks run on local before you can push your — obviously bug-free — code up for the PR.
Isn't it gonna run in CI anyway?!
By the time you're brushing up against git hooks, I promise you some pains have been felt upstream. Unless of course someone in the org just read a book over the weekend and is now implementing every possible Agile philosophy wholesale. Ouch! In addition, you likely have non-dev collaborators and this is one part of a larger picture.
The work needed to alleviate these pains will get parsed, decomposed, and divvied up but often it comes down to a better testing and deployment posture. Better coverage, catching bugs early... Are our bundles growing? When they do, is it justified? Is that partly (wholly?) causing some of these frontend/client issues we're seeing? Can we catalog these somehow? Maybe visualize our coverage and gradually bump that up? Do we have monitoring, telemetry in place? etc etc. Test early, test often, get in a better posture. Can our non-dev bredren see these somewhere, idk, like on a dashboard or something and can we not spend months on a veritably new product just to stand this up aka can we do this with just the tools we have now?
This, on a granular scale on the left-er end of things implementation-wise sometimes means, believe or not, git hooks!
Before any new changesets merge into main, let's just make sure we're not wasting cycles re-linting and formatting code and correcting tests that should have otherwise passed on local first. It's not like we want to sacrifice velocity for this either. We want to make sure we're fixing the real issues not painting bikesheds.
Let's walk it back a bit... typically the primary gripe I've come across is that implementing git hooks interferes with individual engineers' way of working; takes them out of flow. In any case, why should they have to do this on EVERY. SINGLE. COMMIT! It's torture. Incidentally they're usually talking about the pre-commit hook but that's just one of the hooks that a typical js codebase can benefit from. But that's neither here nor there.
If you're on a work machine, lets analyze things a bit:
In this day of remote work, you were probably issued a laptop which, if you're lucky, is new and beefy. Let's just say you're on the latest and greatest Timmy Apple MBP. That's a decision made for the team. You're probably cool with that. Got all the stuff installed. If you're frontend, they probably suggested a specific Node version just so everyone's on the same page. Let's say the codebase targets Node 20+ so err'body is running 20+ on their MBPs. No sweat there.
How are you managing Node versions, btw? n? nvm? asdf? volta? Let's say the team on which you find yourself suggests NVM just coz, well, the decision was made before you got here. Maybe there's a writeup in the repo's Github wiki or in Confluence or on the Eng team's Notion. You can read up on the justification there later. So NVM it is. Homebrew, almost forgot. You'll need that too for managing the other macOS deps.
The team's soft suggestion for text editor (and de facto industry standard) is VS Code. Along with that, there's an arguably optional list of VS Code extensions to help get you set up like the rest of the team. We've got the Tailwind CSS Intellisense extension for obvious reasons. Headwind to make sure those Tailwind classes are uniformly sorted (you don't want a bunch of unrelated changes in every PR just coz everyone is writing the utility classes differently). We've got the Github Actions extension so you can keep an eye on the workflow runs locally; catch fails, trigger reruns faster.
Maybe the whole setup is even containerized. Docker? Dev containers? You know what, while you're at it, go ahead an install the Github CLI, you'll be able to create repos, PRs, comments and whatnot all from the command-line quick-fast. But you probably already knew about all that.
So, as we can see, it's a pretty specific set up. It's not draconian by any means it's just meant to get everyone on the same page, maybe even prevent bike shedding - that nearly unavoidable inclination in our space.
All the setup and installation is behind you now. You're all set to make your first commit so you can get those Slack emoji-based props from the new team. I just made my first commit đź’Ş! Shipping code on day one. That's just how you roll!
So you make your changes, and hit the console to type
$ git commit -am "my first commit"
Text flies up your terminal... some tasks are running... Your staged files are linted... formatted. You're typically a tabs guy but here it's a 2 spaces shop. That's fixed up for you right quick, don't even worry about it (you should probably set that up on save, though - you probably didn't see the nag in VS Code from the .vscode/extensions.json
recommendations, huh?).
Then the test suite runs... thankfully it passes.... you didn't violate the bundle budget... and you're within the coverage threshold, whatever that means...
then everything is calm again... but the cursor's blinking... you're being asked to do, what exactly?
$ lint-staged && cz
âś” Preparing lint-staged...
âś” Running tasks for staged files...
âś” Applying modifications from tasks...
âś” Cleaning up temporary files...
cz-cli@4.3.0, cz-conventional-changelog@3.3.0
? Select the type of change that you're committing: (Use arrow keys)
❯ feat: A new feature
fix: A bug fix
docs: Documentation only changes
style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
refactor: A code change that neither fixes a bug nor adds a feature
perf: A code change that improves performance
test: Adding missing tests or correcting existing tests
(Move up and down to reveal more choices)
What is this?! Now you have to think a bit more about what changes you just made? Some sort of quiz? Is your changeset a feature or a fix or a refactor or what?! Why is this even necessary?
Is this where you draw the "no more mr team player guy" line? Is this where you say, You know what, I can abide all your other team choices on stack and tooling but this is... you've gone too far, here!
First of all, git hooks, commit linting... all these are soft suggestions with escape hatches! This is where the just tape over the swooshes provision kicks in!
If you're hot patching straight to prod or if you find that all these are genuine impediments to your progress, by all means just --no-verify and keep it pushing!
The tech industry, if it's anything, is at least these two things:
I know nothing about fencing, the Olympics just ended and épée is a cool word.
The three main hooks, pre-commit
, prepare-commit-msg
, and commit-msg
should be sufficient for most of the things a typical JavaScript codebase might need.
The pre-commit hook is run first, before you even type in a commit message. It’s used to inspect the snapshot that’s about to be committed, to see if you’ve forgotten something, to make sure tests run, or to examine whatever you need to inspect in the code. Exiting non-zero from this hook aborts the commit, although you can bypass it with git commit --no-verify 👀. You can do things like check for code style (run lint or something equivalent), check for trailing whitespace (the default hook does exactly this), or check for appropriate documentation on new methods.
The prepare-commit-msg hook is run before the commit message editor is fired up but after the default message is created. It lets you edit the default message before the commit author sees it. This hook takes a few parameters: the path to the file that holds the commit message so far, the type of commit, and the commit SHA-1 if this is an amended commit. This hook generally isn’t useful for normal commits; rather, it’s good for commits where the default message is auto-generated, such as templated commit messages, merge commits, squashed commits, and amended commits. You may use it in conjunction with a commit template to programmatically insert information.
The commit-msg hook takes one parameter, which again is the path to a temporary file that contains the commit message written by the developer. If this script exits non-zero, Git aborts the commit process, so you can use it to validate your project state or commit message before allowing a commit to go through.
You seldom, if ever, need to worry about the actual technical implementation of these hooks at the lower levels. Just use ready-made tools to simplify the work i.e. lefthook, commitlint, commitizen and friends.
What you'll want to do with these hooks in a js codebase is at least these 3 things:
Firstly, it's a soft suggestion.
Secondly, its a spec. A spec built on top of git trailers but the particular formatting that's most prevalent is based on Angular's Commit Message Guideline.
Thirdly, used independently, it mostly just does a good job of keeping your commits sane and organized but further upstream, in a collaborative environment, the benefits are numerous.
In open source projects, for instance, it's invaluable! It makes it so much easier to read through changelogs and PRs that added specific features, fixed bugs, etc. if the commit messages follow the conventional commits format.
It all starts with what feels like unnecessary inconvenience only later to yield great benefits. It also allows you to get into the habit of writing well — tersely, descriptively — keeping your changesets reasonably grouped.
A tangential benefit... you know how sometimes appending or prepending your branch name with your kanban ticket number somehow connects up your source control (branches and PRs) and your agile board (tickets and sprints)? Following the spec enables tools that also rely on this same spec to hook into your commits and do... whatever those tools need to do. Some people prefer dashboards and reports to get a sense of e.g. large collaborative projects. Tools that can ingest your team's git commits, reference changelogs, connect those to tickets etc and spit out useful dashboards and reports are, well, useful. A lil sportsmanship, no?
Admittedly all this can be or feel like premature process optimization so... evaluate the needs of your project; use your best judgment.
Yes, yes it is! Especially for a browser extension. A browser extension is just your humble HTML, CSS and JS and a lil json.
But I meeaaan, are you really going to hand write that HTML in the traditional sense? Are you going to write, save, refresh your window then write, save, and refresh your window ad infinitum or would you rather take advantage of something like Vite so you can have some HMR?
Let's even say you're a solo dev. You've wisely chosen Vite for the build process which means you've got a couple dependencies now. Thing is, this is not your main job or even main side project! Or maybe it's one of your many client projects. Do you want to come back to the project after some time and try figure out whether deps are deprecated or would you rather push your code up to a Github repo and let good ol Dependabot keep an eye on things; catch and squash vulnerabilities for you?
You're now well on your way to productionizing your codebase™® since you're source-controlling your code. You're writing git commit messages now. Why not standardize those? When you come back to the project you'll know exactly what you did and why and when.
This is a Bedframe project, so throw in automated changelog generation while you're at it, its easy, and now the repo changes are clearer; you have bonafide versioned releases with useful release notes now.
You know what, don't do all this manually, let the changelog and versioning be automated so you don't have to do too much. Now you're publishing automatically too.
Boom! That's it. That's the whole story.
So yes, the git hooks part of this equation can feel like overkill but really its not. Even for the solo dev/ small projects. Maybe it's the having to write tests for everything and having the git hooks run (like a quiz or literal test on your work) that feels like overhead. Probably. Depends on the size and scope of your project. Use your best judgment there.
As far as Bedframe goes, one of the primary goals is to simplify and automate as much as possible on local all the way up to web store! Git hooks are an integral part of this and are set up for you when you run the project generator: npm create bedframe
.
So, you know, be decisive. Don't just commit, pre-commit!
Nb: