『継続的デリバリー』読書会
      第7章
   コミットステージ
   大崎的デリバリー
     @Yanuto
7.1 導入
「コミットステージは、プロジェクトの状態が変化するところから始まる。」

 コミットステージ
     ・コンパイル                 自動化された
                自動化された               手作業での
    ・ユニットテスト                キャパシティ           リリース
                受け入れテスト               テスト
       ・分析                    テスト
 ・インストーラーのビルド


• 大切な第一歩                  デプロイメントパイプライン


 – コードレベル統合に費やす時間を抑えられる
 – 優れた設計プラクティスが採用され、コードの品
   質が上がる
 – デリバリーの速度も上がる
• 開発者にとって最も重要なフィードバック
 – 早めに失敗させる
7.1 導入
• 理想は5分以内、10分以上はダメ

                   コミットステージ
          ソースコード       ・コンパイル
 バージョン                ・ユニットテスト
 コントロール                  ・分析
                   ・インストーラーのビルド



                         失敗時:レポート
                         成功時:バイナリ、メタデータ、レポート



                     バージョン
                     コントロール
7.1 導入
• 誰かが trunk にチェックインすると検知し、
  チェックアウト後に下記を実行(失敗してもで
  きるだけ最後までやって問題箇所を出す)
 – コンパイル
                       コミットステージ
 – 統合されたソースにコミットテスト        ・コンパイル
                          ・ユニットテスト

 – バイナリ生成                    ・分析
                       ・インストーラーのビルド

 – コードベースの健全性をチェック
 – 成果物(DB 移行スクリプト、テストデータ)
7.2 コミットステージの原則とプラクティス
 7.2.1 役に立つフィードバックを素早く提供せよ

• コミットテストが失敗する原因
 – 構文エラー(コンパイル)
 – アプリケーションの意味上のエラー(テスト)
 – アプリケーションの設定、環境(OS 含む)の問題
• 失敗したらすぐに開発者へ
 – 失敗したテスト
 – コンパイルエラー
 – エラー条件    など
7.2 コミットステージの原則とプラクティス
  7.2.1 役に立つフィードバックを素早く提供せよ

• いまどきの開発環境ならコミットステージ前に
  教えてくれる
 – プログラミング
    • コンパイル時
    • 構文エラー
 – CI
    • プレテストコミット
    • プレフライトビルド
7.2 コミットステージの原則とプラクティス
7.2.2 どういうときにコミットステージを止めるべきか?

• コードの品質が悪い場合
 – 警告が多い
 – コードスタイル違反
 – コード重複量
• コミットステージが失敗したら、デリバリーチー
  ムは何をしていても手を止めて修正しなくて
  はならない
 – コミットテスト失敗の境はチームの合意で決める
   • 合意がないと失敗を深刻に受け止めなくなる
7.2 コミットステージの原則とプラクティス
      7.2.3 コミットステージを丁寧に整備せよ

• 丁寧に保守すべき
 – ビルドスクリプト
  •   高速化
  •   失敗の検知
  •   変更頻度別のモジュール化
  •   環境に特化したスクリプトは書かない
 – ユニットテスト
 – 静的コード解析ツール
7.2 コミットステージの原則とプラクティス
     7.2.4 開発者に所有権を与えよ

• CI システム保守の専門家がいてもよいが、専
  門家しか保守できないのはダメ
 – プロジェクト開始時の構築は専門家でもよい
 – 開発者が変更できないとスピードが落ちる
• ビルドシステム保守に対して、開発者や運用
  担当者は不安を感じず、責任を感じるべき
7.2 コミットステージの原則とプラクティス
 7.2.5 きわめて大規模なチームにはビルドマスターを置け

• 30 人くらいまでのチームなら自己組織化がう
  まくいく
• チームが大きい、作業場所が分散などの場
  合は、「ビルドマスター」の役割を演じてもらう
 – 保守を管理
 – 規律を奨励、強制
 – ビルドが壊れた際、チームに修正を依頼
 – チームが継続的インテグレーションに慣れていな
   いと特に有効
  • 週ごとに交代して練習する
7.3 コミットステージの結果

• 入力
 – ソースコード
• 出力
 – バイナリ
   • リリースされる可能性のあるもの
 – レポート
   • テストレポート
   • コードベース解析レポート
       – テストカバレッジ、サイクロマチック複雑度、コピペ解析、求心
         性結合と遠心性結合
7.3 コミットステージの結果
       7.3.1 成果物リポジトリ

