Pre-emptive Commit Messages

7 min read Original article ↗

Arialdo Martini — 20/02/2026 — git jujutsu written by humans, not LLMs


  1. Write commit messages before coding.
  2. Describe how the software behaves, not what you have done.

Behaviours, Not Tests

Daniel Terhorst-North changed my life.

I always recommend his Introducing BDD. It explains how Behaviour-Driven Development started from the intuition that TDD is not merely about testing. The word “test” itself points developers in the wrong direction, he says, toward verification, toward the past. He prefers using “behaviour”.

It’s a dramatic change of perspective. For Daniel tests document the system’s behaviour from the outside, from the point of view of its clients or users; therefore, they describe what the system should do, using narrative sentences.

They are in fact business requirements, written in the business language and, of course, conceived before the implementation even exists.

Why Test-First?

Some find it counterintuitive, if not crazy, writing tests before the system under test even exists. Yet, if you replace the word “test” with “requirement”, all makes sense, and it’s the opposite approach to sound crazy. What are we supposed to do? To write the code without knowing where to go and then, when we are done, to figure out what the requirements were?

If only TDD was called Requirement-Driven Development, no one would find it counterintuitive. “Test” suggests the idea of verifying something that is already done. Not the most fortunate pick, Kent…

As Daniel wrote:

A really useful way to stay focused was to ask:
“What’s the next most important thing the system doesn’t do?”

This question defines your next test; the test becomes a statement of intent, a promise. With the test in place, you commit (here’s the word!) to a specific, future behaviour for your product.

A Commit Is A Promise

I started thinking to commit messages the same way, and something clicked. Maybe the word “commit” in the Git lingo is not casual. Maybe making a commit means making a promise too.

It’s ironic how Git lets you commit to something when the code is already complete. A bit late, indeed. The real twist is how in Jujutsu it’s the exact opposite to be idiomatic: you first jj commit, then you write the code.
We’ll get this in few seconds. Back to the message.

How To Describe A Promise?

A commit message such as:

is neither a commitment nor a behaviour description: it’s an activity report.
It makes more sense when written in the style of a BDD method:

The cart does not crash when it contains more than 10k items

That’s not a cosmetic difference. The former is about the actions performed by the programmer, the past, the how. The latter about the feature, the present, the what.

Tell Me What The Software Does, Not What You Have Done

The second form is what I would like to read in the Git history, and is in line with the idea of Conventional Commits, which promise to “Automatically generate CHANGELOGs”.

When I check out a commit, I know that someone worked hard on their keyboard to make the software behave like it should. Kudos, I’m grateful. But when it’s my turn to work on top of that commit there’s no point answering the question:

What did the programmers do during that work session?

It’s more useful to have an answer to:

What's the project behaviour NOW?

As Ferdinando Santacroce loves to say: the commit message should capture what cannot be deduced from the code only: the context, the motivation and the thought process behind the solution. To me, that alone is a good reason not to let LLMs write my commit messages.

Write It First

Then I stumbled upon this tweet by Eric Willeke:

Eric Willeke Tweet

A point of view of disarming simplicity. It’s test-first applied to version control. It instantly resonated with me.

If you are accustomed to write tests before the implementation, writing commit messages before coding will be just as natural. Messages become statement of intents, commits become commitments.

This is how the Squash Workflow works in Jujutsu. It revolves around the idea of creating a new empty revision, together with its description, even before touching any file. Not a coincidence that the command to do this is jj commit. The commit message as the intent and the unit of work.

First you write it, then you make it true.

What Happens When You Do It

In 2012, with my teammates we started applying this technique, with Git. It was a matter of doing

git commit --allow-empty -m <message>

followed by

when we were done. Some Git GUIs makes this easy.

Over the years, this is what I experienced:

  • It aligns pair members. Whether I am the driver or the navigator, I must agree with my pair on the message and on the goal. This stimulates a conversation. The moment we start programming, we genuinely agree on what we want to achieve.
    When I code alone, I have to find an agreement with my own understading. It’s equally challenging and it equally pays off.

  • It’s easier to focus: tests and commit message are the coding session’s center of gravity. When I digress, the promise in the commit message is there to bring me back to the point.
    I’m less prone to lose focus.

  • It sets a micro scope: Before, I used to ask myself: “Should I stop coding now? How much is enough? Am I done yet?”.
    Checking if I reached the planned goal is just easier.

  • Simpler commit reviews: I always review my changes before making them final. Instead of asking “Well well well, let’s find out, what have I done here? I don’t even remember…” I can ask myself “Have I done all and only what I committed to?”.
    It’s a much easier and targeted question to answer.

  • More accurate and faithful statements: when writing the message before coding, being very specific comes naturally. It’s easy to refrain from being vague and writing Fix or Update Repository.cs. The message and the resulting commit content naturally match, because the message was the goal since the very beginning.

  • Each message triggers a micro design session: the mere act of defining the commit message requires reaching an agreement with my pairing partner. We need to concord on the words to describe the desired behaviour, on the names to give to domain concepts and, ultimately, on which component owns the behaviour. These are all design questions, and we cannot write the message until we’ve addressed them.
    Starting a pairing session, there’s nothing nicer than discussing about design.

  • It creates a natural timebox: I make my best to commit to baby-step progresses, from a stable state to the next little goal. It’s easy to verify if a chance belongs or not to the goal.

  • It goes well with short-lived feature branches: messages define micro-goals, and they live in the context of the wider goal defined by the branch name. Which is also pre-emptive, by design.
    These 2 levels of goals help me steering the direction.

  • The commit history gains natural granularity. Each commit has a single goal (one could say it respects the Single Responsibility Principle). A feature branch becomes a readable sequence of behaviours, each commit a small step in a larger story.
    A natural changelog emerges.


This experiment was started in 2012 by Arialdo Martini, Mattia Piccinetti, Gian Marco Gherardi, Guglielmo Brasile, Francesco Pichierri, and Giuseppe Mariano. It was initially described in Pre-emptive Commit Comments.
It is described in details in the book Git Essentials by Ferdinando Santacroce.

References