61
継続的デリバリー読書会 第14章 高度なバージョン管理 大崎的デリバリー @hidesuke

継続的デリバリー読書会 14章

Embed Size (px)

Citation preview

Page 1: 継続的デリバリー読書会 14章

継続的デリバリー読書会 第14章 高度なバージョン管理

大崎的デリバリー @hidesuke

Page 2: 継続的デリバリー読書会 14章

14.1 導入 •  バージョン管理システムの目的 – アプリケーションに対して行われたすべての変更の完全な履歴を管理すること

– ソースコード/ドキュメント/ビルドスクリプト/テストも含める

•  本章の目的はバージョン管理システムを活用してチームの生産性をあげること

Page 3: 継続的デリバリー読書会 14章

14.2 リビジョン管理システムの簡単な歴史

•  SCCS – あらゆるバージョン管理システムのご先祖 – 1972年ベル研究所で生まれる – マーク・J・ロックカインドが作った

•  その派生がいろいろいっぱいある

Page 4: 継続的デリバリー読書会 14章

14.2.1 CVS •  Concurrent Version System •  複数の開発者が同時に同じリポジトリ上で作業できる(という意味)

•  クライアントサーバ型アーキテクチャ •  協力なブランチ、タグ機能 •  長い間よくつかわれた(他に自由に使えるものがなかったから)

•  ファイル単位での変更追跡 •  ただ、いろいろ問題もある(P.449参照)

Page 5: 継続的デリバリー読書会 14章

14.2.2 Subversion •  よりよいCVSを目指してつくられた •  CVSの問題を修正。CVSユーザにとって使いやすいように作られた

•  リビジョン単位でのバージョン管理 •  リビジョン番号が各リポジトリ全体に適用される – 各ファイル単位ではない

•  やっぱり問題点もいろいろある(P.451参照)

Page 6: 継続的デリバリー読書会 14章

14.2.3 商用のバージョン管理システム

•  Perfoce – パフォーマンス、スケーラビリティに優れる

•  AccuRev •  BitKeeper – 真の分散型バージョン管理システムとして最初に登場したもの

•  TeamFoundationServer – VisualStudioつかっているなら使ってもいいんじゃない?

Page 7: 継続的デリバリー読書会 14章

14.2.4 悲観的ロックをやめろ •  楽観的ロックに対応してるならそれを使えばよい – つまり、ローカルの作業コピーのファイルを編集しているときに、他のメンバーも各自の作業コピー上で同じファイルを編集できる

•  悲観的ロックの方がマージの際の衝突を防ぎやすいが、開発プロセスの効率を下げる – 悲観的ロック:排他ロックを取得しないと、そのファイルを編集できない

Page 8: 継続的デリバリー読書会 14章

14.3 ブランチとマージ •  ブランチの主な目的 – 複数の作業ストリームを同時に稼働させる – 互いが他のラインに影響を及ぼさないようにする

•  開発はメインライン、バグフィックスはリリースブランチで行うという方法がよくとられる

Page 9: 継続的デリバリー読書会 14章

14.3 ブランチとマージ •  (先ほどの例以外で)チームがコードのブランチを作る理由がいくつかある •  物理的な理由

–  システムの物理的な構成によるブランチ –  サブシステム単位でブランチを切っている

•  機能的な理由 –  システムの機能的な構成によるブランチ –  論理的な変更、バグフィックス、機能拡張、デリバリー可能な機能単位

•  環境的な理由 –  システムの運用環境によるブランチ –  ビルド時や実行時のプラットフォームの違いなど

•  組織的な理由 –  チームの開発活動によるブランチ –  タスク、サブプロジェクト、グループ単位

•  手続き上の理由 –  チームの作業の振る舞いによるブランチ –  運用ポリシーやプロセス、状態単位

Page 10: 継続的デリバリー読書会 14章

14.3 ブランチとマージ •  一連の変更はひとつのブランチで行いそれを別のブランチにコピーする

