Googleのソフトウェアエンジニアリングを読んだ
Software Engineering at Google(邦訳『Google のソフトウェアエンジニアリング』)を読んだ。全 25 章のうち ch 5・8・9・10・11〜12・18・23〜25 について、書籍の要点に自分の感想を交え、章ごとに箇条書きでまとめる。
第 5 章 リーダーとマネージャー¶
リーダーやマネージャーが担う役割を扱う。
- 権限がなくても広範囲に影響を与えることができる
- マネージャーとメンバーで親子のような関係にならないように気を付ける
- 人の問題を無視してはいけない
第 8 章 スタイルガイドとルール¶
スタイルガイドとルールを扱う。
- ほとんどのエンジニアリング組織にはコードベースを管理するルールがある。フォーマットや命名、パターン、例外、スレッドなどに関するルール
- ルールは法律であり厳格で強制的。普遍的に施行可能であり、承認を得た場合を除いてルールを無視できない
- ルールとは別に推奨事項やベストプラクティスを提供するガイダンスを設ける。これに従うことを推奨するが、多少の変更の余地がある
- ルールを設ける理由は良い行動を奨励し、悪い行動を防ぐため
- コードを読む人のために最適化し、一貫性を保つ。エラーが起こりやすい構造を避け、必要なときは実用性に譲る
- 静的チェッカーやフォーマッターを使って機械的にチェックする
第 9 章 コードレビュー¶
コードレビューの目的と運用を扱う。
- readability の確認
- commit 前にレビュー
- code owner を設ける
- コードが完璧でなくても、今のコードより良くなるのであれば受け入れるという組織での合意
- コードを書く時に本当にその機能が必要か確認する。誰かがすでに同じことを実装していないか、コードを書かずに解決できる課題かを確認する
- シフトレフトで早めにミスを検知する
- コードは一度書かれると何度も読まれる
- 24(営業)時間内のレビュー
- 200 行以内の変更
- PR の 35%が 1 つのファイルだけの変更
- レビュワーを最小限にする
第 10 章 ドキュメンテーション¶
ドキュメンテーションの書き方を扱う。
- 自分のために書くドキュメントも良いが、読む人を意識したドキュメントをこころがけよう
- ドキュメントは、すぐに効果が出るとは限らず、組織の拡大時など後から役立つことが多い
- ドキュメントにもオーナーが必要
- 読者を特定したうえでドキュメントを書き始める(経験・ドメイン知識・目的を考慮)
- Design Doc はアーキテクチャのトレードオフについて書こう
第 11 章から第 12 章 テスト¶
テストの位置づけと書き方を扱う。
- テストは、エンジニアの信頼によってその価値を発揮するもの
- もしテストが生産性の低下を招き、常に労力と不確実性をもたらすものであれば、エンジニアは信頼を失い、回避策を見つけるようになる
- 開発を円滑に進めるには、テストの自動化が必須
- プロセスへの高い信頼性を保つには、壊れたテストを迅速に修正する対応を最優先にする
- テストのメリット:デバッグの軽減、変化に対する自信の向上。ドキュメントの補足。レビューをシンプルにできる。コードの責務を意識できる。迅速かつ高品質なリリースの実現
- エンジニアは大規模なシステム全体を対象とするテストを書きたがる。しかしそのテストは小規模なテストよりも遅く、信頼性が低く、デバッグが困難である
- test サイズを意識する
- 何を unit test, integration test と呼ぶかは色々流派があって統一が難しい。単一のプロセスで完結する単位を small、単一のマシンで完結する単位を medium(Docker compose のようなイメージ)、複数のマシンで完結する単位を large と呼ぶ。
- テストは small が多く、medium が少なく、large はほとんどないというピラミッド型の関係になるのが理想
- テストが小さいほど不確実性が減り、信頼性が高くなる。テストを小さくするため、mock などを活用する
- テストサイズが大きくなると実行時間が長くなったり、フレイキーになったりする
- 既存の社員をいきなり教育するのは難しいので、新しい社員のオンボーディングでテストについて教育し、そのメンバー達からテストに対する文化をひろげた
- 公開されてる API を通じて(ユーザーと同じ方法で)テストする
- プライベートな実装は公開されてる API を通じてテストされる
- ふるまいのテスト
- フレイキーなテストは生産性を下げるので減らす
- テストでは、重複を減らす DRY(Don't Repeat Yourself)よりも、意図が伝わる記述を重視する DAMP(Descriptive And Meaningful Phrases)を優先する(第 12 章の該当節)
- テストでは DRY 原則を多少破ってもわかりやすさを重視。ファイルジャンプが多いと理解しづらい
- テストコードは頻繁に読まれるものではないので、メソッド名が長くてもよい。名前を見て処理内容がわかるように、説明的な名前を付ける
-
テストの中で条件分岐やループのような制御フロー文の使用を強く推奨しない。より複雑なテストフローは、それ自体がバグを含む危険性があり、テスト失敗の原因究明をより困難にする。
- テストにロジックを入れるな。ロジック:演算子、ループ。分岐など
- Google ではなるべく mock を使わず本物を使うと書かれている
- あるシステムが特定の動作をすると確信するには、その動作を検証する自動テストを書くしかない
- コードと対象のテストを書く間隔が短いほどいい。後回しにしない
- 複数の動作を同時にテストしない
第 18 章 ビルドシステム¶
ビルドシステムの設計を扱う。
- 外部の依存関係の更新によってビルドが壊れないようにバージョン管理が不可欠
- 依存関係を自動で推測してビルドしてくれるシステムがあると楽
- Pants Python の依存関係を自動で推測してくれるので好き
- 推移的な依存関係の管理難しそう
- シェルスクリプトによるビルドはメンテナンスが大変
- ビルドシステムの拡張。デプロイ用の goal とか作りたい
- アーティファクト単位で処理するアプローチは、成果物の再利用と処理の並列化にメリットがある
- Google だとバイナリが数万のターゲットに依存されてることもある
- リモートキャッシュと分散ビルドで解決
- リモート実行にすれば、各ローカルマシンで毎回ビルドせずに済む
- モジュールの粒度が大きいと並列ビルドができない。毎回大きなビルドが走る
- 各ディレクトリで 1 つのパッケージ、ターゲット、BUILD ファイルにすることで依存を小さくする
- ビルドターゲットを小さくすると、分散ビルドが速くなり、ターゲットの再構築の頻度が減る
- 不必要にモジュールを public にしない
- ほとんどの BUILD ファイルは、public なターゲットを 1 つだけ持つ
- バージョン指定しない latest 依存は事故のもと
- ダイヤモンド依存は避けたい
- コードのサードパーティ製の依存は 1 つのバージョンのみにする
第 23 章から第 24 章 CI/CD¶
CI/CD の運用指針を扱う。
- システム開発の後ろの工程になるほどバグが見つかる
- カナリアテストで少しずつ変更を試す
- FeatureFlag
- コードが古く、規模が大きいほど CI が必要
- 本番環境相当でベンチマークしないと意味ない
- 利用されないものはリリースしない
- 頻繁に小さな単位でリリース
- リリーストレイン
- CI は製品の安全性を高め、開発者の幸福度を高める
第 25 章 コンピューティング基盤¶
コンピューティング基盤の運用を扱う。
- トイルの削減の手法として自動化と監視、スケジューリングがある
- CPU やメモリといったリソースの見極めは人間がやるのは難しい。機械的にやっていい
- サーバレスは 0 までのスケールインが魅力
- サーバレスは一定の制御が失われる。これはトレードオフでコントロールできる状況であればコントロールしにいかなければいけない
- サーバレスを使う時はステートレスを意識
- バッチはスループット重視で生存時間は比較的短め
- サービングはレイテンシ重視。長期間立ち上がっていることが多く、多くの点でバッチより耐障害性が高い
- サービングは大抵オーバープロビジョニングする必要がある
- サービング用にオーバープロビジョニングして過剰リソースにバッチジョブを割り当てられると良い
- サービスの接続をハードコードしている場合はそのインスタンスが交換可能でない。間接的なレイヤーを経由して接続したほうがいい
- Borg は 2003 年からあるプロジェクト
- 共有クラスタは複数のチームで使ってコスト的なメリットがでる
まとめ¶
読んだ章のメモを並べると、共通する考え方が見えてくる。
- ルールは静的チェッカーやフォーマッターで機械的にチェックし、人の注意力に委ねない(ch 8)
- 品質はコードレビューの仕組みやテストサイズの分類で担保し、属人的な判断に頼らない(ch 9、ch 11 - ch 12)
- 規模の問題はリモートキャッシュや分散ビルド、小さなビルドターゲットで解決する(ch 18)
組織やコードベースが大きくなるほど、この「仕組みで担保する」設計を優先するとよい。