• 成果物は再利用でき、チームが入手できるよ
  うにしなくてはならない
• しかし、バージョン管理システムではダメ
 – 必要に応じてバイナリとレポートを成果物リポジト
   リから削除したい
 – パイプラインの一環として全てをソースコントロー
   ルにいれると複雑になる
 – バイナリを削除したうえで、コミットステージを再
   実行した際に、全く同じバイナリを生成したい
7.3 コミットステージの結果
        7.3.1 成果物リポジトリ

• いまどきの継続的インテグレーションサーバ
  は成果物リポジトリの機能を持っている
 – 不要な成果物を一定期間で削除
 – どの成果物をリポジトリに入れるかの宣言
 – レポートやバイナリ閲覧用 Web インタフェース
• Nexus(専用成果物リポジトリ)
• Maven 型リポジトリマネージャ
7.3 コミットステージの結果
                   7.3.1 成果物リポジトリ
                                     テスター
                                                     運用担当者
開発者




                          リリース候補を            リリース候補を
                          デプロイしてテスト          リリース
      チェックイン


バージョン
               コミット     受け入れテスト     手作業テスト   性能テスト   リリース
               ステージ      ステージ        ステージ    ステージ    ステージ
コントロール




                レポート
                バイナリ         成果物
                                              ※プロジェクトごとに
                メタデータ       リポジトリ
                                               順番など変更可能
7.4 コミットテストスイートの原則とプラクティス

• ユニットテストはコードの 80% をカバー
• ユニットテストは数分で終わるべき
 – ファイルシステム、DB、ライブラリ、フレームワー
   クなど外部システムに触れてはいけない
 – モックやスタブを使う
• テストの数
 – ユニット > サービス > UI
7.4 コミットテストスイートの原則とプラクティス
   7.4.1 ユーザーインターフェイスを避けよ

• UI はユーザが最もバグを見つけやすいため、
  労力をさかれがちだが、コミットテスト時には
  一切行わないほうがよい
 – 多くのコンポーネント、レイヤを巻き込み、労力、
   時間がかかる
 – UI は人間の時間感覚にあわせて動作するように
   設計しているため時間がかかる
7.4 コミットテストスイートの原則とプラクティス
   7.4.2 DI(Dipendency Injection)を使用せよ

• 例) Car クラスのインスタンス生成時にEngine
  をインスタンスの中で生成するよりも、インス
  タンス生成時に Engine を渡した方がよい
 – Car と engine を引き離してクラス依存を無くせる
 – TestEngine を使える
7.4 コミットテストスイートの原則とプラクティス
      7.4.3 データベースを避けよ

• あるコードのテスト結果を DB に格納し、それ
  を確認するテストはよくない
 – 実行に時間がかかる
 – 基盤セットアップが複雑
• レイヤ化、DI、インメモリデータベース(最後の
  手段)
• しかし、コミットテストにはスモークテストを含
  めなければならない
7.4 コミットテストスイートの原則とプラクティス
  7.4.4 ユニットテストでの非同期処理を避けよ

• テストを分割して非同期を避ける
 – 例)システムがメッセージをポストし、それに対し
   てふるまう場合
• スタブを使う
• モック化
7.4 コミットテストスイートの原則とプラクティス
        7.4.5 テストダブルを利用する

• スタブ
 – 極端なケースもシミュレーションできる
 – 並行開発できる
 – 実際のシステムとやりとりするか、スタブとやりと
   りするかを環境ごとに選べる
• モック
 – スタブよりコードが少ない
 – ツール(Mockito、Rhino、EasyMock、JMock、
   NMock、Mocha)
7.4 コミットテストスイートの原則とプラクティス
   7.4.6 テスト内の状態を最低限に抑える

• シンプルに、適切なかたちで入力し、期待す
  る出力と結果を比較すればよい
• 手の込んだデータ構造はダメ
7.4 コミットテストスイートの原則とプラクティス
       7.4.7 時間の概念を偽装する

• 「うるう年」「1 時間後」など
• 時間への要求を別のクラスに抽象化
 – DI、スタブ、モック
7.4 コミットテストスイートの原則とプラクティス
           7.4.8 しらみつぶし

• コミットテストが 10 分を超えてはならない
 – こまめにチェックインをしなくなる
 – コミットテストスイートが通ったかどうかを気にしな
   くなる
• コミットテスト高速化
 – テストを分割して並列実行
   • ビルドグリッド
 – あまり失敗しないテストを受け入れテストステージ
   に押し出す(当然リスクもある)
