Slide 1

Slide 1 text

トランクベース開発の実現に向けた 開発プロセスと CIパイプラインの継続的改善 合同会社DMM.com 小林杏理

Slide 2

Slide 2 text

小林杏理 https://twitter.com/aanriit 合同会社DMM.com プラットフォーム事業本部第三開発部 マイクロサービスアーキテクトグループ 認証認可チーム リーダー 専門領域: Webバックエンド、DevOps、Go システムアーキテクチャ

Slide 3

Slide 3 text

トランクベース開発とは ● バージョニング (ex. git) を使った開発の速度を向上するための方法論 ● 認証認可チームでは、トランクベース開発を導入し 開発のサイクルタイムを短く保つことに成功

Slide 4

Slide 4 text

トランクベース開発とは ● バージョニング (ex. git) を使った開発の速度を向上するための方法論 ● 認証認可チームでは、トランクベース開発を導入し 開発のサイクルタイムを短く保つことに成功 →サイクルタイムとは?

Slide 5

Slide 5 text

Four Keys GoogleのDevOps Research and Assesment (DORA) チームが提唱する ソフトウェア開発チームのパフォーマンスを示す 4 つの指標 ● デプロイの頻度 - 組織による正常な本番環境へのリリースの頻度 ● 変更のリードタイム - commit から本番環境稼働までの所要時間 ● 変更障害率 - デプロイが原因で本番環境で障害が発生する割合(%) ● サービス復元時間 - 組織が本番環境での障害から回復するのにかかる時間

Slide 6

Slide 6 text

Four Keys GoogleのDevOps Research and Assesment (DORA) チームが提唱する ソフトウェア開発チームのパフォーマンスを示す 4 つの指標 ● デプロイの頻度 - 組織による正常な本番環境へのリリースの頻度 ● 変更のリードタイム - commit から本番環境稼働までの所要時間 ○ 短ければ短いほど、開発チームのアウトプットの最大化につながる ● 変更障害率 - デプロイが原因で本番環境で障害が発生する割合(%) ● サービス復元時間 - 組織が本番環境での障害から回復するのにかかる時間

Slide 7

Slide 7 text

変更のリードタイム コーディング Pull Request作成 Pull Request レビュー / CIチェック Pull Request merge リリース作業 要件定義/設計 課題定義

Slide 8

Slide 8 text

変更のリードタイム コーディング Pull Request作成 Pull Request レビュー / CIチェック Pull Request merge リリース作業 要件定義/設計 変更のリードタイム 課題定義

Slide 9

Slide 9 text

変更のリードタイム コーディング Pull Request作成 Pull Request レビュー / CIチェック Pull Request merge リリース作業 要件定義/設計 変更のリードタイム 課題定義 開発のサイクルタイム 開発のサイクルタイムの削減が 変更のリードタイムの削減につながる ※どこまでのフェーズをサイクルタイムに含めるかは定義 により異なる

Slide 10

Slide 10 text

トランクベース開発のねらい 開発のサイクルタイムの短縮を通じて変更のリードタイムを短縮し 開発チームのアウトプットを最大化する

Slide 11

Slide 11 text

アジェンダ ● トランクベース開発とは? ● トランクベース開発をどのように実践したか? ● 開発フローをどのように改善していったか?

Slide 12

Slide 12 text

トランクベース開発とは?

Slide 13

Slide 13 text

トランクベース開発とは ● バージョニング (ex. git) を使った開発の速度を向上するための方法論 ● ブランチ戦略の一種とも言える

Slide 14

Slide 14 text

一般的なブランチ戦略 (ex. GitHub Flow) main branch

Slide 15

Slide 15 text

一般的なブランチ戦略 (ex. GitHub Flow) feature branch main branch

Slide 16

Slide 16 text

一般的なブランチ戦略 (ex. GitHub Flow) Pull Request feature branch main branch

Slide 17

Slide 17 text

