HireRoo TechBlog

HireRooの技術ブログ

HireRooのフロント基盤のご紹介

はじめに

初めまして!株式会社ハイヤールーの谷合(@posterkeisuke)です。

HireRoo(ハイヤールー)』とは優秀なエンジニア採用に必須のコーディング試験サービスです。

そんなHireRooテックブログへの初投稿になります。よろしくお願いいたします! 記念すべき初投稿では、今自分が主で担当開発しているフロントエンドの基盤を詳しくご紹介できればと思います。

バックエンドを含むHireRooの含む大まかな基盤に関しては、前回葛岡が投稿したブログをご参照ください。

tech.hireroo.io

フロントエンドの構成詳細

全体構成

HireRooのシステム全体の構成は以下の図のようになっております。 今回は図の上のフロントエンドに関わる部分をご紹介させていただきます。

f:id:poster-keisuke:20210420230200p:plain
図1 HireRoo全体構成図

利用している技術スタック

以下が現在(2021/04/21)HireRooが採用しているフロントエンドの技術スタックになります。(一部割愛しているものもあります。)

概要 利用技術
開発言語 TypeScript
フレームワーク React, Redux
UIライブラリ Material-UI
ビルドツール Webpack
テスト Jest, React Testing Library
Linting ESLint, Prettier
インフラ Firebase Hosting
認証 Firebase Authentication
API grpc-web
デザインツール Figma

それぞれご説明していきます。

フロントエンドのフレームワークとしてはReactを採用しており、状態管理にはReduxを採用しました。 コンポーネントは基本的にはReact-hooksで書かれています。

この規模のアプリへのReduxの導入は賛否両論ありますが、基本的には以下のような状態になり開発スピードが落ちてきたことをきっかけに導入を決意しました。 (このときの背景に関しては、後日別ブログとして記事を書く予定です)

当初はグローバルな値(ユーザーのログイン有無など)ReactのContextAPIを使って管理することによって、機能を実現していましたが次第にそれだけでは不都合が出始めました。

具体例を挙げると、当初は私たちのアプリケーションでは 全てのコンポーネントが共通して参照する "App.tsx" でContextAPIを利用してグローバルな値を管理していました。(ユーザー情報など)ただ、1つのstateを更新しただけですべての子コンポーネントに再レンダリングが走ってしまい、パフォーマンスに問題が起こるようになりました。

また、prop drillingを避けるために、親コンポーネントから子コンポーネントにのみPropsとして値を渡すようなコードが多発するようになったのと、親コンポーネントのstateにデータを管理する設計だったため、リファクタもできず親コンポーネントが肥大化して新機能を追加するの際に非常に時間がかかるようになってしまいました。

このようなこともあってReduxを後から導入することにしました。 (recoilも検討しましたが、公式が本番採用には避けた方がいいと記載されていたので一旦見送りました)

Avoid using React experimental releases in production.

結果そのような状態は解消され親コンポーネントのコードのリファクタができるようになったのと、パフォーマンスは微力ながら向上しました。

フロントエンドの開発言語にはTypeScriptを採用しています。またバックエンドとの通信にはgrpc-webを使用しているためコンポーネントや関数以外にもAPIのリクエストとレスポンスにも型が定義できるようになったため、体感値ですが導入前と比べてデグレが少なく継続的なデリバリーができるようになりました。

UIライブラリとしてはMaterial-UIを利用しています。UIを作成する際は、チームには現状デザイナーがいないため自分がFigmaを使ってUIの構成を考え、それを画面に落とし込むといった形で開発しています。

もともとはゼロベースでデザインしたものをUIに落とし込んでいたのですが、デザインを考えてスタイルを当てて(その間にデザイン自体を見直すことも)とやっているとUIを組み立てるまでにかなり時間がかかってしまうため、基本的にはMaterial-UIベースでデザインを考えて実装するようにしています。 そうすることで画面変更時にもあるものベースで考えることができるので、完成までの時間がかなり短縮されたと思います。 サービスの規模が大きくなり、デザイナーが入社してくれたタイミングでデザインシステムを含めて一新できればと思っておりますが、逆にそれまではそれで十分かなと考えております。

