トレタ開発者ブログ

飲食店向け予約/顧客台帳サービス「トレタ」、モバイルオーダー「トレタO/X」などを運営するトレタの開発メンバーによるブログです。

フロントエンド開発環境スタートセット2024秋

こんにちは、トレタ VPoEの北川です。

今回は弊社でフロントエンドアプリケーションを新しく構築する際の開発環境として、何のライブラリを入れるかという開発環境初期セットを紹介しようと思います。

Web Framework / CSS Framework / Tesing Framework / Linter / Formatter、それぞれ定番で使うデファクトが大体ありましたが、近年では新しいライブラリも登場したので、2024年現在・最新版を、今回は直近で作られた実際のリポジトリを例にご紹介します。 今回紹介するリポジトリのアプリケーションはtoB向けの管理画面のアプリケーションで、特質した部分も特にない一般的なWebアプリケーションです。

それでは早速、package.jsonの内容はを見ていきましょう。

"dependencies": {
    "next": "14.2.13",
    "react": "18.3.1",
    "react-dom": "18.3.1"
},
"devDependencies": {
    "@biomejs/biome": "1.9.2",
    "@types/node": "20.16.9",
    "@types/react": "18.3.9",
    "@types/react-dom": "18.3.0",
    "@vitejs/plugin-react": "4.3.1",
    "@vitest/coverage-v8": "2.1.1",
    "eslint": "9.11.1",
    "eslint-plugin-import-access": "2.2.2",
    "jsdom": "25.0.1",
    "knip": "5.30.5",
    "postcss": "8.4.47",
    "tailwindcss": "3.4.13",
    "typescript": "5.6.2",
    "typescript-eslint": "8.7.0",
    "vitest": "2.1.1"
},

Web Framework

以前に弊社で利用している技術スタックについて紹介しましたが、フロントエンドのフレームワークにはNext.js / Reactを利用しています。 それぞれのバージョンは最新で、Next.jsはAppRouterの使用を前提としています。

nextjs.org

弊社内の既存のNext.jsを利用しているアプリケーションでも、PagesRouterからAppRouterへの移行は少しずつ進めており、AppRouter化の進め方の詳細は先日の弊社イベントで紹介された技術顧問の奥野さんのスライドにも細かく説明されているので、ぜひご参考にしてください。

speakerdeck.com

CSS Framework

CSSフレームワークについては、以前はEmotionを使うことが多かったのですが、Next.jsのAppRouter化に伴いEmotionではRSC(ReactServerComponent)に対応していないため、TailwindCSS を利用しています。

RSCに対応したCSSフレームワークの選定については、決定打があまりなかったため軽量さやシェア、あとは別で進めているデザインシステムとの親和性を考慮してTailwindCSSを選んでいます。

tailwindcss.com

Lint

LinterやFormatterについては以前はEslintPrettierを主に使っていましたが、最近ではBiome に移行する流れがでてきました。

Biomeの良さは実行速度の速さです。実際にCIで実行しているEslintのリントに比べて実行時間は約1/3になりました。

BiomieはPrettierとほぼ互換があるのと、Lintルールも一通り対応しています。Eslintのプラグインは対応していないため、一部のプラグインを使うためにEslintも残して併用していますが、Eslint単独ですべてのルールをチェックするよりもCIの待ち時間が大幅に短くなるメリットがあります。

biomejs.dev

Test

テストツールも以前はJest を主に使っていましたが、現在ではVitest に移行しています。こちらもBiomeと同様に既存資産からの移行がしやすく、実行速度が格段に早くなっています。

vitest.dev

Utility

こちらは必須ではないですが便利ライブラリとしてよく使用しているのがKnip です。KnipはExportsなどを解析して使われていない不要なファイルを抽出して削除してくれるライブラリです。使われているかわからないようなファイルを見つけ出し安全に削除できるため、リファクタリングに重宝します。他のプロダクトではでデータベースの乗り換えなど大規模なリアーキテクトがあったので、その際の旧実装のデッドコードを全て除去するのにも活躍しました。

knip.dev

さいごに

いかがだったでしょうか。少し前まではPrettier などはどのリポジトリにも入れていましたが、それも新しいライブラリに置き換わりより快適な開発環境に移り変わっています。新規プロダクトを作る以外にも、既存のアプリケーションの見直しにぜひご活用ください。

