Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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

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

Go Conference 2023 ( 2023/06/02 Fri. )
https://gocon.jp/2023/
登壇資料

株式会社アンドパッド
プロダクトテックリード
小島 夏海

みなさん、Googleが公開したGo Style Guideは読みましたか?

ソフトウェア開発は継続的な活動であり、一般的に複数人で行うことが多いです。
継続的に複数人で開発を行う場合、自分が書いたコードを他人が読んだり修正したりすることが非常に多いです。
そのため可読性の高いコードを書くことは開発効率やメンテナンス性の向上に役立ちます。

Goはシンプルな言語ですが、どのように書くべきか悩むことが全くないわけではなく、そのような時従来はEffective Go/Uber Go Style Guide/OSSコード等を参考にどのように書くか決めていたと思います。
これらに加え、昨年末にGoogleからGo Style Guideが公開されました。GO Style Guide では従来よりも幅広い範囲について解説がされています。(例えば、テストの書き方については非常に詳しく記載されています)
このトークでは「実際に開発しているプロダクトではどのように書いていたか」や「読んだ内容を元に現在はどう書いているか」を交えつつ「Go Style Guide」の内容から学んだことをご紹介します。

ANDPAD inc

June 02, 2023
Tweet

More Decks by ANDPAD inc

Other Decks in Technology

Transcript

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

    View Slide

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

    View Slide

  3. Go Style Guide 読みましたか?

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  9. 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

    View Slide

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

    View Slide

  11. Go Style Guide の紹介

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  18. Style principles

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  22. 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,
    })

    View Slide

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

    View Slide

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

    View Slide

  25. 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

    View Slide

  26. 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)

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  33. 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)
    }

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  45. まとめ

    View Slide

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

    View Slide

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

    View Slide