🎄

Azure DevOpsでのソースコード管理のベスプラ紹介:後編(コードレビューフロー・コードの静的解析・依存関係管理)

2024/12/21に公開

Azure DevOpsアドベントカレンダー21日目です!

この記事の後編です!
https://zenn.dev/yuriemori/articles/330d53c96d72e3

Microsoftが推奨するバージョン管理のプラクティスのご紹介と、実際Azure DevOpsでやっていくにはどうすればいいんだっけ?というのを紹介していきます。

==前編の記事では以下を解説しました(/・ω・)/

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

==今回の後編では以下の内容を解説==(/・ω・)/

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

コードレビューフローの定義

  • コードに変更が発生したときに、それをマージするまでのレビューフローが定義されていること
  • 共有ブランチにソースをマージする際には、Pull Requestを使用してレビューが完了した後にコードの変更を共有ブランチに統合する。すべての変更がレビューによる承認を経てマージされる

mainブランチなどの長命ブランチにマージする前にはきちんとコードレビューをしましょうね、という内容ですね。
これは当たり前といえば当たり前ですが、個人的にはコードレビューフローとして以下を担保することをおすすめします。

  1. mainブランチなどの大事なブランチはロックを掛けて、Pull Requestを経由しないとマージできないようにしたり誰かがブランチを削除できないようにする
  2. 大事なブランチにはブランチポリシーを設定して、必ず特定の誰かがPull Requestで承認しないとマージできないようにする
  3. Pull Requestには作業項目の紐づけを義務づける

1、2は「大事なブランチに対して意図しない変更が適用されないように」するための方法ですね。
2については、更に厳しく「Pull RequestのコメントがResolveされないとマージ禁止」「自分自身がApproveするの禁止」みたいな制御をすることもできますが、そこまで厳しくするかはチーム次第ですかね。
3は追跡性を担保するためのプラクティスです。後から「この変更はなんの作業でなんのために変更されたんだっけ?」ということを追跡できるように、作業項目の紐づけをブランチのポリシーで設定することで、作業とコードを紐づけて追跡することができます。

※作業項目のDevelopmentのフィールドからcreate branchを押下するとその作業項目に紐づいた形でブランチが作成できます。

忘れちゃった場合でもPull Requestを作成するときにWork Items to linkで検索して紐づけることができます

あとは、作業項目が紐づいてると、レビュアーがチケットに記載されている仕様や受入条件を確認しながらコードレビューすることができるので、レビュアーにとってもやさしいですね。

1-3はすべてAzure Reposのブランチポリシーで設定することができます。過去の記事に具体的な設定方法を記載していますのでご覧ください。
https://zenn.dev/yuriemori/articles/70668dae9fe080

また、こちらのMicrosoftのブログで、コードレビューのGolden Ruleとして以下の内容が記載されています。

Respect others, yet don’t take anything personally! We all make mistakes at some point. What’s important is to learn and improve, and to treat others how we’d like to be treated.
他人を尊重しながらも、何事も個人的に受け止めないこと! 私たちは誰でも間違いを犯す。 大切なのは、学び、改善し、自分がされたいように他人と接することだ。

このほかにも、コードレビュー時に注意することやよいフィードバックの提供の仕方について記載されているので、是非ご覧ください!
https://devblogs.microsoft.com/appcenter/how-the-visual-studio-mobile-center-team-does-code-review/

ブランチの運用について、最大で数日存続するブランチを使用し、少なくとも毎日共有ブランチにすべての変更をコミットする

これはブランチの運用についてですが、最大で数日存続するブランチ=短命ブランチですね。新機能の追加や既存のコードの修正など、なんらかの「変更」を加える場合はmainブランチからブランチを切ってそこで開発して、終わったらそれをmainにマージ、というフローになりますが、できだけその時間を短くしてください、という内容ですね。

Microsoftが推奨するソース管理の推奨事項として、以下のアプローチが紹介されています(前回評価したDevOps Asessmentのソース管理の推奨事項としてサジェストされているものです)。

  • mainブランチは最新の状態、高品質の状態を保つ
  • コードを早めに、そして頻繁に統合する

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

