2024年|CANARY Web の技術スタック
はじめに
こんにちは。カナリーでソフトウェアエンジニアをしている @yoshi-jr です。
私たちは 【もっといい「当たり前」をつくる】
をミッションに掲げている不動産テックカンパニーです。弊社では、現在下記のプロダクトを運用しています。
- 「Canary」: BtoC の部屋探しポータル(アプリ/Web)
- 「Canary Cloud」: BtoB SaaS(不動産の仲介会社様向けの顧客管理システム)
この記事では、 CANARY のウェブ版(以降 CANARY Web)で利用している技術スタックをまとめています。
この記事を読んでわかること
- CANARY Web について技術スタックの全体像・概要が理解できる
話すこと
- CANARY Web のアーキテクチャ
- CANARY Web で利用しているモジュールとその歴史
話さないこと
- CANARY Web 視点以外でのアーキテクチャ
- 各モジュールの細かい内容
アーキテクチャ
まずは、CANARY Web について全体感がわかるアーキテクチャ図となります。
矢印について、おおよその処理・データの流れと捉えてください。また、このアーキテクチャ図は Web の視点でまとめているためバックエンド・インフラについて簡略化している点があります。
では、このアーキテクチャ図で登場する各モジュールについて説明します。
Firebase
Firebase は Google によって提供されているウェブアプリケーション開発プラットフォームです
CANARY Web では主に Firebase の認証機能を利用しています。 CANARY のアプリと同じサービスを利用することで開発者としては運用負荷が低く、アプリケーションとしてシームレスな体験を提供しやすくなっています。
Google Tag Manager
Google Tag Manager はウェブサイトのタグを簡単に管理・実装できるサービスです。
エンジニアの実装を通さずに分析イベントや広告タグを設定できるので、運用コストが少なく済んでいます。
主に Google Analytics への分析イベントの送信を行なっています。
Statsig
Statsig は AB テストや Feature フラグを実装するためのサービスです。もともと AB テストは Google Optimize を利用していましたが、サービスの終了に伴い Statsig へ移行しました。
カナリーでは基本的に機能を追加・修正する場合は AB テストによる効果検証を行なっており、その AB テストの実装のために利用しています。また、部分的にリリースしたい場合も AB テスト同様に Statsig を利用して公開範囲のコントロールを行なっています。
Sentry
Sentry はエラー監視のサービスです。
Sentry を導入する前は、手動でデバッグしている際に偶然見つけたバグしか検知できませんでした。そのため、何かの修正を入れた後、知らないうちにクラッシュが発生していることがありました。
現在では Sentry を導入して継続的な監視を行なっています。Sentry で本番環境で発生したエラーのグルーピングや通知ができるため、エラーについてすぐに把握と調査ができ、品質向上に役立っています。運用の体制としては、新しくエラーが発生したときには運用担当の人がエラー内容を確認しに行き、すぐに対応するべきかの判断を行なっています。
Datadog
Datadog はクラウドインフラやアプリケーションの監視・分析を提供しているプラットフォームです。
CANARY Web のサーバーは GKE にデプロイされており、クラスターからコンテナー、アプリケーション内のトレースまで、監視は Datadog を利用しています。また、Datadog でダッシュボードやアラートを作成することにより、リリース後のレイテンシの悪化をすぐに検知できたり、急激なリクエスト数の増加に対応できるようしています。
直近では、利用するツールを少なくして認知負荷を下げ、より簡単に運用を行えるようにするため、クライアントのエラー監視も Datadog に置き換えるよう検証を行なっています。
GitHub Actions
GitHub Actions は GitHub が提供している CI・CD ツールです。CANARY Web では以下のようなことを行なっています。
- format / lint / typecheck / test の実行
- dev 環境へのデプロイ
- リリース時の build, deploy, リリース通知の実行
CI 上でチェックを入れることで自動的に不正なコードが検知できており、レビューコストが下がっています。また、デプロイ周りも自動化されることで、手動でデプロイすることによるオペレーションミスやデプロイ方法を覚える必要がなくなるなど運用負荷を下げることができています。
技術スタック
TypeScript
TypeScript は JavaScript のスーパーセットな静的型付け言語です。
立ち上げ時から TypeScript を利用しており、型定義からエラーを検知できていることでレビュー負荷を下げることやクラッシュを防ぐことができています。
開発していく中でクラッシュの原因になってしまったコードの書き方があれば、その都度 TypeScript の設定を見直し、調整を行なって、同じエラーが発生しづらくなるような対策を行なっています。
React
React は UI 開発の JavaScript ライブラリです。コンポーネント単位での UI 開発ができます。
CANARY のアプリは React Native で開発されており、同じ React を使用することでチーム内の開発生産性を向上させています。
Next.js
Next.js は、React に基づく Web 開発用のフレームワークです。Next.js を利用することで簡単にパフォーマンスの高い Web アプリケーションの開発ができます。
CANARY Web では、SEO 対策のためにサーバーサイドレンダリング(SSR)が必要でした。しかし、SSR を実装するための知見がなく 1 から実装するのは時間がかかりすぎてしまうため、フレームワークの Next.js を利用しました。
Next.js は整備されたドキュメンテーションがあり、学習コストを抑えられています。また、多くのユーザーがいるため、Web 上のブログ等も参考にしながら、開発中に発生する問題を素早く解決できています。
SWR
SWR はデータ取得のための React Hooks ライブラリです。
CANARY ではバックエンドで API サーバーを構築しています。クライアントからバックエンドへのデータ取得の際に SWR を利用して、キャッシュ管理を行なっています。
API の状態管理はもともとローカルステートで行われていましたが、冗長な処理が多く、書き方が人によって異なるため可読性が低下していました。この問題を解決するために API データのキャッシュ管理ライブラリとして開発されていた SWR を導入しました。
SWR を導入してからは、ローディングやエラー状態の管理はすべて SWR で行われているため冗長性がなくなっており、可読性も向上しています。また、SWR では無限ローディングや楽観的 UI の実装もフォローされているため、簡単にユーザー体験をよくする実装ができています。
styled-components
styled-components は JS でスタイルを記述できる、CSS in JS ライブラリです。カナリーではすべてのスタイルに styled-components を利用しています。styled-components では CSS と同じような書き方でスタイルを当てることができるため、スタイルを記述するための学習コストを少なくできています。
Storybook
Storybook は UI コンポーネント開発のための開発環境です。
CANARY Web の立ち上げ時には、小さなコンポーネントでストーリーを作成しながら開発を進めていました。しかし、運用の中で、カタログのメンテナンスコストが高まったため、現在ではストーリーの作成をストップしています。
Storybook はデザイナーの方との同期やテストなど、さまざまな場面で活用できる強力なツールです。今後、無理のない運用方法を模索し、再度運用を開始したいと考えています。
ESlint + Prettier
Eslint は JavaScript 内の問題のあるコードを検知するための静的コード解析用ツールです。Prettier でのコードフォーマットと併用して利用しています。
CANARY Web ではレビューコストを下げるために、レビュー時点でのよくある修正はなるべく linter で解決するようにしています。また、コードのフォーマットを揃えることで可読性の向上に取り組んでいます。
Jest
Jest は JavaScript のためのテスティングフレームワークです。テストの実行やコードカバレッジの計測等ができます。
CANARY Web では、ロジックを細分化し、可能な限り単体テストを追加するようにしています。単体テストを追加することで、プログラムの基本的な処理ミスにすぐ気付けるだけでなく、テストの行われている範囲は問題ないことが保証され、バグ修正時間も短縮できています。
現状ではコンポーネントや Hooks に関するテストが不足しているため、より充実したテストを行い、カバレッジを向上させることを目指しています。
Playwight
Playwright はブラウザーテストを実行できるライブラリです。
リリース前に、主要な機能が正常に作動するかを簡単に確認するため利用しています。また、障害対応後に同じような障害が再発しないよう、テストケースの追加も行っています。
現在は、E2E 環境が整備されていないため、CI には E2E テストを含めておらず、必要に応じて手動で実行しています。将来的には E2E 環境を整備し、テストを自動で実行することを計画しています。
設計
ディレクトリ構成
ディレクトリの構造は大まかに以下のようになっています。
src
├── pages # Next.js のルーティング定義
├── api # 生成されたAPI
├── assets # 静的リソース
├── config # 環境変数
├── design-system # 汎用的なUIコンポーネント
├── features # 機能単位のモジュール
├── lib # 汎用的なモジュール
├── screens # 画面単位のコンポーネント
└── variants # ABテストの設定
基本的な方針として、CANARY Web のディレクトリ構成は CANARY のアプリのディレクトリ構成と大きな差がないようにしています。CANARY ではウェブの開発だけでなく、アプリの開発もするため、認知負荷を軽減するためにできるだけ同じ構成にしています。
API
CANARY では Protocol Buffer を利用して API を定義しています。
API と疎通するための TypeScript のモジュールは下記のようなフローで自動生成しています。
共通のスキーマをもとにしているため、型安全にバックエンドとのやり取りができています。それによって、タイポなどのヒューマンエラーによるクラッシュをリリース前のビルド時に防ぐことができています。
おわりに
CANARY の Web についての技術スタックをまとめた記事でした。
「より詳細に話を聞きたい」「自分もこの開発に興味がある」と感じて頂けましたら、カジュアル面談などでお気軽にコンタクトをとっていただければと思います!
Discussion