Standardizing Commits and Local Validations with Husky

How Husky hooks can bring commit standards, local checks, and repository governance closer to the developer workflow before code reaches CI.

Recently, I implemented a small improvement in our development workflow using Husky.

The motivation came from a few recurring problems in the day-to-day process:

  • inconsistent commit messages;
  • forgotten local validations;
  • tests being executed only after code reached the pipeline;
  • simple issues being discovered later than necessary;
  • repository rules depending too much on manual attention.

None of these problems are unusual.

In many teams, the workflow relies heavily on individual discipline. Each developer writes commit messages slightly differently, local checks may or may not be executed before pushing code, and some validations only appear later in CI.

That is not only a tooling problem. It is a workflow problem.

When an important step depends entirely on memory, it will eventually be forgotten.

Husky helps by moving part of those validations closer to the developer, before the code even leaves the local machine.

The Problem

Before adding local hooks, the process was mostly manual.

A developer could create a commit like this:

update stuff

Another one could use:

fix login

And someone else might follow a more structured format:

feat: add login validation

All of these commits may describe real work, but they do not create a consistent history.

This becomes more important when commit messages are used by tools, release notes, changelogs, semantic versioning, pipeline rules, or internal governance processes.

The same issue happens with local validations.

If linting, tests, type checks, or formatting depend only on someone remembering to run them, the team will eventually miss something.

The goal was not to make the workflow heavy.

The goal was to automate small guarantees that were already expected from the team.

Adding Commit Message Validation

One simple improvement is validating the commit message before Git accepts the commit.

With Husky, this can be done using a commit-msg hook:

#!/bin/sh

commit_msg_file=$1
commit_msg=$(cat "$commit_msg_file")

if ! echo "$commit_msg" | grep -Eq "^(feat|fix|docs|refactor|test|chore): .+"; then
  echo "Invalid commit message."
  echo "Use the format: type: description"
  echo "Example: feat: add form validation"
  exit 1
fi

This enforces a simple convention:

type: description

For example:

feat: add form validation
fix: handle empty search results
docs: update setup instructions
refactor: simplify user preferences hook
test: add coverage for billing flow
chore: update dependencies

The rule does not need to be complex to be useful.

Even a small standard makes the commit history easier to scan and easier to process later.

Running Local Checks Before Commit or Push

Husky can also run validations before a commit or push.

For example, a pre-commit hook can execute basic checks:

#!/bin/sh

npm run lint
npm run test

This prevents simple problems from reaching the remote repository.

Depending on the project, the hook could run:

  • linting;
  • formatting checks;
  • unit tests;
  • type checks;
  • generated file validation;
  • dependency checks;
  • custom scripts required by the team.

The best set of validations depends on the project.

For a fast codebase, running tests before every commit may be acceptable. For a larger one, it may be better to run lightweight checks on pre-commit and heavier validations on pre-push.

The important part is choosing checks that help the workflow without making developers fight the tool.

Why Local Hooks Help

CI/CD pipelines are still essential.

Local hooks should not replace CI. They should complement it.

The pipeline remains the source of truth because local hooks can be skipped, environments can differ, and final validation needs to happen in a controlled place.

But local hooks improve feedback time.

Instead of discovering a basic lint failure after opening a merge request, the developer gets the feedback immediately.

That creates a better flow:

write code
run local hook
fix simple issue early
push cleaner changes
let CI validate the complete process

This reduces noise in the pipeline and helps the team spend more time reviewing meaningful changes instead of chasing avoidable failures.

Supporting Repository Rules

Another useful case is aligning local development with repository rules.

Some GitLab setups allow restrictions or validations based on specific commit patterns, labels, or flags. In a company environment, that can be important for traceability, review processes, or governance.

For example, imagine a fictional rule that requires a commit to include a review marker:

#!/bin/sh

commit_msg_file=$1
commit_msg=$(cat "$commit_msg_file")

if ! echo "$commit_msg" | grep -q "\[Reviewed\]"; then
  echo "The commit must contain the [Reviewed] flag."
  exit 1
fi

A commit would then need to include:

feat: add report filters [Reviewed]

This is only an example, but the idea is useful.

If a company has internal rules around AI-assisted work, security reviews, ticket references, approval flows, or audit requirements, local hooks can help surface those rules earlier.

The repository and CI should still enforce the final policy, but Husky can make the expected format clear before the developer pushes code.

What This Improved

The main benefit was not only blocking invalid commits.

The real improvement was making the workflow more predictable.

After adding hooks, the team gets:

  • more consistent commit messages;
  • earlier feedback for simple mistakes;
  • fewer avoidable pipeline failures;
  • clearer expectations for contributors;
  • better alignment with repository policies;
  • less reliance on memory and manual discipline.

This is especially useful in teams with multiple developers, active CI/CD pipelines, and internal repository standards.

Small automation steps can remove a surprising amount of friction.

A Practical Approach

The implementation does not need to start big.

A good first version can be simple:

  1. Add a commit message convention.
  2. Validate it with a commit-msg hook.
  3. Run fast checks on pre-commit.
  4. Run heavier checks on pre-push when necessary.
  5. Keep CI as the final source of truth.

The most important decision is not which hook to add first.

The most important decision is identifying which mistakes the team keeps repeating and automating protection around them.

Final Thoughts

Husky does not solve every quality issue in a project.

It will not replace code review, CI/CD, testing strategy, or good engineering judgment.

But it is very effective at automating small guarantees that previously depended on manual attention.

With a few well-defined hooks, a team can improve commit quality, encourage local validation, reduce avoidable pipeline failures, and make the contribution workflow more consistent.

For teams working with commit standards, CI/CD pipelines, and internal repository rules, local automation is definitely worth considering as part of the development process.


Elmano Neto - Senior Software Engineer

Comments