Code reviews containing Go code have unnecessary and ongoing debates about style because gofmt
is not opinionated enough.
gofmt
does not do enough formatting and particularly it does not take the line length into account.
Comparing gofmt
with prettier, in code reviews for a Javascript codebase, there are rarely debates about style-related issues because prettier
is very opinionated.
Given three versions of the same Go code, but formatted differently by different developers:
Version A:
package x
import (
"bytes"
"context"
)
type User struct {}
type Mailer struct {}
// Comment showing where an 80 column ruler would appear.
// ---------------------------------------------------------------------------->
func (m Mailer) sendEmailToUser(ctx context.Context, u User, subject, html, text *bytes.Buffer) error {
return nil
}
Version B:
package x
import (
"bytes"
"context"
)
type User struct {}
type Mailer struct {}
// Comment showing where an 80 column ruler would appear.
// ---------------------------------------------------------------------------->
func (m Mailer) sendEmailToUser(
ctx context.Context,
u User,
subject,
html,
text *bytes.Buffer,
) error {
return nil
}
Version C:
package x
import (
"bytes"
"context"
)
type User struct {}
type Mailer struct {}
// Comment showing where an 80 column ruler would appear.
// ---------------------------------------------------------------------------->
func (m Mailer) sendEmailToUser(
ctx context.Context,
u User,
subject, html, text *bytes.Buffer,
) error {
return nil
}
A code review containing either of these versions unnecessarily creates the possibility of a debate between developers about how the method arguments should be formatted:
- Should they all be on one line?
- Should each parameter have its own line?
- Should some arguments be on the same line while others are on a new line?
We want to avoid the possibility of this debate entirely by having gofmt
format as much as possible (best effort) in the same way.
The code is (arguably) not as readable because the line length has not been taken into account. In Version A, the comment shows that the method arguments extend beyond an 80 column ruler.
With regards to the choice of an 80 column ruler, extracts from prettier's print width reasoning are referred to in order to justify the choice:
In code style guides, maximum line length rules are often set to 100 or 120. However, when humans write code, they don't strive to reach the maximum number of columns on every line. Developers often use whitespace to break up long lines for readability. In practice, the average line length often ends up well below the maximum.
Prettier, on the other hand, strives to fit the most code into every line. With the print width set to 120, prettier may produce overly compact, or otherwise undesirable code.
The example in this experience report shows different ways to format method arguments. However, the problem of gofmt
being not opinionated enough applies to other parts of Go code as well - the underlying problem is that given N ways of writing the same Go code, gofmt
does not output one universal format.
Note: published in "Go Experience Reports"
nice 👍