•  マージ

Page 11: 継続的デリバリー読書会 14章

14.3 ブランチとマージ •  ブランチはあとでマージしなければならない

•  各ブランチがデリバリープロセスにおいてどのような役割を果たすかポリシーを定義する必要がある

Page 12: 継続的デリバリー読書会 14章

14.3.1 マージ •  各ブランチは完全に独立している、他のブランチの存在を全く無視して存在している

•  しかし、ある時点でブランチへの変更内容を別のブランチに適用する時がくる – => とても時間のかかる作業

Page 13: 継続的デリバリー読書会 14章

14.3.1 マージ •  真の問題が発生するケース •  マージしたい二つのブランチでそれぞれ別の変更を行い、それらが衝突している場合 – 機能の実装方法が食い違っているような変更の場合、かなりの量のコードを書きなおさないといけない。

– これをマージする場合はコードを書いた人の意図をしらないと不可能

Page 14: 継続的デリバリー読書会 14章

14.3.1 マージ •  意味的な衝突はバージョン管理システムでは検知できない

•  包括的な自動テストがなければ、実際に障害がおこるまで衝突があったことに気づかない

•  マージするまでの期間が長くなればなるほど、作業する人がおおくなればなるほどマージに悩まされる

Page 15: 継続的デリバリー読書会 14章

14.3.1 マージ •  マージで悩まないためには……

•  ブランチの数を増やし、ひとつのブランチで行われる変更の数を減らす

•  ブランチの作成をなるべく控え、リリース毎に1つずつ程度にする

Page 16: 継続的デリバリー読書会 14章

14.3.2 ブランチ、ストリーム、そして継続的インテグレーション •  ブランチの利用と継続的インテグレーションは対立関係にある – それぞれ別のブランチで作業を行なっているということは継続的インテグレーションをおこなっていないということ

Page 17: 継続的デリバリー読書会 14章

14.3.2 ブランチ、ストリーム、そして継続的インテグレーション •  継続的インテグレーションを行うプラクティス – 全員が少なくとも1日1度はメインラインにチェックインする

– メインラインへのマージを1日1回はやっているのならOK

Page 18: 継続的デリバリー読書会 14章

14.3.2 ブランチ、ストリーム、そして継続的インテグレーション •  より管理しやすいブランチ方針 – 寿命の長いブランチをリリース時にだけ作る(p.459 図14-2)

– 新しい作業は常にtrunkにコミット – マージされるのはリリースブランチに修正がはいったときだけ

– コードが常にリリース可能な状態

Page 19: 継続的デリバリー読書会 14章

14.4 分散型バージョン管理システム

•  Distributed Version Control System •  DVCS

Page 20: 継続的デリバリー読書会 14章

14.4.1 分散型バージョン管理システムとは?

•  各自が自己完結した一人前リポジトリを自分のコンピュータ上に持つ

•  変更はローカルリポジトリにコミット •  他のユーザの変更を直接取り込める •  更新を特定のユーザたちにだけ送ることが可能 •  どのパッチを採用して、どのパッチを却下するのかを簡単にきめられる チェリーピック

•  オフラインでもコミットできる •  ローカルでのコミットを、手軽に変更したり、分岐したり、まとめたりといった作業をしてから他のリポジトリに送ることができる リベース

•  何かのアイディアをローカルリポジトリで試したいときに中央リポジトリにブランチを作らなくてよい

•  高可用性 •  リポジトリのコピーが多数作られているので耐障害性が上がる

Page 21: 継続的デリバリー読書会 14章

14.4.1 分散型バージョン管理システムとは?

•  いわゆるメインラインはどこにも存在しない(P.461 図14-3)

•  GitHub, BitBucket, GoogleCode – 既存リポジトリのコピーを作るの簡単 – 手元で変更した内容を他のユーザに公開してみてもらうのも簡単

