【読書メモ】「継続的デリバリー」を読んだまとめ

はじめに

継続的デリバリーを読みました。
物凄く良い本ですが、ボリュームが多すぎるため、後から見直せるようにまとめながら読んでみました。
気になった項目と該当ページをメモしています。

1章 ソフトウェアデリバリーの問題

  • デプロイメントパイプラインについて(P.40)
    • アプリケーションのビルド・デプロイ・テスト・リリースといったプロセスを自動化する実装のこと
  • デプロイメントパイプラインの一連の動き(P.40)
    1. コミットステージ(コンパイル、ユニットテスト、分析、インストーラーのビルド)
    2. 自動化された受け入れテスト
    3. 自動化されたキャパシティテスト
    4. 手動でのテスト
    5. リリース

2章 構成管理

  • ひとつ残らずすべてをバージョン管理に保存する(P.71)
    • ソースコードだけでなく、ドキュメント、ライブラリ、設定ファイル等もすべてバージョン管理に置く
  • 依存関係の管理(P.77)
    • 外部ライブラリの管理
      • 外部ライブラリーのコピーをローカルのどこかに置いておく
      • npmではpackage.jsonで常に正確なバージョンを指定できるのでそういうケースでは不要?
    • コンポーネントの管理
  • ソフトウェア設定の管理(P.78)
    • 設定を柔軟にしたいという欲求は「究極の設定可能性」というアンチパターン
    • 設定情報が正しいか確認するテストが必要(p.80)
    • 設定はアプリケーションがコンパイルされたりパッケージングされたりするときに組み込んではいけない(p.81)
  • アプリケーションの設定の管理(p.82)
    • あらゆるアプリケーションが設定を取得できる中央集権サービスを置く(p.83)
    • ファイルシステムに置く
    • RDBMSに置く
  • アプリケーションをまたいで設定を管理する(P.86)
    • 各アプリケーションにある設定をすべて一覧化しておく
    • 可能であればビルドプロセスの一環としてアプリケーションのコードから自動生成されるべき

3章 継続的インテグレーション

  • 変更をコミットするたびにアプリケーション全体をビルドし、それに対して包括的な自動テストを実施する。ビルドやテストが失敗したら即座に問題を修正する。継続的インテグレーションの目標は、ソフトウェアを常に動く状態にしておくこと。(P.95)
  • 継続的インテグレーションを始める前に必要なもの(P.97)
    • バージョン管理
    • 自動ビルド
    • チームの合意

4章 テスト戦略を実装する

  • テストの種類(P.129)
    • テストの四象限図
    • 受け入れテストを自動化する(P.130)
      • 全てを自動化する必要はない(P.131)
      • 人間の方がうまくテストできる側面も数多くある
      • 探索的テストは自動化するのが難しい
      • コードカバレッジは80%以上を目指す
        • ただしカバレッジよりテスト品質が重要
        • ユニットテスト、受け入れテストの合計80%という考え方はダメ
      • 重要なのは主要な正常パターンに対するテスト(p.133)
    • プロジェクトを評価する技術的視点のテスト(P.135)
      • 受け入れテストの種類
        • 機能テスト
        • 非機能テスト
          • 非機能要件は他の要件と同じように扱っていない事が多い
          • プロジェクトの最初から日機能要件のテストを実装すべき(P.136)
  • 実際に起こりえる状況と戦略(P137)
    • テストを自動化しようとした時に直面する典型的なシナリオが記載されている
    • 新規プロジェクト
    • プロジェクトの途中(p.139)
      • もっとも一般的で価値が高く重要なユースケースから始める
    • レガシーシステム(p.140)
    • インテグレーションテスト(p.141)
      • 自動インテグレーションテストは本番にデプロイした際のスモークテストに使える(P.143)

5章 デプロイメントパイプラインの解剖学

  • デプロイメントパイプラインとは何か(P.153)
    • ソフトウェアをバージョン管理から取り出してユーザの手に渡るまでのプロセスを自動化して表現したもの
    • チェックインからリリースまでのビルドの進展を見えるようにする(P.189)
  • 基本的なデプロイメントパイプライン(P.157)
    • コミットステージ
    • 自動受け入れテストステージ
      • 自動受け入れテストは時間が掛かるので、CIサーバーを使ってテストを複数のスイートに分割して並列に実行する事でスピードを上げる
    • 手動テストステージ
    • リリースステージ
  • デプロイメントパイプラインのプラスティス(P.159)
    • バイナリをビルドするのは1度限りとせよ
    • あらゆる環境に対して同じやり方でデプロイせよ
    • デプロイメントをスモークテストせよ
    • 本番のコピーにデプロイせよ
  • 各変更は直ぐにパイプライン全体を通り抜けなければならない
    • パイプラインのどの部分であっても、失敗したらラインを止めよ
      • CIのビルド中に他のpushが来てもCIビルドは実行しない。ビルド完了後に最新のバージョンに対してビルドを実行する。
  • 自動受け入れテストのベストプラクティス(P.172)
    • 開発者は自動受け入れテストを自分たちの環境で実行する事ができなければならない
    • 自動受け入れテストの受け入れ基準を取り上げて、すべて何も考えずに自動化しない(P.173)
  • 後に続くステージ(P.173)
    • 自動受け入れテストがあってもリリース前に何らかの形で手動テストを行う事が望ましい

6章 ビルド・デプロイメントスクリプト