その他各自のエディターにPrettierが設定されており、PRが作成される前に最低限のコードスタイルが整うので全体のコードフォーマットを気にせず開発を進められております。また、リリースはCircleCI経由でFirebase Hostingに自動でデプロイされる様になっております。

(それ以外に関しては特筆することは無いので割愛させていただきます。)

技術選定の背景にある考え方

サービスがビジネスの仮説検証の速度についていけるようにする

仮説検証フェーズのスタートアップではヒアリングや実際にサービスを使ってもらいフィードバックを受けて、数週間後には全く違った形で機能を提供するというようなことがよくあります。 その変更ごとに都度1ヶ月程度掛かってしまうようですと、その分だけビジネスは検証の機会を失ってしまうことになります。また意思決定もそれがあって大胆な変更をためらってしまうかもしれません。

まだサービスの開発を始めてようやく半年ほどですが、HireRooも例外ではなく大小問わず2〜3回ほどシステムの構成が変わるタイミングがありました。

実際に当初開発していた主軸となる機能をごっそり削除する際に、ソースコードに大幅な変更が入りました。サーバーのアーキテクチャはマイクロサービスを採用しており、そうした変更に対してある程度耐性があったのですが、フロントのアプリケーションはそれほど疎結合ではなかったため、デグレが発生してしまったことがあります。 また、スピード優先で型定義を "any" で定義していた部分もサーバーのリクエストが変わった際に見落とされてしまっており、触ってバグが出るまで気づかなかったといったようなことも多々ありました。

よくこのフェーズのスタートアップの初期のプロダクト(大企業の社内新規プロダクトにもよくある)にTypeScriptやテストを導入することによってスピード感が保てないため導入を見送ったというような記事があって当初僕たちもそう思って一定妥協してしまっていました部分もありました。

一方でそういったことを後回しにしていると技術負債としてプロダクトに溜まっていきます。大きな変更があったときなどにその負債が障害となってのしかかり、結果スピードが遅くなってしまうということもあります。その時の出来事から、そういったフェーズでもでできる範囲で堅牢なコードを書くということは重要だと学びました。

それ以降は前述した通りgrpc-webなどを用いてAPIへのレスポンスとリクエストレベルで型を定義するようにしました。 また一部にはテストを書き始めるなど、サービスがビジネスの検証スピードをを妨げないような開発を意識しております。

今後の展望

偉そうなことを書きましたが、正直まだまだやれてないことばかりです。 Redux周りの実装もすべてを置き換えられていなので、昔のコードと混在している部分もあります。テストも基本的なところ以外は書けていないので、今後はカバレッジも気にしながら開発を進めていきたいと思っております。本リリース後のサービス運用までにモニタリングツールを導入したいとも思っております。

が、こうしたやりたいと思うことがたくさんある一方で優先度とリソースの兼ね合いで実現できていないことばかりです。

こうした現状を一緒に改善してくれるフロントエンドエンジニアを大大大募集しております。もし記事をお読みいただき、興味を持ってくださる方が入ればぜひ一緒にサービスを作ってきたいと思っておりますので、こちらのフォームTwitterのDMなどからお声がけください!

さいごに

ここまで読んでいただきありがとうございました。

HireRooは非常にモダンな技術の寄せ集めで設計されています。 もちろんこれらはフロントエンドのみではなく、サーバーサイドアーキテクチャも非常に拘って作られており、これらの技術を通しコーディング試験を支えるクラウドでのコンパイルやコードの自動評価、オンラインIDEなど様々な機能を実現しています。

今後もHireRooの技術発信をこの技術ブログを通して行っていきますので、よろしくお願いいたします!それでは!