https://learn.microsoft.com/ja-jp/devops/plan/what-is-agile-development#integrate-early-and-often

リンク先の記事はアジャイル開発における「早期に、頻繁に変更を適用すること、CI/CDによってそれを可能にせよ」というプラクティスについて紹介しているので、ソース管理のベスプラっていうか開発プロセスのベスプラでは?と感ありますが、ソース管理という観点から考えて解説してみます。

「早期に変更を統合せよ、頻繁に統合せよ」という考えは以下のような状況をさけるための考え方ですね。

変更の統合を後回しにして一気にやると、それだけコンフリクトやバグの混入の検知が遅くなり、さらにそれをリカバリするコストも膨大になります。
残業デスマーチでなんとか間に合わせるとか、リリースができなくなってしまって遅延というのもなかなかアレですが、もっと怖いのはリリースした後に気づいてインシデントになってしまう。。。というパターンですね。

なので、このようなリスクを回避するための方法がシフトレフトといわれるテストや様々なチェックを開発ライフサイクルの早期段階にやりましょうというプラクティスですが、

https://learn.microsoft.com/ja-jp/training/modules/configure-provision-environments/5-understand-shift-left

早期でのテスト実行をできるようにするためには、そもそもテスト対象のソースが統合されていて、最新の状態になっていることが必要ですよね、ということですね。
しかし、「ソースを頻繁に統合して、常に最新の状態に保つ」という状態を達成するためには手動でいろいろチェックしたり統合作業したりするのはしんどいので、CI/CDでテストやコードのチェック、ビルド、統合を自動化しましょうという感じです。
これが次の「ソースコードは静的解析によって早期にエラーを特定できること」という推奨事項に繋がります。

ソースコードは静的解析によって早期にエラーを特定できること

「ソースは頻繁に統合されて、最新の状態に保たれている」という状態を達成するためには、その前に「この変更を統合しても大丈夫か」ということを担保する必要があります。

以下のアーキテクチャはMicrosfotが公開しているCI/CDのベースラインアーキテクチャで、開発者が変更を加えてからAzure Pieplinesでさまざまなチェックやテストを実行して各環境にデプロイするフローが示されています。

https://learn.microsoft.com/ja-jp/azure/devops/pipelines/architectures/devops-pipelines-baseline-architecture?view=azure-devops

開発者が変更を加えてその変更分のPull Requestを作成すると、「PRを作成した」というイベントをトリガーにしてAzure Pipelineでlintingやセキュリティスキャン、単体テストを行っています。
これにより、開発者が加えた変更に対するセキュリティリスクの有無やフォーマットミスの有無、変更によって他の箇所が動かなくなったりするリスクを自動で検知することができます。

Azure DevOpsでは、ブランチポリシーのBuild Validationでcheckを実行するためのPipelinesを設定することによって、PR作成時のAzure Pipelinesのチェックを構成することができます。
このチェックを全てクリアしないとマージできないような設定も可能です。

さらに、このPipelinesのチェックとレビューが完了してmainブランチにマージされると、そのイベントをトリガーにしてCI Pipelinesが動きます。
このCI PipelinesではPR Pipelinesで実施しているのと同様のコードの分析、単体テストを実行しますが、それに加えて結合テストも実施します。

これにより、レビュアーのレビュー負担も軽減されますし、かなり早い段階で様々なリスクを回避することができ、安全な状態でコードを統合することができます。

Azure Pipelinesのベースラインアーキテクチャに則ったPR, CI/CDのPipelinesの作り方に関しては、こちらの記事に資料を公開しているのでご覧ください。
https://zenn.dev/yuriemori/articles/5f89bb5403485e

以下に、いくつかの代表的な静的コード解析のツールを紹介しているので、ご自身のコードベースに合わせて選択して下さい。

