Slide 1

Slide 1 text

小島 夏海 「Go Style Guide」から学んだ 可読性の高いコードの書き方

Slide 2

Slide 2 text

自己紹介 小島 夏海 Twitter @replu5 ● 株式会社アンドパッドでテックリードやってます ● Go暦は約4年 ○ バックエンドAPIの開発に主に利用 ● カンファレンス初登壇

Slide 3

Slide 3 text

Go Style Guide 読みましたか?

Slide 4

Slide 4 text

Go Style Guideとは ● 2022/11/23 にGoogleから公開されたドキュメント [1] ○ Go Style Guide と付属のドキュメントは読みやすく・慣用的 なGoを書くための現在のベストアプローチを体系化したもの ○ Goのプロジェクトによって支援されているわけではない ■ 公式ドキュメントではない [1] https://google.github.io/styleguide/go/index

Slide 5

Slide 5 text

Go Style Guideとは ● 2022/11/23 にGoogleから公開されたドキュメント [1] ○ Go Style Guide と付属のドキュメントは読みやすく・慣用的 なGoを書くための現在のベストアプローチを体系化したもの ○ Goのプロジェクトによって支援されているわけではない ■ 公式ドキュメントではない [1] https://google.github.io/styleguide/go/index

Slide 6

Slide 6 text

どうして読みやすいコードを書く必要があるのか ● ソフトウェア開発は継続的な活動 ○ 自分が書いたコードを他人や未来の自分が読んだり 修正したりすることが非常に多い ■ 読みやすいコードを書くことは継続的な開発において大事

Slide 7

Slide 7 text

Go Style Guide 以外には無いのか?

Slide 8

Slide 8 text

Go Style Guide以外の情報元 ● Effective Go[1] ○ 公式のドキュメント ■ 2009年のGoのリリースに合わせて書かれており、それ以 降大きく更新はされていない ○ 網羅的に書かれているが、最新の情報はない ■ 例えば context や generics についての記載はない [1] https://go.dev/doc/effective_go

Slide 9

Slide 9 text

Go Style Guide以外の情報元 ● CodeReviewComments[1] と TestComments[2] ○ 公式のドキュメント ■ どちらもGo言語のリポジトリのWiki内のコンテンツ ○ CodeReviewComments はレビュー中に寄せられた一般的なコ メントがまとめられている ○ TestComments はテストについてまとめられている [1] https://github.com/golang/go/wiki/CodeReviewComments [2] https://github.com/golang/go/wiki/TestComments

Slide 10

Slide 10 text

Go Style Guide以外の情報元 ● Uber Go Style Guide[1] ○ Uber社が公開してくれているドキュメント ■ Uber社内のガイドラインを文書化したもの ○ 具体的なコード例が多め [1] https://github.com/uber-go/guide/blob/master/style.md

Slide 11

Slide 11 text

Go Style Guide の紹介

Slide 12

Slide 12 text

構成 ● 構成としては3つに分かれている ○ Style Guide : 基礎になるもの ○ Style Decisions : Style Guideの内容について特定のポイント について書かれている ○ Best Practices : 時間をかけて定着していったパターン

Slide 13

Slide 13 text

それぞれの対象 名前 対象 Normative (一貫性を確立を目的としている ) Canonical (永続的なルール) Style Guide すべて Yes Yes Style Decisions メンター Yes No Best Practices 興味のある人 No No

Slide 14

Slide 14 text

Style Guide ● Style principles と Core Guideline の2項目 ● Style principles は包括的な原則を優先度順で 5つ定義している ○ Clarity : 目的と根拠が読み人にとって明確になっているか ○ Simplicity : 単純な方法で目的を達するようになっているか ○ Concision : 簡潔な状態になっているか ○ Maintainability : 保守しやすいか ○ Consistency : 既存のコードベースとの一貫性

Slide 15

Slide 15 text

Style Guide ● Core Guideline はスタイルの重要な要素を集めたもの ○ 書式設定 ○ MixedCaps ○ Line length ○ Naming ○ Local consistency

Slide 16

Slide 16 text

Style Decisions ● 統一されたスタイルの決定と標準的な説明・例を提供 ● 大項目としては下記 ○ Naming ○ Commentary ○ Imports ○ Errors ○ Language ○ Common libraries ○ Useful test failures ○ Test structure ○ Non-decisions

Slide 17

Slide 17 text

