Conventional CommitsとCHANGELOGの自動生成でリリースのユーザ影響をわかりやすくした話

ドワンゴ教育事業 バックエンドエンジニアのtakuminishです。

現在、私は教材入稿ツールの開発チームに所属しています。

教材入稿ツールは昨年の2023年06月に社内向けに正式リリースされた比較的新しいツールであり、リリース当初はリリースノートに関する運用について検討が進んでいませんでした。 リリースノートは開発メンバーが手動で作成しており、内容も前回リリース後にマージされたPRタイトルとリンクを箇条書きで記載しているだけの簡素なものでした。 また、PRタイトルのフォーマットも存在しなかったため、英語で記載されたタイトルと日本語で記載されたタイトルが混在している、ユーザ影響度がタイトルからわからないといった問題もありました。

そこで、教材入稿ツール開発チームではリリースノートの運用として、Conventional Commitsを導入するとともに、conventional-changelogによるCHANGELOGを自動で生成する仕組みを採用しました。 この記事ではConventional Commitsの導入にあたって検討したルールとconventional-changelogを使用したCHANGELOGの自動生成について紹介します。

教材入稿ツール 開発フローの特徴

本題に入る前に、教材入稿ツールの特徴的な開発フローについて説明します。

独自のバージョニング手法

セマンティックバージョニングとは異なる独自のバージョニング手法を採用しています。バージョニングのルールは以下の通りです。

  • セマンティックバージョニングと同様、メジャー.マイナー.パッチ の形式でバージョンを管理する
  • 各バージョンを上げるタイミングは以下の通りである
    • メジャーバージョン: 1つのプロジェクトが完遂した段階で上げる
    • マイナーバージョン: 1機能がリリースされるタイミングで上げる(機能ごとにどのバージョンでリリースするか決まっている)
    • パッチバージョン: 毎週の定時リリース時に上げる

GitHub flowを参考にしたブランチ戦略

教材入稿ツールの開発では、GitHub flowを参考にしたブランチ戦略をとっています。 GitHub flowでは、featureブランチがmainブランチにマージされた時に本番環境にリリースするフローになっています。しかし、教材入稿ツールは週次リリースを採用しているため、リリースのタイミングがGitHub flowと異なります。

そこで、リリースの手順を以下のようにしています。

  1. リリース予定日になると、mainブランチからreleaseブランチを切る
  2. releaseブランチ内で、前述したバージョニングのルールに則ってバージョンアップを行う
  3. PRを作成し、mainブランチにマージ
  4. GitHub上でmainブランチからtagを切り、リリースノートを作成
  5. 本番環境リリース承認チケットを作成し、承認をもらう
  6. リリース作業を開始する

また、featureブランチやreleaseブランチをmainブランチにマージする際にはSquashマージを行うようにしています。

Conventional Commitsのルール整備とCHANGELOGの運用を検討した理由

2023年6月に初回リリースをしてから数回のリリース作業を行なっていく中で、以下のような課題があることがわかりました。

  • リリースノートを作成するために、前回のリリース(tag)からのcommit logの差分を確認した後、手動でリリースノートに貼り付ける必要があり、ヒューマンエラーが発生しやすく、また漏れがあっても気付きにくい
  • 本番環境にリリースする際にはリーダークラスの社員の承認が必要だが、リリースノートだけではユーザ影響の有無がわからないため、リリース対象のPRを一通り確認する必要があり、承認者の負荷が高まっている
  • リリース時に社内向けにリリース内容の周知を行なっているが、リリースノートだけではユーザ影響の有無がわからないため、周知文の作成に時間がかかっている

以上の課題から、リリースノートの情報量を高めること、リリースノートの自動生成の仕組み作りの両方が必要であると考えました。

技術調査

リリースノートの情報量を高める方法について検討した結果、Conventional Commitsを採用することにしました。

Conventional Commitsとは、より明示的なコミットメッセージを記述するための規約のようなものです。 以下のようなフォーマットでコミットメッセージを記述します。

<type>[optional scope]: <description>

[optional body]

[optional footer(s)]

mainブランチへのマージは全てSquashマージで行なっています。PRタイトルをConventional Commitsに準拠させると同時に、Conventional Commitsのルールを教材入稿ツール開発に合うように定義し直すことで、リリースノートの情報量を上げることができるのではないかと考えました。

