In this post, I will try to gather all my thoughts on the topic of automatic code formatting and why I personally don't like this approach. We will go for the most hippy tool — Black.
The focus is on consistency, not readability.
there is only one style
For some reason, python developers are sure that the style guide is needed only to make the code consistent and Black as a tool whose meaning in removing the style guide and strongly forcing the consistency. This is one of the author's main ideas — to make a minimally configurable tool that will make everything consistent.
But even PEP8 itself has devoted a whole paragraph to "readability counts" and mentions that a style guide helps avoid bugs as it introduces best practices to avoid code smells. And the key point — consistency is only important as it improves readability. But Black is not about this at all, Black removes visual cues, as it is written in the documentation 'It doesn't take previous formatting into account. It doesn't care about best practices and readable code. It's just formatting. That's it.
On this basis, I prefer to use a strict linter than an autoformatting tool no matter how good it is. A strict linter just won't pass your badly formatted code through CI, so there's nothing to format.
The goal is to reduce the load on the brain. The goal is achieved.
By borrowing ideas from gofmt, Black has close to zero configuration parameters, especially when it comes to formatting style variations. And I see a lot of developers who welcome this, they don't want to do formatting and in their opinion, it's a waste of time. I don't get it — they probably write great code 24/7 and don't read it from other team members. I write the code to make it work, then write tests, and then I format and rewrite it. I get a lot of help from flake8 with his whole army of plugins. But it's just me, I might be a bad example.
I especially like the comments that Black
is an excellent reduction in cognitive load
I don't think that's the goal. The goal here is maintainable code. Yeah, you don't have to think about formatting anymore, that's a plus, but in return, you lose something. Black doesn't give you any warning, no feedback on the code the linter makes.
Black is a cargo cult
Another excellent argument:
The choice of strict unifying, automatically enforced style is beyond any practical value. It is a matter of religion. You don't argue with religion.
Just stop selling Black at all costs, be reasonable.
Guido says the same thing:
You missed mypy. Simpler docs use markdown, not ReST (Sphinx). Black is overrated unless your team argues over style a lot. You don't need Pylint if you're using flake8. Never heard of poetry or dependabot. And you should use a CI solution, e.g. Travis-CI, to run your tests.
— Guido van Rossum (@gvanrossum) February 11, 2020
Configurable
It is not configurable.
Yes, the best tools do not need any configurations, but...
The lack of configurability means that everything you have before Black can be discarded. Code style, liner rules, CI... For me, it is too extreme on existing big projects. Many even integrate Black as part of CI — and if there is a bug in Black? You can't configure it without turning off some functionality, it just formats everything.
On small projects, on projects with juniors where you don't want to follow the code style, it will take root well but not everywhere.
Python is not go
Black is like gofmt for Python.
This is my personal opinion, I think go is a simple language where relatively simple structures are used and the language is not too expressive in terms of runtime execution.
Have a look at this little snippet of Go code from the How I Start Go tutorial.
package main
type openWeatherMap struct{}
func (w openWeatherMap) temperature(city string) (float64, error) {
resp, err := http.Get("http://api.openweathermap.org/data/2.5/weather?APPID=YOUR_API_KEY&q=" + city)
if err != nil {
return 0, err
}
defer resp.Body.Close()
var d struct {
Main struct {
Kelvin float64 json:"temp"
} json:"main"
}
if err := json.NewDecoder(resp.Body).Decode(&d); err != nil {
return 0, err
}
log.Printf("openWeatherMap: %s: %.2f", city, d.Main.Kelvin)
return d.Main.Kelvin, nil
}
If you've just come to Go, but you've already been writing on anything before, it won't surprise you when you read this little piece of code. At the same time, Python allows you to be pretty creative about the code you've written. For example, you can use metaclasses to self-register classes upon code initialization, add functions to the list of built-in functions, overload operators via magic methods, and use decorators...
Conclusion
In general, I believe that autoformatters are not needed by themselves, because it's both easier to automate the linker and easier to understand why something went wrong. And a good linter just doesn't pass the shitty code in the first place and helps you avoid code smells.
Autoformatters like Black are soulless, they won't understand how each specific case will be most readable. This should always be the developer's concern.
Now Black is shoving all over the place, I think it's a bit unreasonable. Black claims that we need to format the code so that it appeals to different scripts and automatic format routines that people have to adapt to. And I haven't mentioned his unreadable configuration file yet. But, of course, Black is not for nothing, and this tool can help a developer achieve his goal - to solve the problem he faces. But it's an option, not a solution. And I would only use Black if:
- You have an old code base that has been made by more than one generation of developers and you want to make it more or less readable.
- You don't want to bother with the styling guide on your little project.
- You have a team of juniors that you don't really want to keep track of but end up wanting a consistent code.
If you want beautiful and consistent code on your project, use style guides and linters, for example, flake8 or prospector on CI with plugins enabled. That allows you to avoid bikeshedding that black is basically doing.