こんにちは。リンクアンドモチベーションでエンジニアをしている梅原です。
現在はコミュニケーションクラウドというプロダクトの開発を担当しています。
現在、自分のチームでは開発組織の健全性の指標として d/d/d (deploys/ a day / a developer) という指標を用いています。
約半年前のチームのd/d/dは、0.01
という状況でしたが、この半年で色々な改善を重ねた結果、 8倍の 0.08
まで改善することができました。
今の自分のチームは開発者が5人なので、1ヶ月(約20営業日)で1回productionにデプロイすると d/d/dが0.01になります。
つまり、もともと月に1回だったところから、2~3日に1回はデプロイができるようになりました。
(指標の目安としては、CTO協会 理事の広木大地さん(@hiroki_daichi)のツイートにあるとおり d/d/d=0.1
というのが健全な組織としての目安になるそうです。)
最近、エンジニアリング組織の健全さの指標に、
— 広木 大地/ エンジニアリング組織論への招待 (@hiroki_daichi) 2019年2月26日
d/d/d というのを用いてる。
deploys / a day / a developer の略で
1日あたりのデプロイ回数を開発者数で割ったもの。
だいたい、0.1 以上なら健全と言える
これが悪化しているとき、最上流含めたエンジニアリングパイプラインのどこかに問題がある
まだ目安である 0.1
までは到達できていないのですが、いったんここまでを振り返りながらどんな課題があったのか、その解消のためにどんなことをやってきたかをまとめようと思います。
現状と課題の把握
まずはチームとしての課題を把握するために、定性・定量それぞれで現在地の把握をしました。
定性: バリューストリームマッピング
リリースごとに、バリューストリームマッピングを行なって開発フローを振り返り、どのフェーズでどんな課題があるのかについて、チームとしての共通認識を持てるようにしました。
(バリューストリームマッピングの参考記事) dev.classmethod.jp
上記の記事では、各プロセスごとにプロセスタイム、リードタイムを埋めていき、手戻り率などを算出していますが、自分のチームではまずは大枠の課題感が把握できればよかったので、おおよそのプロセスタイム、リードタイムを埋めるにとどめ、各プロセスごとに定性的な振り返りをして課題を洗い出す方に注力しました。
実際に振り返りで上がった課題をいくつか挙げると
- ユニットテストがメンテされておらず、単体のケースでは通るのに全ケース通すと落ちてしまう
- 結果としてユニットテストによる品質担保が機能せず、人力QA頼り
- リリースのたびに発生する手動のリグレッションテストが辛い
- フィーチャーブランチが並行&生存期間が長く、コンフリクトも発生しやすい
などがありました。
定量:DX Criteria、開発メトリクスの測定
以下の2つを参考に、チームの状態を定量的に把握しにいきました。
- 日本CTO協会が公開しているDX Criteria
- 『LeanとDevOpsの科学』におけるデリバリーパフォーマンス指標
幸い、弊社の他チームで既に開発組織の習熟度・ケイパビリティを計測するための仕組みを整備していたのでありがたく使わせてもらいました。
詳細はぜひこちらの記事をご覧ください。
qiita.com
いきなり全項目を計測するのも大変だったので、DX Criteriaについては、リンク先に記載のある5つのテーマのうち「チーム」と「システム」の2テーマに絞ってチームの現在地を測定し、そこから改善する項目、一旦は取り組まない項目を決めました。
結果としてやはり定性の振り返りでも上がったものに関連して「継続的インテグレーション」「継続的デプロイ」の項目がかなり低く、優先度高く改善する項目として設定しました。
やったこと
上記で明らかになった課題に対して、この半年で取り組んだものの中から特に大きかったものをいくつか抜粋します。
自動テストを整備する
DX Criteriaでもスコアの低かった継続的インテグレーション・継続的デプロイを実現するために、自動テストの整備に取り掛かりました。
まずユニットテストについては、テストコード自体は存在し、単体のケースは動くが全ケースを通すと落ちるという怪奇現象が起きていたため、開発の優先順位を調整してもらって工数を確保し、地道に直していきました。(この状態がやばいんだということを理解して調整してくれたPMに感謝)
とりあえず全ケースが通るようになったら、Github Actionsを利用してdevelopブランチへのPR作成時に自動でユニットテストが回る状態を作り、マージされるPRは全てユニットテストをパスすることを担保できるようになりました。
また、弊社では昨年よりAutifyを導入し、自動E2Eテストを毎日定期実行しています。これにより、手動でのリグレッションテストの負荷がかなり軽減されました。
フィーチャーフラグを活用する
フィーチャーフラグを活用することで、一部機能をユーザーに隠したままリリースを行うことが可能になります。これにより、まだリリースしない機能でもmainブランチにマージすることが可能になり、featureブランチとmainブランチの差分が大きくなる、featureブランチが並行してしまいコンフリクトが発生する、という事象の発生を減らすことができるようになりました。
また、コードの変更やデプロイを伴わずに機能のON/OFFができるため、デプロイとリリースが分離できたり、切り戻しが瞬時に行えたり等、様々なメリットがありました。
もちろん運用フローをちゃんと考えないとフラグが乱立してしまう等のデメリットはありますが、現状メリットの方が大きいかなと感じています。
実はフィーチャーフラグに関しては、どちらかというと本番環境での機能のβテストを簡単に実現できる仕組みとして導入を進めていました。ただ、導入にあたり色々と調べていく過程で、開発フローの改善にもかなり役立ちそうだということがわかったため、開発フロー改善の文脈でも活用を進めていきました。
ブランチ戦略を変更する
元々のブランチ戦略は、git-flow準拠の形式でした。
(参考:
Gitにおけるブランチ戦略について調べてみた - Qiita)
git-flowだと、課題の部分でも挙げた通り、寿命の長いfeatureブランチが並行してしまってコンフリクトが発生したり、PRあたりの変更量が大きくなってしまうなどの課題がありました。
そこで、開発フローを少しずつトランクベース開発に移行できないかを検討しました。
(参考:トランクベース開発について - 赤帽エンジニアブログ)
しかし、チーム内で議論した結果、トランクベースに移行する際の課題として
- 環境とブランチの紐付けが担保できない(stagingもproductionもmainブランチからデプロイされる)
- 自動でproductionにデプロイされるのはできれば避けたい
といったものが挙げられ、結果として以下のような形に落ち着きました。
- 基本的にdevelopブランチとmainブランチの2つを利用
- developブランチに対して、トランクベース開発のようにどんどん短命のブランチ(short-lived branch)をマージする
- developブランチは常にリリース可能な状態になるように保つ
- developブランチに新しいコミットが追加されると、自動でstaging環境にデプロイ
- develop->mainにマージするとproductionにデプロイ
- mainへのマージタイミングは任意にコントロール可能
改善結果と今後の展望
上記であげたことを中心に色々と改善を続けた結果、冒頭に書いた通りd/d/dの値はこの半年で 0.01
→ 0.08
まで改善することができ、またDX Criteriaの注力改善項目としていた継続的インテグレーション、継続的デプロイのスコアもそれぞれ8点満点中
- 継続的インテグレーション
4.0
->7.0
- 継続的デプロイ
3.5
->7.5
と大幅に改善することができました。
定性的な面では
- 自動テストの比重が上がったことで、テスト駆動的な考えが根付いてきた
- PRの単位が小さくなり、1つ1つのレビューのコストが下がった
- リリースの単位が小さくなり、リリースごとの影響範囲が小さくなった
- 切り戻し対応などの負荷も下がり、そもそものリリースに対する心理的障壁が下がった
などのポジティブな変化が見られました。
d/d/dについてはまだ目安となる 0.1
には到達できていないのですが、まだ運用し始めたばかりで浸透しきっていない部分もあり、これをやり切るだけで到達できそうな感覚はあるため、まずは今の施策をしっかりやり切り、今後のさらなる改善につなげていきたいと思っています。