
\documentclass[12pt]{article}
\usepackage[T2A,OT1]{fontenc}
\usepackage[default]{cantarell}
\usepackage[a4paper, top=20mm, bottom=20mm, left=20mm, right=20mm]{geometry}
\usepackage[utf8]{inputenc}
\usepackage[russian, english]{babel}
\usepackage{tabu}
\usepackage{hyperref}
\usepackage{parskip}
\usepackage{graphicx}
\usepackage{tabularx}
\usepackage[normalem]{ulem}
\usepackage{float}
\floatstyle{boxed}
\restylefloat{figure}
\usepackage{setspace}
\onehalfspacing
\author{Artyom Bologov \href{mailto:commitmsg@aartaka.me}{(email)}}
\date{\today}
\title{A's Commit Messages Guide: Location, Action, Rationale}
\makeatletter
\def\endenv{\expandafter\end\expandafter{\@currenvir}}
\makeatother
\begin{document}
\maketitle

\includegraphics[width=\textwidth,height=\textheight,keepaspectratio]{./assets/commitmsg.png}

I’m writing and searching lots of commit messages every day.
It’s easy to get lost in them.
Yet I don’t.
The reason is that I write reasonable commit messages.
(I’m saying it in such a blunt way because it’s not necessarily my merit—I
had great teachers on Nyxt team and Guix community!)
And I figured I should explain the reasoning behind my style.
Might be helpful to someone starting out or looking for better conventions.

The heuristic is simple:

\begin{description}\item[Location]
Localize the change to file / function / object etc.
\item[Action]
Explain what the change does.
\item[Rationale]
Clarify the reasons and context for it.
\end{description}

Yes, all in one message without description (if possible.)
I’ll go through these parts in the order.
Hopefully explaining things clearly enough for you to embrace the system.

\section*{Location: Isolating the Change} \label{location}

Filesystem is a huge helper and marker for project structure.
Java classpaths, C include paths, conventional Open Source project files
(README, LICENSE/COPYING, CONTRIBUTING.)
Locating the change via file structure is half of the problem for change explanation.
That’s why my commit structure is so elaborate on locations.

First, file paths.
Include full paths to files so that it’s clear where the change happened.
If your project structure is (already) reflecting the code structure
then listing the file/directory should be enough to show change scope.
Some exceptions I use:

\begin{itemize}\item Omit file extension if it’s obvious.
\item Omit full directory path if it’s The Directory,
  like \verb|src/| for code-oriented project, \verb|www/| or \verb|public/| for pages, and \verb|data/| for datasets.
\item Use shell wildcards (\verb|something<wbr|.*.something>) or brace expansion (\verb|file.{c,h}|) to mark groups of files.
\item Or outright omit the location when the commit involves multiple files without any main one.
\end{itemize}

Second, exact change location.
This one is elaborate!
There are several syntactic markers for the location that I use:
Parentheses, square brackets, angle brackets, and some regex syntax.
Mostly in the order of increasing specificity:

\begin{itemize}\item Parentheses are for toplevel entity highlighting.
In case the language is function/procedure-oriented (like Scheme or C), function name is such an entity.
In case the language is object-oriented (like Commmon Lisp or C++), it’s the class that matters.
In other cases... whatever the general structure unit for the file is.
While writing for this blog, I often use section IDs as this toplevel entity markers.
As in \verb|commitmsg<wbr|(#location): ...> for commit relating to the section you’re reading right now.

\item Square brackets are for sub-units of the parenthesized thing.
For OOP codebase, these usually are class methods or slots / properties / fields.
For functional ones, it might be local functions, arguments, or blocks.
It’s rare enough seeing these, you should focus on parenthesized location first.

\item Angle brackets are for sub-sub-entities.
Like individual arguments or variables in the body of the function/method.
Barely ever useful, I’m only including these for completeness.

\item Here’s an atypical bit: I use regex-y syntax
(as suggested in my \href{regex-pronouns}{Regex Pronouns} post)
to shorten/condense the location.
Writing \verb|describe<wbr|(describe-*): ...> (from Nyxt) involves all the \verb|describe-something| functions.
Or doing a change to function named \verb|hello-there| in file \verb|hello.lisp| might be \verb|hello(-there): ...|
Noticed the reuse of file path as function name prefix?
Easy to get carried away with regex, but I try to remain relatively sane about locations.
\end{itemize}

Some examples:

\begin{figure}[h!]\begin{verbatim}
// From https://github.com/aartaka/cl-blc
cl-blc.asd: // Plain file with extension.
reductions: // Obvious extension (.lisp in this case), not included
cl-blc(eval): // Function eval in cl-blc.lisp
(compile): // Omitting the obvious file name (if there is one?)—cl-blc.lisp

// From this website
ideas(How I write commit messages): // Section name as location
screen.css(code,kbd,sample): // CSS selector as location
markup(DETAILS #GEMTEXT): // C Macros with #ifdef-s

// From https://srfi.schemers.org/srfi-253
srfi-253.html(#spec--values-checked): // HTML ID as the unit
impl.{chicken,kawa}.scm: // Bracket replacement
impl.*.scm(%lambda-checked): // Wildcard for all the matching files

// From Nyxt
buffer(buffer,panel-buffer[style]): // Square bracket slots in comma-separated classes
mode((dis|en)able-modes): // Complex regex use

// Not giving examples for angle brackets, sorry
\end{verbatim}\caption{Examples of locations I use in my projects}\end{figure}

The trend is somewhat traceable: simple projects often only need file/function locations.
Complex projects (like SRFIs and Nyxt) need more concrete locations.

\section*{Action: Explaining the Change} \label{action}

Commit messages are the part most commit guidelines focus on.
Writing in imperative mood, expressing the "why" etc.
It’s Writing 101 all over again.
The vital thing I took (and ignored on this blog) from writing classes?
Put important things first.
With commits / changes / alterations / modifications...
the most important thing is the action.
Verb, in other words.

Some possible verbs/actions:

\begin{itemize}\item Add.
\item Remove/Delete.
\item Update/Amend.
\item Refactor.
\item Optimize.
\item Fix.
\item Unfuck.
\end{itemize}

The pattern is simple—whatever happens to the code, ends up in commit message.
The person reading the logs immediately gets the idea of what you did
(and where, if you followed previous section.)
Commits tell a story of what one did to the code/data/narrative.

The action-ability of my commit messages is the reason I dislike
\href{https://conventionalcommits.org}{Conventional Commits} and the kind.
You don’t need to say whether the thing is a feature or a bugfix, you just say what’s there.
Directly.
\verb|Add| and \verb|Optimize| also reads better than \verb|feat| and \verb|perf|.

Oh, and, this goes without saying?
You can only distill the commit to one verb if it does only one thing.
Make changes atomic and self-contained.
You’ll have no trouble explaining what you did then.

\section*{Rationale: Contextualizing the Change} \label{rationale}

Put rationale (the "why") into commit message whenever possible.
That’s where my style breaks some of the conventions.
The reason?
No one reads commit descriptions!
What was the last time you, dear reader, intentionally looked through commit descriptions?
Especially so—using the un-ergonomic CLI git display?
Ugh.

That’s why I try to put rationale for the change into commit message.
The reader (often the future Artyom himself!) will thank me later.

Luckily, verb/action-orientation is good for rationale.
All the "Fix" messages almost automatically hint at what was broken.
All the "Optimize" messages hint at what was slow/over-allocating.
And the "Remove" messages (my favorite!) often end up with
"Remove X—unused!" or "Remove Y—useless!"
A relief.

My favorite composition of the three things I suggested is "Add" message.

\begin{itemize}\item Location (often precise to the new function or slot) shows what was added,
\item Action tells that there was a signification addition,
\item And there’s a looooot of space for rationale after that!
And you might not even need that space—location and action already tell a lot.
\end{itemize}

\section*{Formalities: Capitalization, Punctuation, etc.} \label{formalities}

The main structure (Where, What, Why?) out of the way.
Time to get to formalities:

\begin{itemize}\item Separate location from the rest of the message with a colon and a space.
\item Capitalize the first word of the message (usually verb), unless it’s a code entity or something.
\item Finish messages with whatever you like. I tend to use period out of stubbornness.
  Most other guides recommend not using punctuation, so I guess don’t use it?
\item Try to cap commit message length at 72 chars.
  Note that I’m not saying 50 here, like all the other guides.
  My style requires much more space, but also provides more information to the log reader.
  And then, our screens are much larger now than they used to be when 50 char restriction was useful.
\end{itemize}

\section*{Real World Inspiration: Guix!} \label{real-guix}

I am not trying to say that Guix uses my convention!
It’s rather the reverse: my style is heavily influenced by Guix.
And stripped down for my small scale projects.

So what Guix does is

\begin{itemize}\item Add location (usually a toplevel directory like \verb|gnu/|) to the front.
\item Add the summary for the change after location.
\item And list all the files, functions/variables (in parentheses),
  slots (in square brackets),
  and minor details (in angle brackets) with detailed changes—all in the commit description.
\end{itemize}

Here’s a sample commit:

\begin{figure}[h!]\begin{verbatim}
gnu: kvantum: Update to 1.1.2.

* gnu/packages/qt.scm (kvantum): Update to 1.1.2.
    [source]: Switch to git-fetch.
    [arguments]: Set #:qtbase to qtbase.
    <#:phases>: Adjust patch-style-dir phase.
    [inputs]: Remove libx11, libxext, qtbase-5, qtsvg-5, and qtx11extras; add qtsvg.
    [native-inputs]: Remove qttools-5; add qttools.
\end{verbatim}\caption{One of the (more involved) Guix commit messages}\end{figure}

It’s big and has lots of syntax, but it also gives you a lot of information about what’s changed!
That’s what I strive for in my commits—maximum meaning per minimum space.
Preferably fitting things into one commit message without description (unlike Guix):

\begin{figure}[h!]\begin{verbatim}
packages/qt(kvantum): Update to 1.1.2 \& switch to git-fetch.
\end{verbatim}\caption{Guix commit refactored in my style}\end{figure}

\section*{Summary: Write Commits!} \label{summary}

\begin{itemize}\item Localize the change to file / function / class / slot / variable.
\item Highlight the action you’ve commited.
\item And explain why you did it.
\end{itemize}

\begin{figure}[h!]\begin{verbatim}
file(function): Unfuck---was preventing further refactoring.
\end{verbatim}\caption{Reference for my commit style}\end{figure}

Hopefully y’all learned something from this posts and I can see more informative commits around!
Thanks for your care 🖤


\par\noindent\rule{\textwidth}{0.4pt}
\href{https://creativecommons.org/licenses/by/4.0}{CC-BY 4.0} 2022-2026 by Artyom Bologov (aartaka,)
\href{https://codeberg.org/aartaka/pages/commit/a91befa}{with one commit remixing Claude-generated code}.
Any and all opinions listed here are my own and not representative of my employers; future, past and present.
\end{document}
