【社内勉強会】バージョン管理の重要性とGitの運用について

こんにちは、hachi8833です。
BPSで毎週木曜日に定例で行われている社内勉強会から、Webチームリーダーのmorimorihogeさんによる講義のスライドを元に記事化いたしました。

今回のスライド「Gitとバージョン管理」は、前半はバージョン管理の重要性と限界、後半は主にBPS WebチームのGit運用についての説明です。とくにバージョン管理の重要性についての説明は、開発者に限らずWebデザイナーやディレクターにとっても役に立つ内容です。

社内勉強会スライドは今後も順次記事化いたします。ご期待ください。

バージョン管理の重要性とGitの運用について

対象

  • Gitを使ったことのない人・Git入門して間もない人
  • バージョン管理のメリットがまだ実感できない人
  • Gitの使いこなしに手こずっている人
  • Gitをある程度使えるようになった人

概要

  • バージョン管理が必要な理由
  • バージョン管理がどんなときに役に立つか
  • バージョン管理の限界
  • Gitでバージョン管理する目的
  • Gitブランチのよい使い方
  • Gitを使った開発の注意点

バージョン管理の歴史(大昔)

BPS創立よりはるか昔、バージョン管理システムなどという便利なものはありませんでした。当時の人々は以下のような方法でひたすら手作業でバージョンごとのファイル履歴を残そうとしました。

  • ファイル名やフォルダ名に日時を刻む
  • ソースコードにコメントで修正日時や内容をメモする

もしかしたら今もやっている会社があるかもしれませんね。

するとどうなるか

人力による管理は気を遣わなければならない点が多く、非常に不便です。さらに、手作業の悲しさで以下のような問題が日常的に発生していました。

  • ファイルを以前のバージョンに戻したつもりなのに全然正常に動かない
    • 実は更新ファイルが他にもあったことを見落としてた、など
  • ファイルの日付表現が作業者によってばらつく
    • lsしたらYYYYMMDDだったりYYMMDDだったり
    • 外人がMMDDYYにしてたり
  • 外部に脆弱性チェックを依頼したら古いファイルで脆弱性を指摘された
    • 調べてみると、使ってないファイルが本番環境にデプロイされていた、など
  • 日付管理が嫌になったので「最新」とか「Latest」というフォルダを作ったが、あっという間に最新でなくなってしまい、あとから来た人が知らずに使って死ぬ

バージョン管理システムの誕生

ファイルのリビジョンを人力で管理するのは明らかに限界があります。こうした問題を軽減すべく、黎明期に以下のようなバージョン管理システムが誕生しました。

  • CVS — かなり古株
  • Subversion — Apacheのサブプロジェクト、Git以前にポピュラーだった
  • VSS — マイクロソフト製、Visual Studioに付属

こうした黎明期のバージョン管理システムの多くは今から見れば機能が少なめでした。

リポジトリは単にソースをコミットして保存する場所として使われることも多く、本番環境へのデプロイはコミットの後SCPなりFTPなりで手動で頑張るというスタイルが当時主流でした。

それでもシステムでバージョンを管理しておけばいつでも最新コードを取り出せるし、リリース時には必ずコミットするよう開発規約にも定めたし、もう大丈夫かと思われました。

そしてどうなったか

残念ながらそれでも以下のような問題がしばしば発生しました。

  • サーバーで突然のトラブル→本番とリポジトリのコードが一致していないことが発覚
  • 画像や素材を大量にコミットしようとするとsvn checkoutに半日かかる
  • チームで開発しているとコミット同士が衝突(conflict)する

初期バージョン管理システムの問題点とは

黎明期のバージョン管理システムの問題点は次の3つに集約されます。システムの問題だけではなく、当時の運用にも多くの問題がありました。

  1. 本番とリポジトリの不一致問題
  2. conflict戦争
  3. リポジトリがおそろしく巨大になって速度が低下

次で詳しく説明します。

1. 本番とリポジトリの不一致問題

SCPやFTPでの手動デプロイを注意深く行わないと、本番とリポジトリが一致しなくなるという問題です。

よくある事例

  • 手動作業がバージョン管理システムに反映されていない
    • 「開発環境はこれで動くけど、本番だけはXXの設定が違うからこのコードを手動で足さないとだめなんだよね」
  • 運用ルールの無視
    • 「本番でログを一時的に出力したいから、本番コードを直接Vimで変えちゃった」→そして忘れる

どうしてそうなった?

  • リリース手順が煩雑で面倒なので無断ですっとばした
  • コミットに開発途中のコードが混じってるので本番から手動で取り除いた
  • 開発環境が検証環境として機能していない(開発環境では動かず、本番でしか動かないなど)

根本問題は何か

本番とリポジトリの不一致問題の根本には以下のような原因が潜んでいます。

  • せっかくシステムでバージョン管理しているのに、デプロイが手動のまま
  • 急ぎと称して、リポジトリと別に本番ファイルを直接変更する運用が横行している

2. conflict戦争

チームで開発していると、違う開発者がまったく同じ場所に別々の修正をかけてしまうといった問題がしばしば発生します。

よくある事例

