Skip to content

Commit Messages

Commit messages provide a record of why changes were made to the codebase. Clear, consistent commit messages improve collaboration, simplify debugging, and help maintain a useful project history.

Polk County’s projects use the Conventional Commits standard for commit message formatting.

Using a consistent structure makes commit history easier to read and enables tooling such as automated changelog generation.

Commit messages should follow this format:

<type>: <description>

Example:

feat: add permit search endpoint

The type describes the nature of the change, and the description summarizes what the change does.

Commit messages should:

  • Be written in present tense
  • Use lowercase
  • Remain concise and descriptive
  • Focus on what the change does, not how it was implemented

The following commit types are commonly used in our repositories.

TypeDescription
featIntroduces a new feature
fixFixes a bug
docsDocumentation changes
styleCode style or formatting changes that do not affect behavior
refactorCode restructuring that does not change behavior
perfImproves performance
testAdds or updates tests
buildChanges affecting the build system, tooling, or packaging
ciChanges to CI/CD configuration or automation workflows
choreMaintenance tasks such as dependency updates or tooling changes
revertReverts a previous commit

Examples of well-structured commit messages:

  • feat: add permit search endpoint
  • fix: handle null response from permit api
  • docs: update branching strategy documentation
  • style: format code with prettier
  • refactor: simplify validation logic for permit requests
  • perf: optimize permit lookup query
  • test: add unit tests for permit service
  • build: update docker base image
  • ci: update github actions workflow
  • chore: bump go dependencies
  • revert: remove permit search endpoint

Most dependency updates should use the chore type.

Use build only when the dependency change directly affects the build system, such as updates to bundlers, compilers, Docker images, or build tooling.

A good commit description should clearly communicate the purpose of the change.

Good examples:

  • feat: add search filters to permit lookup
  • fix: prevent duplicate permit submissions

Poor examples:

  • fix stuff
  • update code
  • changes

Descriptions should provide enough context for someone reviewing the commit history to understand what was changed.

In some cases, a commit may include an optional scope to indicate the area of the codebase affected.

Format:

<type>(<optional scope>): <description>

Examples:

  • feat(api): add permit search endpoint
  • fix(auth): correct token validation logic

Scopes are optional but can be helpful in larger repositories.

Commits should be small and focused.

Recommended practices include:

  • Commit related changes together
  • Avoid combining unrelated updates in a single commit
  • Prefer multiple small commits over a single large commit

Focused commits make it easier to review history and revert changes if necessary.

When merging pull requests, commits are squashed to keep the main branch history clean.

Squashed commits should follow the same Conventional Commits standard.

This ensures the main branch history remains readable and meaningful.

Example squash commit:

feat: add permit search endpoint

Avoid the following patterns when writing commit messages:

  • Vague descriptions
  • Multiple unrelated changes in one commit
  • Commit messages that describe implementation details instead of outcomes
  • Excessively long or unclear descriptions

Clear commit messages improve the maintainability of the project history and help other developers understand past changes.

Repositories may use Commitlint to validate commit messages against the Conventional Commits standard.

Commitlint helps enforce consistent commit history by checking that commit messages use approved types and follow the expected format.

Using Commitlint helps:

  • Enforce consistent commit message formatting
  • Prevent invalid or unclear commit types
  • Provide fast feedback during local development
  • Support automated validation in CI workflows

Commitlint should be used to reinforce the standards defined in this document, not replace engineering judgment.

Commitlint can be installed as a development dependency along with the conventional configuration package and prompt CLI.

Terminal window
npm i @commitlint/cli @commitlint/config-conventional @commitlint/prompt-cli -D

Configure Commitlint with a commitlint.config.mjs file:

const config = {
extends: ["@commitlint/config-conventional"],
};
export default config;

Repositories may extend this configuration if additional rules are needed.

To provide immediate feedback when writing commits, Commitlint can be run through a commit-msg hook.

For repositories using Lefthook, add the following configuration to lefthook.yml:

commit-msg:
jobs:
- run: npx --no -- commitlint --edit $1

This validates commit messages before the commit is finalized.

Local hooks provide fast feedback, but CI should be used to ensure commit message rules are enforced consistently.

A GitHub Actions workflow (.github/workflows/commitlint.yml) can be used to validate commit messages on pushes and pull requests:

name: Commit Linter
on:
pull_request:
push:
branches:
- main
jobs:
commitlint:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: latest
- name: Install dependencies
run: npm ci
- name: Validate current commit (push)
if: github.event_name == 'push'
run: npx commitlint --last --verbose
- name: Validate PR commits (pull_request)
if: github.event_name == 'pull_request'
run: npx commitlint --from ${{ github.event.pull_request.base.sha }} --to ${{ github.event.pull_request.head.sha }} --verbose

Repositories may also use @commitlint/prompt-cli to guide developers through creating valid commit messages interactively.

To add a convenient script for interactive commits, include the following in package.json:

package.json
{
"scripts": {
"commit": "commit"
}
}

After staging changes, developers can run the commit script to launch the interactive prompt:

Terminal window
npm run commit

This can improve consistency and reduce commit message errors, especially for contributors unfamiliar with the standard.

Using consistent commit messages helps maintain a clean and understandable project history.

By following the Conventional Commits standard and keeping commits focused, developers can make collaboration, debugging, and long-term maintenance easier for the entire team.