7.5 まとめ
•   早く検知
•   開発者に通知
•   問題を素早く修正
•   できれば 5 分以内

継続的デリバリー読書会 第 7 章 コミットステージ

  • 1.
    『継続的デリバリー』読書会 第7章 コミットステージ 大崎的デリバリー @Yanuto
  • 2.
    7.1 導入 「コミットステージは、プロジェクトの状態が変化するところから始まる。」 コミットステージ ・コンパイル 自動化された 自動化された 手作業での ・ユニットテスト キャパシティ リリース 受け入れテスト テスト ・分析 テスト ・インストーラーのビルド • 大切な第一歩 デプロイメントパイプライン – コードレベル統合に費やす時間を抑えられる – 優れた設計プラクティスが採用され、コードの品 質が上がる – デリバリーの速度も上がる • 開発者にとって最も重要なフィードバック – 早めに失敗させる
  • 3.
    7.1 導入 • 理想は5分以内、10分以上はダメ コミットステージ ソースコード ・コンパイル バージョン ・ユニットテスト コントロール ・分析 ・インストーラーのビルド 失敗時:レポート 成功時:バイナリ、メタデータ、レポート バージョン コントロール
  • 4.
    7.1 導入 • 誰かがtrunk にチェックインすると検知し、 チェックアウト後に下記を実行(失敗してもで きるだけ最後までやって問題箇所を出す) – コンパイル コミットステージ – 統合されたソースにコミットテスト ・コンパイル ・ユニットテスト – バイナリ生成 ・分析 ・インストーラーのビルド – コードベースの健全性をチェック – 成果物(DB 移行スクリプト、テストデータ)
  • 5.
    7.2 コミットステージの原則とプラクティス 7.2.1役に立つフィードバックを素早く提供せよ • コミットテストが失敗する原因 – 構文エラー(コンパイル) – アプリケーションの意味上のエラー(テスト) – アプリケーションの設定、環境(OS 含む)の問題 • 失敗したらすぐに開発者へ – 失敗したテスト – コンパイルエラー – エラー条件 など
  • 6.
    7.2 コミットステージの原則とプラクティス 7.2.1 役に立つフィードバックを素早く提供せよ • いまどきの開発環境ならコミットステージ前に 教えてくれる – プログラミング • コンパイル時 • 構文エラー – CI • プレテストコミット • プレフライトビルド
  • 7.
    7.2 コミットステージの原則とプラクティス 7.2.2 どういうときにコミットステージを止めるべきか? •コードの品質が悪い場合 – 警告が多い – コードスタイル違反 – コード重複量 • コミットステージが失敗したら、デリバリーチー ムは何をしていても手を止めて修正しなくて はならない – コミットテスト失敗の境はチームの合意で決める • 合意がないと失敗を深刻に受け止めなくなる
  • 8.
    7.2 コミットステージの原則とプラクティス 7.2.3 コミットステージを丁寧に整備せよ • 丁寧に保守すべき – ビルドスクリプト • 高速化 • 失敗の検知 • 変更頻度別のモジュール化 • 環境に特化したスクリプトは書かない – ユニットテスト – 静的コード解析ツール
  • 9.
    7.2 コミットステージの原則とプラクティス 7.2.4 開発者に所有権を与えよ • CI システム保守の専門家がいてもよいが、専 門家しか保守できないのはダメ – プロジェクト開始時の構築は専門家でもよい – 開発者が変更できないとスピードが落ちる • ビルドシステム保守に対して、開発者や運用 担当者は不安を感じず、責任を感じるべき
  • 10.
    7.2 コミットステージの原則とプラクティス 7.2.5きわめて大規模なチームにはビルドマスターを置け • 30 人くらいまでのチームなら自己組織化がう まくいく • チームが大きい、作業場所が分散などの場 合は、「ビルドマスター」の役割を演じてもらう – 保守を管理 – 規律を奨励、強制 – ビルドが壊れた際、チームに修正を依頼 – チームが継続的インテグレーションに慣れていな いと特に有効 • 週ごとに交代して練習する
  • 11.
    7.3 コミットステージの結果 • 入力 – ソースコード • 出力 – バイナリ • リリースされる可能性のあるもの – レポート • テストレポート • コードベース解析レポート – テストカバレッジ、サイクロマチック複雑度、コピペ解析、求心 性結合と遠心性結合
  • 12.
    7.3 コミットステージの結果 7.3.1 成果物リポジトリ • 成果物は再利用でき、チームが入手できるよ うにしなくてはならない • しかし、バージョン管理システムではダメ – 必要に応じてバイナリとレポートを成果物リポジト リから削除したい – パイプラインの一環として全てをソースコントロー ルにいれると複雑になる – バイナリを削除したうえで、コミットステージを再 実行した際に、全く同じバイナリを生成したい
  • 13.
    7.3 コミットステージの結果 7.3.1 成果物リポジトリ • いまどきの継続的インテグレーションサーバ は成果物リポジトリの機能を持っている – 不要な成果物を一定期間で削除 – どの成果物をリポジトリに入れるかの宣言 – レポートやバイナリ閲覧用 Web インタフェース • Nexus(専用成果物リポジトリ) • Maven 型リポジトリマネージャ
  • 14.
    7.3 コミットステージの結果 7.3.1 成果物リポジトリ テスター 運用担当者 開発者 リリース候補を リリース候補を デプロイしてテスト リリース チェックイン バージョン コミット 受け入れテスト 手作業テスト 性能テスト リリース ステージ ステージ ステージ ステージ ステージ コントロール レポート バイナリ 成果物 ※プロジェクトごとに メタデータ リポジトリ 順番など変更可能
  • 15.
    7.4 コミットテストスイートの原則とプラクティス • ユニットテストはコードの80% をカバー • ユニットテストは数分で終わるべき – ファイルシステム、DB、ライブラリ、フレームワー クなど外部システムに触れてはいけない – モックやスタブを使う • テストの数 – ユニット > サービス > UI
  • 16.
    7.4 コミットテストスイートの原則とプラクティス 7.4.1 ユーザーインターフェイスを避けよ • UI はユーザが最もバグを見つけやすいため、 労力をさかれがちだが、コミットテスト時には 一切行わないほうがよい – 多くのコンポーネント、レイヤを巻き込み、労力、 時間がかかる – UI は人間の時間感覚にあわせて動作するように 設計しているため時間がかかる
  • 17.
    7.4 コミットテストスイートの原則とプラクティス 7.4.2 DI(Dipendency Injection)を使用せよ • 例) Car クラスのインスタンス生成時にEngine をインスタンスの中で生成するよりも、インス タンス生成時に Engine を渡した方がよい – Car と engine を引き離してクラス依存を無くせる – TestEngine を使える
  • 18.
    7.4 コミットテストスイートの原則とプラクティス 7.4.3 データベースを避けよ • あるコードのテスト結果を DB に格納し、それ を確認するテストはよくない – 実行に時間がかかる – 基盤セットアップが複雑 • レイヤ化、DI、インメモリデータベース(最後の 手段) • しかし、コミットテストにはスモークテストを含 めなければならない
  • 19.
    7.4 コミットテストスイートの原則とプラクティス 7.4.4 ユニットテストでの非同期処理を避けよ • テストを分割して非同期を避ける – 例)システムがメッセージをポストし、それに対し てふるまう場合 • スタブを使う • モック化
  • 20.
    7.4 コミットテストスイートの原則とプラクティス 7.4.5 テストダブルを利用する • スタブ – 極端なケースもシミュレーションできる – 並行開発できる – 実際のシステムとやりとりするか、スタブとやりと りするかを環境ごとに選べる • モック – スタブよりコードが少ない – ツール(Mockito、Rhino、EasyMock、JMock、 NMock、Mocha)
  • 21.
    7.4 コミットテストスイートの原則とプラクティス 7.4.6 テスト内の状態を最低限に抑える • シンプルに、適切なかたちで入力し、期待す る出力と結果を比較すればよい • 手の込んだデータ構造はダメ
  • 22.
    7.4 コミットテストスイートの原則とプラクティス 7.4.7 時間の概念を偽装する • 「うるう年」「1 時間後」など • 時間への要求を別のクラスに抽象化 – DI、スタブ、モック
  • 23.
    7.4 コミットテストスイートの原則とプラクティス 7.4.8 しらみつぶし • コミットテストが 10 分を超えてはならない – こまめにチェックインをしなくなる – コミットテストスイートが通ったかどうかを気にしな くなる • コミットテスト高速化 – テストを分割して並列実行 • ビルドグリッド – あまり失敗しないテストを受け入れテストステージ に押し出す(当然リスクもある)
  • 24.
    7.5 まとめ • 早く検知 • 開発者に通知 • 問題を素早く修正 • できれば 5 分以内