– 本家のメンテナーもこれらの変更を確認でき、気に入ったものは自分のマスターリポジトリに取り込める

•  協調作業におけるパラダイムシフト •  新機能やバグフィックスのデリバリーが高速化

Page 22: 継続的デリバリー読書会 14章

14.4.2 分散型バージョン管理システムの簡単な歴史

•  Linuxの一部のセクションのメンテナーがBitKeeperを使い始めたのが普及のはじまり

•  現在は – Git – Mercurial – Bazaar – Darcs – Monotone – など

Page 23: 継続的デリバリー読書会 14章

14.4.3 企業環境における分散型バージョン管理システム

•  本書執筆時点では営利企業でのDVCSの導入はあまり進んでいない

Page 24: 継続的デリバリー読書会 14章

14.4.4 分散型バージョン管理システムを使う

SVN •  svn up (最新のリビジョンの取得)

•  何らかのコードを書く •  svn upで自分の変更と中央リポジトリの更新をマージ

•  コミットビルドをローカルで実行

•  svn ciで自分の変更をバージョン管理にチェックイン

hg (Mercurial) P.465 図14-4 •  hg pull でリモートリポジトリの最新の更新をローカルリポジトリに取得

•  hg co でローカルの作業コピーの内容をローカルリポジトリから更新

•  何らかのコードを書く •  hg ci で変更をローカルリポジトリに保存

•  hg pull リモートリポジトリの更新を取り込み

•  hg merge ローカル作業コピーの内容をマージ結果で更新

•  コミットビルドをローカルで実行 •  hg ci マージ結果をローカルリポジトリにチェックイン

•  hg push 更新をリモートリポジトリにプシュ

Page 25: 継続的デリバリー読書会 14章

14.5 ストリームベースのバージョン管理システム

•  IBMのClearCaseなど

Page 26: 継続的デリバリー読書会 14章

14.5.1 ストリームベースのバージョン管理システムとは?

•  一連の変更を複数のブランチへ同時に適用出来る – マージの際の問題を解決するため

•  ブランチが協力な概念であるストリーム置き換えられる

•  ストリームが継承できる – 親ストリームに変更を適用すると、子孫ストリームにも変更が同時に適用される

Page 27: 継続的デリバリー読書会 14章

14.5.1 ストリームベースのバージョン管理システムとは?

•  バグフィックスをアプリケーションの複数のバージョンに適用する場合 – ストリームベースのツールじゃない場合は手動でマージ

– ストリームベースでは祖先ブランチに変更を適用するだけでよい

– 各ブランチの利用者は更新するだけで変更をうけいれ、修正を含む新しいビルドを作れる

Page 28: 継続的デリバリー読書会 14章

14.5.1 ストリームベースのバージョン管理システムとは?

•  サードパーティのライブラリをコードベースに追加する場合 – 共通の祖先に新しいバージョンをチェックインすればよい

Page 29: 継続的デリバリー読書会 14章

14.5.1 ストリームベースのバージョン管理システムとは?

•  あるストリームへの変更は、その変更を昇格させるまで他のストリームに何の影響も及ぼさない

•  昇格させるとそのストリームを継承するすべてのストリームから変更が視えるようになる

Page 30: 継続的デリバリー読書会 14章

14.5.2 ストリームを使った開発モデル

•  特定の機能を実装するためのストリームを作る

•  機能の実装が終えたら、そのストリーム内でのすべての変更をチーム全体のストリームに昇格させる

•  継続的インテグレーションに投入する

•  中規模~大規模のチームでも複数の機能を同時に開発しつつお互いに影響を及ぼさないようにできる

Page 31: 継続的デリバリー読書会 14章

14.5.2 ストリームを使った開発モデル

•  別々のチームが共有コードを異なる方法で変更した際に複雑なマージが発生

•  ある新機能が依存する別の機能のコードがまだ昇格していない場合に、依存関係の管理に問題が発生する

