View on GitHub

Today I Learned

Software Engineering Blog

III. Processes

8章 スタイルガイドとルール

Googleのルールとガイダンスの開発を舵取りする原理とプロセス。

Google Style Guide: https://google.github.io/styleguide/

8.1 何故ルールを設けるのか

ルールを設置することのゴールは、「良い」行動を促し、「悪い」行動を思いとどまらせる。

コーディングの共通ルールがあれば、どう書くかより何を書くかに集中できる。

8.2 ルールを作る

スタイルガイドを作る上でのルール

スタイルガイドのカテゴリ

8.3 ルールを変更する

時間の経過によってスタイルガイドを再評価する必要が出てくる。その際は、pros/consやトレードオフを議論し詳細な根拠を元に最終的に変更を決定する。

最終的な決定と承認は、スタイルガイドのオーナーによって行われる。C++は4人のオーナーがいる。

8.4 ガイダンス

推奨するベストプラクティス。

ルールが「絶対やらなければならないこと」であれば、ガイダンスは「やるべきこと」。

8.5 ルールを適用する

ルールを遵守しているかどうかの確認はツールによって自動化する。

自動化によって、ルールがどのように解釈され適用されるかのばらつきを最小化できる。

また、自動化はルールの強制をスケーラブルにもする。

9章 コードレビュー

Googleでは、すべてのコード変更がコミットされる前にレビューされており、園児唖然員がレビューの開始と変更のレビューを担当している

Googleでは、Critiqueと呼ばれるコードレビューツールを使っている。(詳細は19章)

この章ではツールではなく主にプロセスの話。

9.1 コードレビューのフロー

  1. authorは自分のワークスペースで行った変更をコードレビューツールにアップロード。ツールでは変更差分が表示される。
  2. authorは変更を1人以上のreviewerにメールでレビューを依頼する。
  3. reviewerは変更を見て差分上にコメントを残す。
  4. フィードバックに基づいてauthorは変更を修正し、reviewerに返信。必要に応じて3,4を繰り返す。
  5. reviewerが変更に満足したらaproveし、LGTMをする。デフォルトではLGTMが1つあればいいが、全reviewerにapproveを求めても良い。
  6. 全コメントを解決してそれがapproveされていれば、authorは変更をコードベースにコミットできる。

9.2 Googleでのコードレビューはどのように機能しているか

Approveの3つのポイント

9.3 コードレビューの恩恵

コードレビュープロセスは当然コード変更に時間がかかるという問題がある。しかし、Gooogleではどんな小さな変更でもコードレビューを義務としている。

このコードレビュープロセス文化は、以下の恩恵をもたらす。

9.4 コードレビューのベストプラクティス

レビュアーを尊重すべき。不十分な場合にのみ代案の指摘をする。

Googleでは24時間以内にレビューのフィードバックがされることが期待されている。できない場合はその旨を返信する。

レビューは小さい変更に留めるべき。200行程度。Googleのコード変更の約35%がたった一つのファイルの変更

レビュアーは最小限に留めるべき。Googleの大半のコードレビューは1名のレビュアーによってレビューされている。

9.5 コードレビューの類型

コードレビューの分類

10章 ドキュメンテーション

Googleではドキュメントをコードとして扱うのが最も成功しているやり方。

10.1 何がドキュメンテーションとして適格か

独立したドキュメントだけでなく、コードのコメントも含まれる。

10.2 何故ドキュメンテーションが必要なのか

ドキュメンテーションは時間とともにスケールするし、組織においてもスケールする。

設計上の決定理由、実装の根拠、数年後自分のコードを見返したとき、などでドキュメンテーションは有用

10.3 ドキュメンテーションはコードのようなものである

ドキュメンテーションはコードと密結合していることが多いため、できるだけコードとして扱われるべき

Docs as Codeと呼ばれる: https://www.writethedocs.org/guide/docs-as-code/

10.4 対象読者を認識せよ

