フロントエンドリアーキテクチャ(部分刷新)を素早く終わらせるために取り組んだこと

※この記事は  Cybozu Frontend Advent Calendar 2023  の 25 日目の記事です !

こんにちは、サイボウズでスクラムマスターとして働いている村田です。2023 年 8 月から新規機能を開発するチームに移動し、週3日(火水木)勤務で専任スクラムマスターとして活動しています。それ以前の約 1 年間は、kintone のヘッダーを React 化するチームでスクラムマスターとエンジニアの役割を兼務していました。

今回は以前所属していた kintone のヘッダーを React 化するチームで、フロントエンドリアーキテクチャ(部分刷新)を素早く終わらせるために取り組んだことを紹介します。

プロジェクトの概要

kintone の全画面のヘッダー領域を Closure Library から React へ置き換える部分刷新を試みていました。プロジェクトゴールは次の2つです。

  • kintone の全画面のヘッダー領域が刷新されている
  • 移譲先のチームが刷新後のヘッダー領域の開発を行っている

当初の予定では2023年末までかかる見込みでしたが、2023年7月中にゴールを達成しました。

刷新完了までのスケジュールは以下の通りです。

期間 ゴール 概要
2022/01 ~ 2022/06 ページ単位の React 化を行うチームへ React 化したヘッダーを提供する kintone の共通ヘッダー部分の React 化に責任を持つチームの紹介!
2022/06 ~ 2023/06 kintone の全画面のヘッダー領域を刷新する React 化した共通ヘッダーを kintone の全ページに適用しました!
2023/06 ~ 2023/07 ヘッダー領域のコードのオーナーシップを他チームへ移譲する コード移譲先のチームメンバーにドキュメントの提供や説明会を実施。テスト&CIの整備やライブラリアップデートも行う。

リリースの流れや技術的な詳細については次のブログ記事をご覧ください。

blog.cybozu.io

この活動で得られた学びを Spotify で配信しているので、お時間があればこちらも聞いてみてください。

open.spotify.com

刷新スピードを上げるために取り組んだこと

このプロジェクトにおいて、フロントエンド刷新を加速するために実施した 5 つの取り組みを紹介します。

クロスファンクショナルな小さいチームで 1 つのことに集中する

小規模なチームはコミュニケーションのオーバーヘッドが少なく、調整もしやすいため俊敏に行動することが可能です。

ヘッダー領域を刷新するチームでは最終的に 4 人体制で、以下の役割を担ってスクラムで開発をしていました。

  • プロダクトオーナー兼エンジニア: 1人
  • スクラムマスター兼エンジニア: 1人
  • エンジニア: 1人
  • QA: 1人

このチームでは 1 人で解決できない問題に直面すると、すぐに Slack のハドルで集まって問題解決に向き合い、解決後はハドルを切ってそれぞれの業務に戻るという流れが自然に行われていました。チームメンバーが少ないことから、合意に至るまでの時間が短く、迅速な意思決定が可能でした。

もちろん、最初からこのような動きができたわけではありません。

私がスクラムマスター兼エンジニアとしてチームに入った 2022/06 の状況は、「ページ単位の React 化を行うチームへ React 化したヘッダーを提供する」ゴールを達成した直後で、燃え尽き症候群のような雰囲気が漂っていました。さらに、多数のチームメンバーがゴール達成のタイミングで別のプロジェクトに移ることが決まっており、ほとんどが新しいメンバーで「kintone の全画面のヘッダー領域を刷新する」ゴールに向かう必要がありました。

ほぼゼロからの再スタートを余儀なくされた状況の中で、チームが安定して成果を生み出せる状態へ素早く移行するために 2 つのことを実施しました。1つはチームが達成すべきゴールの設定、もう1つは目標達成に向けた進捗の確認とふりかえりです。

speakerdeck.com

これらの取り組みは、初期段階で目標に対する共通の理解を深め、その後のチームメンバー同士の信頼関係を強化することに繋がりました。

チームで独立したデプロイを可能にする

チームが管理しているコードの外に影響することなく更新をデプロイできるようになると、他チームとの調整作業がなくなり、リードタイムが短縮できます。

ページ単位の React 化を行うチームへ React 化したヘッダーを提供していた頃は、ヘッダーを npm パッケージとして配布していました。npm パッケージを配布する場合におけるデプロイでは、パッケージを利用しているすべてのチームにバージョンの更新を依頼し、その依頼が完了するまで更新が反映されるのを待つ必要がありました。

また、Closure Library を使用しているページでは npm などのパッケージマネージャーを使用したライブラリを import するのに一工夫必要で、React 化された画面と Closure Tools を用いて開発された画面それぞれで異なるデプロイ方法を行う必要がありました。

更新のデプロイをチームのコントロール下において素早くリリースできるようにしたかったため、全画面刷新に踏み切ったタイミングで npm による配布を廃止し、ヘッダー領域の JavaScript ファイルを HTML で読み込む形式に変更しました。

React 化したヘッダーを全ページへ適用

これにより、基本的にチーム外への依頼が不要になり、自由にデプロイすることが可能になりました。「基本的に」と書いたのは、共通ヘッダーとページコンテンツ間で利用しているインターフェースを変更する場合にはチーム間の調整が必要になるためです。現状ではヘッダーとページコンテンツ間は疎結合になっており、インターフェースの変更はほとんど行われません。

ヘッダーとページコンテンツの結合方法はマイクロフロントエンドを参考に Custom Event で実装しました。マイクロフロントエンドの実践にあたって検討した様子もブログで公開しているのでご一読いただければと思います。

blog.cybozu.io

刷新する価値のある機能を絞る

当たり前の話ですが、刷新する機能が少なければ少ないほど素早く刷新することができます。