一般的なブランチ戦略 (ex. GitHub Flow) Pull Request feature branch main branch ✅Review approved ✅Review approved …

Slide 18

Slide 18 text

一般的なブランチ戦略 (ex. GitHub Flow) merge feature branch main branch

Slide 19

Slide 19 text

一般的なブランチ戦略 (ex. GitHub Flow) main branch feature branch A feature branch B merge merge

Slide 20

Slide 20 text

一般的なブランチ戦略 (ex. GitHub Flow) main branch feature branch A merge merge merge feature branch D feature branch B feature branch C merge

Slide 21

Slide 21 text

feature branchの課題: merge conflict main branch file A

Slide 22

Slide 22 text

feature branchの課題: merge conflict main branch feature branch A file A

Slide 23

Slide 23 text

feature branchの課題: merge conflict main branch feature branch A file A file A’

Slide 24

Slide 24 text

feature branchの課題: merge conflict main branch feature branch A file A file A’’ file A’

Slide 25

Slide 25 text

feature branchの課題: merge conflict main branch feature branch A file A file A’’ file A’ file A → file A’ の変更と file A → file A’’ の変更との間で conflictが発生する file A file A’’ file A’

Slide 26

Slide 26 text

merge conflictの解消コストが大きくなる要因 ● feature branchとmain branchとの差分 (≒ Pull Requestのサイズ) が大きいほどconflict箇所が増える ● feature branchの生存期間が長くなるほど差分が大きくなる ● conflict箇所が多ければ多いほど、解消にコストがかかる

Slide 27

Slide 27 text

conflictのリスク・コストが現実的に問題になる場面 ● ひとつのコードベース (リポジトリ) を大人数で開発する場合 ○ 人数が増えれば増えるほど conflict頻度・解消コストが高まる → 開発者を増やしても開発速度が上がりにくくなる ● feature branchのサイズが大きい場合 ● コーディング完了からコードレビューまでの待機時間が長い場合 etc

Slide 28

Slide 28 text

弊社での事例 ● DMMプラットフォームでプルリクエストのマージ時間を 250時間から50時間に減らした話 https://speakerdeck.com/juve534/improve-development-efficiency-with-dmm- platform ● Pull Requestの作成からmergeまで250時間もかかっていた ○ conflict解消に大きなコストが費やされていた

Slide 29

Slide 29 text

トランクベース開発

Slide 30

Slide 30 text

トランクベース開発の種類 ● feature branchを使わない方法 (committing straight to the trunk) ● feature branchを使う方法 (short-lived feature branches) ○ 認証認可チームで採用している方法はこれ

Slide 31

Slide 31 text

トランクベース開発の種類 ● feature branchを使わない方法 (committing straight to the trunk) ● feature branchを使う方法 (short-lived feature branches)

Slide 32

Slide 32 text

トランクベース開発 (feature branchを使わない方法) main branch (trunk)

Slide 33

Slide 33 text

トランクベース開発 (feature branchを使わない方法) 󰢿 Developer A Developer Aがコードに変更を加えた時 feature branchは切らずに main branchに直接commit & pushする 各開発者はmain branchに対し 1日に数回のcommit & pushを行う main branch (trunk)

Slide 34

Slide 34 text

トランクベース開発 (feature branchを使わない方法) 󰢿 Developer A 󰢴 Developer B 他の開発者も同じく main branchで作業を行い 変更をcommit & pushする main branch (trunk)

Slide 35

Slide 35 text

トランクベース開発 (feature branchを使わない方法) 󰢿 Developer A 󰢴 Developer B 🧓 Developer D 󰬋 Developer C main branch (trunk)

Slide 36

Slide 36 text

トランクベース開発 (feature branchを使わない方法) 󰢿 Developer A 󰢴 Developer B 🧓 Developer D 󰬋 Developer C ひとつの機能は複数回のcommitにより実現される つまり、main branchには “未完成なコード” が (一時的に) 混入することがある main branch (trunk)

Slide 37

Slide 37 text

