plaintext.brennan.day

Every Commit A Sentence: Git Commit Messages for Bloggers

| writing-craft, technical-tutorials | 1,123 words
TAGS: git, technical, writing, IndieWeb, personal site, web development
ORIGINAL: https://brennan.day/every-commit-a-sentence-git-commit-messages-for-bloggers/

If you look at the changelog for my site, Brennan.day, you'll notice two things. First, I am working on this project way too much, and second, my attempt at good git commit message hygiene.

There is a problem, though. Across over a thousand commits, 448 were prefixed feat: and 417 were fix:. Nearly the same count, for wildly different kinds of work. A new 2,000-word essay? feat:. A corrected typo? fix:. An automated comment sync running at 3am? feat:. A CI pipeline toggle? feat:. Everything is flattened into two buckets.

I have been following what you might loosely call "best practices" for commit messages, but using conventions designed for software library authors is, at the end of the day, a writing practice of its own. I was losing meaningful signal in the process.

If you're a writer (or maybe you just call yourself a blogger) first and a developer second, using git as a way to maintain your writing more than anything else, then this blog post is for you.

A Brief History of the Typed Commit

You might be someone who doesn't even know what exactly to put for a commit message when you're pushing code to your repository. Or maybe you're more in the XKCD camp of ebbing and flowing between trying to be informative and just typing whatever-the-hell.

The modern convention for structured commit messages has lore. It starts with Tim Pope in April 2008, writing a now-famous post as a contributor to the Rails project.

His focus wasn't on type prefixes at all; rather, it was structural:

Pope established the grammar of a well-formed commit. Git itself, he argued, was designed with this structure in mind: git log --oneline, git shortlog, git rebase--all of these commands assume a subject line that can stand alone as a complete thought.

The type-prefix convention came later. Igor Minar and Vojta Jína introduced it while working on Angular in 2011. Their original AngularJS contributing guidelines specified a format of <type>(<scope>): <subject> with a fixed vocabulary: feat, fix, docs, style, refactor, perf, test, chore.

The stated goals were explicit: allow automated changelog generation, allow ignoring unimportant commits when using git bisect, and provide better information when browsing history.

That convention was eventually generalized into the Conventional Commits specification, a proper versioned standard that formalized the format. The specification's most important design decision was to tie feat: and fix: to Semantic Versioning: a feat: commit signals a minor version bump (e.g., version 0.0.1), a fix: signals a patch (e.g., version 0.1.0).

Tools like semantic-release can then read your commit history and automatically determine the next version of your npm package, without a human needing to make the call.

The Mismatch

This system I'm describing above was designed for programming, for people publishing npm packages and software library authors.

But I'm writing a blog. I don't have a public API, nor a semver. When I publish a new essay, it isn't like my site is deploying a MINOR release. When I correct a typo, it is not a PATCH. The vocabulary doesn't align.

The original Angular type list has no way to distinguish the writing of "new blog post published" from "automated comment sync." Both would reach for feat:. Likewise, there isn't any way to distinguish "corrected a sentence in an old post" from "fixed a null pointer in the comment fetcher" as both would use fix:.

The more I look at the history of my website, the more I saw the changelog becoming illegible because the categories weren't useful. As I've mentioned before, being an IndieWeb creator means you wear a lot of hats. There are writing-heavy days, there are design-heavy days, and there are coding-heavy days.

Under the feat:/fix: monoculture, that information had disappeared entirely.

My Redesign (Brennan's Type Prefix Conventions for Writers)

Since this is my site and I'm the sole maintainer, I get to change the rules. After auditing my commit history to understand the real distribution of work, I arrived at the following eleven types:

As you can see, this is opinionated. Maybe even a little controversial. Having content-focused prefixes reshapes the blog to be, well, a blog instead of a software project. There is the much more readable separation of edit: for typos, and fix: for bugs.

I've retired feat: entirely (no pun intended), and now a new CSS animation would go in design: and a new Eleventy shortcode would go in build:.

The Changelog as Parallel Text

There's something unexpectedly pleasing about thinking of the commit history as a parallel text to the blog itself. Both my posts themselves and the git commits are records of the same creative practice at different zoom levels.

When I look back at an active week in my changelog (which is essentially every week), I want to be able to read it as a diary in itself: a few post: entries, some edit: corrections as I noticed things after publishing, an update: or two from automated comment syncs, maybe a design: or a11y: if I'd been tinkering. That's a much more accurate account of what happened. A week that reads as twelve feat: commits and fifteen fix: commits is not.

Writing commits with more care is a small act of care toward future-me and others looking to build upon my work, and really anyone curious enough to browse the source on GitLab. Every commit is a sentence. It should say what it means.

The full convention, with a complete table and examples, is documented in my CONTRIBUTING.md doc and on the colophon page of the site.