🚧

【大長編】地獄のリファクタリング〜第一章 我々は何をしなくてはならんのか〜【フロント編】

2024/10/28に公開

まずは、私がこのような記事を書かせていただけることに感謝申し上げます。
元はといえば、私のクソコードが原因ですからね。
(もしかしたら覚えていらっしゃる方、いるかもしれません。あのときのアレです。)

いや、何回も確認したんですよ?
「私が記事を書くことでブランドを毀損しないか」って。
それでも「書いていいよ」って言ってくださったので、微力を尽くせればと願うところ。
まぁ企業のテックブログですからね。真面目に書きたいと思います。

現在の技術構成

現在、LX DESIGNでは、複業で先生をしたい外部人材と学校をつなぐ教育特化型外部人材マッチングサービスを運営しています。詳しくはこちら
して、そのプラットフォームを開発するのが私の仕事です。

プラットフォームの技術構成は以下のようになっています。

  • 利用技術
    • フロント:React
    • バックエンド:Ruby on Rails
    • インフラ:AWS
  • 運用システム
    • メインシステム:React + Ruby on Rails
    • 社内システム:Ruby on Rails

後々、メインシステムのDB・バックエンドを中心に、周辺の管理システムを展開しようという目論見ですね。
先の先まで見通した、素晴らしい設計!
あとはこれを綺麗に運用していければ完璧だな()

今回からの記事は、中でもメインシステム。
Reactで作られたフロントのリファクタに関するお話です。

開発の進め方

LX DESIGNでは、フェーズ、トピックを分けて開発を進めています。
具体的にはこんな具合。

  1. 先生と学校の先生のマッチングまで(ph1)
  2. 授業の進行管理まで(ph2)
  3. 授業の評価(トピック)
  4. 授業の募集(トピック)...
    (実は授業の募集なんかが軽く負債になるんですが、それはまた別のお話ということで。)

あとは毎日朝会を実施し、各フェーズ内のタスクがきちんと進んでいるか、困っていることはないかなどを確認しながら進めました。
要はスクラム開発ですね。
開発に参加する人数が少ないので、そこはうまくアレンジしています。

この開発の進め方は非常に素晴らしく、

  • ロードマップと歩調を揃えやすい
  • 毎日の情報共有で、タスクの融通・新規タスクの確保など、細かくできる
  • 私のような浅瀬の民が日々ご指導を受けられ、ハマって消費する時間がへる

などなど、(主に私に)メリットが盛りだくさん!
これも別途、超優秀なエンジニアが記事にしてくださるので、乞うご期待!

突然始まった、「千と千尋の神隠し」(大嘘)


スタジオジブリ公式より−オクサレ様の対応をする千[1]
なお、このような素晴らしい開発をしていても、負債というのは積み上がるものだそうです。
さらに開発担当者が私のような浅瀬チャプチャプ勢であれば尚のこと。

気がつくと、森の中の不思議なトンネルの前に、私は立っていました。
いや、厳密には「トンネルをくぐったことにすら気づいていなかった」んです。
トンネルの先で、Reactの書きやすさやある程度どんな書き方でも動く柔軟性など、豪華な料理を食い散らかしてしまった私は、そのまま豚と成り果てました。
千尋はハクと仲良くなりましたが、私が仲良くなる相手だったはずの澄んだリポジトリは、豚のミスが積み上がり、オクサレ様と化しました。
最終的に、千尋はハクの名前を取り戻し、天色の空を舞いました。
最終的に、開発現場では豚とオクサレ様が破滅のダンスを踊っていました。

…どうしてこうなった??

オクサレ様を生み出した不法投棄

リファクタ対象となったリポジトリは、あの恐怖の開発開始から3年が経過したもの。
フェルン。それは流石に嘘だよ。開発開始は去年だよ
当然、あの頃はとにかく早く開発を終わらせることに必死。
なんとなくのルールは決めていましたが、満足に運用できるわけもなく。

不法投棄とは書きましたが、そもそも無法地帯だったと言っても過言。
少なくとも当時の私には、法を整備するスキルも、法を守るための力すらありませんでした。
その結果、清涼だった川はあっという間にドブ川へと変わってしまったわけです。
ドブポイント、いくつかあげましょうか。

