Code Formatting and Version Control

How to Avoid Unjust Git Blame

For the last couple of years, I didn’t care about code formatting at all, as long as it was done automatically. Go (go fmt) and Rust (cargo fmt) are an absolute pleasure in that respect. The actual format doesn’t matter that much, as long as formatting happens automatically. But more important, there are no unproductive discussions about formatting.

I carried over my philosophy of not caring about the actual formatting rules to other programming languages and environments, such as Python, Ruby, and JavaScript. Just give me the formatter and the settings, and I’ll be happy. However, I noticed that some choices really have downsides, and it’s nost just taste. Enter version control.

The Oxford Comma

For a native German speaker, the Oxford Comma looks strange. In German we write:

Ich mag A, B und C.

Whereas in English an additional comma shall be placed before the “and”:

I like A, B, and C.

In programming, the comma within lists is seen as a delimiter:

likes = [a, b, c]

If the items are getting longer, it’s useful to spread them over multiple lines:

preferences = [
    drinking_coffee_with_hot_milk,
    getting_up_early_and_exercise,
    using_automatic_formatting
]

This is all fine, until you add another item:

preferences = [
    drinking_coffee_with_hot_milk,
    getting_up_early_and_exercise,
    using_automatic_formatting, # line changed
    using_the_oxford_comma      # line changed
]

How many lines were affected by this change? Two, even though I just wanted to add one line. I had to place the comma as a separator after the last item in order to add a new item.

This is no big deal, until git blame or the like is used: Now it was me who modified the last two items in the list.

Using the Oxford Comma consequently, adding a new item only affects one single line. And git blame won’t blame you for adding a (missing) separator.

So this is the code I’d like to commit:

preferences = [
    drinking_coffee_with_hot_milk,
    getting_up_early_and_exercise,
    using_automatic_formatting,
    using_the_oxford_comma, # line changed
]

With Go and Rust, by the way, using a trailing comma is perfectly fine. Other languages and their linters either discourage or outright forbid the usage of the Oxford comma.

And I’d like to stick to it for a good reason: version control reveals what actually happened in terms of my intention.

First Rule

Use the trailing comma where possible.

Multi-Line Indentaion

Let’s consider this function call:

report_preferences(drinking_coffee_with_hot_milk, getting_up_early_and_exercise, using_automatic_formatting, using_the_oxford_comma)

It is arguably too long for a single line. So let’s break it down:

report_preferences(drinking_coffee_with_hot_milk,
                   getting_up_early_and_exercise,
                   using_automatic_formatting,
                   using_the_oxford_comma)

Nicely formatted and properly aligned. (However, no Oxford Comma is used here.)

Now let’s change the name of the function from report_preferences to report_choices:

report_choices(drinking_coffee_with_hot_milk,
                   getting_up_early_and_exercise,
                   using_automatic_formatting,
                   using_the_oxford_comma)

Now that looks ugly, let’s fix it.

report_choices(drinking_coffee_with_hot_milk, # line changed
               getting_up_early_and_exercise, # line changed
               using_automatic_formatting,    # line changed
               using_the_oxford_comma)        # line changed

Much better. However, I had to modify four lines for a change that affected (besides the actual function declaration) only one function call. I didn’t change anything about the arguments. I just had to re-align them.

Version control will report that I modified the entire function call, when in fact I only changed the name of the function.

This can be avoided by inserting a line break after the opening bracket and by using a fixed indentation size for the subsequent lines:

report_preferences(
    drinking_coffee_with_hot_milk,
    getting_up_early_and_exercise,
    using_automatic_formatting,
    using_the_oxford_comma
)

Now changing the function name only affects a single line:

report_choices( # line changed
    drinking_coffee_with_hot_milk,
    getting_up_early_and_exercise,
    using_automatic_formatting,
    using_the_oxford_comma
)

Again, now git blame reveals my intention properly.

There are different formatters with different rules, but I strongly prefer the ones that don’t make the indentation dependent of identifiers.

Second Rule:

Indentation must be independent of identifiers.

Conclusion

When it comes to formatting code, some rules have clear benefits when working with version control. Some environments (programming languages, linters, formatters) discourage or forbid those beneficial choices, whereas others encourage or even enforce them.

My clear preferences are:

  1. Use the trailing comma where possible.
  2. Indentation must be independent of identifiers.

May Git help but not blame you!

PS (2024-10-02): Having picked up JavaScript again, I looked at prettier and deno fmt (actually the same formatter?). Both share my preferences in their default settings. They even use the Oxford comma for function calls in the argument list.