3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

年末だしgolangci-lintのルールを見直してみる

Last updated at Posted at 2025-12-16

この記事は カオナビ Advent Calendar 2025 17日目の記事です。

golangci-lint

golangci-lint とは、Go の様々な linter などを一元的に管理してまとめて実行することが出来る静的解析ツールです。
今ではデファクトとなっているのでお世話になっている人も多いかと思います。

ただそのルールの運用となると、基本的には一度チームでコーディングルールを定めて運用が始まると、その後はバージョンアップに追従するくらいになるケースも多いと思います。
ですが、リリースを追いかけて見ると新しいバージョンで追加されている便利な機能も少なくありません。

どんなリリースがあったのかは公式の Changelog からおおよそ知ることが出来ます。

今回は最近のアップデートから私たちのチームで取り入れたものをいくつか紹介してみたいと思います。

gofumpt の Naked Return

これは検知された方も多いかもしれませんが、golangci-lintの v2.5.0 (gofumpt v0.9.1) において、名前付き戻り値を利用した関数で空のreturnを実行しているとエラーとして検知されるようになりました。
[参考] ProposalのIssue

名前付き戻り値とは、関数やメソッドの戻り値に名前を付けることができる記法で、特に同じ型の戻り値を複数返す場合などに名前をつけておくことで利用者側の可読性を高めることができます。

func div(a, b int) (q, rem int) {
	q = a / b
	rem = a % b
	return   // 戻り値を省略してもよい
}

ただこの機能の Naked Return (戻り値の変数を指定しないreturn) の部分については"可読性が悪い"として、エラーとして検知する変更が入りました。

func div(a, b int) (q, rem int) {
	q = a / b
	rem = a % b
	return q, rem // 戻り値を明記すべし
}

Proposalから2年弱を掛けて慎重に導入された機能でしたが、変更の影響範囲が大きかったことでライブラリのバージョンアップを躊躇う動きも一定あったようで、結局その後の golangci-lint v2.6.0 (gofumpt v0.9.2)で -extraに移動することとなりました。

extraはgofumptのオプション扱いの機能で、他には「同じ型の引数はまとめよう」というものがあります。

func Foo(bar string, baz string) {}  // こっちより
func Foo(bar, baz string) {}         // こっちのほうがいいよね

golangci-lintでは .golangci.ymlextra-rules オプションを有効化することでgofumptの -extraオプションが適用されます。

formatters:
  enable:
    - gofumpt
  settings:
    gofumpt:
      extra-rules: true

私たちのチームでは元々 extra-rules が有効化されていたのでこの件は対応しましたが、Naked Returnの変更差分が多すぎてバージョンアップをためらっているチームがあれば一度このオプションを無効化してみるのもアリかもしれません。

modernize

modernize は、golangci-lintには v2.6.0 から導入されたlinterです。
「古い書き方を、現代的なGoの書き方に書き換えましょう」と提案してくれます。

今回私たちのコードで modernize を有効化した際には

  • interface{}any に書き換える
  • slices.Contains を有効に使う
  • strings.Builder を有効に使う
  • range を利用する

あたりが検知されました。
主にテストコードでつい非効率な書き方をされていたものが多かったです。

strings.Builderについては、テストコードでテストデータを生成している箇所で

// バリデーションテスト: 101文字の名前を生成
longName := ""
for range 101 {
    longName += "あ"
}

のように書かれていた箇所を、

var longName strings.Builder
for range 101 {
    longName.WriteString("あ")
}
n := longName.String()

のように書けるよ。というものです。
テストコードの性能改善なのでクリティカルな箇所ではなかったですが、普段から検知してくれていると安心です。

ついつい古い書き方をしてしまう自分のようなエンジニアにも助かりますし、
昨今だとAIエージェントが古いコードを生成してくるのを防ぐにも一役買ってくれそうです。

revive

revive は強力なlinterですが、個別の細かなルールが多くアップデートも盛んなためメンテナンスが大変なlinterの1つかと思います。

ですが日々追加されているルールには有効なものも多いので、たまに棚卸しを実施出来ると発見があります。
オススメの棚卸し方法は enable-all-rules を活用する方法です。
「全てのルールを有効にする」という力強いオプションなので普段は false にしておいて、たまに余裕がある時にこの値を true に変更してチェックしてみるというやり方です。

  settings:
    revive:
      enable-all-rules: false  // 一時的にtrueにする
      rules:
        - name: var-naming // チームとして無視できるルールは明示的にdisabledにしておく
          disabled: true
        ...

今回は golangci-lint v2.5.0 ( revive: v1.12.0 )で追加された package-directory-mismatch によって、テストパッケージの定義ミスが見つかりました。

path/to/package/service/main_test.go:1:1: 
package service_test; expected package usecase_test (typecheck)
package usecase_test
// ディレクトリがserviceなので'service_test'が期待値だけどパッケージ名が'usecase_test'だよ
1 issues:
* typecheck: 1

こうすることで、新しく追加されているルールによって見落としていたちょっとしたミスが見つかることもあります。

おわりに

リリースが出るたびに毎回リリースノートを読んで設定を見直すというのは正直なかなか大変です。
余裕のあるときにしか出来ない活動ですが、年末の大掃除としてコードフリーズの合間に .golangci.yml を見直してみるのもよいのではないでしょうか?

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?