•  リリースストリーム上でのインテグレーションテストやリグレッションテストが新しい設定ではうまく動かないという場合に統合時の問題が発生する

Page 32: 継続的デリバリー読書会 14章

14.5.2 ストリームを使った開発モデル

•  チーム数やレイヤの数が増えるほど問題は悪化

•  影響はどんどん増殖 •  チームレベル、ドメインレベル、アーキテクチャレベル、システムレベル、本番レベルの5つのストリームを作ることで解決

•  ……しかし、ストリームを昇格させるたびに先に挙げたような問題が発生した

Page 33: 継続的デリバリー読書会 14章

14.5.2 ストリームを使った開発モデル

•  上位ストリームへの昇格は可能な限り頻繁に行う

•  可能な限りの頻度で自動テストを実行する

Page 34: 継続的デリバリー読書会 14章

14.5.3 静的ビューおよび動的ビュー

•  ClearCaseの「動的ビュー」機能 – ファイルがストリームにマージされたときにそのストリームを継承するすべてのストリームを使う開発者のビューを更新する機能

– 各開発者が変更をこまめにチェックインしていれば、マージが楽になる

– 超低速 •  「静的ビュー」 – 開発者が自分でビューを更新しない限り変更は反映されない

Page 35: 継続的デリバリー読書会 14章

14.5.4 ストリームベースのバージョン管理システムによる継続的

インテグレーション •  利点 – 開発者が自分だけのストリームで作業するのが容易

– マージも容易 •  変更が定期的に昇格されていれば継続的インテグレーションが可能 – これをやると、前述した利点もそれなりの制約を受けることになる

Page 36: 継続的デリバリー読書会 14章

14.5.4 ストリームベースのバージョン管理システムによる継続的

インテグレーション •  ClearCaseでは…… – 完全にサーバーベースのモデル – マージ、タグ付け、ファイルの削除まですべてがサーバ側で処理

– 親ストリームに変更を昇格させようとすると、兄弟ストリーム上で発生する衝突をこみったが解決しないといけない

– どんなサイズのリポジトリでも操作(チェックイン、ファイル削除、タグ付けなど)に恐ろしく時間がかかる

– 頻繁にチェックインしようとすると大きなコストになる

Page 37: 継続的デリバリー読書会 14章

14.5.4 ストリームベースのバージョン管理システムによる継続的

インテグレーション •  変更セットの昇格機能について、継続的インテグレーションを組み合わせるときは問題がある

•  バグフィックスをストリームの祖先に昇格すると、その子孫のストリームすべてに対してビルドを起動しなくてはならない – 無理

Page 38: 継続的デリバリー読書会 14章

•  以下ではでは様々なパターンのブランチやマージについて、それぞれの利点・欠点とどんな場面で使えばいいかについて述べる

Page 39: 継続的デリバリー読書会 14章

14.6 メインライン上での開発 •  開発者は常にメインラインにチェックインする •  ブランを切ることはめったにない

•  すべてのコードが継続的インテグレーションの対象

•  各開発者が他のメンバーによる変更をすぐに受け取れる

•  プロジェクトの終盤になって「マージ地獄」や「インテグレーション地獄」に陥らずに済む

Page 40: 継続的デリバリー読書会 14章

14.6 メインライン上での開発 •  通常の開発では開発者がメインライン上で作業を進める

•  少なくとも一日に一度はコードコミットする •  基本的にブランチは作らない •  ひとつひとつの変更が充分に小さく、インクリメンタルなものになるように計画・実装し、各段階でテストを通し、既存の機能を壊さないことを確認しながら進めていく

Page 41: 継続的デリバリー読書会 14章

14.6 メインライン上での開発 •  ブランチを否定しているわけではない •  すべての開発作業は、いつかひとつのコードラインに落ち着く

•  ブランチを作るのは、メインラインへのマージが不要な場合のみ(リリースや、変更前のスパイク)

Page 42: 継続的デリバリー読書会 14章

