Movatterモバイル変換


[0]ホーム

URL:


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

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

Avatar for ANDPAD inc ANDPAD inc
June 02, 2023

「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」の内容から学んだことをご紹介します。

Avatar for ANDPAD inc

ANDPAD inc

June 02, 2023
Tweet

More Decks by ANDPAD inc

See All by ANDPAD inc

Other Decks in Technology

See All in Technology

Featured

See All Featured

Transcript

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

  2. 自己紹介 小島 夏海 Twitter @replu5 • 株式会社アンドパッドでテックリードやってます • Go暦は約4年 ◦

    バックエンドAPIの開発に主に利用 • カンファレンス初登壇
  3. Go Style Guide 読みましたか?

  4. Go Style Guideとは • 2022/11/23 にGoogleから公開されたドキュメント [1] ◦ Go Style

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

    Guide と付属のドキュメントは読みやすく・慣用的 なGoを書くための現在のベストアプローチを体系化したもの ◦ Goのプロジェクトによって支援されているわけではない ▪ 公式ドキュメントではない [1] https://google.github.io/styleguide/go/index
  6. どうして読みやすいコードを書く必要があるのか • ソフトウェア開発は継続的な活動 ◦ 自分が書いたコードを他人や未来の自分が読んだり 修正したりすることが非常に多い ▪ 読みやすいコードを書くことは継続的な開発において大事

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

  8. Go Style Guide以外の情報元 • Effective Go[1] ◦ 公式のドキュメント ▪ 2009年のGoのリリースに合わせて書かれており、それ以

    降大きく更新はされていない ◦ 網羅的に書かれているが、最新の情報はない ▪ 例えば context や generics についての記載はない [1] https://go.dev/doc/effective_go
  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
  10. Go Style Guide以外の情報元 • Uber Go Style Guide[1] ◦ Uber社が公開してくれているドキュメント

    ▪ Uber社内のガイドラインを文書化したもの ◦ 具体的なコード例が多め [1] https://github.com/uber-go/guide/blob/master/style.md
  11. Go Style Guide の紹介

  12. 構成 • 構成としては3つに分かれている ◦ Style Guide : 基礎になるもの ◦ Style

    Decisions : Style Guideの内容について特定のポイント について書かれている ◦ Best Practices : 時間をかけて定着していったパターン
  13. それぞれの対象 名前 対象 Normative (一貫性を確立を目的としている ) Canonical (永続的なルール) Style Guide

    すべて Yes Yes Style Decisions メンター Yes No Best Practices 興味のある人 No No
  14. Style Guide • Style principles と Core Guideline の2項目 •

    Style principles は包括的な原則を優先度順で 5つ定義している ◦ Clarity : 目的と根拠が読み人にとって明確になっているか ◦ Simplicity : 単純な方法で目的を達するようになっているか ◦ Concision : 簡潔な状態になっているか ◦ Maintainability : 保守しやすいか ◦ Consistency : 既存のコードベースとの一貫性
  15. Style Guide • Core Guideline はスタイルの重要な要素を集めたもの ◦ 書式設定 ◦ MixedCaps

    ◦ Line length ◦ Naming ◦ Local consistency
  16. Style Decisions • 統一されたスタイルの決定と標準的な説明・例を提供 • 大項目としては下記 ◦ Naming ◦ Commentary

    ◦ Imports ◦ Errors ◦ Language ◦ Common libraries ◦ Useful test failures ◦ Test structure ◦ Non-decisions
  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
  18. Style principles

  19. Style Guide • Style principles は優先度順で5つ定義されている ◦ Clarity : 目的と根拠が読み人にとって明確になっているか

    ◦ Simplicity : 単純な方法で目的を達するようになっているか ◦ Concision : 簡潔な状態になっているか ◦ Maintainability : 保守しやすいか ◦ Consistency : 既存のコードベースとの一貫性 Clarity Maintainability Concision Simplicity Consistency 優先度: 高 優先度: 低
  20. Clarity • 目的と根拠が読む人にとって明確になっているか ◦ 読む人にとってのわかりやすさが最も大事 • Clarityには2つの側面がある ◦ コードが実施には何をやっているのか ◦

    なぜそのようにコードが書かれているのか
  21. Clarity 複数の型で現れる値はraw や型名などで補足する // Good limitStr := r.FormValue("limit") limit, err

    := strconv.Atoi(limitStr)
  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, })
  23. Clarity 0値のフィールドを省略する ことで指定されている オプションが目立つ // Good ldb := leveldb.Open("/my/table", &db.Options{

    BlockSize: 1<<16, ErrorIfDBExists: true, })
  24. Simplicity • ユーザー・読む人・メンテナーにとってシンプルに なっているか • 同じアイデアを実現する方法がいくつかある場合は、 最も標準的なツールを使用する ◦ 言語のコアとなる機能 ◦

    標準ライブラリ ◦ サードパーティライブラリの利用や自作
  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
  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)
  27. Concision • 理解する上で混乱するような状態となっていないか ◦ 以下のようなものがノイズとなる ▪ 繰り返されるコード ▪ 外部のシンタックス ▪

    不透明な命名 ▪ 不必要な抽象化 ▪ 不必要な空白
  28. Concision 繰り返しを避ける 以下のようなもので繰り返 しが発生しないように注意 する • ファイル名 • インポートパス •

    パッケージ名 • メソッド/関数名 • 型名 // Bad widget.NewWidget db.LoadFromDatabase // Good widget.New db.Load
  29. Concision pkg名でローカル変数と 被りそうなものは使わない // Bad package buf // Good package

    bufio
  30. Concision パッケージ名と同じ変数名 を使う時にパッケージ名に エイリアスを設定する場合 は urlpkg のように pkg を suffix

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

    とする // Good import urlpkg "net/url" func main() { url := "https://gocon.jp/2023/" urlpkg.Parse(url) )
  32. Maintainability • 何度も編集されるものなのでメンテナンス性も大事 • ここでのメンテナンス性の定義 ◦ 正しく修正することが容易 ◦ 拡張しやすいように構造化されている ◦

    前提条件が明確になっていてコードの構造ではなく、 問題の構造に対応する抽象化をしている ◦ 不要な結合を避けて、使用する機能だけを含める ◦ 重要なロジックが正しいことを確認するための 包括的なテストスイート持つ ◦ テストが失敗した場合、明確で実用的な内容を出力する
  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) }
  34. Maintainability 機能が特定できる失敗 メッセージを表示する 入力が短い場合は入力も 失敗メッセージに含める // Bad t.Errorf("got %v, want

    %v", got , want) // Good t.Errorf("YourFunc(%v) = %v, want %v", arg, got, want)
  35. Consistency • 既存のコードベースと一貫している ◦ チーム・パッケージ・コンテキスト内・単一ファイル等で 似たようなコードに見え・感じ・振る舞う • 他の原則を上書きするものではないが、状況によって は一貫性を優先することも有益

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

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

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

  39. 可読性の高いコードの書き方 • 可読性を高くしたいと言ってもStyle principlesで紹介 されていたように可読性は複数の要素でなりたっている ◦ どれかを優先するとどれかが成り立たない場合もある ▪ 何を大事にするか優先度を決めていくことが大切 ◦

    可読性は読む人とってのものなのでチーム全体で 同じ方向を向いていく必要がある
  40. 読んだ後どうするのがよさそうか • Go Style Guideがすべての場面で正しいというわけでは なく、チームのフェーズや規模等によって重要視 するものは異なる ◦ Go Style

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

    Guide等を元に共通認識を作っていくことが大切 • 最終的にはGoogleやUberのようにガイドラインを 作っていくのもよさそう ◦ 努力だけで守り続けるのは大変なのでリンターやコード生成も 活用していくとよい ▪ 既存のリンターを活用することで検出できることも多い
  42. アンドパッドではどうだったか

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

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

    いる等がある
  45. まとめ

  46. まとめ • Googleが公開した「Go Style Guide」についての概要 を紹介しました • その内容を元に可読性の高いコードをどう書いていく かについて紹介しました ◦

    可読性は複数の要素でなりたっている ◦ Go Style Guideが絶対ではないのでチームで同じ方向を 向けるように話していくことが大切
  47. おまけ • リンター作ったので紹介 ◦ sliceがnilかどうかの比較は slice == nil ではなく len(slice)

    == 0 で比較する必要がある ◦ Style Guideでも紹介しているのですがこれを検出する リンターがなさそうだったので作りました https://github.com/replu/slicenilcmp

[8]ページ先頭

©2009-2025 Movatter.jp