はじめに
2019年のアドベントカレンダーではGo言語がその7まで続いて 3 、盛り上がりを見せたのは記憶に新しいです。当社でもGoを扱う案件が増えてきて、社内でもいろいろなメンバーが趣味や業務でGoを書いています。日々新しいGopherが生まれています。
さて、GoをはじめようとA Tour of Goをやってみたが、Goらしいコードがわからない、Go言語をより深く学びたい、という方も多いのではないでしょうか。Go言語は一部を除いて言語自体がGoで書かれており、標準パッケージはGoらしい書き方のお手本とも言われたりします。
標準パッケージのコードリーディングをしてみたいという方も多いのではないでしょうか? しかし社内で標準パッケージのコードリーディング会を取り組んでいきたいけど難しそう、以下のような疑問があるかもしれません。
- 進め方がわからない
- どのパッケージを読んだらいいかわからない
- 1回あたりの粒度感はどれくらいが妥当なのかわからない
そこで本記事では社内で取り組んでいるGoの標準コードリーディング会の取り組みを紹介しながら、上記のような質問に答えていきたいと思います。
なおコードリーディング自体のテクニックはインターネット上でもたくさん紹介されていると思いますので、本記事では省略します。
目的
そもそもこのGoの標準ライブラリのコードリーディング会をどういった目的で実施しているか説明します。
- 標準パッケージの使い方を知る
- GoDocを読んで仕様を知る
- テストコードを読んで仕様を知る
- Goらしいコードの書き方を知る
- コードを読んでGoの標準ライブラリのコードの書き方を知る
- Goのテストの書き方を知る
- テストコードを読んでGoのテストの書き方を知る
コードリーディング会では、コードを読むだけではなく、GoDocを読んだり、テストを読んだりして、そもそものパッケージの仕様や使い方をおさらいしています。例えばコマンドラインでのフラグを扱う flag
パッケージは -myflag=x
などとしてフラグ名を引数で指定することが多いと思います。ところがテストを見ると --myflag=x
という --
という形式でも引数を受け取けられることが分かります。仕様を確認した上で実装を読むことで、仕様を満たすためにGoの標準ライブラリはどのように実装しているのか、という実装そのものに着目できます。
また、参加しているメンバーは初心者からベテランまで幅広く参加しています。最大公約数的な形をとって、なるべく多くのメンバーにとって役に立つようにしています。
読んでいるパッケージ
まず社内で読んでいるパッケージは以下です。全部で 12 回実施する予定です。現在(2020/03/02)のところ path
パッケージまで実施しました。
# | パッケージ名 |
---|---|
1 | io |
2 | errors |
3 | hash/maphash |
4 | context |
5 | flag |
6 | path |
7 | testing |
8 | iotest |
9 | sort |
10 | net/http (client) |
11 | net/http (server) |
12 | database/sql |
パッケージの選定
そもそも、どのパッケージを読んだらいいんだろう? という疑問があると思います。上記のパッケージを選定した方法を紹介したいと思います。
読むパッケージの選定は有志のメンバー数名で一緒に決めました。
まずパッケージのステップ数を見てスクリーニングしています。gocloc というツールを使うと以下のフォーマットで簡単にステップ数を確認できます。
$ gocloc archive |
runtime
パッケージは極端な例ですが、コードの読む前に明らかにやばい、、、ということがわかるわけです。このような要領でまずは $GOROOT/src/
配下にあるパッケージの一覧に対して gocloc
を実施し、パッケージの全体感を把握しました。 2
12回やるっ! というのは決め打ちです。標準パッケージだけでも200近くあって 1、もちろん全部を取り上げて読むことは難しいです。かといって目標を決めないと達成感を得られにくいと考えています。Futureでは技術書の社内輪読会を実施しています 4 が、技術書の場合は一冊を技術書を読了することで達成感を得ることができます。しかし、標準パッケージのコードリーディングの場合はどの範囲をとりあげるかは任意です。
そのため今回は予め12回は実施すると決め打ちしてパッケージを選んでいきました。キリの良い10回を考えていたのですが、読みたいパッケージがあり、最終的に少し増やして12回になりました。
次に具体的に読みたいパッケージを選びます。先程の gocloc
で得たステップ数を踏まえながら、みんなで議論しています。例えば io
パッケージはGoらしいインタフェースの使い方を学べるからおすすめ、とか hash/maphash
は Go1.14 で新しく追加されるパッケージだからみんなで確認しておこう、やっぱり net/http
のHTTPサーバ、クライアントの実装は読んでおきたいよね、じゃあダメ押しで database/sql
もやりましょう、みたいな感じです。
標準パッケージ以外のパッケージ(たとえば golang.org/x
などです)も検討したのですが、まずは標準パッケージのみに絞って実施することにしています。
運営方針
開催頻度/時間
発表するメンバーと参加するメンバーも負担にならないように、開催頻度と時間は以下のようにしています。
- 開催頻度
- 週1回
- 毎週水曜日の夕方
- 時間
- 1回あたり45分程度
進め方
- 発表
- 予め決めた2名がまとめる
- 資料
- 非公開のQiita記事やGitHub Gist、Google slideにまとめる
各パッケージごとに発表するメンバーを2名決めました。io
パッケージなら A さんと B さん、errors
は C さん、D さん、が内容をまとめて発表する要領です。事前に決めた2人が宿題形式でコードを読んで資料にまとめています。まとめた資料とGoのソースコードをもとに40分程度(1人あたり20分程度)で発表しています。パッケージのコードすべてを取り上げるのではなく、コアな部分や興味がある部分を取り上げて発表しています。2人で取り組むと、同じコードを読んでいても気づかなかったり、面白かったポイントも異なり、補完できるのでオススメです。
とはいえ進め方はやっぱり難しいと感じています。いろいろなバックグラウンドを持ったメンバーが参加しているので、参加したメンバーが満足いくような会になっているかは悩みポイントです。
このあたりは勉強会の運営に長けているメンバーがアンケートを取ってくださり、フィードバックをもらいました。アンケートの一部を抜粋します。
コードリーディング会の雰囲気
参加しているメンバーは、はじめてGoをさわったメンバー、A Tour of Goはとりあえずやってみたメンバー、業務でGoをガンガン書いているメンバー、趣味でGoを書いているメンバー、Goに関する本を出版しているメンバーなど様々です。
また参加方法も、会議室から参加しているメンバー、リモートから参加しているメンバーなど様々です。
発表内容を聞きながら、気になることや良くわからなかったこと、疑問に思ったことなどわいわい議論しています。
やってみて良かったこと
Goのテクニックを学べる/議論できる
以下は参加している/発表しているメンバーが紹介していた内容の一部です。実用的なテクニックや、ライブラリ独特なテクニックまでいろいろあります。
- インタフェースがコンパイル時に型を満たしているかチェックする方法
[0]func()
のような比較不能な型を構造体のフィールドにもたせると構造体が==
演算子で比較ができなくなる//go:linkname ...
とすると外部パッケージのプライベートな関数が呼び出せる- クローズされているチャネルを生成するために
init
関数でチャネルをクローズしている for range
構文はチャネルにも使える- 組み込み関数の
close
でチャネルをクローズすると、複数のゴルーチンが一斉に同じチャネルを Read することができる context
パッケージでのインタフェースを部分実装方法context
のWithDeadline
関数でtime.AfterFunc
関数で使われているmap
に値が存在するかのチェック、第二引数ok
じゃないこともあるflag
パッケージやpath
パッケージでパース処理は丁寧に場合分けしたり、一文字ずつ文字列を解析したり細やか- 典型的な アルゴリズムの問題
- テストで異常系と正常系を分けたほうが、テストコードがシンプルになりそう
- 異常系のテストの命名で
bad
という変数名を使っていてエラーになることが命名から明らか - 標準出力を使うパッケージ
bytes.Buffer
でキャプチャしてbuf.String()
で結果を取得してテストしている - テストで標準出力が不要であれば
ioutil.Discard
で捨てれば良い ioutil.Discard
は/dev/null
っぽいけどGoのコード上でのアナロジーiota
のイディオムよく見る
Go以外のテクニックも学べる/議論できる
bytes/buffer.go
の Read では最小のバッファサイズが 512 バイトなんだけど、これって何?- HDD の 1 セクタの容量 512 バイトにあわせているのではないか
flag
のExitOnError
のときos.Exit(2)
を呼び出しているけど、2
って何?- Bash の 終了ステータス の
ビルトインコマンドの誤用
のことを指しているのではないか
- Bash の 終了ステータス の
まとめ
社内で取り組んでいるコードリーディング会の内容と運営方法を紹介しました。Goの標準ライブラリのコードリーディング会は標準ライブラリを学べ、またGoらしいコードを学べる良い会です。本記事がみなさんの会社でコードリーディング会を実施するときの参考になれば幸いです。
Goに関連した連載企画がありますので宣伝です。
- DynamoDB×Go連載
- Go Cloud 連載
- 1.
go list std | grep -v vender | wc -l
で分かります。 ↩ - 2.今回はパッケージの一番上の階層で集計しています。実際には例えば
runtime
パッケージなどはruntime/debug
,runtime/pprof
など細かく分かれています。 ↩ - 3.https://qiita.com/advent-calendar/2019/categories/programming_languages ↩
- 4.https://future-architect.github.io/articles/20190729/ ↩
- 1.