14.6 メインライン上での開発 •  メインラインで開発を行うということは、メインラインが常にリリース可能な状態であるとは限らない

•  変更に対するフィードバックを素早く得られるという利点

•  完全に結合されたアプリケーションにどのように影響を及ぼすかすぐにわかるという利点

Page 43: 継続的デリバリー読書会 14章

14.6.1 複雑な変更をブランチなしでおこなう

•  複雑な変更をブランチを切って行う場合 – メインブランチと大きくかけ離れる – マージで死ぬ – メインラインが安定するまでマージできない – リリースが大幅に遅れる – リファクタリングが困難になる

Page 44: 継続的デリバリー読書会 14章

14.6.1 複雑な変更をブランチなしでおこなう

•  コミットは常にtrunkに •  少なくとも一日一度はコミット •  メインラインへのインクリメンタルな変更をこまめに進めていくのが重要

Page 45: 継続的デリバリー読書会 14章

14.7 リリース用のブランチ •  リリース直前は常にブランチを作ってもかまわないだろう

•  リリースブランチをつくればコードフリーズを止めることができる – コードフリーズ:バージョン管理システムへのチェックインを数日から数週間にかかえて止めてしまうこと

•  リリースブランチをきれば、開発者はメインラインで開発を進められる

Page 46: 継続的デリバリー読書会 14章

14.7 リリース用のブランチ •  新規機能の開発は常にメインラインで行う •  ブランチを作るのは、特定のリリース用に必要な機能が完全に揃った段階で、さらに新しい機能の開発を始めたくなったときだけ

•  致命的な欠陥の修正だけをブランチに適用し、それはメインラインにもただちに反映

•  リリースが完了したら、そのブランチにタグを打つ

Page 47: 継続的デリバリー読書会 14章

14.7 リリース用のブランチ •  リリースブランチからさらに別のブランチを切ってはいけない

•  リリース用のブランチはすべてメインラインからつくる

Page 48: 継続的デリバリー読書会 14章

14.8 フィーチャによるブランチ •  大規模なチームが並行して機能を開発しながらメインラインもリリース可能な状態に保つ

•  すべてのストーリー/フィーチャーは個別のブランチを用意して、そこで開発を進める

•  trunkを常にリリース可能な状態にしておきたい場合に使う

Page 49: 継続的デリバリー読書会 14章

14.8 フィーチャによるブランチ •  メインライン上での変更は常にブランチにマージ •  一つのブランチの活動期間を短めに •  ある時点でのアクティブなブランチ数がその時点で作業中のストーリー数を決して超えない

•  一つ前のストーリーのブランチがメインラインにマージされるまでは新しいブランチは作らない

•  テスターによるストーリーの受け入れテストをしてからマージする •  リファクタリングはすぐにマージして、マージの際の衝突を最小化する

•  リーダーの役割の一つはtrunkを常にリリース可能な状態に保つことにあんる

•  リーダーはすべてのマージをレビューしなければならない •  リーダーはtrunkを壊す可能性のあるパッチを却下する権限を持つ

Page 50: 継続的デリバリー読書会 14章

14.8 フィーチャによるブランチ •  長期にわたって使われつづけるブランチを作りすぎるのはよくない

•  マージで死ぬ •  ブランチを切るという行為は本質的に継続的インテグレーションに反する

•  真の継続的インテグレーションに近いのはCIシステムが全部ランチを一つの仮想trunkにマージすること – でもこれはたぶん失敗する

Page 51: 継続的デリバリー読書会 14章

14.8 フィーチャによるブランチ •  分散型バージョン管理システムはこの手のパターンを想定して作られている

•  メインラインからのマージ、最先端にに対するパッチの作成が簡単にできる

•  オープンソースの世界ではうまくいく •  商用製品のプロジェクトでも、コア開発チームが少数精鋭であればうまくいくだろう

Page 52: 継続的デリバリー読書会 14章