「エセ」Atomic Designによるわけわからん肥大化

今回の開発では、「エセ」Atomic Designを採用しました。
「エセ」です。とてもAtomic Designと呼べたようなものではありません。
そもそもAtomic Designの目的をまるで理解しておらず、形から入ったバカがいたとのこと(←私です)。
結果、わけわからんファイル−特にmolecuresと呼称されたモノ-がデカ盛りされ、同時にツラミもマシマシとなっていきました。
これだけでも割とお腹いっぱいです。

自室の如くとっちからった状態管理

今回の開発では状態管理にstatestore (jotai)を併用しています。
併用そのものはありがちだと思うのですが、問題はその使い分け。
ざっと挙げるだけでも

  • 同じページでもstoreが利用されており、
    • 無駄に別ファイルに分けられていて見通しが悪い
    • 何よりパフォーマンスの観点から微妙
  • storeへの格納の仕方が決まっておらず、見通しが悪い。具体的には
    • APIからのデータをkeyごとにバラバラで入れているところとObjectとして利用しているところが混在している
    • エンドポイントが増えるたび、storeが違った形で、際限なく積み重ねられてしまう

などなど。
序盤の規模が小さい時期にはそこまで問題にならなかったものの、最近は「同じ名前なのに保存場所と利用場所が違うstoreが参照される」ことによるバグなどが発生。
…冷静に考えると、かなりのパワーワードだ。

とにかく、「どのデータを」「どこに保存し」「どこで更新・表示するのか」 の整理の必要性は、最近の物価同様、急激な高まりを見せていました。

メシマズ特有のお手製フォーム管理

フォームについてもまぁひどかった。
まず元々のフォーム、手動管理です

…手動管理です!(迫真)

そう、一切のライブラリを入れず、フォームに対して都度Stateをあて、useEffectで条件を管理。
ときにはAtomを活用し、バリデーションを押し込むなど、一切統一感のない無駄なアレンジが繰り返された結果…。

何ということでしょう!
以前はなんとか見えていた見通しは、Storeと密結合したFieldに埋もれ、足の踏み場もありません。
代わりに、小規模のときには見えていなかった問題が、バグとなって顔をのぞかせているではありませんか!

正直なところ、開発開始当時の私には、react-hook-formを始めとする各種ライブラリの必要性が見えていませんでした。
なまじ動作はしていた()分、導入もせずにズルズルと来てしまった、というところ。
結局膨れ上がったfield, storeは…何件だ?もう忘れました(記憶喪失)
少なくとも、忘却の彼方へと溶けてなくなる程度には、何も考えとらんブツが粗製乱造されてしまったわけです。

とまぁこんな具合で、それはそれは強烈な呪いに支配されていたわけです。
他にもパッケージやNodeのバージョンが古いとかとか、むしろ問題しかないんですが、些末な問題に過ぎません。そう思い込むことにします。

投入されたありったけの薬湯


スタジオジブリ公式より−オオトリ様の入浴[1:1]
さて、ここまで見て「触りたくないな」と思ったそこのあなた。
全くもって正常です。
自分で書いていてなんですが、私だって嫌です。
(書き直しませんか?とかボソッとこぼしてたのは内緒)

オクサレ様がお越しになったあのシーン、千尋は正面からお客様の応対を試みました。
一方、リファクタ開始時の私は、「自分が連れ込んだオクサレ様から逃げ回る」みたいなことになっていました。
その精神性には魅力も成長もありません。
豚とオクサレ様の追いかけっこなど、見る価値もありません。
もしこんな主人公なら、あの作品の興行収入は1億分の1にも満たなかったことでしょう。

しかし幸いにも今回のリファクタ、私はモブAでしかありません。
このあと颯爽と主人公が登場し、オクサレ様に取り込まれつつあった私に、ありったけの薬湯を投入してくださったのです!
※いずれ、本作の主人公とも言える超優秀なエンジニアが、具体の記事を書いてくれます!お楽しみに!