7章 コミットステージ

  • コミットステージの原則とプラクティス(P.222)
    • 役に立つフィードバックを素早く提供せよ(P.223)
      • コンパイルエラーのように続きが実行できない時はすぐに止める(P.224)
      • そうでなければコミットステージを最後まで実行して、エラーや失敗をすべてまとめて見せる
  • コミットテストスイートの原則とプラクティス(P.230)
    • データベースを避けよ(P.232)
      • ユニットテストはデータベースに依存してはならない
      • 最終手段としてはインメモリデータベースを使用する
  • テスト内の状態を最小限に抑える(P.232)
    • ユニットテストはシステムのふるまいに集中するべき
    • テストがどんどん複雑になってきたら構造を見直す必要がある

8章 自動受け入れテスト

  • 保守しやすいテストスイートの作り方(P.244)
    • 受け入れテストの受け入れ基準を定義する
    • 重要なのは、テストの実装でドメインの言語を使うようにする事と、アプリケーションとどのようにやりとりするかの詳細を含まない事(P.245)
      • テストの実装がAPIやUIを直接参照しているとテストがもろくなる
      • UIを変更しただけで、UIを参照しているテストが全て壊れてしまう
    • アプリケーションドライバレイヤという、より抽象度の低いレイヤを呼び出して、テスト対象システムとやりとりするべき(P.246)
  • アプリケーションドライバレイヤ(P.253)
    • ウインドウドライバーパターン(P.257)
      • GUIに対して実行するテストがもろくならないように、抽象レイヤーを導入して受け入れテストとテスト対象システムの結合度を減らしている
      • GUIの各部品に対してデバイスドライバを書く
      • 受け入れテストコードはウインドウドライバーを通じてGUIとやりとりする
  • 受け入れテストにおける状態(P.260)
    • 量は最小限に抑えて整合性を保ったデータセットを保守する
    • テストはアトミックであるべきで、どの順序で実行してもいいし、並列実行も可能
  • テストダブルを使用する(P.266)
    • やりとりする全ての外部システムに対してテストダブルを作成する(P.267)
    • 各統合地点の周りに小粒のテストスイートを構築して外部システムと実際に通信できる環境で実行できるようにする
  • デバッグのために受け入れテストを記録する(P.270)
  • 受け入れテストのパフォーマンス(P.275)
    • 受け入れテストは10分で終わらせるよりも包括的なテストスイートを揃えるほうが重要
    • 数時間掛かるくらいなら問題ない
  • 名言(P.280)
    • これまで我々がソフトウェア業界で経験してきたことに照らせば、手動テストが標準であるし、チームで採用されたテスト形式がそれだけだという場合も少なくない。だが手動テストは、ひどく高くつくし、高品質の結果を保証できるほど適切に行われる場合も少なくない。だが手動テストは、ひどく高くつくし、高品質の結果を保証できるほど適切に行われることがほとんどない。手動テストにももちろん役目はある。すなわち、探索的テストや、ユーザービリティテスト、ユーザー受け入れテストやショーケースなどだ。しかし、人間というものは、複雑な作業をいつも同じように効率的に繰り返すことが得意でない。手作業でのリグレッションテストにはこうしたことが欠かせないし、どうしても惨めな感じを受けてしまう。品質の低いソフトウェアは、このような品質の低いプロセスから生み出されるのだ。

9章 非機能要件をテストする

  • 定義(P.281)
    • パフォーマンス:単一のトランザクションの処理に要する時間
    • スループット:一定の期間内にシステムが処理できるトランザクション数
    • キャパシティ:一定の負荷がかかった状態で、個々のリクエストを許容範囲のレスポンスでさばききれる最大のスループット
  • 非機能要件を分析する(P.283)
    • 非機能要件を特別な苦労をしなくても満たせるものであると考えてしまいがち
    • パフォーマンス要件をユーザビリティの問題にすり替えない(P.284)
  • キャパシティテスト環境(P.292)
    • ハードウェアを2倍にすれば、キャパシティも2倍になるという考え方はダメ
  • キャパシティテストを自動化する(P.294)
    • 実際にあり得るシナリオをテストする(P.295)
    • どこまでいけば成功か事前に決めておくこと
    • 変更に対して堅牢にしておくこと、アプリケーションに変更があるたびにテストをそれに追従させる仕組みではいけない
    • 複数を組合わせて大規模で複雑なシナリオをテストできるようにしておくこと
    • 繰り返し実行できるようにしておくこと
  • GUIベースでのキャパシティテストは避けるべき(P.298)
  • システムに対する捜査テンプレートを作って、それを受け入れテストで使えるようにする(P.299)

第10章 アプリケーションをデプロイ・リリースする

第11章 基盤と環境を管理する

第12章 データを管理する

  • すべてのアプリケーションを一つのデータベースではなく、アプリケーション毎に個別のデータベースとやりとりさせ、共通部分は切り出す(P.392)
  • テストの状態を管理する方法(P.399)
    • テストの分離(P.400)
    • 順応型テスト
    • テストの順序付け
  • 一貫したテストシナリオがダメな理由(P.401)
  • コミットステージでのテストデータ(P.402)
    • テストが実装の詳細と密結合になると変更への妨げになる
      • 密結合になる原因はテストデータの凝りすぎが原因の事が多い
    • 最小限のデータで、テスト対象が期待通りにふるまいをするのか確かめる
  • 受け入れテストにおけるデータ(P.403)
    • アプリケーションのAPIを使用してテストできる状態にする(P.404)
  • テスターはテストの目的に合わせて適切な大きなのデータを作って管理する(P.407)

第13章 コンポーネントや依存関係を管理する

第14章 高度なバージョン管理

  • ブランチ分けによる開発に対して反対する記述が多かったのですが、Gitのような分散型バージョン管理システムの登場により「場合によっては使えるもの」と軟化していました(P.482)

第15章 継続的デリバリーを管理する

  • 成熟度モデル(P.487)