Best Practices ● Dicisionsの内容の具体的な例や補足がある ● 大項目としては下記 ○ Naming ○ Package size ○ Imports ○ Error handling ○ Documentation ○ Variable declarations ○ Function argument lists ○ Complex command-line interfaces ○ Tests ○ String concatenation ○ Global state

Slide 18

Slide 18 text

Style principles

Slide 19

Slide 19 text

Style Guide ● Style principles は優先度順で5つ定義されている ○ Clarity : 目的と根拠が読み人にとって明確になっているか ○ Simplicity : 単純な方法で目的を達するようになっているか ○ Concision : 簡潔な状態になっているか ○ Maintainability : 保守しやすいか ○ Consistency : 既存のコードベースとの一貫性 Clarity Maintainability Concision Simplicity Consistency 優先度: 高 優先度: 低

Slide 20

Slide 20 text

Clarity ● 目的と根拠が読む人にとって明確になっているか ○ 読む人にとってのわかりやすさが最も大事 ● Clarityには2つの側面がある ○ コードが実施には何をやっているのか ○ なぜそのようにコードが書かれているのか

Slide 21

Slide 21 text

Clarity 複数の型で現れる値はraw や型名などで補足する // Good limitStr := r.FormValue("limit") limit, err := strconv.Atoi(limitStr)

Slide 22

Slide 22 text

Clarity 0値のフィールドを省略する ことで指定されている オプションが目立つ // Bad ldb := leveldb.Open("/my/table", &db.Options{ BlockSize: 1<<16, ErrorIfDBExists: true, // These fields all have their zero values. BlockRestartInterval: 0, Comparer: nil, })

Slide 23

Slide 23 text

Clarity 0値のフィールドを省略する ことで指定されている オプションが目立つ // Good ldb := leveldb.Open("/my/table", &db.Options{ BlockSize: 1<<16, ErrorIfDBExists: true, })

Slide 24

Slide 24 text

Simplicity ● ユーザー・読む人・メンテナーにとってシンプルに なっているか ● 同じアイデアを実現する方法がいくつかある場合は、 最も標準的なツールを使用する ○ 言語のコアとなる機能 ○ 標準ライブラリ ○ サードパーティライブラリの利用や自作

Slide 25

Slide 25 text

Simplicity genericsは使わないで済む 場合は使わない // Bad func ReadFour[T io.Reader](r T) ([]byte, error) // Good func ReadFour(r io.Reader) ([]byte, error) The original code was shown by GopherCon 2021: Robert Griesemer & Ian Lance Taylor - Generics! https://www.youtube.com/watch?v=Pa_e9EeCdy8

Slide 26

Slide 26 text

Simplicity %qを使う // Bad fmt.Printf("value \"%s\" looks like English text", someText) fmt.Printf("value '%s' looks like English text", someText) // Good fmt.Printf("value %q looks like English text", someText)

Slide 27

Slide 27 text

Concision ● 理解する上で混乱するような状態となっていないか ○ 以下のようなものがノイズとなる ■ 繰り返されるコード ■ 外部のシンタックス ■ 不透明な命名 ■ 不必要な抽象化 ■ 不必要な空白

Slide 28

Slide 28 text

Concision 繰り返しを避ける 以下のようなもので繰り返 しが発生しないように注意 する ● ファイル名 ● インポートパス ● パッケージ名 ● メソッド/関数名 ● 型名 // Bad widget.NewWidget db.LoadFromDatabase // Good widget.New db.Load

Slide 29

Slide 29 text

Concision pkg名でローカル変数と 被りそうなものは使わない // Bad package buf // Good package bufio

Slide 30

Slide 30 text

Concision パッケージ名と同じ変数名 を使う時にパッケージ名に エイリアスを設定する場合 は urlpkg のように pkg を suffix とする // Bad import "net/url" func main() { url := "https://gocon.jp/2023/" url.Parse(url) // エラーになる )

Slide 31

Slide 31 text

Concision パッケージ名と同じ変数名 を使う時にパッケージ名に エイリアスを設定する場合 は urlpkg のように pkg を suffix とする // Good import urlpkg "net/url" func main() { url := "https://gocon.jp/2023/" urlpkg.Parse(url) )

Slide 32

Slide 32 text

