見出し画像

【入社エントリ】機能開発を止めずにWebフロントエンドを改善

はじめまして、2024年8月にPIVOTにジョインしました、@tawachanです。ジョインしてからそろそろ半年弱経とうとしていますが、簡単な自己紹介とその間に主に取り組んだ、PIVOTでのWebのフロントエンドの改善について書いていこうと思います。


自己紹介

改めまして、@tawachanです。

基本的に小さな会社やチームでソフトウェア開発をしており、フロントエンドを中心にしながらもバックエンドやインフラ側にも携わってきました(最初の最初はOracle DatabaseでPL/SQLを書いたりしていたのでぜんぜん違うことをやるようになりました)。

途中でフリーランスとして仕事をしたり、合間に大学院(政治学)に行ったり、紆余曲折あって、PIVOTにたどり着きました。

「PIVOT」はビジネス映像メディアと銘打っていますが、ビジネス領域に留まらず、政治や社会など、経済やお金に必ずしも直結はしないけれども重要なテーマを幅広く取り扱う珍しいメディアだと思い、その成長の一端に携わりたいと思い、入社いたしました。

本記事のテーマ

自己紹介はそこそこに、本記事では主に私が入社して半年弱の間に行った取り組みについて紹介させていたければと思います。

PIVOTでは、自分が初めてフロントエンドを中心にやってきた人だったので、フロントエンド周りのタスクは基本的に自分が担うことになりました。蓋を開けて見てみると、(歴史的背景により)Reactでありながら、適切なご作法にあまり則り切れておらず負債が少々ある状況でした。

過去の自分の経験から考えても実装効率がだいぶ低く、機能開発、更には事業成長の足枷にもなると思い、ここの改善を入社最初の取り組むべき課題としました。

本記事では、この課題への取り組みを、フロントエンドの開発効率を改善した事例として紹介できればと思います。

中長期的には開発効率を上げるための改善が重要なのはわかりつつも、当然実装すべき機能も数多ある中で改善を進めていくのは、必ずしも容易ではありません。それでもなんとか機能開発を止めない範囲で、できる限りの改善を入れていく一事例として少しでも参考になるところがあれば幸いです。

入社当初のフロントエンドの課題

PIVOTのWebフロントエンドはNext.jsで実装されていて、スタイリングにはSCSSが使われています。これらの技術選定そのものには大きな問題はありませんが、次のような課題がありました。

コンポーネントの責務が不明確

TemplateやComponentなど、何かしらの意味でディレクトリが分かれ、多層的にコンポーネントが分割されているのですが、それぞれの責務が不明確な状態でした、それにより、再利用可能性が低く、認知コストは高いという問題を引き起こしていました。

一見再利用できそうな下層のシンプルな見た目のコンポーネント内で実はAPIを呼び出してその見た目だけを使いたいところで使えなかったり、Propsではなくコンポーネント内部で不必要にContextで値を参照しているため外部から挙動が分かりづらかったり、挙げたらきりがないですが、開発を引き継いだPIVOT社内のエンジニアも何がなんだか…という感じ。

どこで何が行われているのかを把握するだけでも認知コストが高く、修正時に影響範囲を特定しづらい状態でした。また、状況を把握したとしても、あまり共通化ができず、つらみが増していました。

SCSS運用の複雑さ

これは好みの問題でもありますが、SCSSですと、JSXとは別のファイルにスタイルを書くことになるので、ファイルを行き来するのが手間に感じます。またTypeScriptの型の恩恵も受けられないので、実装に注意力が必要です。

さらに、BEMを適用したようなクラス名になっていたので、階層構造を変えるとスタイリング全体を修正する必要が出るなど、かなりコンポーネント指向の開発には不向きなところもあり、複雑度を高めていた要因でした。うまく運用すれば問題ないのかもしれませんが、命名規則も煩雑で保守性が低下していました。

不必要な自前実装

ボタンやモーダル、ドロワーなどの基本的なUIコンポーネントも独自実装しており、アクセシビリティやユーザビリティの標準的な期待値を満たせていない状況でした。

実績のあるUIライブラリを活用することで、これらの基礎的な品質を効率的に担保できたはずですが、基本的なコンポーネントまで独自実装していたことで、開発リソースが分散し、本来注力すべき機能開発に時間を割きづらい状況でもありました。

その他の細かいところ

useMemoで済む計算がuseEffectとsetStateで処理されているなど、Reactのアンチパターンがあったり、無駄に複雑であったり効率的ではない実装が散見されました。挙動を正確に把握するにはコードを実行する必要があり、リファクタリングにも大きなコストがかかる状況でした。

今期実施した改善施策

基本方針:機能開発を止めない

上記の課題を解決するためには、実装自体を大幅に変える必要があります。しかし、全体をまとめてリファクタリング等をすると機能開発にし支障が出るため、あまり現実的な方針ではありませんでした。

そこで、可能な限り抜本的にコードを改善しつつも、並行して機能開発をするために、古いコードベースをまとめてサブディレクトリ(今回はunorganized-compoenents)に移管し、修正すべきファイル群を明確にしました。

その上で、機能開発で新しく実装する箇所や既存の改修箇所を可能な限り新しい方針(後述)に則って実装をしていく、という方針で進めました。ボタンや動画のサムネ表示など、すでに存在するコンポーネントも該当箇所は基本的にすべて実装し直しました。

その基本方針の上で、具体的にどのような変更を今回入れたのか紹介します。

ディレクトリ構成の整理

上記の課題のうち、コンポーネントの責務が不明確だった課題に対処すべく、ディレクトリ構成を整理しました。

まずは、最近流行りのPackage by Featureを導入し、その中でContainer/Presentationalパターンを採用しました。具体的には以下のようなディレクトリ(例)を追加しました。

