Pre-Commit Hooks
Automate code quality check with pre-comit hooks
A pre-commit hook is a script that runs automatically every time you execute git commit. If the script fails, the commit is aborted.
Why They Matter?
1. The “Broken Build” Nightmare
The Scenario: A developer finishes a feature late on Friday. They commit their code, push it to the main branch, and head home. Ten minutes later, the CI/CD pipeline fails because they forgot to update a single unit test. Now, the entire team is blocked from merging until Monday.
The Fix: A pre-commit hook that runs mvn test or gradle test locally.
- Result: The commit would have failed on the developer’s machine, forcing them to fix the test before the code ever left their laptop.
2. The “Format War”
The Scenario: Developer A uses tabs; Developer B uses spaces. Every time they touch the same file, Git flags dozens of “changes” that are actually just formatting updates. The Pull Request (PR) becomes a nightmare to review because the actual logic changes are buried under 200 lines of shifted indentation.
The Fix: A hook that runs a formatter like Checkstyle or Google Java Format.
- Result: All code is automatically standardized to the team’s style guide before it is committed.
3. The “Secret Leak”
The Scenario: While debugging a connection issue, a developer temporarily hardcodes an AWS Secret Key or a database password into a application.properties file. They forget to remove it and commit the file.
The Fix: A hook that scans for high-entropy strings or keywords like “PASSWORD” or “API_KEY”.
- Result: The hook catches the sensitive data and prevents the secret from being pushed to a remote server.
4. The “Empty” Commit Message
The Problem: A developer is in a rush and commits with the message ".", "fixed stuff", or "asdf". Later, when an incident occurs in production, the SRE team looks at the git history to find the root cause, but the history is a cryptic mess of non-descriptive labels.
The Fix: A commit-msg hook that runs a regex check against the message. It can enforce a format like [JIRA-1234] Short description.
- Result: The commit is rejected unless it includes a valid ticket number from your bug tracker (like Jira or GitHub Issues). This ensures every line of code in the repository is traceable to a specific requirement or bug report.
5. The “Expert Oversight” (Directory-Based Reviews)
The Problem: A junior developer makes a change to the /security or /billing module. These are highly sensitive areas of the Java application. Without a notification system, the security lead might not realize these files were touched until after the code is merged.
The Fix: While local git hooks can’t easily “assign” reviewers on a remote server like GitHub, a pre-commit script can scan the staged files. If files in /src/main/java/com/app/security/ are modified, the hook can:
- Append a mandatory tag to the commit message (e.g.,
cc: @security-team). - Trigger a local warning: “You are modifying a sensitive directory. Ensure you have consulted the Security Playbook.”
6. The “Vulnerable Dependency”
The Problem: A developer adds a new library to pom.xml to handle JSON parsing. Unknown to them, that specific version of the library has a known CVE (Common Vulnerabilities and Exposures) that allows remote code execution.
Get Kandaanusha’s stories in your inbox
Join Medium for free to get updates from this writer.
The Fix: A pre-commit hook that runs a lightweight Software Composition Analysis (SCA) tool like dependency-check.
- Result: The developer is notified immediately that the library they just added is unsafe. They can upgrade the version before the code even reaches the peer review stage.
7. Static Analysis (The “Pre-SonarQube” Check)
The Problem: A developer leaves an unused HashMap or a potential NullPointerException (NPE) in the code. They rely on SonarQube in the CI/CD pipeline to catch it. However, the CI pipeline takes 15 minutes to run. The developer switches tasks, only to find out 20 minutes later that they have to go back and fix a trivial NPE.
The Fix: Integrate a tool like SpotBugs or Error Prone (by Google) into the pre-commit phase.
How the Workflow Functions
When you initiate a commit, Git checks the .git/hooks directory for a script named pre-commit. If that script exits with anything other than a zero status, the commit fails.
1. A Simple Shell Script
If you were to manually create a hook in .git/hooks/pre-commit, it might look like this:
#!/bin/sh
# Run the Maven test suite
echo "Running pre-commit checks..."
mvn clean verify
# Check the exit code of the build
if [ $? -ne 0 ]; then
echo "Tests failed! Commit aborted."
exit 1
fiPro-Tip: Don’t make your hooks too slow. If running the full test suite takes 10 minutes, developers will start using
--no-verifyto skip the hooks. Aim to run only "Fast" tests or specific checkstyle tasks during the commit phase.
2. The “Pro” Way: The pre-commit Framework
While Git supports hooks natively in the .git/hooks directory, these are not version-controlled and thus not easily shared across a team. To make this maintainable across a team of 50+ developers, don’t ask them to copy-paste scripts into .git/hooks
The recommended approach is to use a package manager framework like pre-commit (language-agnostic, popular in Python and general use) or Husky (popular in the Node.js ecosystem)
It manages the hooks for you via a simple .pre-commit-config.yaml file.
A typical config looks like this:
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- repo: https://github.com/psf/black
rev: 23.10.1
hooks:
- id: blackNote: Hooks are local to your machine. They don’t automatically travel with the repository when someone clones it. You usually have to run pre-commit install once when setting up a new project to activate them.
How to get started:
- Install the framework:
pip install pre-commit - Create the file: Save the YAML above in your project root.
- Activate it: Run
pre-commit install. - Test it: Run
pre-commit run --all-filesto see how your current code holds up.
Java : Best Practices for Implementation
pre-commit framework: This is a multi-language, independent manager (written in Python but language-agnostic). It manages hooks for any language (Python, Java, Go, JS, Terraform) using a central .pre-commit-config.yaml file.
gradle-git-hooks: This is a Gradle-native plugin. It is designed specifically for the JVM ecosystem. It lives inside your build.gradle file and its primary job is to bridge the gap between Git and your existing Gradle tasks (like checkstyleMain or test).
For most enterprise Java applications, gradle-git-hooks(for Gradle) or git-build-hook(for maven) is the standard. This is because Java developers already have Gradle installed, and it ensures that the "rules" used by the hook are exactly the same as the "rules" used by the CI/CD pipeline and the IDE.
Monitor RunTime
To implement these effectively without annoying your team, follow the “10-Second Rule”: If your hooks take longer than 10 seconds to run, developers will hate them.
Tip: Use a tool like Lint-staged (even in Java projects) to ensure your hooks only run on the files you actually changed, rather than scanning the entire 1,000-file monolith every time you fix a typo.
Bypassing Hooks
Occasionally, a developer may need to bypass the hooks, which can be done using the --no-verify (or -n) flag with the git commit or git push commands. This should be used cautiously and with team agreement