トランクベース開発 (feature branchを使わない方法) release 1.0 release 1.1 release 1.2 特定の機能が “未完成な状態” であっても 関係なくリリースされることがある main branch (trunk)

Slide 38

Slide 38 text

トランクベース開発 (feature branchを使わない方法) ● feature branchを切らず、main branchに1日複数回のcommitを行い 大規模なmergeが発生しないようにすることでconflictのリスクを回避する ● それぞれの開発者が高頻度でmain branchをpullするため main branchの更新状況をキャッチアップしやすい

Slide 39

Slide 39 text

疑問 1. main branchにバグを含むcommitが混入するのをどう防ぐのか? 2. 開発途中の機能がリリースされたら不具合が発生するのではないか?

Slide 40

Slide 40 text

バグの混入をどう防ぐのか? ● バグの混入を未然に防ぐ ● バグが発見されたら除去する

Slide 41

Slide 41 text

バグの混入を未然に防ぐ ● feature branchを使った開発なら… ○ Pull Request を出し、コードレビューを行う ○ Pull Requestに対してCIによるチェック (自動テスト) を行う

Slide 42

Slide 42 text

バグの混入を未然に防ぐ ● feature branchを使った開発なら… ○ Pull Request を出し、コードレビューを行う ○ Pull Requestに対してCIによるチェック (自動テスト) を行う しかし、feature branchがそもそも存在しないので Pull Request が出せない

Slide 43

Slide 43 text

バグの混入を未然に防ぐ ● feature branchを使った開発トランクベース開発なら… ○ Pull Request を出し、レビューを行う 同期的にコードレビューする ○ Pull Requestに対してCIによるチェック (自動テスト) を行う ローカルでテストする

Slide 44

Slide 44 text

同期的にコードレビューする ● 変更をいち早くmain branchに取り込むために必要 ○ レビュー着手が遅れれば遅れるほど main branchの変更に追従しにくくなり、 conflictが発生しやすくなる ● 具体的な方法 ○ commitの準備ができた段階でレビューを依頼する ■ レビュイーとレビュアーが同席のもとでレビューを行う ○ ペアプログラミングする ■ 複数人でコードを書きながら、内容について相互に合意を取っていく

Slide 45

Slide 45 text

同期的なコードレビューを実現するために ● 「レビュー待ち」の状況をなるべく回避する ○ 「後で見ておきます」は極力避ける ○ レビュー待ちの間に他のタスクを進めることも原則禁止 ● 承認プロセスをシンプルに保つ ○ commitするのに多くのレビュアーによる承認が必要な場合 レビューに多くの時間が費やされてしまう

Slide 46

Slide 46 text

バグの混入を未然に防ぐ ● feature branchを使った開発トランクベース開発なら… ○ Pull Request を出し、レビューを行う 同期的にコードレビューする ○ Pull Requestに対してCIによるチェック (自動テスト) を行う ローカルでテストする

Slide 47

Slide 47 text

ローカルでテストする ● 変更をcommitする前に、開発者自身で品質を保証する ● 開発者自身が速やかに品質保証できる手段を用意する必要がある ○ 自動テスト, Lint, etc ○ 速やかに結果が得られるようにしておく ■ ビルドやテストはなるべく早く終わらせる ○ 全ての開発者が等しい結果を得られるようにしておく ■ 開発環境によってはテストが失敗する、などという状況は好ましくない

Slide 48

Slide 48 text

それでもバグは混入してしまう可能性がある ● 例: ローカルでのテスト実行を忘れてしまう

Slide 49

Slide 49 text

バグが発見されたら除去する ● ローカル環境だけでなく、CI環境でもテストを行う ○ main branchへのpushにフックさせてビルドやテストを実行する ● CI環境でのテストが失敗したら速やかにロールバックする ○ アラートを発報して開発者に手動ロールバックさせる ○ CIパイプラインの中で自動ロールバックする

Slide 50

Slide 50 text

