🌟

Azure DevOpsでのソースコード管理のベスプラ紹介:前編(バージョン管理・ブランチ戦略・リポジトリとブランチのアクセス制御と保護)

2024/12/18に公開

Azure DevOpsでソースコード管理をはじめたい

Azure DevOpsアドベントカレンダー19日目です!
以下のようなおたよりがあったので、今回はAzure DevOpsのAzure Reposを使ったソースコード管理について書いてみようと思います。

  • Azure Reposを使ってチームでのソースのバージョン管理を始めようと思っている。
  • Azure Reposを使ったソースコード管理のベストプラクティスについて簡単に説明してほしい。

Microsoftが推奨するバージョンコントロール

まず、Azure Reposを使う前に、バージョン管理はどうあるべきか?ということを確認してみましょう。

DevOps機能の評価項目から推奨プラクティスを確認

Microsoft Learnでは、DevOps機能の評価のアセスメントが提供されています。
https://learn.microsoft.com/ja-jp/assessments/56ec577c-acb6-4c7b-ad13-e224b0846153/

ここでは、以下の分野について、現状の環境を評価してもらうことができます。

  • アジャイル ソフトウェア開発
  • バージョンコントロール
  • 継続的インテグレーションと継続的デリバリー (CI/CD)
  • 柔軟なリソースとしてのインフラストラクチャ
  • 継続的セキュリティ
  • 構成管理
  • 継続的な監視
  • カルチャー

「アセスメントを開始」を選択すると、

評価項目が表示されるので、今回評価したい項目をチェックして開始すると、

こんな感じで現状の環境について「今どうやってますか?」的な質問が表示されるので、これに回答していきます。

回答していくと、その回答をベースに現状の環境や運用方法に対しての評価が提示され、改善が必要な項目については推奨アクションと関連するドキュメントへのリンクが表示されます。

DevOpsはじめたい!でも最初にASISの状態を把握してTOBEを検討したい!という際にはとてもおすすめです。

ソースコード管理についての推奨事項

これらの評価項目から、ソースのバージョン管理については以下のプラクティスを実施していることが推奨事項とされています。

==(前編)この記事ではここを解説==(/・ω・)/

  • ソースファイル、構成ファイル(.configとか)を共通のコードリポジトリ(Gitベースリポジトリなど)で管理していること
  • アプリケーション関連のファイルはすべて、ソース管理下に置くこと(サービスのビルド、テスト、構成、デプロイ、実行、文書化に使用されるすべてのファイル)
  • ブランチ戦略が定義されていること
  • ブランチとリポジトリは適切にアクセス許可が構成されていること
  • ブランチポリシーが適用されていること

==後編は次回の記事で解説==(/・ω・)/

  • コードに変更が発生したときに、それをマージするまでのレビューフローが定義されていること
  • 共有ブランチにソースをマージする際には、Pull Requestを使用してレビューが完了した後にコードの変更を共有ブランチに統合する。すべての変更がレビューによる承認を経てマージされる
  • ブランチの運用について、最大で数日存続するブランチを使用し、少なくとも毎日共有ブランチにすべての変更をコミットする
  • 依存関係はパッケージとしてリポジトリに発行され、コード内の依存関係を追跡することができること。 独自の成果物も公開して、依存する他のチームも新しい成果物を確認できること。
  • ソースコードは静的解析によって早期にエラーを特定できること

次のセクションから、前半の推奨事項の詳細と具体的にどのようなAzure DevOpsのどのような機能を活用できるかご紹介します。

そもそものソースのバージョン管理として

  • ソースファイル、構成ファイル(.configとか)を共通のコードリポジトリ(Gitベースリポジトリなど)で管理していること
  • アプリケーション関連のファイルはすべて、ソース管理下に置くこと(サービスのビルド、テスト、構成、デプロイ、実行、文書化に使用されるすべてのファイル)

まずソースコード管理計画のはじめの一歩として、チームがコードを共有し、協力して開発するためのリポジトリを作成することが必要です。
そして、アプリケーションコードだけでなく、構成ファイルやドキュメント、パイプライン定義、データスキーマ定義、インフラの構成やプロビジョニングスクリプトなど、アプリケーションのビルド、テスト、デプロイ、実行に必要なすべてのファイルをリポジトリに含めることが重要です。
「ソース管理でコードとして管理されていること」というのは、CI/CDをしていくための前提条件となり、きちんとソース管理でバージョン管理され、変更履歴を追跡することができるようになっていること、きちんとコードレビューのフローが確立されていることが重要です。