技術顧問からのひとこと

今回の環境構築において、Prettierがレギュラーから外れたことには驚きがありました。Prettierは、その完成度の高さから、永続的に利用され続けるだろうと思われていたフォーマッタですが、時代の流れを感じます。このように、数年前には当たり前だったライブラリ構成も、年月とともにトレンドが移り変わることがよくあります。

弊社では、ただ流行を追うのではなく、安定性やCIでの処理時間なども考慮して環境構築を行っています。特に、Node.jsで実装されていた処理がRustやGoで開発されたライブラリに移行していく流れには、時代の変化を強く感じます。今後もJavaScript以外のエコシステムがさらに発展していくことに興趣が尽きません。

フロントエンドチームのリリースサイクルとブランチ運用

こんにちは、トレタ VPoEの北川です。

カジュアル面談などでよく聞かれる質問として「開発プロセスはアジャイルですか?」という質問とあわせて「リリースサイクルはどれくらいですか?」という質問を受けることがあります。

そこで今回は弊社のフロントエンドチームでのリリースサイクルとそれに対応するブランチ運用を例にして紹介しようと思います。

リリーストレインによる定期リリースサイクル

弊社のモバイルオーダー・トレタO/Xのオーダーアプリチームのリリースサイクルは週1回となっています。毎週水曜日の定期リリースという形でいわゆるリリーストレイン(ReleaseTrain)の手法となっています。

背景としては、リリース前に必ずQAによる動作確認・品質確認を行なっているという点があります。また、リリース前には利用店へのリリース内容の告知準備などもあります。そのため、開発してすぐにリリースではなくリリース日までに一定のリードタイムを設けています。関係する人が複数いるため、リリースの曜日が固定されているとでコミュニケーションは円滑になります。

ブランチ運用としては以下の図のようになります。

エンジニアは随時開発した機能はdevelopブランチへマージしていきます。毎週水曜にコードフリーズを行い、そこまでのdevelpブランチに入っている差分をpreviewブランチにマージします。previewブランチは主にQA作業を行うブランチです。ここでQAを行い問題がなければ次の水曜日にpreviewブランチをmainブランチへマージしてリリースします。

リリーストレインのデメリット

この運用におけるデメリットは、開発完了したものがリリースされるまでに1週間以上空くという点で、単純にデリバリーが遅くなります。そのため、機能追加や運用がある程度落ちついて開発が安定しているフェーズに向いています。

もう一つデメリットとしては緊急のバグ修正(Hotfix)が多いとブランチが煩雑になるという点です。リリースサイクルが短ければHotfixとせずに次のリリースタイミングに含めればよいものが、上記の周期では修正したものがリリースされるまでに最低でも1週間以上かかるため、mainブランチからHotfixブランチを切るケースが多くなってしまいます。

大規模な機能追加などを行なった後などに起こりがちで、その場合はリリースサイクルは保ちつつコードフリーズ期間を縮めるといった調整を行うことで対応しています。

トランクベースによる不定期サイクル

上記のオーダーアプリのブランチはいわゆるGitflowの手法ですが、同じトレタO/Xの開発チームの中でも管理画面アプリのチームはトランクベースでの開発プロセスをとっているのでそちらも紹介します。

トランクベースでのリリースプロセス

トランクベースの場合はエンジニアは常にmainブランチに対してマージしていきます。ただし上記の背景にもある通り、リリース前にQAを行うためmainブランチにマージされたタイミングではリリースは行われずにリリースタグを打った時点でリリースを行うようにしています。

弊社のフロントエンドのアプリケーションはVercelでホスティングをしているため、こちらもmainブランチでの自動デプロイはせずにPromoteを使ってデプロイを行っています。

QAを行う環境としてはstagingブランチを使い検証環境を用意しています。こちらはmainブランチにマージが行われたタイミングでstagingブランチにも自動で差分を取り込むようにしているため、常にmainとstagingのコードベースは同じ状態になっています。

リリースサイクルとしては不定期です。数日おきにリリースする時もあれば数週間空く場合もあり、その時の開発している機能の粒度に依存します。

トランクベース採用の背景

トランクベースにしている理由としては、オーダーアプリチームに比べてエンジニアが多かったため、featureブランチからdevelopブランチへマージする際にコンフリクトが多発していたことです。業務委託のエンジニアの出入りも多く運用ルールとして保つのも困難であったため、各自でfeatureブランチは作らずに常にmainブランチにマージし続けるという運用に切り替えました。

