2  Commits

Note

This guidebook is written following the diátaxis “how-to guide” style. And because this document reflects how we work in the Seedcase Project, it is living and constantly evolving. It won’t ever be in a state of “done”.

This guide covers how we in the Seedcase Project commit changes to our repositories. Following these guidelines makes changes easier and faster to review and creates a clear and consistent history. It also allows for automatic changelog generation and version bumps in a project.

This page describes when and what to commit, the Conventional Commits specification, and how to apply it to both code and non-code projects.

2.1 When and what to commit

Each commit should be a useful checkpoint that another person can understand. In general, aim to:

  • Make atomic commits: Each commit should represent one logical change. For example, update the same file path across several files in one commit without including an unrelated text edit.
  • Commit often: Make commits frequently enough to capture meaningful progress and create useful points to return to.
  • Write meaningful messages: Summarise what changed and, when it is not obvious, explain why it was changed. Follow the Conventional Commits specification.

Atomic does not necessarily mean changing only one file or a few lines. A commit can include several files when all their changes are necessary for the same purpose. Conversely, unrelated changes in one file should be committed separately.

2.1.1 Review and stage changes

Before committing, review the changes as if you were seeing them for the first time. Check for accidental edits, generated files, debugging code, secrets, and changes that belong in another commit. Also consider whether the code or text could communicate its intent more clearly.

Stage only the changes that belong in the commit. In a terminal, git add --patch (or git add -p) lets you review and stage one part of a file at a time. You can do the same kind of selective staging and review from the Source Control view in VS Code. This helps keep individual commits scoped to one logical change, rather than adding entire files which might contain several unrelated changes.

2.2 Conventional commits

Conventional Commits is a specification for writing commit messages that makes it easier to understand the history of a project and automate versioning and changelogs. To learn more about the reasons for using Conventional Commit (with Gitmoji), check out the decision post documenting it.

The Conventional Commits specification follows this general format:

<type>[optional scope]: <description>

[optional body]

[optional footer(s)]

For example:

fix: prevent overwriting existing raw files

Where:

  • <type> is the type of change.
  • [optional scope] indicates the area of the codebase affected by the change.
  • <description> is a brief summary of the change (also called the “subject line”).
  • [optional body] provides additional context or details about the change.
  • [optional footer(s)] includes breaking changes or references to issues/tasks.

2.2.1 Required: type and description

The <type> in Conventional Commits is inspired by Angular’s commit message conventions and is used to categorise the changes made in the commit:

  • build: Changes that affect the build system or external dependencies.
  • ci: Changes to the CI configuration files and scripts.
  • docs: Documentation only changes.
  • feat: A new feature.
  • fix: A bug fix.
  • perf: A code change that improves performance.
  • refactor: A code change that neither fixes a bug nor adds a feature.
  • style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc).
  • test: Adding tests or correcting existing tests.
  • revert: Reverts a previous commit.
  • chore: Other changes that don’t modify source or test files.

The <description> section is a short summary of the change. Write it in the imperative, present tense (i.e., “change” not “changed” or “changes”) without a period at the end. Be specific enough that another person can distinguish the commit from others without reading the commit diff.

Tip

It can be difficult to remember the available types and what they represent. If you use VS Code, you can install the extension Conventional Commits to help write messages adhering to the Conventional Commit format. This extension also includes the emojis from Gitmoji. Adding emojis is a way of making the messages more colorful.

2.2.2 Optional: scope, body, and footer(s)

Tip

In Seedcase, we haven’t found a use for using scope in our work. For larger, more complex repositories (e.g. monoliths) using scope is a good way to communicate the change, but because we make smaller packages, scope hasn’t been very useful for us. When we initially tried using it, we didn’t end up having many scope tags and often used the same one or two. So it just added redundant text to the commit message.

Add an [optional body] when a future reader would need context that cannot be understood from the commit diff and <description> alone, such as the motivation, constraints, or reason for choosing one approach over another. Focus the body on why the change was needed; the commit diff usually shows what changed. It should be separated from the <description> with a blank line.

Tip

Because we advocate and use a trunk-based development (every pull request is atomic and we regularly make pull requests) where each pull request is squashed (more on that below), the [optional body] of commit messages isn’t kept during the squash. For that reason, we also don’t use [optional body] often.

The [optional footer(s)] is used for various purposes, such as indicating breaking changes or referencing issues. The main use for this footer is to indicate a breaking change, where you would add a footer in the format BREAKING CHANGE: <description>. Whenever a breaking change is committed, you also need to add a ! after the <type> in the commit message. For example:

feat!: new feature that breaks existing functionality

BREAKING CHANGE: This feature changes the way the existing functionality
works, so it is not backward compatible.

2.2.3 Commits in a squash-merge workflow

We normally use squash and merge, which turns all commits from a pull request into one commit on main. The individual branch commits should still be atomic and have meaningful messages so that reviewers can follow the work. However, the final squash commit is the commit that must provide the Conventional Commit-compliant, durable, release-relevant record.

2.3 Conventional commits for non-code projects

The Conventional Commit specification was created for software or code-related projects, but it can also be used for documentation, workshops, and other non-code projects. In non-code projects, the <type> can be adapted so that meaningful changelogs and versioning updates can still be generated automatically.

Some of the <types> are the same in documentation and other non-code projects as in software projects. The ones that are the same between code and non-code projects are build, ci, style, chore, and revert. The ones that are not relevant for non-code projects are test, and perf. The tricky one is docs, since the documentation is the main thing being changed when the product is documentation (like this guidebook). However, we treat docs as changes to text files that aren’t related to the product itself, but support development in some way. For example, changes to the README.md would fall under a docs commit type. The last three commit types are the ones that are most different from code-based projects.

  • feat: Changes that add new content.
  • refactor: Changes that modify or revise existing content, where the meaning or intent stays the same. This is used in editing or proofreading phases or tasks.
  • fix: Changes that fix typos, grammatical errors, incomplete sentences, or other similar writing mix-ups/situations where the text doesn’t align with our original intent.

As for emojis, use them in the same way as in software projects. The only emoji exception is the :memo: (📝) emoji that is used for documentation-related commits. In the same way as the docs <type>, it is redundant when almost all changes are related to documentation. Instead, use other emojis that are more descriptive of the change being made or omit the emoji altogether.