インフラストラクチャの構成も含めてコード化して管理するIaC(Infrastructure as Code)のプラクティスを実践することで、インフラ構成をコードとして管理することで、インフラの変更を追跡し、インフラの構成やプロビジョニングの再利用性の向上や自動化を実現することができます。
インフラ構成をどのようにリポジトリで管理していくか、どのようにCI/CDしていくかについては過去の記事に資料をアップしていますので、気になる方はご覧ください。
https://zenn.dev/yuriemori/articles/55a19961dc23cd

まだ、データスキーマをReposでのどういう風にコードとして管理してCI/CDしていくのかについては、yutakaosadaさんと2024/12/14のTFSUGでお話させて頂きましたので。興味があればこちらの資料もご参照ください。

https://zenn.dev/yutakaosada/articles/34c5e55988c8c9

「Gitベースリポジトリなど」とありますが、Azure Reposでは2024年12月現在、新規にプロジェクトを作成する場合はソース管理の方式はデフォでGitしか選べないようになっているので、いまはGit一択ですね(;^_^A

以前はTFVCも選択肢として表示されていましたが、2024年6月のアップデートでOrganization Settingsで明示的にTFVCでのリポジトリ作成をOnにしないとTFVCリポジトリは作成できないようになっています。

この辺の内容は過去の記事に書いているので、なんでTFVCが使えなくなったのか気になる方はこちらをご覧ください。
https://zenn.dev/yuriemori/articles/b78adb2aa15f3e

ブランチ戦略

  • ブランチ戦略が定義されていること

チームが安全にコードを共有し、協力して開発するためには、ブランチ戦略を定義することが重要です。代表的なブランチ戦略としては、以下のようなものがあります。

  • Git-Flow
  • GitHub Flow
  • Git Feature Flow
  • トランクベース開発

どのようなブランチ戦略にするかは、開発チームがどのように開発を進めるか、どのようなリリースサイクルを持つかによって異なります。また、チームの規模や開発スピード、リリース頻度によっても異なります。
リリースの頻度が高く、頻繁に統合を行うことが必要になるような場合は、たくさんの長命ブランチ(mainやdevelop, releaseのような数日以上存続するブランチ)を作成するのではなく、シンプルなブランチ戦略で、機能ごとに短命ブランチ(数日未満で削除されるブランチ)を切ってスピーディーにmainにマージしていくGitHub FlowやGit Feature Flowが適していると思います。
こちらのdocsでは、ブランチ戦略の採用についての推奨事項が記載されていますが、「ブランチ戦略はシンプルにする」という考えをベースに、以下の3つを考慮しながらブランチ戦略を採用していくようにと記載されています。

  • すべての新機能追加、バグの修正を適用するなど変更を加える際には、機能ブランチを作成すること。そのブランチにはわかりやすい名前を付けること。
  • 機能ブランチ→mainブランチへのマージはPull Requestを経由すること
  • mainブランチは高品質で、最新に。

https://learn.microsoft.com/ja-jp/azure/devops/repos/git/git-branching-guidance?view=azure-devops

Git-Flow

Git Flowは、Vincent Driessen氏が提唱したブランチ戦略で、main, release, develop, hotfixの複数の長命ブランチを持つ長期的なプロジェクト向けの複雑なワークフローです。
このブランチ戦略では、developブランチを中心に機能追加やバグ修正を行い、リリース前にはreleaseブランチを切ってリリース準備を行います。リリース後にはhotfixブランチを切って緊急のバグ修正を行います。最終的には、mainブランチにリリースされたコードがマージされます。

Git-Flowは、mainブランチにマージされる前に複数の長命ブランチへのマージが必要とされるので、mainブランチが安定した状態を保つことができるというメリットがありますが、複雑なブランチ構造になるため、運用が複雑になるというデメリットもあります。
また、mainブランチまでの統合に時間がかかるため、頻繁にリリースを行うような場合にはあまり向いていません。
個人的には、mainブランチまでの統合に時間がかかるということと複雑で開発者の運用ルールの学習コストや運用コストの高さがmainブランチの安定性を保つことに対してメリットが少ないと感じることがあるので、あまりおすすめはしないかなと感じてます。
mainブランチの安定性を保つという意味では、昨今ではCI/CDでの自動テストの実行や静的コード解析のツールがたくさんあるので、そちらを活用していく方がいいかなと思っています。
https://git-scm.com/book/en/v2/Git-Branching-Branching-Workflows

Feature Branch Workflow

Feature Branch Workflowは、チームが各機能に対して専用のブランチを作成して作業するアプローチです。この戦略は、すべての新機能、修正、または更新がメインブランチではなく、個別のフィーチャーブランチ(Feature Branch)で開発されるべきだという考えに基づいています。

運用方法としては以下のようになります。

  1. featureブランチを作成
    開発者は新しい機能の開発を開始する前に、mainブランチから新しいフィーチャーブランチを作成します。

  2. 開発とコミット
    開発者はfeatureブランチ上で作業を進め、定期的に変更をリモートブランチにコミットします。

  3. Pull Requestのレビューとmainブランチへのマージ
    機能が完成したら、Pull Requestを作成してコードレビューが完了したのち、変更をmainブランチにマージします。mainブランチへのマージ後、featureブランチは削除されます。

このブランチ戦略は、mainブランチにマージされる前に複数の長命ブランチへのマージが必要とされるgit-flowとは異なり、mainブランチにマージされる前に短命ブランチでのレビューを行うことで、mainブランチの安定性を保ちながら、スピーディーに機能追加やバグ修正を行うことができるというメリットがあります。

https://learn.microsoft.com/ja-jp/training/modules/manage-git-branches-workflows/3-explore-feature-branch-workflow

GitHub Flow

GitHub Flowは、Feature Branch Workflowと同様に、機能ごとに短命ブランチを切ってmainにマージしていくブランチ戦略です。

GitHub Flowは、以下のように運用方法が定義されています(原文ではmasterとなっていますが、昨今はmainが主流なのでそちらにしています)。

  1. mainブランチは常にデプロイ可能でなければならない
  2. 機能追加やバグ修正は、mainブランチから新しいブランチを切って行う
  3. 機能追加やバグ修正のために切り出したブランチは、同じ名前のリモートブランチに対して定期的に作業内容をpushすること
  4. フィードバックや助けを必要とする際、もしくはマージできる状態にある場合はプルリクエストを作成する
  5. ブランチでの変更は、Pull Requestを使ってレビューを行う
  6. 第三者によるPull Requestのレビューが完了したら、mainブランチにマージする
  7. マージ後は、mainブランチをただちにデプロイせねばらない

ソース:https://githubflow.github.io/

ブランチの切り方はFeature Branch Workflowと同じですが、運用の大きな違いとしてはmainブランチへのマージ後にmainブランチをデプロイするということを明示している点と、mainブランチが常にデプロイ可能な状態を保つことを強調している点です。これはCI/CDを活用していく上で重要なポイントで、mainブランチが常にデプロイ可能な状態を保つことで、本番環境にデプロイする際のリスクを最小限に抑えることができます。CI/CDを前提とした頻繁にリリースを行う開発スタイルには、GitHub Flowを採用するのがおすすめです。

トランクベース開発

これはFeature Branch WorkflowやGitHub Flowよりもさらにシンプルなブランチ戦略で、mainブランチに直接コミットしていく開発スタイルです。早い話が「mainブランチ以外のブランチを切らない」というブランチ戦略ですね。
「えっ!ブランチを切らないでmainに直接コミットするなんて?!」と思われるかもしれませんが、1日に何度もデプロイしたり頻繁にリリースをすることが求められる場合は、マージのコストを最小限に抑えてコードの統合までのスピードを最大化させるこの戦略は有効です。

しかし、このブランチ戦略を運用していくためには、以下のような環境が整っていないとだいぶ厳しいかなというのが個人的な感想です(;^_^A

  • 開発チーム間でペアプログラミングやモブプログラミングなどで密なコミュニケーションが取れる
  • 静的解析や自動テスト、CI/CDが整備されている(変更適用後のブランチの問題を早期に検知できる)
  • Feature Flagやリリーストグルなどの機能を活用して、未完成の機能を本番環境にデプロイしても問題がないようにできる

開発チームのメンバー間での密なコミュニケーションが必要とされるスクラム開発や、モブプログラミングなどの開発プラクティスを推奨するXPなどのアジャイル開発手法を採用しているチームであり、コードがmainに統合された後に仮に問題が発生しても、静的解析や自動テスト、CI/CDを活用して問題を早期に検知できる環境が整っている場合には、トランクベース開発は開発スピードを最大化させるための有効なブランチ戦略と言えるでしょう。
https://trunkbaseddevelopment.com/

リポジトリとブランチのアクセス制御と保護

  • ブランチとリポジトリは適切にアクセス許可が構成されていること
  • ブランチポリシーが適用されていること

リポジトリのアクセス制御

Azure DevOpsのプロジェクトでは、Project > Repository > Branchの階層で細かくアクセス制御をすることが可能です。

- 開発者にはContributorの権限を
リポジトリに関しては、基本的にリポジトリにコードをコミットしたり、プルリクエストを作成する、いわゆる開発を行うメンバーに関してはAzure DevOpsのプロジェクトでContributorの権限を持っている必要があります。

- リポジトリの削除やポリシー変更ができるProject Adminの権限付与はお気をつけて
開発に必要な権限を適切に与えることと同時に、過剰な権限を与えないようにすることも重要です。
例えば、Project Administratorsの権限ではリポジトリの作成、削除、リポジトリ名の変更やポリシーの変更ができるため、限られたメンバーにのみ与えるようにするなど、適切な権限管理を行うことが重要です。

- 直接コードを触らない/触らせたくない人にはReaders権限を
また、リポジトリに直接コミットしない、Readのみでよいユーザーに対してはReadersの権限を与えておくことで、リポジトリに変更を加えることを制限したうえで、リポジトリの閲覧、コードの変更履歴やコードレビューのフローを理解するための学習の場としても活用できます。コードに直接コミットしないステークホルダーの方々もこの権限を付与することになるかなと思います。

リポジトリへのアクセス制御については、こちらのdocsをご覧ください。
https://learn.microsoft.com/ja-jp/azure/devops/repos/git/set-git-repository-permissions?view=azure-devops

ブランチポリシーによる大事なブランチの保護

Azure DevOpsではブランチ単位にもセキュリティグループを定義して細かくアクセス制御することも可能ではあるんですが、そこまでガチガチに最小特権の原則を実践するとメンテコストが高くなるのと、実際「このブランチは誰と誰がコミットできるようにしてこの人はReadのみで。。。」みたいな細かい制御をやるか?っていったらあんまりやらないかなと(;^_^A

実際の運用としては、ブランチの保護はブランチポリシーというAzure Reposの設定を利用してmainなどの大事なブランチを保護していくという運用になるかなと思います。
ブランチポリシーの設定の方法については、以前こちらの記事に詳細を書いたのでこちらからチェックしてください。
https://zenn.dev/yuriemori/articles/70668dae9fe080

まとめ

今回は以下のベストプラクティスとAzure DevOpsでの実践方法をご紹介しました。

  • ソースファイル、構成ファイル(.configとか)を共通のコードリポジトリ(Gitベースリポジトリなど)で管理していること
  • アプリケーション関連のファイルはすべて、ソース管理下に置くこと(サービスのビルド、テスト、構成、デプロイ、実行、文書化に使用されるすべてのファイル)
  • ブランチ戦略が定義されていること
  • ブランチとリポジトリは適切にアクセス許可が構成されていること
  • ブランチポリシーが適用されていること

残りの以下のプラクティスについては、後半の記事で書きます!お楽しみに('ω')

  • コードに変更が発生したときに、それをマージするまでのレビューフローが定義されていること
  • 共有ブランチにソースをマージする際には、Pull Requestを使用してレビューが完了した後にコードの変更を共有ブランチに統合する。すべての変更がレビューによる承認を経てマージされる
  • ブランチの運用について、最大で数日存続するブランチを使用し、少なくとも毎日共有ブランチにすべての変更をコミットする
  • 依存関係はパッケージとしてリポジトリに発行され、コード内の依存関係を追跡することができること。 独自の成果物も公開して、依存する他のチームも新しい成果物を確認できること。
  • ソースコードは静的解析によって早期にエラーを特定できること

Discussion