features/
└── account
    ├── feature-a.container.tsx
    ├── feature-a.presenter.tsx
    ├── feature-a.presenter.stories.tsx
    ├── feature-b.container.tsx
    ├── feature-b.presenter.tsx
    ├── feature-b.presenter.stories.tsx
    ├── account-common.tsx
    └── account.hooks.ts

機能(Feature)ごとに、ディレクトリを分け、その中で大きく、hoge.container.tsxhoge.presenter.tsxを置くようにしました。さらに、機能間で共通で使いそうなコンポーネントやロジックも必要であれば、フラットのこの階層に置くことを想定しています。機能依存のコードは他のところで再利用することも想定されづらく、Package by Layerのように整理する旨味も少ないのでこのようにまとめてしまっています。

ですが、機能横断で共通で使う見た目やロジックは当然あると思うので、そこは依然としてPackage by Layer的に残しているので、適宜棲み分けて配置しています。全体としては以下のとおり(抜粋)です。

├── features
├── components
├── hooks
├── pages
├── services
└── util

簡単に各ディレクトリの棲み分けだけ書いておきますが、こちらは今後実装を進めていく中で要改善ポイントです。


  • /components:機能に依存しない共通の見た目を定義する。主にRadix UIを使った基礎的なコンポーネントを配置する。

  • /hooks:特定の機能に依存しないhooksを配置。機能に依存するhooksはfeatures配下に置く想定。LocalStorageの操作やIntersectionObserver関連などが入ってくる。

  • /pages:Next.jsの仕様。ここは主にfeatures内のContainerを呼び出したり、Page単位の値(pathからの値やQueryParams)を取得、ルーティングなどが責務。

  • /services:フロントエンドで持つドメインロジックはここに集約。

  • /utils:ドメインにも関係ない便利な機能をここに置く。Assertion関連やTime関連の処理など。


上記のいずれかのディレクトリに入っていれば、改善済みのコードベースとして参照できる想定で、実装のたびに可能な限りunorganized-componentsディレクトリを使わないように書き換えていくという方針で進めました。

Tailwind CSS、Radix UI(+ shadcn/ui)の導入

SCSSで運用・開発していることのつらみが上記の課題で触れたとおりであり、ここを刷新したいと考えていました。

以前自分はChakra UIを使っており開発効率がとても高いという肌感はあったのですが、PIVOTはC向けのプロダクトでありデザインにこだわりがあるため、既存でスタイルがあたっているライブラリだとカスタマイズの手間がむしろかかる懸念がありました。

そこで、Headless UIであるRadix UIを使い、相性のいいTailwind CSSをスタイリングとして採用することにしました。また、shadcn/uiというRadix UIとTailwind CSSの組み合わせで事前に用意されているUIコンポーネントの存在も上記の選定の大きな要素でした。こちらを活用させていただくことで、一定程度当たり前品質を担保しやすい仕組みになると考え採用しました。

Chakra UIに比べれば、自前で基礎的なコンポーネントを管理・修正する手間が多少ありますが、shadcn/uiの力を借り、そこからさらにデザインシステムに沿うコンポーネントを作っていくと、結果的にそこまで大きな差はなかったようにも思います。

最初は若干時間はかかるものの、必要なコンポーネントや仕組みが揃ってくると、徐々に開発速度が上がる実感がありました(定量的に計測する仕組みが現状はないので、そこはチームとして要改善)。Tailwind CSSはユーティリティ・ファーストといわれるように、最初に学習コストは多少あるものの、慣れれば用意されたClassを組み合わせればスタイリングができるので、SCSSより効率が高く保守性は高くできるように思いました。

実際に改善施策を進めてみて

自分が入社してからの大きな機能として「ホーム画面の刷新」と「ソーシャルログイン」という2つを実装しました。これらを含め、フロントエンド開発はほとんど自分ひとりで担当しましたが、スケジュール的にも当初の想定通りでリリースすることができました。

特にホーム画面刷新は既存の見た目に大きく関わるところだったので、コードベースも大きく書き換えていく絶好の機能開発だったので、これに着手する前に今回のフロントエンド改善施策を定義できたのはよい意思決定だったと思っています。

「機能開発と並行して進める」ことを至上命題として実際に進めてみましたが、技術的な改善と事業成長は必ずしもトレードオフではないことを改めて実感しました。最初は、ボタンなどの基本的なコンポーネントを拵える必要があるので、あまり速度は出ずに、少々遅れ気味という印象の時もありました。

しかし、後半は再利用できるものも増え開発効率が上がりました。体感としても、従来の方法で書き続けるよりも、実装すべきものが明確になり心理的にもかなり気持ちがよく開発ができる環境になり、また開発のスピードも出せるようになってきました。バランスよく着手すれば、改善のためのコスト分以上に開発効率が改善でき、むしろ機能開発も同じくらいかむしろ早くなる余地すらあるので、今後もよりよくするために改善施策を進めていこうと思います(PIVOTの場合は伸びしろがありすぎただけかもしれませんが)。

今後の展望

この半期で少しずつフロントエンドの改善を進めてきましたが、当たり前水準を達成するにはまだまだ課題が山積している状態です。今後も事業成長と技術的改善のバランスを意識しながら、開発者体験も維持・改善しつつユーザーにとって価値のあるプロダクト提供に努めていく所存です。裁量を持って自分たちが開発しやすい環境を形作っていけるのはとてもいい経験でした。

PIVOTでは、一緒に改善や開発を楽しめる仲間を募集中です(唐突)。興味があれば、ぜひチェックしてみてください。

その他、PIVOTのテックブログはこちらにマガジンとしてまとめているので、こちらもぜひご覧ください。


いいなと思ったら応援しよう!

この記事が参加している募集