常にmainブランチに入れていくとリリースタイミングをずらしたい場合も発生するので、その際にはFeatureFlagを設けてリリースはするが機能は公開しない制御を行なっています。FeatureFlagの実装面についてはこちらの記事をご参照ください。

さいごに

今回2つのチームのリリースサイクルとブランチ運用を紹介しましたが、わりと流動的に変更しています。その時のプロダクトのフェーズやチームの体制に適した運用を常に模索して、少しずつ調整したり時にはガラッと変えたりしています。今回紹介したパターンが読者の参考になれば幸いです。

FeatureFlagの運用パターン -トレタのテックトーク

こんにちは、トレタ VPoEの北川です。 9月に入りましたが残暑が厳しすぎます…

テックトークとは

隔週に一回開催し、当番の発表者が最近の気になる技術情報であったり、業務する中での技術的な学びを社内に発信する場です。 全体30分の前半はLT(ライトニングトーク)、後半はその内容について参加者内で議論、という形式で行なっています。

今回のテックトーク

今回の発表は自分、北川の担当でした。

先日行なったエンジニアイベントでの質問の中にFeatureFlagをどう運用しているのか、という質問をいくつかいただいたので、今回の発表では弊社内でのFeatureFlagの使い方について発表しました。

FeatureFlagと一口にいってもその用途・使い方は様々なので、まずはそれらを整理しながら社内のいろいろなプロジェクト内での使われ方を分類して紹介しています。

FeatureFlagとは

まずFeatureFlag(フィーチャーフラグ)とは、特定の機能を動的にオン・オフするための仕組みです。アジャイル開発や継続的デリバリー(Continuous Delivery)のプラクティスとして、頻繁に機能をリリースしながらシステムの安定性を保つために、Facebook、Googleなどの企業で利用されてきた手法です。

FeatureFlagを使うユースケース

FeatureFlagを使う目的は様々です。いろいろなユースケース、目的を達成するための手段としてFeatureFlagが利用されます。一般的な利用目的を以下にまとめてみました。

  • 段階的リリース(Gradual Rollout) 新機能を一部のユーザーにのみ提供し、問題がないことを確認しながら徐々に全ユーザーに展開する。これにより、システムの安定性を保ちつつ、フィードバックを受けて調整することが可能です。

  • A/Bテスト 異なるバージョンの機能をユーザーごとに切り替えて提供し、どちらがより効果的かを比較するテストに利用できます。例えば、異なるUIデザインを一部のユーザーに試し、コンバージョン率を測定する場合などです。

  • カナリアリリース(Canary Release) 新機能をまず小規模なユーザーグループに提供し、そのパフォーマンスや安定性を観察して問題がないことを確認した後、全ユーザーにリリースする手法です。

  • リスク軽減 重要な機能を導入する際に、万が一不具合が発生した場合でも、即座にその機能を無効化できるようにして、システム全体に悪影響を与えるリスクを軽減します。

  • 特定のユーザー層への限定機能提供 プレミアムユーザーやベータテスターなど、特定のユーザー層だけに新しい機能を提供することができます。これにより、ユーザーに応じたカスタマイズされた体験を提供できます。

  • デプロイとリリースの分離 新機能のコードをあらかじめデプロイしておき、特定のタイミングで機能を有効化することで、デプロイとリリースを分離し、リリースのタイミングをより柔軟に管理することができます。

FeatureFlagの導入パターン

次に、特定の目的にFeatureFlagを使って解決しようとした際に、FeatureFlagの導入パターンも複数あります。こちらも一般的なパターンを以下にまとめてみました。

それぞれのパターンにはPros&Consがあり、どのパターンが適しているかは目的やサービスの規模、システムの非機能要件などによって変わってきます。

1. コード埋め込みパターン

概要

FeatureFlagのロジックをコード内に直接埋め込むパターンです。条件分岐を使用して、特定の機能を有効化または無効化します。

特徴

  • シンプルで導入が容易。
  • 開発者が直接コード内でフラグを制御できる。
  • 設定の変更にはコードの修正と再デプロイが必要。
  • 設定はコード内にあるため、外部依存がない。
  • 大規模なシステムではフラグが増えると管理が複雑になる。

