Standardizing Code Quality Across Teams with Pre-Commit CI
Every CI pipeline that’s failing because of a formatting issue is waste. A linter check that could have run in 3 seconds locally is instead burning cloud minutes on a runner. Pre-commit hooks fix this. However, they only work if they’re configured correctly and run in CI as a safety net.
The Pre-Commit Pipeline
%%{ init: { 'look': 'handDrawn' } }%%
graph TD
A[git commit] --> B[Pre-commit hooks run locally]
B --> C{All hooks pass?}
C -->|No| D[Commit rejected<br/>dev fixes locally]
C -->|Yes| E[Commit lands on branch]
E --> F[CI pipeline triggered]
F --> G[Pre-commit CI also runs<br/>as backup safety net]
G --> H[PR ready for review]
The local hook catches trivial issues immediately. Pre-commit CI (the cloud service) runs on every PR as a backup — it catches issues devs forgot to set up locally.
Setting Up pre-commit
# .pre-commit-config.yaml
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
- id: check-added-large-files
- repo: https://github.com/psf/black
rev: 24.1.1
hooks:
- id: black
- repo: https://github.com/pyupio/safety
rev: 3.0.1
hooks:
- id: safety
args: ["--file=requirements.txt"]
Install with:
pip install pre-commit
pre-commit install
Now every git commit runs the hooks locally first.
Terraform Formatting in Pre-Commit
For infrastructure teams:
- repo: https://github.com/antonbabenko/pre-commit-terraform
rev: v1.83.0
hooks:
- id: terraform_fmt
- id: terraform_validate
- id: terragrunt_fmt
- id: tfsec
This catches terraform fmt issues and validates HCL syntax before any terraform plan runs in CI.
Adding a GitHub Actions Safety Net
Even with local hooks, a developer might skip installation or temporarily disable a hook. Pre-commit CI runs on every PR opened:
# .github/workflows/pre-commit.yml
name: pre-commit
on:
pull_request:
push:
branches: [main]
jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
- uses: pre-commit/[email protected]
This catches anything that slips through local hooks — formatting, secret scanning, YAML validation.
What Pre-Commit Catches That CI Shouldn’t Have To
| Check | Why It Belongs in Hook | CI Alternative |
|---|---|---|
| Formatting (black, gofmt, prettier) | Runs in milliseconds | Would waste runner minutes |
| Secret scanning (git-secrets, trufflehog) | Catches before push | Too late if already in repo history |
| Large file detection | Prevents bloat in Git history | Hard to clean up later |
| YAML/TOML validation | Instant feedback | CI would fail on obvious syntax errors |
| Terraform fmt/validate | Catches HCL errors before plan | terraform plan would fail anyway |
The Shared Quality Bar
The real value of pre-commit hooks isn’t catching formatting — it’s establishing a team-wide contract. When everyone has the same linter running locally, PRs stop being blocked on style nits. Reviewers focus on logic, not whitespace.
Pre-commit CI extends this: new team members get the same quality bar automatically, without needing to manually set up their local environment.
The CI half of that deserves the same care as the hooks themselves — pinning tool versions explicitly rather than trusting whatever the runner image ships this month, which I went into in GitHub Actions and template actions.
Run locally. Run in CI. Ship clean.