ここではざっくり、どんな薬湯が投入されたのかを紹介します!

エセアトミックデザインの改善

まずはコンポーネント肥大化の文脈。
そもそも今回コンポーネントが肥大化してしまった理由は、再利用できないコンポーネントが増殖したこと
詳しい内容は個別記事に任せますが、

<SchoolNameField />
<TeacherNameField />

みたいな使われ方をしている状況。
当たり前ですが、アトミックデザインの観点からは

<TextField value={schoolName} setValue={setSchoolName}>
<TextField value={teacherName} setValue={setTeacherName} />

が正しいですよね。
こんな修正をひたすら実施しました。

StoryBookの導入

そして、コンポーネントの再利用が前提となる正道に戻ったことで、StoryBookの導入ができるようになりました!
今回は

  • ほぼAtomは作り直し
  • Moleculeもすべて再利用前提
  • Organismのみ、再利用しないことを許容

みたいな方向性となったので、それぞれ単体でのレビューをやりやすく、コンポーネント駆動開発(CDD)を明確化する狙いがあります。
知らんけど。(ここも主人公となるエンジニアに是非書いていただきたいポイントです。)
実際この方針は、今回レベルの更新だと結構刺さった印象。
特に再利用可能なコンポーネントの設計と整合性を保つための視覚的なドキュメンテーションとしての機能が持たせられたのが大きいように感じました。
まぁ、実は一緒に導入しているMUIとの間でとんでもない事故が発生するんですが、これも個別記事で!

APIとの通信の効率化

ここ、マジで神ポイントでした。
今まではAxiosでゴリゴリ手書きしていたんですが、3種の神器が導入されたことにより、開発効率が爆上がりしました。
その伝説の武器とは…

  1. react-hook-form + yup m9(^Д^)
  2. aspida (と aspidaSWR)
  3. openapi2aspida

本当に全部お手製の丸腰だった人間からすると、react-hook-form + yupだけでも革命レベルです。
そして、更に凄かったのがaspida
この辺はAPIとの通信をよしなにやってくれるツールです。
導入したことによって、Loading等の管理をする必要がなくなったのが本当に偉い!

さらにさらに、openapi2aspidaがこれまた凄い!
コイツはなんと、aspidaで通信するエンドポイントの型を、APIで記述したopenapi形式のファイルから自動で生成してくれるスグレモノ!
今までの2つが「火縄銃」「アサルトライフル」ぐらいだとすると、「巡航ミサイル」ぐらいの威力があります(今はGitHub Copilotとか言う核兵器があるからね)。
これにより、aspidaでありもしないエンドポイントにアクセスすることがなくなったのはもちろん、APIの返却値を格納するStoreの型指定に適用することで、Storeへ格納方法が定まり、必要なデータの取り出しが非常に簡単になりました!
まぁ、API側のopenapi系ファイルが手書きなので、そこが間違ってるなんて問題も起きますが、そこは追々、ということで。
ここも個別記事出ます!

ディレクトリ構造の変更

アトミックデザインやStoreの運用がヘドロ溜まりだったのはお話したとおり。
して、その運用で発生したくっさいヘドロはディレクトリ構造にも溢れてくるんですね。
ちなみに、リファクタ前は気がついていませんでした。
そのレベルではなかったので(いつもの)。
臭い部屋で暮らすことで、自室の匂いが認識できなくなる、的な。
…ちょっと換気してくる。

して、その中身をば。
ざっくり申し上げると、ありとあらゆるファイルから、ありとあらゆるストアやコンポーネントが利用されていたんですね。
外部人材用フォルダに入っているラベルが、学校の先生向けのフォルダから使われていたり。
その他にも色々と…あれ?事故とか起こってないよな……??
−−そうして不安になった投稿者は、考えるのをやめた。

ディレクトリ構造変更後は、だいぶスッキリした印象。
特に、リソースベースのディレクトリ構造にしたのがインパクト強かった印象ですね。
別領域の話だけど、Feature FirstかLayer Firstかってやつ??
で、Feature Firstがイケてるって話かな。
例の如く、真に受けてはいけない(戒め)。