ヘッダー領域を刷新するときに、この機能は本当に刷新する価値があるのかチームメンバーで議論しました。価値のある部分を見極めて刷新対象を絞ったことで、当初の計画よりも3~4ヶ月前倒しで kintone の全画面に刷新後のヘッダーを適用することが可能となりました。

全画面リリースに向けた準備

刷新のスコープから除外した機能については、React で実装したコードから Closure Library で実装された機能を Custom Event を使って呼び出しています。理想は機能を削除することでしたが、諸々の理由で自チームで機能を削除を実現することは難しく、他チームの協力を仰いで優先順位を上げて取り組むこともこの時点では困難であったため、このような方法で刷新のスコープから除外しました。

この段階では機能削除を諦めましたが、移譲先のチームで引き続き検討しています。

早すぎる最適化を避ける

ゴール達成に直接影響せず、全体に及ぼす影響がほとんどないものは、問題が顕在化してから取り組めば良いと考えています。

サイズの大きいライブラリの重複読み込み解消や、インターフェース部分のコードを切り出して共通化を図るといったタスクを今回の刷新のスコープから意図的に除外しました。これらは部分刷新が増えると、ユーザー体験が著しく低下したり、実装時の認知不可が高まる問題を抱えています。しかし、この段階で部分刷新を行っているチームは我々のみであり、最適化を図るには時期尚早だと判断しました。

パフォーマンスよりもチーム間の独立性を重視し、重複読み込みを許容しました。インターフェースに関しても Custom Event のイベント名にどの領域から飛ばされてきたイベントなのか判別できる prefix を付け、後に差し替えるタイミングで影響範囲を最小限にするために Custom Event をラップする層を作ってそれを呼び出す形にしました。

// 例: header/event-listener.js
header.eventlistener.addShowNotifierListener = function () {
    document.addEventListener('header:show-notifier', function (evt) {
      ...
    }
}

不具合を検出してから修正までのフィードバックループを短くする

不具合を早期に検出することで、手戻りに要する時間を短縮できます。

kintoneのフロントエンドを刷新する前は、多くのテストが E2E で自動化されていました。自動化されたテストが全くない状態よりは良いですが、E2E Test の増加に伴い不安定なテスト(Flaky Test)の数も増え、さらにテスト実行時間の増加によるフィードバックの遅れが開発生産性を低下させていました。そのため、刷新のタイミングで QA 観点で作成したテスト仕様書をベースに、Integration Test の実装を増やし、E2E Test の実装を最小限にしました。

最終的な E2E Test と Integration Test の件数を以下に示します。

テストの種類 テストの件数
E2E 22件
Integration 352件

補足:Unit Test はエンジニア判断で実装しています。中には Integration Test と重複する項目もありますが、重複を省いてもテストの実行時間短縮には効果がなく、QA と調整する時間を省ける方が開発効率の向上に繋がると判断したため重複を許容しました。最終的な Unit Test の件数は374件になりました。

この結果、ヘッダー領域における不具合を検出してから修正にかかる時間を大きく短縮することができました。環境依存ではありますが筆者のローカル開発環境で Integration Test と Unit Test の合計726件のテストを実行するのにかかる時間は約23秒です。

エンジニアと QA 間で協力して Integration Test を実装した様子もブログで公開しているのでもしよければご一読ください。

blog.cybozu.io

まとめ

  • 小さいチームで1つのことに集中する
  • チームで独立して顧客に価値提供できるデプロイパイプラインを作る
  • 顧客に提供している価値を見極めリアーキテクチャの規模を小さくする
  • 早すぎる最適化を避ける
  • 不具合を検出してから修正までのフィードバックループを短くする

これらが素早いリアーキテクチャを実現するための鍵です。※素早くリアーキテクトする手段はもちろんこれだけではないと思います

特段目新しいことは 1 つも述べていませんが、大規模なアプリケーションでリアーキテクチャに取り組んでいる方々の参考になる情報があれば嬉しく思います。

あとがき

2023 年は「kintone の全画面のヘッダー領域を刷新する!」というゴールに一直線に向かうために、多くの「やらないこと」を決めた 1 年でした。この活動を通して自分自身のリアーキテクチャに対する姿勢も大きく変化しました。

リアーキテクチャを始めた初期の頃は刷新へのモチベーションが高く、とにかく技術を磨いてスピードを出すことに意識が向いていました。しかし、時間が経過するにつれて刺激が減少し、その結果、徐々にモチベーションも下がっていきました。同じ環境で 1 度下がったモチベーションを高めるのは難しいです。

人のモチベーションが下がりきる前にゴールを達成する。そのために、技術を磨く以外の方法で刷新のスピードを上げることにも向き合いました。

リアーキテクチャでは技術力の向上は基本ですが、それだけでは不十分だと思います。顧客価値を生まないコードを負債と見なし、除外することの重要性を痛感しました。これは特に大規模なアプリケーションにおいて効果的だと思います。

そして、機能やテストを削除し、代替手段を提案するには、アプリケーションが顧客にどのような解決策を提供しているかを理解する必要があります。理解がなければ、仕様をそのまま踏襲して再現する以外に方法がないのです。

リアーキテクチャの文脈においても、迅速に目標を達成するためには、チームが顧客のニーズを深く理解していることが不可欠であるということを強く実感しました。以前は、リアーキテクチャを技術に特化し、その分野に全力を尽くす精鋭たちの取り組みだと考えていました。しかし、このプロジェクトを経て、リアーキテクチャにおいてもドメインの理解が重要であることを学びました。

価値を生み出している要素を理解していない場合、変更可能な範囲が狭くなります。その結果、スピードを上げることが難しくなります。

この学びを新しいチームに共有し、今後もリアーキテクチャに臨んでいきたいと思います。