Skip to main content

Why Pre-Commit Still Matters

A look at pre-commit, newer tools like prek, and why deterministic development guardrails remain valuable even as AI becomes part of the coding workflow.

In 2026, development workflows have no shortage of automated checks. For any given project, we might have linters, formatters, static analysis tools, dependency management frameworks, unit tests, and increasingly, AI tools that are capable of reviewing and even generating significant portions of code.

With all of this automation in our development processes, it is fair to ask if something as simple as a pre-commit hook still fits.

I think the answer is pretty straightforward: pre-commit gives developers fast, consistent feedback at exactly the point where it is most useful.

What is Pre-Commit?

Git has supported the concept of hooks for a long time. A hook allows developers to execute scripts when certain Git operations occur, such as creating a commit or pushing changes to a remote repository.

A pre-commit hook, as the name suggests, runs before Git creates a commit.

At its simplest, that means a typical developer’s workflow can look something like this:

Pre-commit feedback loop

Those automated checks can do almost anything, but common examples include:

  • Formatting source code (multiple languages)
  • Running linters
  • Checking YAML, JSON, or TOML syntax
  • Removing whitespace
  • Performing static analysis

This is where the pre-commit framework becomes useful.

Instead of every developer on your project maintaining their own collection of Git hook scripts, a project can define its hooks in a .pre-commit-config.yaml file that lives in the repository.

For example, a basic configuration file might look something like:

repos:
# General Hooks
- repo: https://github.com/pre-commit/pre-commit-hooks
  rev: v6.0.0
  hooks:
  - id: check-merge-conflict
  - id: check-yaml
  - id: end-of-file-fixer
  - id: mixed-line-ending
  - id: trailing-whitespace

# Python Hooks
- repo: https://github.com/psf/black
  rev: 26.5.1
  hooks:
  - id: black
    name: black (python)
    args: [--safe, --quiet, --line-length=100, --config=pyproject.toml]

- repo: https://github.com/pycqa/isort
  rev: 9.0.1
  hooks:
  - id: isort
    name: isort (python)
    args: ["--settings-path=pyproject.toml"]

- repo: local
  hooks:
  - id: pylint
    name: pylint (python)
    entry: pylint --rcfile=pyproject.toml
    language: system
    types: [python]

# C Hooks
- repo: https://github.com/pre-commit/mirrors-clang-format
  rev: v17.0.4
  hooks:
  - id: clang-format
    args: ['--style=file']

The configuration becomes part of the repository and defines a consistent set of checks that developers on the project can run locally.

That consistency is one of the biggest reasons I am such an advocate for using pre-commit

Move Feedback Closer to the Developer

One of the biggest reasons I find pre-commit valuable is that it shortens the feedback loop.

Without local checks, even a simple linting or formatting issue might not be discovered until after the code has been committed, pushed, and processed by CI. At that point, the developer has to leave their current context, inspect the failure, make the fix, create another commit, and push again.

With pre-commit, that same issue can be caught before the commit is ever created.

Comparison of development workflows with and without pre-commit

The problem isn’t that CI caught the issue; it’s that the feedback arrived later than it needed to.

Software development already requires keeping a lot of context in your head. If a tool can identify a straightforward problem locally within seconds, I see very little benefit in waiting for CI or another developer to find it later.

A Consistent Quality Floor

I also think pre-commit provides something slightly different from simply making development faster: it establishes a quality floor.

One developer might configure their editor to automatically run a formatter whenever they save a file. Another might run the formatter manually. Someone else might forget entirely.

Repository-level hooks move some of those expectations out of individual development environments and into project configuration.

It no longer matters as much whether someone remembers to manually run black, isort, or whatever collection of tools a project uses. The repository itself defines what should happen.

That is particularly useful when joining an unfamiliar project. A well-configured pre-commit setup provides an immediate indication of some of the project’s expectations without requiring a developer to manually reconstruct the workflow from documentation.

Reviewers should ideally spend their time thinking about things like architecture, correctness, security, maintainability, and whether a change actually solves the intended problem.

They should not have to spend much time pointing out trivial issues that an automated tool already knows how to detect.

Where Does prek Fit?

One newer project that caught my attention is prek, a Rust-based implementation of the same general pre-commit flow.

prek is designed to be compatible with existing pre-commit configurations and hooks while addressing some of the friction that can come with the traditional implementation. It is distributed as a single binary, does not require a Python runtime just to execute the framework, can share toolchains between hooks, and supports parallel execution.

Performance is also one of prek’s major selling points. Its Rust implementation, shared hook environments, and parallel execution can make it significantly faster than traditional pre-commit workflows.

Whether a project uses pre-commit, prek, or another implementation is ultimately less important to me than the development practice itself.

The important idea is having a fast and repeatable set of checks between making a change and allow that change to progress further through the development pipeline.

Pre-Commit Does Not Replace CI

I don’t think pre-commit should become the final authority on whether code is acceptable.

Local hooks can be skipped, intentionally or accidentally. Some checks are also far too expensive to run every time a developer creates a commit.

Complete test suites or integration tests might take several minutes or require complex infrastructure.

So I view the two as complementary:

Pre-Commit and CI complement one another

Pre-commit handles the cheap, fast feedback that makes sense locally.

CI provides the authoritative validation that should happen regardless of a developer’s environment.

The goal is not to move everything into pre-commit. The goal is to catch problems at the earliest reasonable point in the development lifecycle.

What Changes When AI Writes the Code?

This becomes particularly interesting as AI-assisted development becomes a normal part of our software development processes.

AI tools can now do the bulk of the legwork in terms of generating functions, modifying multiple files, refactoring code, and writing test suites.

That dramatically increases the speed at which code can be produced. However, it does not necessarily increase the speed at which code can be trusted.

An AI-generated change still needs to conform to the same repository requirements as something written manually: formatting, linting, configuration validation, tests, and everything else the project requires.

In that environment, I think deterministic tooling becomes more valuable rather than less.

AI is probabilistic. Pre-commit checks are deterministic.

I don’t need to rely on an AI tool remembering every convention used by a repository. I can define those requirements as executable checks and require every change to pass through them regardless of whether the code was generated by AI or written by a human.

If an AI agent can modify ten files in seconds, having an automated validation layer immediately following those changes makes a lot of sense.

The developer’s role increasingly becomes not only producing code, but also defining the constraints under which code is allowed to enter the system.

Pre-commit is one small place where those constraints can live.

Small Guardrails Add Up

Pre-commit is not a particularly exciting technology.

And I think that is part of why I love it.

It solves a relatively simple problem: take checks that developers know should happen and make them happen automatically, consistently, and close to where the code is being written.

The result is faster feedback, cleaner commits, less unnecessary CI churn, and fewer trivial issues reaching code review.

Pre-commit will not tell us whether we designed the right system or solved the right problem.

But for the things a machine can check deterministically, I would rather have it check them before I commit.