また、Conventional Commitsに関連するOSSも多く開発されています。中にはConventional Commitsに準拠したコミットメッセージをもとにCHANGELOGを自動生成するようなものもあったため、Conventional CommitsをもとにCHANGELOGを自動生成するOSS を調査することにしました。

調査の結果、以下の2つについて検討しました。

一見すると、semantic-releaseが多機能で良さそうに見えますが、前述した通り、教材入稿ツールはバージョニング手法が特殊です。feat の場合はメジャーバージョンアップ、fix の場合はマイナーバージョンアップといった自動バージョンアップができません。npm packageを公開するわけでもないためsemantic-releaseは適さないと判断し、CHANGELOGの生成のみを行うconventional-changelogを採用することにしました。

教材入稿ツール開発におけるConventional Commitsのルール

Conventional Commitsのルールを教材入稿ツールの開発フローに合うように再定義していきます。

ルールの定義にあたり、以下の内容を考慮しました。

  • リリース承認の負荷を下げるためユーザ影響の有無をわかるようにしたい
  • Squashマージなので、branch内のコミットメッセージはConventional Commitsに準拠している必要はなく、PRタイトルが準拠している必要がある
  • 導入にあたり、開発者の覚えるルールを減らし、開発者の負荷があまりかからないようにしたい

考慮すべき内容をもとに以下のように方針を決めました。

  • 各typeごとにユーザ影響有/無を定義する
  • 使用頻度が比較的少なそうなtypeは他のtypeに集約することで開発者の負荷を下げる
  • mainブランチへのマージはSquashマージで行うため、PRタイトルをConventional Commitsに準拠させる

決定したConventional Commitsのルール

前述した考慮すべき内容や方針から以下のようにConventional Commitsのtypeとscopeのルールを定めました。

typeについては、@commitlint/config-conventionalを参考に以下のようなルールを定めました。

type 概要 ユーザ影響
feat 新規機能を追加する(既存機能に機能を追加する作業も含む)場合はfeatとする。
fix バグ修正。既存機能に要件と異なる動きをする機能が存在し、修正する場合はfixとする。レイアウト修正などの軽微な修正も含む。
perf パフォーマンス改善。 ユーザから見た振る舞いを変えずにパフォーマンス改善を行なった場合はperfとする。(test、build時間の短縮など、ユーザに関係ないものは対象外)
chore その他の修正。利用ユーザにとっては関係ないコードの追加、修正(eslintのruleの追加など)。依存ライブラリのアップデートもchoreとする。
docs ドキュメントを修正した場合はdocsとする。
refactor リファクタリング、リアーキテクチャを行なった場合はrefactorとする。
test テストの追加など、テストに関する作業を行なった場合はtestとする。

scopeの内容は多岐にわたるため、厳密なルールを決めることはできませんが、おおまかに以下のようなルールを定めました。

scope 概要
app システム全体に関わる場合
feature名 各機能に関わる場合
ライブラリ名 ライブラリのconfigの変更などの場合
その他 deps、build、ciなど

conventional-changelogのconfigファイルの設定

決定したConventional Commitsのルールに沿う形でCHANGELOGが出力されるように、conventional-changelogのconfigファイルを設定していきます。

conventional-changelogは様々な設定をconfigファイルで変更できますが、今回はCHANGELOGに出力されるtypeとそれに対応するCHANGELOGの見出し文を定義していきます。具体的には以下のようになります。 typesに設定している配列の順番がそのままCHANGELOGに出力される順番となるため、ユーザ影響があるtypeがCHANGELOGの上部に表示されるように配列の順番も考慮しています。

// conventionalcommits.config.js
const config = require('conventional-changelog-conventionalcommits');

module.exports = config({
  types: [
    { type: 'feat', section: 'Features' },
    { type: 'fix', section: 'Bug Fixes' },
    { type: 'perf', section: 'Performance' },
    { type: 'chore', section: 'Chores' },
    { type: 'docs', section: 'Documentation' },
    { type: 'refactor', section: 'Refactor' },
    { type: 'test', section: 'Tests' },
  ],
});

作成したconfigファイルをもとにCHANGELOGを生成するには、以下のようにoptionでconfigファイルを指定します。