ドキュメンテーションを書く前に、読む対象の読者をはっきりさせる。その読者に応じたスキルレベルとドメイン知識に応じてドキュメントを書くべき。

デザインドキュメントであれば、意思決定者を説得させる内容を書くようになる。

チュートリアルであれば、コードベースに馴染みのない読者になるので、詳細まではっきりと書かなければならない。

主要な想定読者はSeekers(捜索者)とStumblers(遭遇者)

10.5 ドキュメンテーションの類型

ドキュメントのタイプによって書き方が異なる

ドキュメントのタイプ

10.6 ドキュメンテーションのレビュー

ドキュメントレビューの種類

10.7 ドキュメンテーション哲学

技術的な文章を書くためのベストプラクティス

Howの他に、Who, What, When Where, Whyを意識する。

完全性、正確性、明確性は、優れたドキュメンテーションの特徴的要素

古くなったドキュメントは、削除するか、古いものとして分類する。Googleではドキュメントのメタ情報としてfreshness dateをつけておく。数ヶ月変更がない場合はメールでリマインドを送る。

10.8 テクニカルライターが必要なのはどんなときか

Googleでは、昔は重要なプロジェクトはテクニカルライターにドキュメント作成を任せていた。プロジェクトメンバーのドキュメント作成や保守の負担を軽減するため。

しかし、これは誤りであった。対象読者がエンジニアリングチームならプロジェクトメンバーが書くことができる。対象読者がプロジェクトメンバー以外の人であれば、テクニカルライターにお願いする。

テクニカルライターのリソースは限られているので、そのような場合にのみ依頼したほうがスケールする。

11章 テスト概観

自動テストの導入にによって、バグを捕捉できる、変更が容易になり変化に合わせて高速に反復できる。

11.1 何故テストを書くのか

テストは後からの付け足しであってはならない。プロダクトの構成要素の一つ。

Googleでは、2005年頃にGoogleWebサーバに自動テストのポリシーを追加したことによって、1年以内にロールバックやバグフィックス等の緊急リリースが半減した。

自動テストによって、プロダクトのバグ発見についてプログラマーの能力だけに頼ることを避けることができる、

自動テストは手動デバッグにかけるコストを減らすことができる。

コードの正しさ、エッジケース、エラー条件のテストがコードレビューに含まれていれば、レビューを単純化できる。

テストがあると自信をもってリリースできる。

テストがスケールするには、自動化しなければならない。

11.2 テストスイートを設計する

テストには、規模(Size)と範囲(Scope)の2つの次元がある

テストカバレッジは、テストされていないコードを知りために役立つが、システムがどれだけ適切にテストされているかまではわからない。

規模

小テスト

中テスト

大テスト

テスト環境の構築から解体までをテストに含むべき。外部環境に対する前提条件を持つべきではない。

テスト内での複雑なロジックは避ける。条件分岐やループはなるべき使わない。

範囲

狭い範囲のテスト(ユニットテスト)、中範囲のテスト(インテグレーションテスト)、大範囲のテスト(ファンクショナルテスト、エンドツーエンドテスト、システムテスト等)

Googleのガイドラインとして、これらテストの割合は、小テスト80%、中テスト15%、大テスト5%を目指している。

11.3 Google規模でのテスト

Googleのコードの大半がモノレポで管理されている。

テストが遅くなると、生産性が下がる。質の高いテストを保つため、テストを改善したエンジニアに対しても新機能リリースと同等となるようなインセンティブを与えるべき。

11.4 Googleでのテストの歴史

テスト文化を構築するためにGoogleが行っている試み

11.5 自動テストの限界

自動テストは必ずしもすべてのテストに適しているわけではない。

検索結果の品質チェックなどは、人間によるチェックが必要な場合がある。

12章 ユニットテスト

テストのメンテナンス性(Maintainability)とそれを達成するためのテクニックについて

12.1 保守性の重要さ

テストの保守性とは、「just work」するものであり、テストが失敗するまではそのテストについて考える必要がなく、テストの失敗時は明確にバグがあることが示される状態。