疑問 1. main branchにバグを含むcommitが混入するのをどう防ぐのか? 2. 開発途中の機能がリリースされたら不具合が発生するのではないか?

Slide 51

Slide 51 text

開発途中の段階でのcommit & pushを実現するために ● feature flags (toggles) ● branches by abstraction ○ 今回は説明しません

Slide 52

Slide 52 text

feature flags func oldFunc() { // すでに開発されたコード } func main() { oldFunc() }

Slide 53

Slide 53 text

feature flags func oldFunc() { // すでに開発されたコード } func newFunc() { // 開発中のコード } func oldFunc() { // すでに開発されたコード } func main() { oldFunc() }

Slide 54

Slide 54 text

feature flags func oldFunc() { // すでに開発されたコード } func newFunc() { // 開発中のコード } func main() { newFunc() } func oldFunc() { // すでに開発されたコード } func main() { oldFunc() } 開発中の newFunc() が 本番環境で実行されてしまう

Slide 55

Slide 55 text

feature flags func oldFunc() { // すでに開発されたコード } func newFunc() { // 開発中のコード } func main() { if os.GetEnv(“ENABLE_NEW_FUNC”) == “true” { newFunc() } else { oldFunc() } } func oldFunc() { // すでに開発されたコード } func main() { oldFunc() } env: - name: ENABLE_NEW_FUNC value: false

Slide 56

Slide 56 text

feature flags func oldFunc() { // すでに開発されたコード } func newFunc() { // 開発中のコード } func main() { if os.GetEnv(“ENABLE_NEW_FUNC”) == “true” { newFunc() } else { oldFunc() } } func oldFunc() { // すでに開発されたコード } func main() { oldFunc() } ENABLE_NEW_FUNC環境変数 (= feature flag) が “true” の場合のみ newFunc() を実行するようにする env: - name: ENABLE_NEW_FUNC value: false

Slide 57

Slide 57 text

feature flags func oldFunc() { // すでに開発されたコード } func newFunc() { // 開発中のコード } func main() { if os.GetEnv(“ENABLE_NEW_FUNC”) == “true” { newFunc() } else { oldFunc() } } func oldFunc() { // すでに開発されたコード } func main() { oldFunc() } env: - name: ENABLE_NEW_FUNC value: false newFunc() が開発完了するまで ENABLE_NEW_FUNC環境変数は falseにしておく

Slide 58

Slide 58 text

feature flags func oldFunc() { // すでに開発されたコード } func newFunc() { // 開発完了したコード } func main() { if os.GetEnv(“ENABLE_NEW_FUNC”) == “true” { newFunc() } else { oldFunc() } } func oldFunc() { // すでに開発されたコード } func main() { oldFunc() } env: - name: ENABLE_NEW_FUNC value: true newFunc() が開発完了したら ENABLE_NEW_FUNC環境変数を trueにすることで デプロイ不要でリリース できる

Slide 59

Slide 59 text

feature flags func oldFunc() { // すでに開発されたコード } func main() { oldFunc() } func newFunc() { // 開発完了したコード } func main() { newFunc() } func oldFunc() { // すでに開発されたコード } func newFunc() { // 開発完了したコード } func main() { if os.GetEnv(“ENABLE_NEW_FUNC”) == “true” { newFunc() } else { oldFunc() } } newFunc() に問題ないことが 十分確認できたら feature flagを削除する

Slide 60

Slide 60 text

feature flagsのメリット ● 「コードの変更」と「機能のリリース」 それぞれのタイミングを別々にコントロールできる ○ main branchに開発途中のコードがあってもノーリスクでデプロイできる ● コードに問題があっても迅速にロールバックできる ○ feature flagsを折るだけでよい

Slide 61

Slide 61 text

feature flagsのデメリット ● コードが複雑になる ○ if文、デッドコード、etc ● feature flags自体の管理コストがかかる ○ flagが増えれば増えるほど管理が煩雑になる ○ 専用の管理ツールもある (ex. LaunchDarkly)

Slide 62

Slide 62 text

feature flagsのデメリット ● コードが複雑になる ○ if文、デッドコード、etc ● feature flags自体の管理コストがかかる ○ flagが増えれば増えるほど管理が煩雑になる ○ 専用の管理ツールもある (ex. LaunchDarkly) → 不要になったflagやif文、デッドコードはなるべく速やかに消す

Slide 63

Slide 63 text

トランクベース開発まとめ (feature branchを使わない方法) ● メンバー全員がfeature branchを切らず main branchに直接commit & pushをすることで merge conflictのリスクを回避する ● 同期的なコードレビュー、ローカルでの自動テストにより 開発のアジリティを維持しつつコードの品質を保証する ● feature flagsを用いることでmain branchを常にproduction readyにする

Slide 64

Slide 64 text

実際、feature branch無しで開発ができる? ● 認証認可チームの判断としては「できなそう」 ○ 同期レビューのタイミングを合わせることが困難 ■ レビューの回数自体が多い ■ レビュアーのスケジュールが詰まっている ○ Pull RequestでのCI checkなしでmain commitの判断を下すことが困難 ■ 当時チーム内の開発スキルにばらつきがあった

Slide 65

Slide 65 text

トランクベース開発の種類 ● feature branchを使わない方法 (committing straight to the trunk) ● feature branchを使う方法 (short-lived feature branches)

Slide 66

Slide 66 text

トランクベース開発の種類 ● feature branchを使わない方法 (committing straight to the trunk) ● feature branchを使う方法 (short-lived feature branches)

Slide 67

Slide 67 text

トランクベース開発 (feature branchを使う方法) ● 基本的なブランチ戦略はGitHub Flowなどと変わらない ● feature branchの代わりにshort-lived feature branchを導入する

Slide 68

Slide 68 text

short-lived feature branch feature branch main branch (trunk)

Slide 69

Slide 69 text

short-lived feature branch feature branch まとまった機能が開発完了するまで、ひとつの feature branchにcommitし続ける 機能のサイズが大きいほど、生存期間は長くなる main branch (trunk)

Slide 70

Slide 70 text

short-lived feature branch short-lived feature branch feature branch main branch (trunk) main branch (trunk)

Slide 71

Slide 71 text

short-lived feature branch short-lived feature branch feature branch 同じ機能を複数の小規模な short-lived branchによって開発する 個々のbranchを数日以内にmergeすることで mergeのconflictリスクと解消コストを抑える main branch (trunk) main branch (trunk)

Slide 72

Slide 72 text

short-lived feature branchのルール ● 作成後数日以内にmergeし、削除しなければならない ○ trunkbaseddevelopment.com では2日以内を推奨している ○ merge時点で必ずしも機能の開発が完了している必要はない ■ feature branchを使わないトランクベース開発 (committing straight to the trunk) と同じ ● main branch (trunk) 以外にmergeしてはいけない ● branchを切った開発者、およびペアプログラミングでの共同作業者以外がcommit してはいけない ○ あくまでmain branchを開発の中心とするべきである

Slide 73

Slide 73 text

feature branchを使わない方法 (committing straight to the trunk) との比較 ● short-lived feature branchによりPull Requestが使える ○ Pull Requestによるレビューが可能 ■ レビューの非同期化による mergeの遅延リスクがある ○ CIパイプラインによるmerge前のコードチェックが可能 ■ レビュアーが結果を参照できる

Slide 74

Slide 74 text

どちらを選択するか? ● feature branchを使わないほうが高い開発速度を得られる ○ Pull Requestを作成する手間が省ける ○ feature branchが肥大化するリスクをより減らすことができる ○ 同期レビューを強制しやすい ● (short-lived) feature branchを使った方が trunk (main branch) をバグから保護しやすい ○ Pull Request に対してCI checkをかけられる

Slide 75

Slide 75 text

どうやってトランクベース開発を実践するか?

Slide 76

Slide 76 text

僕が所属する マイクロサービスアーキテクトグループ 認証認可チーム での取り組みを紹介します

Slide 77

Slide 77 text

認証認可チームの技術スタック ● VCS: Git (GitHub) ● 言語: Go ● 実行環境: Kubernetes ● CI: GitHub Actions ● CD: ArgoCD

Slide 78

Slide 78 text

トランクベース開発の導入背景 ● PFや全社のマイクロサービス化推進に伴い トランクベース開発の導入が検討された ○ Two pizza rule ■ マイクロサービスを運用するチームの人数は 2つのピザを分け合える程度が妥当 ● 6-10人ぐらい、という説も : https://jasoncrawford.org/two-pizza-teams ■ これだけの開発者がひとつのコードベースを積極的に保守する場合 conflictのリスクが現実的に避けられなさそう ● マイクロサービスアーキテクトグループで 人柱 PoCを実施することとなった ○ チーム発足後初めてのサービス開発において適用されることとなった

Slide 79

Slide 79 text

トランクベース導入にあたってやったこと ● 開発フローの整備 ● 効果の測定

Slide 80

Slide 80 text

トランクベース導入にあたってやったこと ● 開発フローの整備 ● 効果の測定

Slide 81

Slide 81 text

short-lived feature branchの採用 ● 開発着手後1日以内でPull Requestを作成することを目標とした ● Pull Request作成後 2日以内でbranchのmergeおよびdeleteをすることを目標とした

Slide 82

Slide 82 text

(同期 or 非同期) コードレビュー ● 基本的には非同期なコードレビューを採用 ○ Goのコードレビューを務められるレビュアーが 1人しかおらず 同期レビューのタイミングを確保することが困難 ● PR作成後なるべく24時間以内にレビュー対応するようにした ○ GitHubのSlack連携機能でアサインされたレビュワーに通知を送るようにした ● 一部のレビュー観点についてはCIパイプラインで自動化した ○ 単体テストの成否、Lintによるコーディングルール違反の検知、 etc

Slide 83

Slide 83 text

トランクベース導入にあたってやったこと ● 開発フローの整備 ● 効果の測定

Slide 84

Slide 84 text

測定指標: サイクルタイム ● ここでは「feature branchにfirst commitされてからmain branchにmerge されるまでの期間」と定義 ○ 変更をどれだけ早くmain branchに取り込めたかを示す指標として採用 ○ gitにおいて、branchの作成日時はそもそも記録されていない ため branchの生存期間を直接計測することはできない

Slide 85

Slide 85 text

Code Climate Velocity

Slide 86

Slide 86 text

Code Climate Velocity ● エンジニア組織のパフォーマンス可視化ツール ○ サイクルタイムの計測や、 PRレビューまでの待機時間を可視化 ○ 現在はFindy Teamsも並行運用中 ● 週次の保守運用定例でチェック ○ 目標を達成できなかった場合、その原因を考える

Slide 87

Slide 87 text

反省シート ●

Slide 88

Slide 88 text

反省シート ● トランクベース開発のルール (1日以内にPR作成、2日以内にmerge) を 守れなかった場合、その反省を行い、スプレッドシートに記載 ○ 週次の保守運用定例で確認・記入 ● ルールや仕組みの改善につながる意見の抽出を目指す ○ 「頑張りが足りませんでした」「注意が足りませんでした」は禁止

Slide 89

Slide 89 text

成果: サイクルタイム比較 (2022/10〜2023/03) 期間中のPR総数 (件) 平均サイクルタイム (時間) 全社平均 - 68.8 チームA 1763 27.7 チームB 2086 32.1 チームC 489 108.5 認証認可チーム 1787 11.5 全社およびPF事業本部で、規模感が似ている他チームと比較 ※チームによってサービスの性質や開発体制が異なるため、あくまで参考値

Slide 90

Slide 90 text

開発フローをどのように改善していったか?

Slide 91

Slide 91 text

トランクベース開発実践の中で直面した課題 ● short-lived feature branchの実現が難しい ○ 「気をつける」だけでは無理 ○ branchの生存期間が伸びるのには、 構造的な要因がある

Slide 92

Slide 92 text

feature branchの生存期間が伸びる要因 ● feature branchの肥大化 ● レビューの遅延 ● 仕様の複雑度 ● 開発者のスキル不足

Slide 93

Slide 93 text

feature branchの肥大化 ● feature branchのサイズ (feature branchに含まれるコードの変更行数) が 多ければ多いほど、mergeまでにかかる時間が伸びる ● 認証認可チームでは 変更行が600行以上あるPull Requestは原則rejectしている ○ 問答無用でCI checkを落とす ■ https://github.com/CodelyTV/pr-size-labeler ○ 閾値は過去の事例をみて判断した

Slide 94

Slide 94 text

feature branchのサイズが大きくなる要因 ● ひとつのfeature branchで全てを実装しようとしてしまう ● 例: サービスにWebAPIを新規追加する ○ ハンドラーの追加、ドメインモデルの追加、 DBや外部APIとの通信などの実装を すべての一つのfeature branchの中で完結させる必要はない ○ ハンドラーを後で実装するなり、 feature flagsを使うなりすれば 開発中の機能をクライアントから隠蔽することができる

Slide 95

Slide 95 text

feature branchのサイズを小さく保つために ● タスク整理 (Planning) の段階で個々のタスクの粒度を確認する ○ 目標を具体的にイメージできる粒度にまでタスクを分解する ■ ex. WebAPIを新規作成するために必要なサブタスクを事前にリストアップする ● ハンドラーの追加, ドメインモデルの追加 , DBや外部APIとの通信, etc ■ 分解できないようなら、まず調査タスクを切る ○ 細分化されたひとつのタスクにつき、一個以上の feature branchを切る ● 途中で必要なタスクに気づいても、そのbranchの中ではやらない ○ TODOコメントをつけておいて、一旦その branchはmergeしてしまう ○ あとで新たにfeature branchを切って対応する

Slide 96

Slide 96 text

タスクの分解にかかる難しさ ● コーディングの前に必要な実装タスクを洗い出すことの難しさ ○ コーディングしながら考えるのよりも難しい ● 細分化されたPull Requestをみて変更の妥当性を 検証することの難しさ ○ 全体像を把握しにくい

Slide 97

Slide 97 text

タスクの分解にかかる難しさ ● コーディングの前に必要な実装タスクを洗い出すことの難しさ ○ コーディングしながら考えるのよりも難しい ● 細分化されたPull Requestをみて変更の妥当性を 検証することの難しさ ○ 全体像を把握しにくい → ふつうの開発よりも難易度は上がる   (難易度の高さとconflictリスク・解消コストとのトレードオフ)

Slide 98

Slide 98 text

feature branchの生存期間が伸びる要因 ● feature branchの肥大化 ● レビューの遅延 ● 仕様の複雑度 ● 開発者のスキル不足

Slide 99

Slide 99 text

レビューの遅延 ● 非同期レビューは後回しになりがちである ○ (大抵チームをリードするポジションにある)レビュアーは普段から忙しかったりする ● レビュアーを増やすことができればいいが、現実的には難しい ○ チームの中にレビューできるだけの知識とスキルをもったエンジニアがそう何人もいるわけではな い

Slide 100

Slide 100 text

なるべくレビューのコストを下げる ● Pull Request (feature branch) のサイズを小さくする ○ ひとつのPull Requestに対するレビュー所要時間を短縮できる →スキマ時間でレビューができる →スケジュールの融通が効く ● 時間を確保して同期レビューする ○ コードを書いた人に直接説明してもらうのが一番早い ○ 僕自身、ぱっと見で指摘事項が多くなりそうな時や レビュー→修正が一巡してもなお修正点が残っている場合は同期レビューに切り替える

Slide 101

Slide 101 text

それでもレビューに時間がかかる場合 ● 時間をかけてPull Requestの完成度を追求している間に main branchがどんどん更新されていく ○ conflictのリスクが高まる ● 途中で区切りをつけてmergeしてしまうのも手 ○ ビルドやテストが通れば一旦 OK、でもよい ● TODOコメントをつけておいて、後で直す ○ TODOコメントからGitHub Issuesを作成 ■ https://github.com/alstr/todo-to-issue-action ■ main branchへのpushにフックさせて実行している ○ 切ったIssueはDaily standupで拾って修正スケジュールを組む

Slide 102

Slide 102 text

feature branchの生存期間が伸びる要因 ● feature branchの肥大化 ● レビューの遅延 ● 仕様の複雑度 ● 開発者のスキル不足

Slide 103

Slide 103 text

仕様の複雑度 ● 複雑な仕様を実装したコードは、どうしても複雑になってしまう ○ 実装にもレビューにも時間がかかる ● レビュアーとの認識違いにより、手戻りが発生するリスクがある ● 仕様を単純にできるなら、それに越したことはない ○ 要件定義に介入する ○ 込み入った仕様も、課題を分割して小さい単純な仕様に落とし込めるかもしれない ● それがダメなら、仕様や実装方針について レビュアーと事前にすり合わせておく

Slide 104

Slide 104 text

feature branchの生存期間が伸びる要因 ● feature branchの肥大化 ● レビューの遅延 ● 仕様の複雑度 ● 開発者のスキル不足

Slide 105

Slide 105 text

開発者のスキル不足 ● そもそも開発経験に乏しかったり、ドメイン知識が不足しているメンバーが 妥当性のあるコードを書くのは難しい ○ チームに入って間もないエンジニアなど ● 知識をキャッチアップしてもらうしかない ○ オンボーディングとして簡単なタスクを与える ○ ペアプログラミングで密にコミュニケーションをとる ● 既存メンバーをメンターとしてアサインする ○ わからないことがあれば即質問してもらう

Slide 106

Slide 106 text

サイクルタイムの不安定化 ● チームとしてのサイクルタイムはチームの状況によって大きく左右される ○ チームに新規メンバーが参入したとき ○ (特にジュニアな) エンジニアにチャレンジングなタスクがアサインされたとき

Slide 107

Slide 107 text

サイクルタイム推移

Slide 108

Slide 108 text

サイクルタイム推移 チームに新規メンバーが アサインされたタイミング チームにインターン生が アサインされたタイミング インターン生が 抜けたタイミング

Slide 109

Slide 109 text

まとめ

Slide 110

Slide 110 text

トランクベース開発を導入してよかったこと ● サイクルタイムを短く保つことで、開発速度を維持することができた ● 開発着手から1日以内でPull Request作成、2日以内でmergeという 明確な目標のおかげで、開発速度改善に向けた建設的な議論ができた ● チーム内のノウハウを用いて、他チームの開発速度を改善することもできた ○ Developers Summit 2023でチームメンバーが発表 ■ DMMプラットフォームでプルリクエストのマージ時間を 250時間から50時間に減らした話 ■ https://speakerdeck.com/juve534/improve-development-efficiency-with-dmm-platform

Slide 111

Slide 111 text

課題 ● 現状計測しているサイクルタイムは 個々のfeature branchの生存期間を示しているに過ぎない ○ 本質的には、個々の機能やそれによる価値を どれだけ早くユーザーにデリバリーできたか を測る必要がある ■ 変更のリードタイムそのもの をみなければいけない ● 計測を試みているが、まだできていないポイント ○ (トランクベース開発自体の課題ではないが …)

Slide 112

Slide 112 text

採用 合同会社DMM.comマイクロサービスアーキテクトグループ認証認可チームではエンジ ニアを募集しています! マネージャーのpospomeによる カジュアル面談も実施しています ご興味のある方は是非 https://dmm-corp.com/recruit/engineer/2/