ともかく、一つのリソースに対して、APIリクエストを投げるファイル(custom hook)も一つ、storeファイルも1つ、ページもリソース下のURLに合わせて保存。
この形になることで、別リソースのフォルダから利用することを避ける実装のルール化が視覚的に示されたのは、今後の開発に良い影響を与えたと思います。
これも個別記事が出る、ハズ!

不要なstoreの駆逐

「Store(あいつ)ら……駆逐してやる! この世から……一匹残らず!」
↑嘘です、流石に使います。
ただ、アトミックデザインを運用していく意志決定との関係上、Storeへ頼る項目にルールを設け、最小限の使用に留める方向に。
特に、元々はFormのFieldでもAtomを利用しており、これがFieldとStoreの密結合化→再利用性の大幅な低下を招いているという課題につながっていたんですね。
こういった思いつきに近い使い方を辞め、あるべきではないものを少しずつ剥がしていった結果、本当に必要な役割だけが残ったと考えています。
最終的に、StoreはAPIのデータのみを管理することを基本となり、先に示したAspidaの導入などと合わせて、圧倒的に見通しがよく、使いやすいStore環境が実現されました!
どちらかというと、アトミックデザインの運用やフォーム実装方法のルール化などで、自然とこうなった、というイメージが近いかもしれません。

元の世界への帰還


スタジオジブリ公式より−元の世界へ戻る千尋[1:2]
その他にも、様々な薬湯が投入されました。
その旅路では、対応内容の変更や、開発方針の変更などなど、紆余曲折もありました。
が、既にこの記事も6700文字を超え…6700!?それは嘘(ry
ともかく流石に長くなりすぎました。
足掛け1年近く、十数万行に及んだリファクタ冒険譚の仔細は、個別の記事に任せます。

こうしてようやく、リポジトリは当初理想とされていた清い姿を取り戻し、呪いをかけられていた私も無事人間に戻ることができ(?)、くぐってしまった奇妙なトンネルは、姿かたちを変えていました。
そしていつものようにまた、開発は目指す未来へ向けて、ロードマップを進んで行くのです。
めでたしめでたし。
…どう?映画化できそう??

さて、結論です。
相当パワーのかかる作業にはなりましたが、書けたリソースを上回るメリットを享受できたと思います。
具体的には以下でしょうか?

  • 設計や自動生成等の整備・実装方法ルール化に伴う、開発体験・開発速度の向上
  • 無駄なコードの削減による、見通しの大幅な向上
  • 見通しがよくなったことによる、バグの軽減
  • より優秀な方とリファクタを行うことによる、自身の学習(やっちゃ〜♪)

特に見通しの向上は、その過程の実益が非常に大きく、各種ライブラリの導入を始めとする今後の実装箇所の実質的な減少などが、今後の開発に大いに良い影響を与えてくれると確信するところ。
このあとはまた、大きなトピックの開発が始まります。
今回のリファクタリングが、強い追い風となってくれることは言うまでもありません。

そして何より、今後は2柱の優秀なエンジニアの方々による詳細記事が控えています
こんなやつが生成したおどろおろどしい特急呪物を、一体どのような魔法でもって浄化したのか。
純粋な感想も聞きたいですよね。
また今回はフロントの話でしたが、APIでもなんやかんやありました。
このあたりのお話も結構面白いものが上がると思いますので、楽しみにお待ちください!

最後に結論。
毎回ながら、私は周囲の環境に恵まれました。
今回も優秀なエンジニアのお二人のご指導があったからこそ、ここまでのリファクタリングを走りきれたんだと思います。
ま、ご指導に従っただけなので、大したことしてないんですけどネ☆

苦しいときの神頼み。できる御仁に拝み倒せば勝てるってことやな!
いやー、詳細記事、楽しみだね〜(他人事)

脚注
  1. ※無駄な脚注です。
    いまってジブリが公式で画像提供してくれてるんだね。
    1920x1038っぽいからPC壁紙にはちょっと小さいかも
    https://www.ghibli.jp/works/chihiro/ ↩︎ ↩︎ ↩︎

Discussion