テスト保守性のために、

12.2 脆いテストを防ぐ

脆いテストとは、コードの変更でバグがないにも関わらず無関係なテストが失敗してしまうこと。

理想のテストは、変化しないテスト。

テストを書いた後、システムをリファクタリングし、バグを修正し、新機能を追加する際に、そのテストに再度触れなければならないというのは何かが間違っているということである。

公開APIに対してテストをすれば、それが失敗した時ユーザのアクティビティもまた失敗することがわかる。

相互作用(インタラクション)ではなく状態(ステート)をテストする。つまり、どのようにその結果になったかの過程をそれぞれテストするのではなく、どういう結果になったかだけをチェックする。例えば、ユーザ追加のテストでHTTP PUTメソッドが呼ばれたことをチェックするのではなく、オブジェクトにユーザが追加されたかどうかをチェックする。

12.3 明確なテストを書く

テストが失敗した時、テスト対象に問題があるか、テスト自体に問題があるかを特定し、問題を究明する。

テストは完全かつ簡潔にする。つまり、読み手が必要なすべての情報が含まれていて、他の無関係な紛らわしい情報が含まれていない。

メソッドに対してではなく、各挙動に対してテストを書く。

テストの名称は、テスト対象の挙動を要約する。システムに対して行われる動作と、期待される結果が含まれていると良い。困ったら、shouldで始めるのが良い。

12.4 テストとコード共有:DRYではなくDAMP

テストにおいては、DRY(Don’t repeat yourself)ではなくDAMP(Descriptive And Meaningful Phrases)。

「説明的かつ意味がわかりやすい言い回し」。テストを単純かつ明確なものにするのであれば、多少の重複は許容される。

13章 テストダブル

テストダブルはモッキングとも呼ばれるが、それよりは少し抽象的な概念。

13.1 テストダブルの、ソフトウェア開発への影響

テストダブルは本物の実装と置き換えられるようになっているべき。

テストダブルは、適切に応用できればエンジニアリングのスピードを速めることができるが、不適切に利用すると脆く複雑なものになる。

どれだけ本物の実装の挙動に近いか。

13.2 Googleでのテストダブル

教訓として、モッキングフレームワークを使いすぎるのは危険である。バグを滅多に発見しない割には、保守に労力がかかる。

13.3 基本概念

Dependency Injectionは、テストダブルを利用できるようになり、コードをテスト可能にする。

モッキングフレームワークは、テストダブルを簡単に生成できるライブラリ。

13.4 テストダブル利用のためのテクニック

テストダブル利用のための3つのテクニック

13.5 本物の実装

モッキングフレームワークを多用せず、なるべく現実的なテストを優先する。

例えば、値オブジェクトには本物の実装が利用されるべき。

本物の実装よりテストダブルを利用した方がいい場合:

13.6 フェイキング

フェイクは本物の実装同様に振る舞うため、他のテストダブルのテクニックより好まれる。

フェイクの作成に最も重要な概念は、忠実性。フェイクの挙動を、本物の実装の挙動にどれだけ近づけられるか。

13.7 スタビング

挙動のハードコード。

スタビングを使いすぎると、テストが不明確になり、脆くなる。

スタビングが適切なのは、テスト対象をある状態に遷移させるために関数が特定の値を返さなければならない場合。

13.8 インタラクションテスト

インタラクションテストは必要な場合だけ実施する。

このテストが検証できるのは、あるかんせううが期待通りに呼ばれている点だけ。テスト対象が正常に動作しているかはわからない。

本物の実装やフェイクが使えない場合にはインタラクションテストの実施を検討。また、関数の呼び出し回数や順序によって望ましくない挙動を引き起こす可能性がある場合も実施を検討する。

14章 大規模テスト

大規模テストとはなにか、それをいつ実施するか、大規模テストのベストプラクティス

14.1 大規模テストとは何か

数時間から数日の間に実行されるテストもある。