14.8 フィーチャによるブランチ •  大規模なプロジェクトでこのパターンでうまくいく場合 – コードベースがモジュール化されている – きちんとリファクタリング済み – デリバリー舞台が複数の小規模なチームにわかれている

– 経験豊富なリーダーが率いている – チーム全体がチェックインやメインラインへの統合を頻繁に行なっている

– デリバリーチームにリリースを迫るようなプレッシャーを与えないこと

Page 53: 継続的デリバリー読書会 14章

14.9 チームによるブランチ •  大規模チームが複数の作業を並行で進めている時に、メインラインも常にリリース可能な状態にしたい

•  trunkを常にリリース可能にしてしておく •  チーム毎にブランチを切り、ブランチが安定した時点でtrunkにマージする

•  あるブランチに何らかのマージをした場合、他のブランチにもすぐに同じ内容をマージしなければならない

Page 54: 継続的デリバリー読書会 14章

14.9 チームによるブランチ •  小さめのチームを作り、チーム毎のブランチで作業を進める

•  あるフィーチャ/ストーリーの実装が完了したら、ブランチを安定させてそれをtrunkにマージ

•  trunkへの変更は常にすべてのブランチにマージする

•  ユニットテストや受け入れテストは各ブランチでのチェックインのたびに実行する

•  インテグレーションテストを含むすべてのテストは、各ブランチからtrunkへのマージがあるたびに実行する

Page 55: 継続的デリバリー読書会 14章

14.9 チームによるブランチ •  trunkへチェックインさせていると定期的なリリースを保証しにくくなる

•  複数のチームがそれぞれのストーリーに対応しているとtrunkはほぼ常に中途半端な作業が残った状態になる

•  そのままではアプリケーションをリリースできなくなる

Page 56: 継続的デリバリー読書会 14章

14.9 チームによるブランチ •  開発者たちはみな自分のチームのブランチにのみチェックインする

•  このブランチをtrunkにマージするのは作業中のすべてのフィーチャーが完了したときのみ

Page 57: 継続的デリバリー読書会 14章

14.9 チームによるブランチ •  うまくいく場合 – 複数の小規模なチームが比較的独立して作業 – システム内で機能的にそれぞれ独立した部分を扱っている

•  すべてのブランチはオーナーを任命し、オーナーにそのポリシーをまとめさせる必要がある

Page 58: 継続的デリバリー読書会 14章

14.9 チームによるブランチ •  trunkを常にリリース可能な状態に保つ •  ブランチが安定するまでtrunkにマージできない – 安定:trunkにマージするときに自動テストを一切壊さない状態

Page 59: 継続的デリバリー読書会 14章

14.9 チームによるブランチ •  マージを頻繁に行わなければ、真の継続的インテグレーションが実現できなくなる – マージの際の衝突地獄

•  すべてのチームはひとつのストーリーが完了した時点でtrunkにマージする

•  trunkでの変更の取り込みも一日一度は行う

Page 60: 継続的デリバリー読書会 14章

14.10 まとめ •  洗練されたモダンなバージョン管理システムの使いやすさは、チームでのソフトウェア開発において最も重要

•  バージョン管理のパターンはデプロイメントパイプラインを設計する際に重要なポイント

•  バージョン管理がうまくできてないと素早くローリスクなリリースをするのは難しい

•  使える機能を理解した上で正しいツールを選んで適切に使うことがプロジェクト成功の鍵

Page 61: 継続的デリバリー読書会 14章

14.10 まとめ •  継続的インテグレーションを推進したいという思いと、ブランチを切りたいという思いは基本的に相反する

•  CIを元にした開発を進めている時にブランチを切ろうとすると、なんらかの妥協が必要 – 完全にCIの立場で考えるとすべての変更はできるだけ早くtrunkにコミットするべき

–  trunkは常に最新の完全な状態を保つ •  あらゆるブランチの内容を一日一度以上の頻度でメインラインにマージするべし