クライアントからの機能開発リクエストとリリースリクエストの順序は、多くの場合きれいには並びません。

例として、以下の3つの開発リクエストA、B、Cを受けた場合を考えてみます。

A(新機能)
優先順位が高いのですぐリリースしたい
リリース前にクライアント側で開発内容をチェックしたい
B(既知のバグ修正)
致命的なので修正次第すぐリリースして欲しい
クライアント側でのチェックは不要
C(超急ぎの致命的なバグ修正)
早急にリリースすべし

A、B、Cがいずれも「すぐリリースしたい」という指示を含んでいます。対応のために複数のリクエストで開発とリリースが同時進行すると、しばしばコードでconflictが発生します。

根本問題は何か

バージョン管理システムのconflict解決支援機能が現在よりも貧弱だったこともありますが、当時の運用の多くが未熟だったことが主要な要因でした。

たとえば、同時に複数の機能を開発していて「この機能だけ本番に取り込みたい」といった事態が運用で想定されていなかったりしました。

また、発生したconflictはバージョン管理システムだけで機械的に解決できるものではありません。どちらの修正を採用するかを開発者やレビュアーがコードをじっくり読み比べて決定をくださなければなりません。

3. 巨大リポジトリ問題

画像や動画のような巨大な素材をソースコードと一緒に多数リポジトリにぶちこんでしまうと、リポジトリが巨大になるだけでなく、バージョン管理システムの動作速度が激しく低下します。

よくある事例

  • 「仕様書PDFや画像の素材(PSDやAIファイルなど)もソースコードと一緒に入れときました」
    • ファイルを取得したりコミットしたりするたびに数百MBのファイルがサーバーとローカルを行き来する
    • (ソースコードは多くてもせいぜい数百KB)
  • マイクロコミットを大量に行ったためにシステムの動作速度が著しく低下
    • CVSやSubversionは1コミットごとにファイルを取得するので深刻

根本問題は何か

バージョン管理システムは、ソースコードのようなテキストファイルのバージョンを管理する目的で開発されたものであり、画像や動画、Excelファイルのようなバイナリファイルの管理は不得意であることを知っておきましょう。

もちろん、初期のバージョン管理システムが未熟だったせいもあります。

Gitはすべてを解決するのか?

ここからはGitについて説明します。

初期のバージョン管理システムにおける前述の3つの問題↓は、バージョン管理システムを改良すればきれいに解決できるようなものではありません。

  1. 本番とリポジトリの不一致問題
  2. conflict戦争
  3. 巨大リポジトリ問題

これらの問題の多くは、バージョン管理という作業そのものの問題であることにご注意ください。そしてオフィス業務の大半は、コンピュータを使おうと使うまいと実はバージョン管理作業なので、バージョン管理は避けてはとおれません。

Gitだとどうなのか

GitはCVSやSubversionと比べて多くの点で進歩していますが、バージョン管理作業そのものの困難までは変えられません。

本番とリポジトリの不一致問題
デプロイを自動化するだけではなく、運用とリリース手順のポリシーを適切に定める必要もある
conflict戦争
開発ブランチをどう運用・管理するかにかかっている(運用と管理まではなくせない)
巨大リポジトリ問題
Gitは処理速度面で大きく改善されているが、ファイルサイズそのものはどうしようもない

この点をぜひご理解いただきたいと思います。

Gitでできること

以上を踏まえた上で、GitがCVSやSubversionなどと比較してどのような点で優れているかを以下に示します。

  • commit、fetch、history検索が高速
  • ブランチ管理が軽量
  • リポジトリがローカル環境にも複製されるので分散開発しやすく障害に強い

CVSやSubversionはサーバーですべてを管理するので、サーバーが死ぬと全部吹っ飛びます。Gitのリポジトリはローカル環境に複製されるので、サーバーがいなくなっても作業を続行できます。

しかしながら、Gitがシステムとして有利な点はせいぜいこのぐらいです。

バージョン管理は運用が命

繰り返しになりますが、バージョン管理は運用こそが命です。Gitを導入すればバージョン管理の問題がスッキリ解決するわけではない、この点をぜひご理解いただきたいと思います。

バージョン管理システムの運用の要点を以下にまとめます。

リポジトリと環境の対応付け
開発サーバや本番サーバの環境と、リポジトリをどう対応付けるかを明確にする
ブランチの運用ルール
ブランチをどのように作成するか、してはならないかのルールを定める
置いてはいけないファイル
リポジトリに置いてよいファイル、置いてはいけないファイルのルールを定める
デプロイの自動化
以上のルールに沿ってデプロイをCIツールなどで自動化し、リポジトリと開発環境・本番環境を常に同期する

ここまでやれて初めて「Git使えます」と言ってよいと思います。Gitに限りませんが、システムがどれだけ整備されていても、誰かがルールから逸脱すれば台無しになってしまいます。

Gitをどう使うのがよいか

Gitに何を期待するのか

Git及びブランチ運用によって、何をできるようにしていきたいのかを整理してみます。