Maintainability ● 何度も編集されるものなのでメンテナンス性も大事 ● ここでのメンテナンス性の定義 ○ 正しく修正することが容易 ○ 拡張しやすいように構造化されている ○ 前提条件が明確になっていてコードの構造ではなく、 問題の構造に対応する抽象化をしている ○ 不要な結合を避けて、使用する機能だけを含める ○ 重要なロジックが正しいことを確認するための 包括的なテストスイート持つ ○ テストが失敗した場合、明確で実用的な内容を出力する

Slide 33

Slide 33 text

Maintainability 構造体のフィールドごと ではなく、全体を比較する var got, want BlogPost // Bad if got.Title != want.Title { ... } if got.Body != want.Body{ ... } // Good if diff := cmp.Diff(got, want); diff != "" { t.Errorf(diff) }

Slide 34

Slide 34 text

Maintainability 機能が特定できる失敗 メッセージを表示する 入力が短い場合は入力も 失敗メッセージに含める // Bad t.Errorf("got %v, want %v", got , want) // Good t.Errorf("YourFunc(%v) = %v, want %v", arg, got, want)

Slide 35

Slide 35 text

Consistency ● 既存のコードベースと一貫している ○ チーム・パッケージ・コンテキスト内・単一ファイル等で 似たようなコードに見え・感じ・振る舞う ● 他の原則を上書きするものではないが、状況によって は一貫性を優先することも有益

Slide 36

Slide 36 text

Style principlesまとめ ● 5つの要素を定義しており、それらについて優先度が 設定されている Clarity Maintainability Concision Simplicity Consistency 優先度: 高 優先度: 低

Slide 37

Slide 37 text

「Go Style Guide」から学んだ 可読性の高いコードの書き方

Slide 38

Slide 38 text

「Go Style Guide」から学んだ 可読性の高いコードの書き方

Slide 39

Slide 39 text

可読性の高いコードの書き方 ● 可読性を高くしたいと言ってもStyle principlesで紹介 されていたように可読性は複数の要素でなりたっている ○ どれかを優先するとどれかが成り立たない場合もある ■ 何を大事にするか優先度を決めていくことが大切 ○ 可読性は読む人とってのものなのでチーム全体で 同じ方向を向いていく必要がある

Slide 40

Slide 40 text

読んだ後どうするのがよさそうか ● Go Style Guideがすべての場面で正しいというわけでは なく、チームのフェーズや規模等によって重要視 するものは異なる ○ Go Style Guide等を元に共通認識を作っていくことが大切 ● 最終的にはGoogleやUberのようにガイドラインを 作っていくのもよさそう ○ 努力だけで守り続けるのは大変なのでリンターやコード生成も 活用していくとよい ■ 既存のリンターを活用することで検出できることも多い

Slide 41

Slide 41 text

読んだ後どうするのがよさそうか ● Go Style Guideがすべての場面で正しいというわけでは なく、チームのフェーズや規模等によって重要視 するものは異なる ○ Go Style Guide等を元に共通認識を作っていくことが大切 ● 最終的にはGoogleやUberのようにガイドラインを 作っていくのもよさそう ○ 努力だけで守り続けるのは大変なのでリンターやコード生成も 活用していくとよい ■ 既存のリンターを活用することで検出できることも多い

Slide 42

Slide 42 text

アンドパッドではどうだったか

Slide 43

Slide 43 text

できていたこと ● 書かれていた内容自体は割とできていた ○ 週1でgopher会と題してチームの垣根を超えて話しており、 そこで書き方について話すこともあり改善が進んでいる ○ 後から入ってきたメンバーにも既存メンバーがPR等で過去の 情報を共有している

Slide 44

Slide 44 text

できていないこと ● レビュー時に何を意識してコメントしているかは 話せておらず、Style principles のような明確な 優先順位は決まっていない ● 具体的な内容としても、テスト失敗時のメッセージに 関してできている箇所はできていない箇所が混ざって いる等がある

Slide 45

Slide 45 text

まとめ

Slide 46

Slide 46 text

まとめ ● Googleが公開した「Go Style Guide」についての概要 を紹介しました ● その内容を元に可読性の高いコードをどう書いていく かについて紹介しました ○ 可読性は複数の要素でなりたっている ○ Go Style Guideが絶対ではないのでチームで同じ方向を 向けるように話していくことが大切

Slide 47

Slide 47 text

おまけ ● リンター作ったので紹介 ○ sliceがnilかどうかの比較は slice == nil ではなく len(slice) == 0 で比較する必要がある ○ Style Guideでも紹介しているのですがこれを検出する リンターがなさそうだったので作りました https://github.com/replu/slicenilcmp