ユースケース

  • 小規模なプロジェクトや、一度しか使用しない一時的なフラグ。

2. 環境変数パターン

概要

FeatureFlagの状態を環境変数として管理するパターンです。環境ごとに異なる値を設定でき、コードから環境変数を参照して機能を制御します。

特徴

  • デプロイ時に設定を変更可能。
  • 環境ごとに異なる設定を簡単に持てる(開発環境、本番環境など)。
  • 一時的なフラグなら良いが、長期間のフラグ管理には向いていない。

ユースケース

  • デプロイごとに簡単に設定を変更する必要がある場合。
  • 環境に応じて異なる設定を適用したい場合。

3. データベース連携パターン

概要

FeatureFlagの状態をデータベースに保存し、アプリケーションがその情報を参照してフラグを評価するパターンです。フラグの状態はデータベースから動的に取得されます。

特徴

  • フラグの状態を永続化できる。
  • フラグの状態変更が即座に反映され、アプリケーションの再デプロイが不要。
  • データベースに保存するため、設定変更の柔軟性が高い。
  • フラグの変更履歴や状態の追跡が可能。
  • 高度なフィーチャーフラグの管理が可能(例: ユーザーごとのカスタマイズ)。
  • データベースのパフォーマンスに依存するため、アクセス頻度が高い場合は最適化が必要。

ユースケース

  • ユーザーごとの個別設定や、複雑なフラグ管理が必要な大規模なアプリケーション。
  • フィーチャーフラグの状態や履歴を保持したい場合。

4. 分離パターン

概要

FeatureFlagを管理するコンポーネントを、アプリケーションコードとは別に管理するパターンです。設定ファイルや専用のコンフィグ管理システムを使ってフラグを管理します。

特徴

  • 設定ファイルを用いてフラグの状態を管理。
  • フラグはアプリケーションの外部に保存され、アプリケーションはその状態を参照して機能を制御。
  • コードベースから設定を切り離せるため、変更が柔軟。
  • 変更が即座に反映される。
  • 複数のアプリケーションやサービス間で統一したフラグ管理が可能。
  • 外部設定を管理するためのインフラが必要。
  • 複雑な設定管理が必要になることがある。

ユースケース

  • 大規模なシステムで、複数のアプリケーションやサービス間で統一的なFeatureFlag管理が必要な場合。
  • 設定変更を頻繁に行いたいが、アプリケーションの再デプロイを避けたい場合。

5. 外部サービスパターン

概要

FeatureFlag管理の外部サービス(LaunchDarkly、Flagsmith、Split.ioなど)を利用するパターンです。フラグの状態は外部サービス上で管理され、アプリケーションはAPIを通じてその状態を取得します。

特徴

  • 外部サービスがFeatureFlagの状態を一元管理。
  • 各種プラットフォームでフラグを統合管理可能。
  • フラグ管理に特化したツールを使用するため、スケーラビリティが高い。
  • リアルタイムでフラグの変更が反映され、ユーザーごとの柔軟な制御が可能。
  • 分析ツールやA/Bテスト機能が組み込まれていることが多い。
  • 外部サービスへの依存が生じ、サービス障害や遅延が発生するリスクがある。
  • コストがかかる(特に大規模なアプリケーションでは料金が高くなることがある)。

ユースケース

  • 大規模なマイクロサービスアーキテクチャや、複数のプラットフォームでFeatureFlagを統一的に管理する必要がある場合。
  • FeatureFlagの運用管理や分析を外部に委託したい場合

トレタでのFeatureFlagを使っている実例

さて、弊社でのFeatureFlagの導入事例についての紹介です。

上記でまとめたパターンごとに紹介していきます。

コード埋め込みパターンの例

{
  "USE_CALENDAR": true,
  "IS_FULLSCREEN": false,
}

弊社では設定画面のプロダクトにてコード埋め込みパターンでのFeatureFlag運用を行っています。 FeatureFlagを使う目的としては、クライアントアプリ向けのAPIとのその設定画面のリリースタイミングをずらすために利用しています。

新機能を出す際に、まずはAPIを先行してリリースし、利用するクライアントアプリ側が対応の後に設定画面をリリース、という流れとなります。 切り替えを行うのはデプロイを伴っても構わないため、一番簡易な手段としてコード埋め込みでFeatureFlagを扱っています。

