Code Quality on Autopilot: Demystifying pre-commit Hooks
Published: 2024-01-17 10:38:16

---
title: Code Quality on Autopilot: Demystifying pre-commit Hooks
published: true
date: 2024-01-01 21:00:00 UTC
tags: 
canonical_url: https://agathabahati.hashnode.dev/code-quality-on-autopilot-demystifying-pre-commit-hooks
---
Maintaining precise and consistent code formatting standards, commit message conventions and spell checking have all become part of my ritual when I work on projects; they are now practically woven into the fabric of my work.

_Picture this:_ you're going through multiple files, one by one, just to spot any missing newline, throw it in there then finally hit save. It's a repetitive and time consuming process really, who's got time for that? What if I told you that there's a quicker way?

Now, imagine if this process could be automated. Let me introduce you to **pre-commit hooks** , one of my fave Git features and the unassuming defenders of clean code.

## What are pre-commit hooks?

Pre-commit hooks are scripts executed before committing changes to a repository i.e when you run `git commit`. They can be used to automate tasks, such as testing and enforcing code quality standards by way of formatting, linting, spell checking etc.

The concept is simple, elegant and intuitive yet yields a significant impact: before you commit any changes, automatically perform formatting and code checks to ensure a high-quality, consistent codebase.

They guarantee code validation, proper formatting and successful testing triggered before a Git commit event. Consider this one less manual task on your plate, while maintaining high code quality and consistency.

## Setup and installation

To set up a pre-commit hook in your python project, follow the steps below:

### Step 1: Install `pre-commit`

Let's start by installing the `pre-commit` package using `pip`:

```bash
pip install pre-commit
```

To verify the installation and find out the `pre-commit` package version, run:

```bash
pre-commit --version
```

### Step 2: Create `.pre-commit-config.yaml` file

Next, create a `.pre-commit-config.yaml` in the root of your project.

### Step 3: Define hooks

Now let's get our hands a little dirty, we will define a simple hook that checks for and corrects end-of-file issues in your project.

```yaml
# pre-commit-config.yaml
repos: 
  - repo: https://github.com/pre-commit/pre-commit-hooks 
      rev: v4.0.1 
      hooks: 
        - id: end-of-file-fixer
```

- `repos:` this key presents the section to define the repositories and hooks to be utilized.

- `repo:` represents the repository url where the hooks will be fetched (git cloned) by pre-commit.

- `rev:` or revision, specifies the exact version or tag of the repository to use. In this case, it's set to version 4.0.1 of the `pre-commit-hooks` repository.

- `hooks:` this key points to the section that lists the particular hooks from the specified repository that need to be used.

- `id: end-of-file-fixer` here, we are specifying the hook to use from the repository. In this example, it's the `end-of-file-fixer` hook. This hook automatically adds a newline character to the end of each file in your project.

### Step 4: Initializing `pre-commit`

To create the scripts that add the hooks to your project, run:

```
pre-commit install
```

And just like that, the pre-commit hook will run automatically on `git commit` , only on files staged for commit. To run pre-commit on all files, run the initialize pre-commit command with the `--all-files` option:

```
pre-commit install --all-files
```

## When and how to use pre-commit hooks?

Let's look at practical ways you can use pre-commit hooks in your python project.

### i. Formatting

**Issue:** Inconsistent code structure, indentation, line breaks and spacing can hinder collaboration and make the codebase difficult to read and understand.

**Solution:** A consistent code style can be automatically enforced throughout the entire codebase by using third party tools like [black](https://pypi.org/project/black/).

Make sure to install black in your project.

```yaml
# pre-commit-config.yaml
repos:
  - repo: https://github.com/psf/black
    rev: v23.12.1
    hooks:
      - id: black

```

It is worth noting that your changes will be committed successfully **if and only if** your committed code passes all checks. Otherwise you (or the hooks) have to change the code accordingly then **commit again.**

### ii. Linting

**Issue:** Linting identifies problems in code like syntax mistakes, undefined variables, unused imports etc that could affect code readability.

**Solution:** To ensure that code style consistency is maintained in your project, we can integrate linters like [flake8](https://flake8.pycqa.org/en/latest/).

```yaml
# pre-commit-config.yaml
repos:
  - repo: https://github.com/PyCQA/flake8
    rev: v7.0.0
    hooks:
      - id: flake8
```

### iii. Spell checking

**Issue:** Typos, variable name inconsistencies or even overlooked documentation errors might bring about code readability issues.

**Solution:** Automating the process of spell checking - scanning code and identify spelling errors, providing suggestions for corrections - ensures that code remains free of spelling issues before being committed. Using tools like [codespell](https://pypi.org/project/codespell/) come in handy.

Make sure to have codespell installed in your project.

```yaml
# pre-commit-config.yaml
repos:
  - repo: https://github.com/pre-commit/mirrors-codespell
    rev: v2.2.6
    hooks:
      - id: codespell
```

### iv. Consistent imports

**Issue** : Ensuring that imports follow a certain style guide and ordering might be time consuming especially if done manually.

**Solution:** Using a tool like [isort](https://pycqa.github.io/isort/), the process of organizing imports according to predefined style guides, such as PEP 8, is automated.

```yaml
# pre-commit-config.yaml
repos:
  - repo: github.com/PyCQA/isort 
    rev: v5.15.2
    hooks:
      - id: isort
```

### v. Dockerfile Linting

**Issue:** Any mistakes in Dockerfiles relating to conventions and standards could lead to errors in the resultant docker image impacting deployment and even security.

**Solution:** Automating the linting process for Dockerfiles by analyzing configuration files to catch issues, enforce coding standards and ensure consistency by using the `dockerfilelint` hook.

```yaml
# pre-commit-config.yaml
repos:
  - repo: https://github.com/pryorda/dockerfilelint-precommit-hooks
    rev: v0.1.0
    hooks:
      - id: dockerfilelint
```

## Customizing pre-commit hooks

You can enhance the usefulness of your hooks by adding extra configuration. These are a few examples of such ways:

### Excluding files from formatting

To prevent black from formatting certain files:

```yaml
#pre-commit-config.yaml
repos:
  - repo: https://github.com/psf/black
    rev: stable
    hooks:
      - id: black
        args: ['--exclude', 'path/to/excluded/files/']
```

### Specifying words to ignore

To make codespell ignore spelling errors in certain words and files/directories:

```yaml
# pre-commit-config.yaml
repos:
  - repo: https://github.com/pre-commit/mirrors-codespell
    rev: v2.2.6
    hooks:
      - id: codespell
        args: ['--ignore-words', 'myword', '--skip', 'tests/']
```

In this example, the `--ignore-words` argument is used to specify custom words to ignore, and the `--skip` argument excludes the `tests` directory from spell checking.

### Removing unused imports

To customize `isort` for additional functionality, such as removing unused imports:

```yaml
# pre-commit-config.yaml
repos:
  - repo: https://github.com/pre-commit/mirrors-isort
    rev: v5.10.3
    hooks:
      - id: isort
        args: ['--remove-unused-imports']
```

## Conclusion

Pre-commit Git hooks can be used to automate tasks and ensure that code meets a certain level of quality and consistency. By using pre-commit hooks, you can catch potential issues before they are committed to the repository, saving time and improving productivity.

## Resources

To learn more about Git hooks and see the many ways you can apply them in your project, check out the links below:

[Git hooks](https://githooks.com/)

[Pre-commit-config yaml file] (https://githooks.com/)

[Pre-commit hooks](https://pre-commit.com/hooks.html)

Got thoughts on pre-commit hooks or have a cool hack? Share the wisdom, let's geek out together!🚀