こんにちは。
FindyでTech Leadをやらせてもらってる戸田です。
昨年(2023年)、Findy Team+ にて、4名で3ヶ月ほどかけて大規模なフロントエンドの設計刷新を行いました。
Findy Team+はエンジニア組織のパフォーマンス向上を支援するSaaSサービスで、2020年から開発がスタートしました。
3年以上の間、機能開発を行ってサービスを伸ばしてきましたが、同時に様々な課題も生じていました。
今回はそこに至るまでの経緯と、実際に行ったことを紹介します。
なぜフロントエンドの設計刷新を決断したのか
当時、自分は他のプロダクトの開発チームでコードを書いていたのですが、ある日Findy Team+の開発チームへ異動することになりました。
異動後に2週間ほどFindy Team+のフロントエンドの開発をしたのですが、あることに気づきます。
コード設計が思ったより複雑かもしれない
異動前後で理解のしやすさやプルリクを上げるまでの時間に差があるように感じたので、Findy Team+での自分の生産性の数値を比較してみることにしました。
すると、異動前と比べて自分のアウトプットの量が半分以下まで下がっていることが明らかになりました。
感覚値と実際の生産性の数値が一致したため、どこかしらに何か問題があるのでは?という仮説を立てました。
異動前後での変化を洗い出してみた結果、コードの設計に問題がある高いことを突き止めました。
しかし、コードの設計を刷新しようにも、そこには大きなコストと時間が掛かります。
その大きなコストを、異動前後の生産性低下の感覚値だけで周りの人間を納得させることは不可能です。
そこで、今のコードの設計の問題点を洗い出して、何がどう問題で生産性が低いのか具体的な洗い出しに着手しました。
問題点の洗い出し
過度な共通化
まず全体的に過度な共通化が行われていました。
例を上げると、グラフやテーブルに数値を表示する際に表示するcomponent内で、データ取得も同時に行っていました。
こうなってしまった場合、グラフやテーブルの見た目は同じだけど表示するデータが異なる場合に対応できなくなります。
このような複数責任を持ったコンポーネントが幾つも存在していたため、コードリーディングやデータの流れを追うことが非常に困難になっていました。
データの取得と描画は責務が異なる処理なので分けて実装するべきでした。
処理に一貫性がない
APIの呼び出しに関するルールが存在しておらず、複数個所から色々なタイミングで呼び出されており、データの流れを追うことが非常に困難になっていました。
前述した過度な共通化がこの問題を引き起こしており、1つのcomponent内で全てのことを処理しようとしていました。
データ取得と描画が密結合で実装されているので、この部分は共通化してるのでcomponent内でデータ取得して描画してるけど、この部分は共通化してないからデータをprops経由で貰ってきてる。といったような状況が多発していました。
これにより、どこからデータを取ってきているのか探しに行く作業が多発し、コードリーディングに必要以上の時間を取られているような状況でした。
テストコードを書きにくい
過度な共通化によりデータの取得と描画が同時に行われていたため、責務の分担がほとんどできていない状況でした。
そのため、テストコードを書きにくい状況にも陥っていました。
描画に専任するべきcomponentの中でデータ取得もしているせいで、データ取得部分のモックを用意しなければテストができず、テストコードが必要以上に肥大化していました。
設計刷新に対する認識合わせと合意形成
メンバーとの認識合わせ
Tech LeadやCTOが「やってほしい」と言って作業してもらうことは比較的簡単ですが、自分たちで「やる」と決意してやることは価値が大きく違います。
ある程度の問題点を洗い出すことが出来たので、これを元にメンバーと意見交換をすることにしました。
この時メンバー全員を一同に集めて話すのではなく、メンバー1人ずつ時間を取って話をすることを決めました。
メンバー全員を一同に介してこの手の話をすると、声が大きいメンバーの意見が反映されがちだと考えたからです。
真に自分たちで「やる」と決意して貰いたかったので、1人ずつ時間を取って本音を聞くことにしました。
フロントエンドのメンバー全員と1on1で話を聞いてみた結果、現行の設計に対する意見と感覚値がほぼ一致していました。
- ずっとリファクタを入れたかったけど、この規模まで来るとリファクタ自体に時間が掛かりすぎてしまう
- 機能追加に時間を取れなくなってしまうので、リファクタをやりたくても言い出せなかった
- 今の設計の状態のまま、更に開発スピードを上げていくのは難しい
これから先の開発スピードを更に加速するためにも、フロントエンドの設計刷新は必要不可欠であるとチーム全体で結論が出ました。
経営陣との合意形成
そこで経営陣やPdMのメンバーと、設計刷新に対する合意形成を作りに行きました。
全体的に作り直す必要があるため、新規開発を可能な限りストップして、一定期間を作り直しに集中する必要があります。
これに関しては大きな反対意見が出ることもなく、比較的スムーズに了承を得ることが可能でした。
議論したのは、どのくらいの期間を新規開発ストップするかどうかくらいです。
なぜ大きな反対意見が出なかったかと言うと、以前にも同様のシステムの設計刷新 を行ったことがあり、それに対する成功体験が大きかったためです。
確かに大きなコストは掛かりますが、比較的早い段階でそのコストを回収できたという成功体験があり、リファクタや設計刷新に対する抵抗感が比較的低い点は弊社の良い点です。
過去の成功体験と現在の状況を比較し、掛かるコストと回収期間を提示できたため、比較的スムーズに了承を得ることが可能になりました。
着手前の準備
新設計の方針を固める
新設計の基本方針として、多少冗長となっても構わないので共通化を最小限に留めることとしました。
最初から共通化して実装するのではなく、実装後に共通化するべきかどうかを議論し、その議論が通ったものだけ後から共通化対応することにしました。
また、コードの責務を明確にし、必要以上のことを1つのファイル内で実行しないことを徹底しました。
更にcomponentとロジックを分離することを基本とし、依存関係を単方向にさせました。
データ取得と描画の処理を明確に分け、描画に必要なデータは全てpropsリレーで受け取ることを義務付けました。
propsリレーが長くなるとツラくなる可能性もありますが、責務が混在してテストコードを書きづらくなることと比べたらマシという判断です。
小さな画面を新設計で1つだけ作り直してみる
新設計の方針がある程度見えてきたタイミングで、簡単な画面を1つだけ作り直して、メンバーに方針を展開することにしました。
方針を文字や口頭で説明するよりも、実際のコードを見てコメントベースで説明する方が理解しやすいからです。
そこから新設計に対する意見をメンバーから貰い、1ヶ月程度で方針が固まりました。
実際の作り直しの流れを決める
実際に本番環境が動いており、多数のクライアントが利用している状況だったので、次の3つを大原則に掲げました。
- 現状の機能を維持する
- 現状のスタイルを維持する
- 安全に移行する
ページ単位で作り直しを進め、APIは既に利用しているものを使い回すことを決定しました。
作り直しの流れ
基本的な作り直しの流れは次の通りになります。
- 同じ画面を別URLで実装
- 既存画面のコードに手を加えるのは基本NG
- 一時的に同じ画面が異なるURLで公開される
- 既存画面と異なるコードで実装しているので、Pullrequestをガンガンmergeできる
- 新設計での実装が完了後、本番環境で動作確認を行う
- 動作確認がOKであれば旧設計のURLのルーティングを新設計の画面に向ける
- 新画面で何かしら不具合が発生した場合は、ルーティングを戻すことですぐに切り戻しが可能
- 動作確認がOKであれば旧設計のURLのルーティングを新設計の画面に向ける
- 一定期間様子を見て、問題なさそうなら旧実装のコードを全削除
この流れにより、旧実装の画面のことを気にすることなく新設計のPullrequestをmergeすることが可能になり、問題が発生しても切り戻しが容易になります。
振り返り
結果として新設計のコードへの移行完了後のチーム全体の生産性が、前年比で2.5倍程度まで上がりました。
作り直しやリファクタ、設計刷新を提案する前に、まず感覚値と実際の数値を見比べ、メンバーと認識合わせをすることが重要です。
また、経営陣を始めとした決裁権がある人間を納得させるために数字は必要不可欠です。
成功体験があれば次の提案は思った以上にスムーズに進みますが、一番最初の成功体験を作ることは非常に難しいものです。
小さくてもいいので数字を出し、小さな成功体験を多く詰むことが重要です。
まとめ
いかがでしたでしょうか?
リファクタや作り直し、設計刷新を検討している方の参考になれば幸いです。
現在、ファインディでは一緒に働くメンバーを募集中です。
興味がある方はこちらから↓ herp.careers