conventional-changelog -n conventionalcommits.config.js -i CHANGELOG.md -s

CHANGELOGの出力結果

前述した内容をもとに、CHANGELOGを出力した結果が以下のようになります。 Conventional Commitsのtypeごとに見出しが分かれており、リリース内容がより分かりやすくなっています。また、typeごとにユーザ影響の有無を定義しているため、リリース承認者や周知文作成者はユーザ影響のあるtypeのみに注目すれば良いです。scopeが記載されていることで、影響範囲も一目でわかります。

CHANGELOGの出力結果。typeごとに見出しが分かれているためリリース内容が一目でわかる

Conventional Commitsに準拠させるための工夫

運用を始めてから、PRタイトルがConventional Commitsに準拠していない状態でmainブランチにマージされてしまい、CHANGELOG生成時に内容が不足していることが複数回ありました。

そこで、DangerJSを導入し、PRタイトルがConventional Commitsに準拠しているかどうかをCIでチェックし、準拠していなければ以下のようにPRにコメントを残すと同時に、mainブランチへのマージをできないようにしました。これにより、PRタイトルがConventional Commitsに準拠していない状態でmainブランチにマージされるのを防ぐことができるようになりました。

DangerJSが出力したコメント。PRタイトルがConventional Commitsに準拠していないことが記載されている DangerJS実行の結果がfaildのため、mainブランチへのマージができない

適用した結果どうだったか

type別にユーザ影響の有無、scopeで機能を表すようにしたことで、リリース承認時にどの機能に対してユーザ影響があるのか一目でわかるようになり、承認者の認知負荷を下げることに成功しました。 社内向けのリリース内容の周知文を作成する際も、ユーザ影響のあるtypeのみに注目すればよく、リリース内容の周知漏れも防ぐことに貢献しています。

また、今回の取り組みの結果、開発メンバーが作成するPRタイトルがより具体的になりました。Conventional Commitsを取り入れたのも1つの理由だと思いますが、PRタイトルがそのままリリースノートに記載されるようになったことで、より内容が他者に伝わりやすいタイトルにしないといけないという意識がメンバー内に芽生えたのだと思います。 PRタイトルがより具体的になったことで、PRのレビュー時にタイトルを見て内容を把握できるといった副次的な効果もあリました。

今後の展望

Conventional Commitsとconventional-changelogを導入して数ヶ月間運用をしましたが、まだ以下のような課題が残っています。

リリースノートの作成に対して手作業がいくつか残っている

現在の運用では、releaseブランチ内でCHANGELOGを生成し、mainブランチにマージ後、手動でtagの作成とリリースノートの作成をしています。 CHANGELOGに詳細な内容は出力されているので、リリースノートは以下のようにCHANGELOGの該当のバージョンへのリンクを添付する簡素なものにしていますが、なるべく手動作業は減らしたいと考えています。今後はtagの作成、リリースノートの作成の自動化についても検討していきたいです。

リリースノートCHANGELOGのリンクのみが添付されている

scopeの内容が開発メンバーによって統一されていないことがある

typeについては前述した通り7パターンしかないため厳密にルールも決めていますが、scopeに記載する内容は多岐にわたることもあり、ルールを厳密に決めることができていません。 そのため、同じscopeにすべき2つのPRタイトルのscopeが異なっていたり、大文字/小文字が統一されていないなどの問題が発生してきています。

scopeが統一されていないのは開発メンバー間でscopeに対する認識が揃っていないことが原因であると考えており、解決するためにはメンバー間で密に認識合わせを行うことが必要になります。運用を開始してからの数ヶ月間で何百ものPRが開発メンバーによって生まれています。過去のPRでどのscopeが設定されていたのかをある程度資料化し、それをもとにscopeを決定することでscopeに対する認識が揃うと同時に、scopeの統一も図ることができると考えています。

We are hiring!

株式会社ドワンゴの教育事業では、一緒に未来の当たり前の教育をつくるメンバーを募集しています。 カジュアル面談も行っています。 お気軽にご連絡ください!

カジュアル面談応募フォームはこちら

www.nnn.ed.nico

開発チームの取り組み、教育事業の今後については、他の記事や採用資料をご覧ください。

speakerdeck.com