ゆるやかにgolangci-lintのルールを強くする / Kyoto.go #56
by
utagawa kiki
Link
Embed
Share
Beginning
This slide
Copy link URL
Copy link URL
Copy iframe embed code
Copy iframe embed code
Copy javascript embed code
Copy javascript embed code
Share
Tweet
Share
Tweet
Slide 1
Slide 1 text
ゆるやかにgolangci-lint のルールを強くする id:utgwkk / @utgwkk (うたがわきき) 2024/12/15 Kyoto.go #56 オフライン忘年LT会@マネフォ京都 1
Slide 2
Slide 2 text
自己紹介 ● うたがわきき (@utgwkk) ● 株式会社はてな ○ Webアプリケーションエンジニア ● 好きなパッケージはreflect 2
Slide 3
Slide 3 text
アジェンダ ● golangci-lintについて ● lintルールを強くするためにまずやったこと ● あとからlintルールを強くするコツ 3
Slide 4
Slide 4 text
アジェンダ ● golangci-lintについて ● lintルールを強くするためにまずやったこと ● あとからlintルールを強くするコツ 4
Slide 5
Slide 5 text
golangci-lintについて ● https://golangci-lint.run/ ● Goのlinterをまとめて実行するrunner ● アンケート: golangci-lintを使っている? 5
Slide 6
Slide 6 text
アジェンダ ● golangci-lintについて ● lintルールを強くするためにまずやったこと ● あとからlintルールを強くするコツ 6
Slide 7
Slide 7 text
golangci-lintは使っていたが ● 最小構成でスタート ● 検査しないファイルだけ設定する ● デフォルトで有効のlinterのみ ● CIで走らせる 7
Slide 8
Slide 8 text
設定をよくする機運が高まる ● 実装ミスがlinterで検知されると嬉しい ● どこまで検査してくれるのか知らなかった ● 底力を引き出せていないのでは 8
Slide 9
Slide 9 text
9 よっしゃやっていくぞ
Slide 10
Slide 10 text
10 まずは何をするか ● 全てのlinterを有効にする?
Slide 11
Slide 11 text
11 全てのlinterを有効にする? ● エラーが大量に出てしまう ● 既存のコードベースに合わせた調整が困難 ● 設定項目が無限にある!!
Slide 12
Slide 12 text
まずはgolangci-lintを知る (1) ● golangci-lint自体の設定項目を知る ● どこまで柔軟にできるか・できないか知る ● https://golangci-lint.run/usage/configur ation/ 12
Slide 13
Slide 13 text
まずはgolangci-lintを知る (2) ● 搭載されているlinter一覧がある ● 上から順番に見ていく ● 1つずつ有効にして検査してみる ● https://golangci-lint.run/usage/linters/ 13
Slide 14
Slide 14 text
14 golangci-lintを知った ● どんなlinterが搭載されているのか知る ● 無理なく導入できるlinter・設定が分かる ● 既存のコードベースに合わない設定を知る
Slide 15
Slide 15 text
15 完全に理解した
Slide 16
Slide 16 text
アジェンダ ● golangci-lintについて ● lintルールを強くするためにまずやったこと ● あとからlintルールを強くするコツ 16
Slide 17
Slide 17 text
17 あとからlintルールを強くするコツ ● linterの象限を考えて導入する ● 部分的にlinterを有効化・無効化する
Slide 18
Slide 18 text
linterの象限 ● コーディング規約に合う・合わない ● 簡単に導入できる・できない ● (他にもあると思うけど簡略化した) 18
Slide 19
Slide 19 text
コーディング規約に合う ● コーディング規約に合う・簡単に導入できる ○ linterに引っかかるコードがない (多くない) ○ 機械的に修正できて動作確認が容易である ○ 設定を少し調整したら有効化できる ● 迷わず有効化する 19
Slide 20
Slide 20 text
コーディング規約に合う ● コーディング規約に合う・簡単に導入できない ○ 既存のコードが引っかかりまくる ○ 機械的に修正できるか判断しづらい 20
Slide 21
Slide 21 text
コーディング規約に合う ● 部分的にlinterを有効化できないか検討する ○ 特定のファイルだけ有効・無効にする ○ 特定のディレクトリ以下だけ有効・無効にする ○ 既存のコードに対してlinterを無効化する ● (具体的な実現方法は後述します) 21
Slide 22
Slide 22 text
コーディング規約に合わない ● コーディング規約に合わないlinter ● 基本的には導入しなくていいだろう ○ 簡単には導入できないなら、なおさら ○ こういうlinterがある、というのを知っておくことは 損にはならない 22
Slide 23
Slide 23 text
23 部分的にlinterを有効化・無効化する ● ファイル単位で切り替える ○ 例: テストコードかどうかで切り替える ● //nolint: ディレクティブを活用する ● 固有のディレクティブを活用する ○ 例: exhaustruct
Slide 24
Slide 24 text
24 ファイル単位で切り替える ● exclude-files ○ 指定したファイルは検査しない ● exclude-dirs ○ 指定したディレクトリ以下は検査しない ● exclude-rules ○ 指定したファイルで有効にするlinterを変える
Slide 25
Slide 25 text
25 例: テストコードかどうかで切り替える # テストコードでは特定の linterを無効化する例 exclude-rules: - path: _test\.go linters: - contextcheck - noctx
Slide 26
Slide 26 text
26 //nolint: を活用する ● //nolint: で始まるコメントを書いた行に対す るlintエラーは報告されない ○ eslint-disable-next-lineみたいなイメージ ● lintエラーを抑制する理由を書ける ○ //nolint:unused // ここに理由
Slide 27
Slide 27 text
//nolint: を活用する var x int //nolint:unused // インライン //nolint:unused // 次の行 var y int 27
Slide 28
Slide 28 text
28 ディレクティブを活用する ● linter固有のディレクティブが使える場合があ る ○ 同じファイル内でも挙動を切り替えられる ○ 徐々にコードベースを良くする足がかりとなる
Slide 29
Slide 29 text
29 固有のディレクティブを活用する ● 例: exhaustruct ○ structリテラルの全てのフィールドに値が埋まってい ることを検査するlinter ○ //exhaustruct:enforce と書いた次の行だけlinterの 検査対象となる
Slide 30
Slide 30 text
まとめ ● 一気に全部やろうとしない ○ enable-all, disable-all以外の選択肢もある ○ 「要はバランス」 ● 部分的に有効化できるルールを活用する ○ 敷いたガードレールに行き手を阻まれないように 30
Slide 31
Slide 31 text
31 ここから先 時間余ったら・質問があっ たら取り上げるコーナー
Slide 32
Slide 32 text
トピック集 ● linterから見るGoの進化 ● 独自linterの運用 (未解決) ● 循環的複雑度をlintする? ● チームで有効にしているlinter ● ISUCONでもlinterを活用している 32
Slide 33
Slide 33 text
linterから見るGoの進化 ● exportloopref ○ ループ変数をコピーせずに参照を取るコードを検出する ● copyloopvar ○ ループの先頭でループ変数をコピーするコードを検出する ● linter同士が衝突している? 33
Slide 34
Slide 34 text
linterから見るGoの進化 ● Go 1.22でループ変数がコピーされるように なった ○ Fixing For Loops in Go 1.22 ● exportlooprefがdeprecatedになった 34
Slide 35
Slide 35 text
linterから見るGoの進化 ● 言語の進化によって正解が変わる ● Go 1.23で導入されたiteratorに対する linterもいつかは出てくるでしょう 35
Slide 36
Slide 36 text
linterから見るGoの進化 ● Go 1.22以降のループ変数のコピーについて は以下の資料が詳しい ○ 詳解 "Fixing For Loops in Go 1.22" 自作linterを golangci-lintへコントリビュートした話 36
Slide 37
Slide 37 text
独自linterの運用 (未解決) ● 独自linterは意外と簡単に書ける ○ コツが掴めたらいける ○ 日本語の情報も割とある ■ つくってまなぶ静的解析のすすめ - LayerX エンジニアブロ グ など 37
Slide 38
Slide 38 text
独自linterの運用 (未解決) ● CIにどうやって組み込む? ○ CIがコケないと無視される (無視してしまう) ● golangci-lintにプラグインの仕組みはあるが ○ カスタムビルドしたバイナリを使う必要がある ○ linter側でinitでプラグインを登録する必要がある 38
Slide 39
Slide 39 text
独自linterの運用 (未解決) ● go vet -vettoolの仕組みがgolangci-lintでも 使えると楽になりそうだが…… ○ Goの静的解析ツールを簡単に使うためのエコシステム について考える #golang - tenntenn.dev 39
Slide 40
Slide 40 text
独自linterの運用 (未解決) ● 既存のlinterで解決できないか検討する ○ 探すと意外と見つかることもある ○ 正規表現マッチで事足りるならforbidigoとか ● 探したけど見つからない場合 ○ 新規性チャンス 40
Slide 41
Slide 41 text
独自linterの運用 (未解決) ● 作らずに話すのもな、と思って実装した ○ https://github.com/utgwkk/goqumysqllint ○ goqu (クエリビルダ) に対するlinter 41
Slide 42
Slide 42 text
循環的複雑度をlintする? ● gocyclo linterで検査できるが ● 答えを持ち合わせていない ○ 最大値をどこに設定すべきか ○ 「lintエラー」にすべきなのか 42
Slide 43
Slide 43 text
チームで有効にしているlinter ● contextcheck ● depguard ● exhaustive ● exhaustruct ● fatcontext ● gocheckcompilerdirec tives ● makezero 43 ● mirror ● misspell ● nilerr ● noctx ● nolintlint ● predeclared ● reassign ● unconvert
Slide 44
Slide 44 text
contextcheck ● context.Contextを引き回しているかどうか検査 するlinter ● テストコードでは無効にしている ○ ヘルパー関数がいっぱいあるから ● https://github.com/kkHAIKE/contextcheck 44
Slide 45
Slide 45 text
exhaustive ● enumに対するswitch-caseの分岐が網羅され ているか検査するlinter ● https://github.com/nishanths/exhaustive 45
Slide 46
Slide 46 text
exhaustruct ● structの全てのフィールドに値が埋まってい ることを検査するlinter ● https://github.com/GaijinEntertainment /go-exhaustruct 46
Slide 47
Slide 47 text
fatcontext ● context.Contextが肥大化するコードを検出 するlinter ○ 例: forループ内でcontext.Contextを同じ変数に再代 入する ● https://github.com/Crocmagnon/fatcon text 47
Slide 48
Slide 48 text
gocheckcompilerdirectives ● compiler directiveが正しいことを検査する linter ○ //go: で始まるコメント群のこと ● https://github.com/leighmcculloch/goch eckcompilerdirectives 48
Slide 49
Slide 49 text
makezero ● スライスのlenを指定して初期化したのに appendしているコードを検出するlinter ● https://github.com/ashanbrown/makezero 49
Slide 50
Slide 50 text
misspell ● スペルミスを検出するlinter ● 同名のツールは有名だと思う ○ 実はGoのlinterとしても走らせられる ● https://github.com/client9/misspell 50
Slide 51
Slide 51 text
nilerr ● 適切にエラーハンドリングできていないコード を検出するlinter ○ if err != nil の分岐内でエラーを返していない ○ if err == nil の分岐内でエラーを返している ● https://github.com/gostaticanalysis/nilerr 51
Slide 52
Slide 52 text
noctx ● context.Contextを使わずにHTTPリクエス トを送るコードを検出するlinter ● https://github.com/sonatard/noctx 52
Slide 53
Slide 53 text
nolintlint ● //nolint コメントが適切に使われていることを 検査するlinter ○ 抑制するlinterが指定されている、など ● https://github.com/ashanbrown/nolintlint 53
Slide 54
Slide 54 text
predeclared ● Goの予約済キーワードと重複する変数名を使っ ていないか検査するlinter ○ Goの予約済キーワードの一覧 ● https://github.com/nishanths/predeclared 54
Slide 55
Slide 55 text
reassign ● package単位のグローバル変数を他のpackageか ら再代入していないか検査するlinter ● https://github.com/curioswitch/go-reassign 55
Slide 56
Slide 56 text
unconvert ● 不要な型変換を行うコードを検出するlinter ○ int型をint型に変換する、など ● https://github.com/mdempsky/unconvert 56
Slide 57
Slide 57 text
チームで有効にしているlinter ● いかがでしたか? ● デフォルト有効のlinterも有効です 57
Slide 58
Slide 58 text
ISUCONでもlinterを活用している ● 前提: ISUCONについて ○ >ISUCONとはLINEヤフー株式会社が運営窓口となっ て開催している、お題となるWebサービスを決められ たレギュレーションの中で限界まで高速化を図る チューニングバトルです ○ https://isucon.net/ 58
Slide 59
Slide 59 text
ISUCONでもlinterを活用している ● 前提: ISUCONについて ○ つまり? ○ 8時間でWebサービスを高速にする 59
Slide 60
Slide 60 text
ISUCONでもlinterを活用している ● デフォルト設定でlintされている ● 呼んでない関数や使ってない引数が分かる ○ 実装をスリムにできると有利 ○ 使ってない実装をじゃんじゃん消せると有利 60