TL;DR

  • Go は学ぶのは簡単だが、習得するのは必ずしも容易ではない。
  • 本書で扱う間違いは、バグ、不必要な複雑さ、可読性の低下、最適でないプロジェクト構成、API の使いにくさ、最適化されていないコード、生産性の欠如の 7 つに分類される。

内容

この章の原文は 100go.co で読める。

単純と容易

技術の文脈で「単純(simple)」とは、学んだり理解したりするのが複雑でないことを指す。一方「容易(easy)」とは、大した努力をしなくても何でも達成できることを指す。Go は単純だが、容易とは限らない。

並行処理のバグの研究

2019 年に、並行処理のバグを調べた研究 Understanding Real-World Concurrency Bugs in Go が発表された。この論文は「メッセージパッシングはメモリ共有より扱いやすく、エラーが起きにくいと思われているにもかかわらず、ブロッキングバグの多くはチャネルを介したメッセージパッシングの誤った使い方から生じている」と述べている。

つまり、チャネルやゴルーチンの概念を学ぶのは簡単だが、使いこなすのは難しい。これは並行処理以外の機能にも当てはまる。熟練した Gopher になるには Go の多くの側面を深く理解する必要があり、それには時間と努力と失敗が要る。本書は、その道のりの近道を示すものである。

7 つの間違い

バグ: データ競合、メモリリーク、ゴルーチンリーク、ロジックエラーなどである。正確なテストで早く見つけられるが、時間の都合や複雑さなどの理由で見逃すこともあるので、よくあるバグは知っておくべきである。

不必要な複雑さ: ソフトウェアの複雑さは、想像上の未来を考えようとすることから生まれる。将来起こりうるあらゆるユースケースに対応しようとすると、コードベースを理解しにくくなり、デメリットがメリットを上回る。Go では、インタフェースやジェネリクスなどの抽象化を多用することがこれにあたる。「早すぎる最適化は諸悪の根源」という言葉にも通じる。

可読性の低下: Clean Code によれば、コードを読む時間と書く時間の比率は 10 対 1 を超える。ネストの深いコード、データ型の表し方、名前付き結果パラメータを使わないことなどが、可読性を下げる。

func f(a int) (b int) { // int の結果パラメータに b と名前を付ける
	b = a
	return // b の現在の値を返す
}

最適でない、または慣用的でないプロジェクト構成: プロジェクトの構成、ユーティリティパッケージの扱い、init 関数の扱いなどを誤ると、プロジェクトを理解し保守するのが難しくなる。

API の使いにくさ: any 型の多用、オプションを扱うのに不適切な生成パターンを使うこと、OOP のベストプラクティスをやみくもに当てはめることなどで API が使いにくくなる。するとコードの表現力が下がり、理解しにくく、エラーも起きやすくなる。

最適化されていないコード: 言語の機能を理解していない、基本的な知識が足りないといった理由で起こる。並行処理をうまく扱えない、メモリ割り当てを減らす方法を知らない、データのアラインメントの影響があるといったことによる性能の低下や、浮動小数点数演算の精度の問題が含まれる。

生産性の欠如: 新しいプロジェクトでは、最も生産性の高い言語を選ぶべきである。生産性を上げるには、言語の動きに慣れて最大限に活用することが大切である。効率的なテストの書き方、標準パッケージの活用、プロファイリングツールやリンタの活用がその例である。

キーワード

自分の理解

論文の指摘は、メッセージパッシングの間違った使い方、たとえばチャネルの使い方の誤りが、ブロッキングバグの主な原因になっているということだと理解した。