I’ve always been a bit skeptical about code formatters. I don’t know, I always felt like they would curb my freedom to format the code in my own way. Because, you know, no one formats code better than me. 😛

Joking aside, I got to know black about 2 years ago. Everyone was talking about it. A bunch of people adopted it. Massive codebases were being reformatted daily. It was the new kid on the block.

My initial reaction? Contempt. I didn’t want to use a code formatter that did not allow me to customize it to format the code the way I like. Double quotes? Get the hell out of here!

Black is opinionated and so was I.

Silly me. It should have never been about me or my own taste, but the opposite. Black’s goal is to make Python codebases all around look at least similar in their format.

What changed my mind?

Fast forward one year and I see myself posting a lot of change requests in PRs asking people to format their code to match my personal preferences. That attitude can delay PRs and trigger long and frustrating discussions.

Perhaps having coding guidelines outlining the rules on code style could have helped. It would certainly have helped onboarding new team members. While we all have PEP-8 as a common idiom, there are many issues that go beyond what’s defined there and that’s why having a written reference is always a good idea.

I do not think that coding guidelines is the ultimate solution though. People will challenge what’s defined there. Discussions will still take place. Your guidelines will have several gaps that will leave margins for pure interpretation.

That’s exactly why these days I think that not being highly customizable is black’s greatest strength. Once you adopt it, your “code style czar” badge will be instantly dropped. And what a relief!

How do I feel about it now?

I love black. Code reviews these days have less bike-shedding and more meaningful contributions. They focus on what really matters, basically. Don’t get me wrong, I do think that style matters, but we now have black as our (not so) benevolent dictator in any discussion regarding that. The code doesn’t necessarily look exactly how I would like it to look. But at least there is consensus now and black is always ready to take the fall. No hard feelings at all.

A word of advice, if I may

I work on a small team, in a relatively new codebase well covered with tests. We rarely have more than 20 pull requests open simultaneosuly. That all made it easier to start using black.

Once you decide to adopt it, you’ll want to reformat your whole codebase using it. That means that most of your open PRs will have some sort of conflict, and that can be a pain if you have tons of them. The PR reformatting your code will likely be humongous. A solution to this may be to apply black incrementally in your codebase. Check out this Github Action to help you with that: Gradual Black Formatter.

Finally, your revision history will now have a huge “Reformat codebase” commit under your name. If you have the habit of digging into your project’s revisions, I am sure that’s going to bother you. The good news is that git blame allows you to ignore specific revisions so that they don’t show up when you are scavenging commits. You can do that via the --ignore-rev and --ignore-revs-file options.

This section is not meant to discourage you, as adopting black is worth the potential trouble. I just want you to know that you may face some roadbumps to get there.

A suggested setup

Once you managed to apply black to your whole codebase, you have to make sure that any new changes will be black-compliant. The easiest, but not so effective, way to do that is by kindly asking everyone to run black before any commit. Don’t get me wrong, it’s not that I don’t trust people to run it. The thing is that we’re all humans and we’ll just forget it.

My team enforces black via git commit hooks. To do that, we use the excellent pre-commit package and ask all the team members to run pre-commit install in their local setup. Once everyone does that, no one will be allowed to even commit their changes locally in case there are violations.

This is somewhat effective. But, as I said before, we’re all humans and humans forget stuff. I did forget it once (or maybe twice… 😄) when setting up the development environment in new machines. Thankfully, the project has a CI setup that fails the PR build in case black detects violations.

So this is what I suggest you to do:

  1. Use pre-commit to enforce black in local commits.
  2. Make sure your “Contribution Guidelines” doc provides the installation instructions.
  3. Setup a check on your CI to fail the build in case black detects violations.

This can all be easily achieved with pre-commit and GitHub Actions. I’ve created a very simple project to demonstrate that setup: https://github.com/stummjr/black_setup_project

Wrapping up

These days, I am a huge fan of black. Of course, there are still some lingering pet-peeves. But that’s just because black is as opinionated as me. Black ain’t gonna change, but I can. :)

Setting up black worked pretty well on my team, and may be worth a shot on yours as well.