このエントリは、SmartHR Advent Calendar 2024 シリーズ2の10日目の記事です。
こんにちは!SmartHRで基本機能の開発をしているプロダクトエンジニアのyamakeeeeeeeeenです。この記事では、基本機能のレガシー画面のリプレイスプロジェクトについてご紹介します。
基本機能の技術スタックとレガシー画面の現状
SmartHRは2015年にサービスをローンチ時、フロントエンドにjQuery、バックエンドにRuby on Railsを採用して開発されました。その後、2017年頃からReactが導入され、新機能の開発では主にReactが利用されています。
現在、基本機能のフロントエンドは「React on Rails」という構成を採用しています。この構成では、Railsのapplication.hamlにReactを描画するpartialが差し込まれる形を取っており、ReactとjQueryが共存する状態です。React化が進んでいる画面でも、ヘッダーやフッター、サイドメニューといった共通部分がjQueryベースで動作しています。
サービス初期に作られた画面の多くは未だに画面全体がjQueryベースで動作しており、これらの画面をこの記事では「レガシー画面」と呼びます。このレガシー画面は、サービスの中核を支える基本機能に多く含まれており、技術的負債として課題視されています。
課題の洗い出し
プロダクトエンジニアやプロダクトオーナーの間では、レガシー画面が抱える課題について一定の共通認識がありました。しかし、課題の全体像までは把握しきれておらず、負債解消の優先度は長らく低いままでした。例えば以下のような理由から、問題を具体的なタスクに落とし込むことや、長期的なロードマップに組み込むことが難しい状況が続いていました。
- レガシー画面がどれほどの数あるかが不明
- リプレイスにかかるコストが見積りずらい
- 開発のボトルネック以外にどのような影響があるかが分からない
そこで、React化プロジェクトを進めるにあたり、まずはレガシー画面にどのような課題があるのかを洗い出すところから始めました。このプロセスではプロダクトエンジニアだけでなく、デザインユニット、アクセシビリティユニット、多言語対応ユニットなど、複数のステークホルダーへのヒアリングを実施しました。 その結果明らかになった課題の中から2つを紹介します。
開発スピードが落ちる
レガシー画面の多くはjQueryをベースに作られていて、コードがかなり複雑になっています。そのせいで、機能を追加したり改修したりするだけでも、React化された画面と比べてかなり手間がかかるのが実情です。特に、古いコードを読み解いたり、当時どんな意図で設計されたのかを理解するのにかなり時間がかかってしまい、スムーズに開発を進めるのが難しい状況でした。
さらに、jQueryを使った経験があるエンジニアが減っていることも問題です。限られたメンバーに頼らざるを得ない場面が増えてしまい、「あのコードのことがわかるのは○○さんだけ」のような属人性が高まっていました。そうなると、チーム内で抱えきれないことも多くなり、他のチームにヘルプをお願いするケースが増えていきます。
実際、レガシー画面に手を加えるたびに、まず調査スプリントを挟む必要がありました。さらに、Pull Requestのレビューがチーム内だけで完結せず、別チームにサポートを依頼することもありました。
UIの一貫性やアクセシビリティ品質の低さ
SmartHRでは、画面デザインやコンポーネントのルールをまとめたSmartHR Design Systemを活用しています。これはSmartHRらしい見た目や操作感を提供するための指針で、SmartHR UIを中心に構築されています。このデザインシステムを基にUIを作ることで、画面の統一感やアクセシビリティの品質を自然と高めることができます。
ただし、SmartHR Design SystemができたのはSmartHR UIが生まれた後のこと。そのため、jQueryを使って作られたレガシー画面ではデザインシステムを完全に反映することが難しい状況です。結果として、レガシー画面では見た目や操作感が画面ごとにバラバラになり、ユーザーが異なる画面を行き来する際に違和感を覚えることもありました。
さらに、アクセシビリティ対応も十分ではなく、特にスクリーンリーダーを使うユーザーにとっては操作しづらい箇所が目立ちます。一方、React化された画面では、デザインシステムに沿ったSmartHR UIコンポーネントを使うことで、アクセシビリティの品質が自然と高く保たれる仕組みになっています。
UIの一貫性やアクセシビリティの低さは、SmartHRが進めるマルチプロダクト化戦略において非常に大きな課題です。基本機能はサービス全体の中心となる部分なので、ここに問題があると他のアプリケーションにも影響が波及してしまいます。この点も、React化を進めるうえで重要な理由のひとつになっています。
課題の全体感の把握
レガシー画面のリプレイスを進めるためには、まずどれだけの画面が対象になるのか、全体像を把握する必要がありました。
jQueryを使ったレガシー画面は、共通化された1枚のapplication.hamlから呼び出され、Railsが部分ごとにpartialをうまく差し込む仕組みになっています。この構造自体は便利なのですが、grepコマンドなどを使って機械的にレガシー画面を特定することが難しい状況でした。単純にコードを検索して「この画面はレガシーだ」と自動で判定することはできなかったのです。
そこで、手作業で確認するプロセスを採用しました。まず、rails routesコマンドを使ってアプリケーション内のすべてのルートをリストアップ。その後、画面ごとに「これはjQueryベースか?」「React化されている部分はあるか?」と目視で確認していきました。
結果、アプリケーション内には約200のレガシー画面が存在していることが判明しました。ただし、これらすべてがリプレイス対象になるわけではありません。例えば、将来的に独立したアプリケーションへ移行する予定の画面などは、現時点で優先度が低いため対象から除外しました。こうした取捨選択を行った結果、最終的にリプレイス対象として残ったのは約140画面でした。
正直なところ、最初は「リプレイス対象は50画面くらいかな」と予想していたので、この結果には驚きました。ただ、このプロセスを通じて、自分が知らなかった画面の存在をキャッチアップできたのは収穫でした。こうした地道な作業を経て、課題の全体像をつかむことができました。
優先度の調整
リプレイス対象として約140画面が明確になったところで、それぞれの画面について優先順位をつける作業に取りかかりました。ここで重要だったのは、すべてを一度に進めようとしないことです。既存機能の仕様から見直したい気持ちなどもありましたが、現状の機能をそのままReact化して再現することを優先しました。
その上で、ステークホルダーからのヒアリングを基に、以下の3つの観点から優先順位を決めていきました。
- ユーザーメリット
- リプレイスによってユーザー体験がどれだけ向上するかを評価しました。頻繁に利用される画面や、重要な業務フローに関わる画面を高く評価しました
- 開発者メリット
- リプレイスによる開発効率やメンテナンス性の向上が期待できるかを評価しました。特に、頻繁に機能追加が発生する画面や、今後のロードマップで重要な役割を果たす画面は優先度を高めました
- 工数
- リプレイスにかかる難易度を「低」「中」「高」の3段階で分類し、大まかな工数を見積もりました。具体的には次のような基準で分類しています
- 低:データの一覧画面や詳細画面など、シンプルな構造の画面
- 中:単純な入力フォームを含む画面
- 高:複雑な入力フォームや条件分岐が多い画面、またはレイアウトの見直しが必要な画面
- リプレイスにかかる難易度を「低」「中」「高」の3段階で分類し、大まかな工数を見積もりました。具体的には次のような基準で分類しています
サイドプロジェクトとしてのリプレイス
優先度が明確になったものの、140画面を一括でリプレイスするのは現実的ではありません。そこで、まずは有志メンバーによるサイドプロジェクトとして、実験的に数画面のリプレイスを実施することにしました。この取り組みの目的は、リプレイス手順の整理や工数の見積もり、ステークホルダーとの調整方法の確立です。
最初に従業員招待フォーム一覧画面のリプレイスに取りかかりました。この画面を選んだのは以下の理由からです。
- ユーザーメリット観点
- 従業員を招待する業務は、ユーザーによって毎月発生します。そのため、従業員招待フォーム一覧画面の利用頻度は高く、リプレイスによるメリットも大きいと判断しました。また、この業務は別アプリである文書配布機能と連携して行われることが多く、両機能間の整合性を高めるという点でも、ユーザー体験を向上させる余地があると考えました
- 開発者メリット観点
- 従業員招待関連の機能追加の頻度が高く、一覧画面への機能拡張や改修が今後も予想されるため、開発効率を向上させるためのリプレイスが優先度の高い課題でした
- 工数観点
- データを表示するだけのシンプルな構造の画面であったため、工数としては「低」と見積もりました。初回の実験的なリプレイスであったため、工数が低い画面から着手することが現実的と判断しました
これらの観点を総合的に評価した結果、従業員招待フォーム一覧画面が初回のリプレイス対象に選ばれました。
リプレイスには約1か月かかりました。初回だったため、プロジェクトの進め方をビジネスサイドとすり合わせる期間や、同期的に進める作業も多く含まれていました。具体的な作業内容は以下の通りです。
- 既存仕様の整理
- デザインモック作成
- ReactでのUI実装
- API実装と繋ぎこみ
- テスト設計
- 自動テスト実装
- QA
- リリース
このプロセスを通じてリプレイス手順が整理され、今後は非同期的に進められる部分が増えることで、リードタイムの短縮が期待されています。
以下の画像は、リプレイス前のレガシー画面と、リプレイス後のReact化された画面の比較です。コンテンツ部分がReact化され、デザインシステムに沿ったよくある一覧画面になりました。
レガシー画面
コンテンツ部分がReact化された画面
リプレイスの振り返り
サイドプロジェクトとして進めたリプレイスを通じて、効率的に進めるためには、関連性のある画面群をまとまりごとにリプレイスしていく進め方が有効であることがわかりました。 たとえば、基本機能内の「共通設定」と呼ばれる設定画面群には、共通のUIであるサイドメニューがあります。この場合、まず設定画面のコンテンツ部分をReact化し、その後にサイドメニューのReact化を進めることで、効率良くReact化の範囲を広げていくことができます。
さらに、このような進め方を繰り返すことで、最終的にはヘッダーやフッターなどの全体共通部分にReact化の範囲を広げていくことが可能です。一気に全体をReact化するのではなく、関連性の高い画面群を少しずつReact化することで、効率的かつ計画的に進めることができます。 当初は画面ごとに前述した優先度を設定し、順番に進める計画を考えていましたが、この方法では部分最適に留まりがちでした。振り返りを通じて、機能や関連性の高い画面群を単位としてフェーズを区切り、計画的に進める方が全体として効率的であると判断しました。
今回のリプレイスプロジェクトの取り組みは、SmartHR基本機能だけでなく、今後のマルチプロダクト化を支える基盤となる重要なステップです。この学びを活かし、着実にReact化を進めていきます!
We Are Hiring!
SmartHRでは一緒にSmartHRを作りあげていく仲間を募集中です! 少しでも興味を持っていただけたら、カジュアル面談でざっくばらんにお話ししましょう!