大規模テストは密閉されていない(nonhermetic)かもしれない。密閉性は、テスト対象システムが他のコンポーネントから分離されている度合い。

大規模テストは忠実性(Fidelity)に対処する。忠実性は、テストがテスト対象の本物の挙動を反映している度合い。

大規模テストは、ユニットテストが扱えないものを扱う。

14.2 Googleの大規模テスト

可能な限り最小のテストにする。巨大なテスト1つより、いくつかの大テストに分かれていたほうが好ましい。

14.3 大テストの構造

大テストの共通の流れ

14.4 大規模テストの類型

1つ以上のバイナリの機能テスト

パフォーマンス、負荷、ストレステスト

デプロイ設定のテスト

探索的テスト(Explaratory Testing)。ユーザシナリオに対するシステムのふるまい。

A/B差分リグレッションテスト。

ユーザ受け入れテスト(UAT)。特定のユーザの挙動が意図通りであるかを担保するための、公開APIを通じた自動テスト。

プローバーとカナリア分析。本番環境自体の健全性を担保する方法。

障害復旧、カオスエンジニアリング。予期しない変更や障害に対してどれだけ反応できるかのテスト。

ユーザ評価

14.5 大テストと開発者ワークフロー

打規模テストは開発者のワークフローに統合する。ビルドを別に分けるのがおすすめ。

大テストを作成するための、ライブラリ、ドキュメント、テストコード例を用意しておく。

テストの高速化のために、タイムアウトと遅延を減らす、不要なビルドを減らす。

信頼不能性を排除する。つまり、たまに成功したり失敗したりをなくす。そのためにテストの範囲を狭める。

テストを理解可能にする。つまり、何が失敗しているかを知るための明確なメッセージを出す。

大規模テストには、オーナーとなるエンジニアがいなければならない。オーナーがいないと、テストの変更と更新が難しくなり、失敗の原因究明に時間がかかり、最終的にそのテストは廃れる。

15章 廃止

deprecation。システムの廃止。

廃止を正しく計画実行すれば、リソースのコストが減少し、システム内に蓄積されてきた冗長性と複雑性が除去され、速度が改善される。

一方、やり方を間違えた場合には、放置した場合より大きなコストが掛かる可能性がある。

15.1 何故廃止するのか

コードは債務であり資産ではない。

古くなったシステムを稼働させ続けるか、もしくはシステムの停止に取り組むのか、のトレードオフ

15.2 何故廃止はこれほど難しいのか

Hyrumの法則によって予期しない使われ方をしている可能性があるので、システムを廃止することは難しい。

旧システムと新システムは機能面で全く同じにはならない。

旧システムへの管理者の愛着。

廃止のために予算をつけることは政治的な側面で難しい。

旧システムから新システムへの移行コストが極めて高い。コストは実際のコストよりも低く見積もられがち。

設計の際に、自分のプロダクトから移行することになったときの容易さはどの程度か、どうすれば部分的にインクリメンタルに置き換えることはできるか、を検討した方がいい。

15.3 廃止の類型

勧告的廃止

締切がなく低い優先度で行われる廃止。

新システムに移行するメリットが大きいとき。

チームが積極的に移行することは稀。旧システムの利用が多いことが、旧システムの新たな利用者を増やすことにもなる。

強制的廃止

旧システムの撤去の締切を伴う廃止。その日を過ぎるとシステムは動作しなくなる。

ユーザに十分に警告を行っていれば、それに従わないユーザの利用を止められる権限を持つことになる。

警告は、行動可能性と関連性を持っていなければならない。さもなければユーザは警告をすべて無視してしまう。関連性は、ユーザが警告された行動を行う際に警告が表示されること。

15.4 廃止プロセスを管理する

明示的なプロジェクトオーナーが必要。

廃止プロセスでも、新システムの構築と同様、完全な撤去のマイルストーンだけではなく、具体的でインクリメンタルなマイルストーンを持つべき。