履歴管理
「このバグはいつ発生したのか」「このコードを書いたのは誰か」「以前はどう実装されていたか」などを後から調べる
リリース管理
「どのバージョンのコードがいつリリースされたか」を記録・管理する
機能の管理
「この機能に関係する修正はどこからどこまでか」を把握し、機能単位でソースコードを追えるようにする

他にもGitを使ってできることは運用方法に応じて様々です(開発環境・ステージング環境・本番環境のソースをまとめて管理する、CI連携でコード検査を行うなど)。
しかし、まずは上記3つがGitを効果的に運用する上で基本となる要素かと思います。

参考: BPS WebチームのGit運用ポリシー

参考までに、BPS WebチームではGit運用ポリシーを以下のように決めています。案件によって多少異なることもありますが、新規プロジェクトはこのポリシーで進めます。

Gitリポジトリサーバー
顧客側の指定がなければ社内のGitLabサーバーを使う
ブランチ運用ポリシー
git-flowを使う
開発手順
developブランチからfeatureブランチを作成し、Merge Request(MR)でレビューを依頼。パスしたらレビュアーがコミットする
デプロイ
Railsプロジェクトであればcapistranoで自動化し、1コマンドでできるようにしておく
登録してはいけないファイル
初期データではないDBのdumpファイルや画像素材(PSD等のasset化されていないファイル)など
(少量のテストデータは除く)
その他の注意
・ READMEに環境構築やデプロイの手順を記載し、引き継ぎ自に誰でも作業できるようにしておく
・ 本番環境のパスワードなどの機密情報をリポジトリに登録しない

MRの導入は比較的最近ですが、BPS Webチームではかなり定着してきました。

Gitのコミットとブランチ運用の心がけ

コミットやブランチ運用では、自分の手元ばかり見るのではなく、他の人がコミットやブランチ、MRを扱いやすいよう配慮していただきたいと思います。

コミット

コミットする側にありがちな意識
作業ログとコメントを残しておけばとりあえずOKかな
コミットログを見る側が求めるもの
コミットごとに「開発者が具体的に何をしたのか」をひと目で把握できるコミットメッセージだと助かるし、そうでないと困る

ブランチ運用

ブランチを自分で切って使う側にありがちな意識
自分の作業がコミットの集合としてブランチにとりあえずまとまってればいいかな
ブランチを運用する側が求めるもの
ブランチはそれ自体で完全であって欲しいし、そうでないと困る
(他のfeatureブランチを組み合わせないと動かない、みたいなのは困る)

ブランチやコミットの大きさ

featureブランチで追加・変更する機能や、ブランチのコミットのサイズが大きすぎないか/小さすぎないかにも注意しましょう。

ブランチで扱う機能が細かすぎる
ブランチの数が増えてレビュー件数が増える(例えば大量の文言修正が全部別ブランチになると辛い)
ブランチで扱う機能が大きすぎる
ブランチあたりのコード更新量が増えてレビューしづらくなる(100行超えのdiffがあるとレビュアーにかなりの気合が必要なため、後回しになる)
ブランチあたりのコミットの粒度が細かすぎる
コミットの数が増えてレビューしづらくなる。コミット単位で何をしたかがわかりにくくなる(エディタの保存とコミットを自動連携とかしてると最悪)
ブランチあたりのコミットの粒度が大きすぎる
大きな機能ブランチの場合、コミットあたりのコード更新量が増えて後から履歴を追いかけづらくなる。

機能の大きさの目安として、GitLabでMRのソース変更が画面に収まりきれないのは機能が大きすぎると考えるとよいでしょう。

こうした点を意識し、他の人にとって理解しやすく、扱いやすいコミットやブランチ運用を心がけましょう。

最後に: 「Gitをツールとして使える」段階を卒業しよう

「Git使えます」の段階から一歩進んで、Gitを共同作業でスムーズに運用できるようになることを目指しましょう。そのためにも、

  • branch切ってcommitしてpush、といったGitの基本操作は必須なので、まだ心もとないと思ったら十分に練習しておいてください。

  • 前述のとおり、コードレビュアーにとって理解しやすい親切な運用を心がけましょう。

関連記事

Ruby on RailsによるWEBシステム開発、Android/iPhoneアプリ開発、電子書籍配信のことならお任せください この記事を書いた人と働こう! Ruby on Rails の開発なら実績豊富なBPS

この記事の著者

hachi8833

Twitter: @hachi8833、GitHub: @hachi8833

コボラー、ITコンサル、ローカライズ業界、Rails開発を経てTechRachoの編集・記事作成を担当。
これまでにRuby on Rails チュートリアル第2版の半分ほど、Railsガイドの初期翻訳ではほぼすべてを翻訳。その後も折に触れてそれぞれ一部を翻訳。
かと思うと、正規表現の粋を尽くした日本語エラーチェックサービス enno.jpを運営。
実は最近Go言語が好き。
仕事に関係ないすっとこブログ「あけてくれ」は2000年頃から多少の中断をはさんで継続、現在はnote.muに移転。

hachi8833の書いた記事

BPSアドベントカレンダー

週刊Railsウォッチ

インフラ

BigBinary記事より

ActiveSupport探訪シリーズ