環境変数パターンの例

export const isExtraColorPattern = (organizationId: OrganizationId): boolean => {
  const organizationIdsEnv = process.env.EXTRA_COLOR_PATTERN_ORGANIZATION_IDS;
  if (!organizationIdsEnv) {
    throw new ProcessEnvNotFoundError(500, "EXTRA_COLOR_PATTERN environment not set");
  }
  const organizationIds = organizationIdsEnv.split(",");
  return organizationIds.includes(organizationId);
};

上記のコードは、組織が特定のIDの場合にのみ特別なカラーパターンを利用できるようにするための判定式です。 利用可能な組織IDをEXTRA_COLOR_PATTERN_ORGANIZATION_IDS というkeyの環境変数に設定しています。 環境変数なので文字列しか設定できないため、カンマ区切りで配列を表現しています。

PoCであったり個社向けの対応に利用する用途なので、値の変更頻度は高くないため値の変更時にデプロイが必要になっても問題にはなりません。

データベース連携型の例

type Item {
  id: ID!
  name: Name!
  createdAt: Timestamp!
  updatedAt: Timestamp!
  ...
  metadata: KV  
}

データベースにFeatureFlagを設定する場合にはmetadata といったようなプロパティ名のKey-Value型のフィールドを用意して利用しています。 metadata のkeyの定義はDBスキーマ上は行っていないので必要応じてFeatureFlagのkeyと値を自由に追加・削除できます。

こちらは値の変更時にデプロイが不要なため、上記の環境変数パターンに比べて変更頻度が高い場合に利用しています

分離型の例

{
  "organiaztionIds": [
    "clilazd3oidfx0mm2akbmf1dj3",
    "cl3d3jpoklpx9jlkmlass31smpo"
 }

こちらは分離型として、静的ファイルに分離するという手法を取りました。

Githubで専用リポジトリを作り、/public にjsonファイルを配置しており、Vercelでプロジェクトを配信することで運用しています。 構成としてはあまり手間がかからないのと、Githubで管理されているため変更履歴が残るのとブランチを切り替えることで本番用と開発用で分けられるのが良いところです。

用途としては、リプレイスを行なったシステムに対しての移行をフラグ管理しています。 移行が終わっていない組織のIDをリストに入れておき、該当する組織は各アプリケーションではリプレイス前のシステムを利用します。 移行が済んだ組織はリストから外していき、リストが空になったらこの仕組みごと削除する、という流れになります。

複数のアプリケーションがその組織の移行状況を確認する必要があったため特定のアプリケーションではない場所で管理したかったのと、 最終的には不要になるため削除する時に後が残らず、また一覧性もあることが望ましかったため、この手法を取りました。

FeatureFlagを消すタイミング

FeatureFlagがそもそも永続化されるか一時的なものかは、パターンとあわせて利用するユースケースによります。 コード埋め込みパターンや環境変数パターンは比較的に短期利用で採用することが多いため、基本的には不要になった時点で削除を行います。

ただ、実際のところでゆうと消し忘れることは多いです。 フラグをオンにするタイミングで消すといいのですが、大体のケースはオンにして様子見てから消す、とするとそのまま放置されることが多いです。 この辺は運用ルールとして定めても守れらないと意味がないので、自動的に守られるような仕組みが必要ですが今のところそこまで至っていないのが現状です。

まとめ

長くなったので上記の内容のまとめです。 私見でのマトリクスを作ったので、FeatureFlagを使う際の判断の参考にぜひご活用ください。

  • FeatureFlagには、複数のユースケースやそれに適したアプローチがある。
  • どのアプローチでFeatureFlagを実装するかはユースケースに応じて、以下の観点で選択するとよい。
    • 外部依存度
      • レイテンシ
      • 障害点
    • 変更容易性
      • 変更にデプロイが必要でも問題ないか
    • 設定柔軟性
      • 環境にごとに切り替えたいか
      • 特定の店舗への設定か
    • 管理のしやすさ
      • 一覧性
      • 消しやすさ
コード埋め込み 環境変数型 データベース連携 分離 外部サービス
外部依存度 × ×
変更容易性 × ×
環境ごとの切り替え
設定柔軟性 ×
管理のしやすさ × ×
コスト ×
© Toreta, Inc.

Powered by Hatena Blog