ツール 概要 主な機能
SonarQube SonarSourceによって提供されているオープンソースの静的解析プラットフォームで、形式的なソースコードのバグや不適切な記述の検出、カバレッジの計測などを行い、品質を可視化することが可能。 - 重複コードの発見
- コーディング標準の定義
- 単体テストの実施
- カバレッジレポート
- セキュリティ脆弱性検出
SonarCloud SonarQubeのクラウド版であり、SaaS (Software as a Service)として提供される。 SonarQubeと同じ
ESLint JavaScriptおよびTypeScriptの静的コード解析ツールであり、コードの一貫性を保ち、エラーチェックを自動化する。 - カスタムルールの定義
- プロジェクト固有のコーディングスタイルの強制
- プラグインサポート (React, Vue.js などのフレームワーク用プラグイン)
- 拡張性の高いルールセットのカスタマイズ
CodeQL GitHubが提供するセキュリティ解析ツールで、クエリ言語を使用してコードベースからセキュリティ脆弱性を検出する。 - SQLに似たクエリ言語を使用したコードパターンの検索
- 大規模なコードベースの解析
- GitHub Actionsとの統合によるセキュリティ自動化 (CI/CD)
Mend Bolt (旧WhiteSource Bolt) オープンソースライブラリの脆弱性やライセンスリスクを自動的に検出するツール。 - オープンソースライブラリの脆弱性検出
- ライセンスコンプライアンスリスクの管理
- リアルタイムでのセキュリティレポート作成
GitHub Advanced Security GitHubが提供するセキュリティスキャン機能であり、依存関係の脆弱性をチェックし、コードのセキュリティ脆弱性を早期に検出する。 - 依存関係スキャン (Dependency Scanning)
- シークレットスキャン
- コードスキャン
- セキュリティアラートの自動通知

依存関係はパッケージとしてリポジトリに発行され、コード内の依存関係を追跡することができること。 独自の成果物も公開して、依存する他のチームも新しい成果物を確認できること。

「依存関係」とは、アプリケーションが他のアプリケーションやライブラリ、フレームワークなどに依存していることを指します。
例えば、NuGetパッケージやnpmパッケージ、Dockerイメージなどがあります。

NugGetなどの外部のパッケージを使っている場合、そのパッケージのバージョンが古いと脆弱性がある場合があるので、最新のバージョンにアップデートする必要がある場合があります。また、パッケージのバージョンが異なると動作が変わることもあるので、依存関係をきちんと管理していくことが重要です。
外部ライブラリはもちろん、自社で開発したライブラリやツールなども同様です。

このような依存関係は、Azure DevOpsではAzure Artifactsという機能を使って管理することができます。
Azure Artifactsを使ってフィードを作成することで、パッケージの保存、またチーム外への公開も可能です。

Azure Artifatcsを使ったパッケージ管理の具体的な実装方法については、こちらの記事でとっても詳しくstep by stepでの方法が紹介されていますので是非ご覧ください。
https://zenn.dev/yutakaosada/articles/40ed3f9888a021
https://zenn.dev/yutakaosada/articles/a1ecd898397387

Azure Artifactsでパッケージを管理していく上では、以下のポイントについて注意してください。

  1. パッケージのバージョンをきちんと管理する
  2. フィード(パッケージを保存する場所)のアクセス権限を適切に設定する

パッケージのバージョン管理については、セマンティックバージョニングで「V.1.0.1」というように、メジャーバージョン.マイナーバージョン.パッチバージョンの形式で管理します。

https://learn.microsoft.com/ja-jp/training/modules/implement-versioning-strategy/2-understand-versioning-of-artifacts

https://semver.org/lang/ja/

Azure Artifactsでは、Feed Owner、Feed Publisher(Contributor)、Feed Readerの3つのロールがあります。以下のドキュメントを参考にして、適切なアクセス権限を設定してください。
https://learn.microsoft.com/ja-jp/azure/devops/artifacts/feeds/feed-permissions?view=azure-devops

まとめ

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

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

前編の記事でも紹介しましたが、今回の記事の内容はDevOps機能の評価で紹介されている推奨事項をベースにしております。
ソース管理だけでなく、継続的セキュリティ、監視、インフラストラクチャなどDevOps環境に対するさまざまな側面の評価と、現在の評価に基づいた実行すべきアクションが紹介されていてとても便利です。ぜひ使ってみてください(/・ω・)/

Discussion