ゆるやかにgolangci-lintのルールを強くする / Kyoto.go #56
by
utagawa kiki
×
Copy
Open
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
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