Slide 1

Slide 1 text

© Findy Inc. ⼤規模モノレポの秩序管理 1 トラックD 熊野 修太 / Shuta Kumano 失速しない多⾔語化フロントエンドの運⽤ 2025.11.16

Slide 2

Slide 2 text

© Findy Inc. 2 @shoota 熊野 修太 [くまの しゅうた] ファインディ株式会社 Team+開発部 Enablementチーム / フロントエンドリード フロントエンドリード、スクラムマスター、エンジニア リングマネージャーを経験 Findy Team+に魅⼒を感じ2023年1⽉よりジョイン 同2023下期に全社MVP受賞 フルリモート in ⻘森

Slide 3

Slide 3 text

© 2024 Findy Inc. 挑戦するエンジニアの プラットフォームをつくる。 ビジョン つくる⼈がもっとかがやけば、 世界はきっと豊かになる。 経営理念 会社概要 会社名 ファインディ株式会社 / Findy Inc. 代表取締役 ⼭⽥ 裕⼀朗 設⽴ 2014 年 2 ⽉ ※ 本格的な事業開始は2016年7⽉ 社員数 297 名 資本⾦ 18 億 5,043 万円 ※ 資本準備⾦含む 住所 東京都品川区大崎1-2-2 アートヴィレッジ大崎セントラルタワー 5階 事業許可番号 13-ユ-308478 サービス ‧ スカウト型リクルーティングサービス「Findy」 ‧ ハイスキルな業務委託エンジニア紹介サービス「Findy Freelance」 ‧ エンジニア組織⽀援SaaS「Findy Team+」 ‧ 開発ツールに特化したレビューサイト「Findy Tools」 投資家 グローバル‧ブレイン、ユナイテッド、SMBCベンチャーキャピタル、KDDI、JA三 井リース、みずほキャピタル、博報堂DYベンチャーズ、Carbide Ventures、等 3

Slide 4

Slide 4 text

© Findy Inc. 4

Slide 5

Slide 5 text

© Findy Inc. Introduction 5

Slide 6

Slide 6 text

© Findy Inc. 6 Introduction テスト、書いてますか?

Slide 7

Slide 7 text

© Findy Inc. 7 Introduction エンジニアリングの主体はテスト https://findy-code.io/engineer-lab/t-wada

Slide 8

Slide 8 text

© Findy Inc. 8 Introduction テスティングトロフィー ⾼品質なフロントエンドソフトウェアを⽀えるテスト構造のフレームワーク ❏ E2Eテスト ❏ Integration Test ❏ Unit Test ❏ 静的解析

Slide 9

Slide 9 text

© Findy Inc. 9 Introduction トヨタ⽣産⽅式 「品質を⼯程で作り込む」 テストに「次⼯程はお客様」の思想をもたらす ❏ E2Eテスト ❏ Integration Test ❏ Unit Test ❏ 静的解析

Slide 10

Slide 10 text

© Findy Inc. https://queue.acm.org/detail.cfm?id=3595878 10 Introduction 継続的デリバリー ビルドプロセスとテストプロセスを短く保て ❏ ⽬安は10分以内 > 我々の考えでは10分がほぼ限界である

Slide 11

Slide 11 text

© Findy Inc. 11 Introduction LeanとDevOpsの科学 注⼒すべきはデプロイとテストの容易性 ❏ ハイパフォーマンスな組織を作るうえで重要 > テストの⼤半を、統合環境を⽤意せずに実施できる > アプリケーションを、それが依存するほかのアプリ ケーションやサービスから独⽴した形で、デプロイま たはリリースできる

Slide 12

Slide 12 text

© Findy Inc. 12 Introduction つまり、 ❏ 階層化されたテストによってソフトウェアの品質を担保しなさい ❏ それぞれのテスト階層は独⽴させ、閉じた状態で品質を作り込みなさい ❏ すべてのテストは10分以内で終わらせなさい ❏ テストの容易性を保ち、独⽴したデプロイを実現しなさい _(´ཀ`」 ∠)_

Slide 13

Slide 13 text

© Findy Inc. 13 Introduction テスト、書いてますか? つらくない?

Slide 14

Slide 14 text

© Findy Inc. 14 Introduction 余談 テストに厳しいサバンナの王者、作者は古川さん

Slide 15

Slide 15 text

© Findy Inc. Findy Team+ フロントエンドの リポジトリ構造 15

Slide 16

Slide 16 text

© Findy Inc. 16 技術スタック ❏ graphql-codegen / Apollo client ❏ eslint / prettier ❏ Storybook ❏ Playwright (E2E) ❏ i18next / react-i18next ❏ React / TypeScript ❏ Vite / Vitest リポジトリ構造

Slide 17

Slide 17 text

© Findy Inc. 17 技術スタック ❏ graphql-codegen / Apollo client ❏ eslint / prettier ❏ Storybook ❏ Playwright (E2E) ❏ i18next / react-i18next ❏ React / TypeScript ❏ Vite / Vitest with リポジトリ構造

Slide 18

Slide 18 text

© Findy Inc. 18 技術スタック リポジトリ構造

Slide 19

Slide 19 text

© Findy Inc. 19 Nxの機能と恩恵 1. Code generatorの標準装備 2. モノレポ内の依存関係の⾃動検出と変更検知 3. モノレポ内の依存関係の制約と管理 4. コマンド結果のキャッシュ 5. ライブラリマイグレーション リポジトリ構造 https://tech.findy.co.jp/entry/2024/08/05/090000

Slide 20

Slide 20 text

© Findy Inc. 20 Nxの機能と恩恵 1. Code generatorの標準装備 2. モノレポ内の依存関係の⾃動検出と変更検知 3. モノレポ内の依存関係の制約と管理 4. コマンド結果のキャッシュ 5. ライブラリマイグレーション リポジトリ構造 https://tech.findy.co.jp/entry/2024/08/05/090000

Slide 21

Slide 21 text

© Findy Inc. ❏ モノレポ内の依存関係の⾃動検出と変更検知 ❏ 実際にimportしているモジュールの依存関係を解析 ❏ nx affected : 影響のあるもののみにコマンド実⾏ 21 Nxの機能と恩恵 リポジトリ構造

Slide 22

Slide 22 text

© Findy Inc. ❏ モノレポ内の依存関係の⾃動検出と変更検知 ❏ 実際にimportしているモジュールの依存関係を解析 ❏ nx affected : 影響のあるもののみにコマンド実⾏ 22 Nxの機能と恩恵 リポジトリ構造

Slide 23

Slide 23 text

© Findy Inc. ❏ モノレポ内の依存関係の⾃動検出と変更検知 ❏ 実際にimportしているモジュールの依存関係を解析 ❏ nx affected : 影響のあるもののみにコマンド実⾏ 23 Nxの機能と恩恵 リポジトリ構造

Slide 24

Slide 24 text

© Findy Inc. 24 Nxの機能と恩恵 リポジトリ構造 ❏ モノレポ内の依存関係の制約と管理 ❏ Nx独⾃のeslint ruleを採⽤することで実現 ❏ moduleにtagを指定し、tag間の依存可否をeslint ruleに記述する。 例1) feature から ui には依存できるが、 ui から feature は依存できない 例1) feature 同⼠では依存できない UI shared feature feature feature app app

Slide 25

Slide 25 text

© Findy Inc. moduleの役割や責務を分離(設計)して、 実際に影響のあるモジュール(実体)のみのテストやビルドを⾏う ❏ モノレポ内の依存関係の⾃動検出と変更検知 ❏ モノレポ内の依存関係の制約と管理  25 Nxの機能と恩恵 リポジトリ構造 = 実体 = 設計 CI/CDの省⼒化

Slide 26

Slide 26 text

© Findy Inc. ❏ モノレポのもつmoduleが125個 ❏ 機能(画⾯)moduleが112 個 ❏ UI やデザインを内包したComponent moduleが3個 ❏ 通信関連、ドメイン共通定義、その他ワークアラウンドが 7個 ❏ ユーティリティ関数関連が 2 個 ❏ 多⾔語化モジュールが 1個 26 現時点のコード規模 リポジトリ構造

Slide 27

Slide 27 text

© Findy Inc. ❏ TypeScriptコードとしては60万⾏ ❏ 多⾔語化のため、JSONファイルが7.4万⾏ 27 現時点のコード規模 リポジトリ構造

Slide 28

Slide 28 text

© Findy Inc. ❏ 平均のPull Request数 :23.8 /day ❏ 平均のcommit数:72.7 /day 28 コード変更量 リポジトリ構造

Slide 29

Slide 29 text

© Findy Inc. 多⾔語化モジュールの構築 29

Slide 30

Slide 30 text

© Findy Inc. 30 多⾔語化プロジェクト 多⾔語化モジュールの構築 ❏ 多⾔語化の同時並列作業&確認作業に全振り ❏ Pull Request数が当時⽐で2.5倍以上 ❏ 3ヶ⽉弱で3000以上のセンテンスの対象抽出と⽇英翻訳処理が完了

Slide 31

Slide 31 text

© Findy Inc. 31 初期の翻訳アーキテクチャ 多⾔語化モジュールの構築 ❏ i18nモジュールが全ての翻訳リソースを持つ ❏ 翻訳リソースはモジュール名ごとに名前空間とファイルを分離 ❏ 機能モジュールごとにファイル分割されているため変更範囲が理解しやすい ❏ i18nモジュールが⼀元管理することで翻訳リソースの管理がシンプル

Slide 32

Slide 32 text

© Findy Inc. 凝集度の7段階 ❏ 機能的凝集(Functional Cohesion) ❏ 1つの明確な機能のみにまとまっている ❏ 逐次的凝集(Sequential Cohesion) ❏ ⼀⽅の出⼒がもう⼀⽅の⼊⼒になる。メソッドチェーン。 ❏ 通信的凝集(Communicational Cohesion) ❏ 同じデータを扱う部分を集めている ❏ ⼿続き的凝集(Procedural Cohesion) ❏ 時間的凝集(Temporal Cohesion) ❏ 論理的凝集(Logical Cohesion) ❏ 偶発的凝集(Coincidental Cohesion) 32 多⾔語化モジュールの構築 アーキテクチャと凝集度

Slide 33

Slide 33 text

© Findy Inc. 33 多⾔語化モジュールの構築 レガシーコードからの脱却 > ⾼品質のコードは凝集性が⾼い > ⾼品質のコードは疎結合である > ⾼品質のコードはカプセル化されている

Slide 34

Slide 34 text

© Findy Inc. 32章 凝集 34 多⾔語化モジュールの構築 Tidy First? > 結合した要素は同じ親要素の⼦要素同⼠であるべきだ。 > これが凝集(Cohesion)の1つめの意味合いだ。

Slide 35

Slide 35 text

© Findy Inc. ❏ 1つの明確な機能(翻訳処理)のみを持つ ❏ 他の機能と疎結合で、カプセル化されている ❏ 結合した要素(⾔語ごとの翻訳ファイル)は同じ親要素の⼦要素 35 初期アーキテクチャの評価 多⾔語化モジュールの構築 妥当な設計と判断した

Slide 36

Slide 36 text

© Findy Inc. 36 初期アーキテクチャの問題点 - CI速度の劣化 多⾔語化モジュールの構築 ❏ i18n moduleの変更頻度が想定よりも多い ❏ 辞書jsonの変更がi18n moduleの変更として認知される(考慮もれ) ❏ 機能変更の多くが辞書とセットなので、i18nが定常的に影響範囲になる nx affected の理解不⾜ 変更単位

Slide 37

Slide 37 text

© Findy Inc. 37 初期アーキテクチャの問題点 - CI速度の劣化 多⾔語化モジュールの構築 ❏ i18n moduleに依存が集中 ❏ ほぼすべてのmoduleがi18nの影響範囲になった ❏ i18nの変更が発⽣するとほぼすべてのmoduleのCIが必要 nx affected が無⼒に

Slide 38

Slide 38 text

© Findy Inc. ❏ 急激な機能追加で毎⽉、複数の新しいmoduleが増え、 影響範囲と変更頻度が同時に成⻑ 38 初期アーキテクチャの問題点 - CI速度の劣化 多⾔語化モジュールの構築 moduleが 増える CI対象が 増える 変更頻度が 増える 実際には影響がないのに、 ⼤量のテストを⾼頻度に実⾏

Slide 39

Slide 39 text

© Findy Inc. 39 初期アーキテクチャの問題点 - CI速度の劣化 多⾔語化モジュールの構築 我々の考えでは10分がほぼ限界である ❏ CI時間 約7分から13分以上に ❏ デプロイ 約30分、多いときで40分以上

Slide 40

Slide 40 text

© Findy Inc. 40 初期アーキテクチャの問題点 - ビルドサイズの肥⼤化 多⾔語化モジュールの構築 ❏ 他のmoduleと同様、i18n moduleは独⽴したモジュールとしてビルド ❏ i18nクライアント+翻訳リソース(JSON) ❏ どのSPA画⾯を開いても数MBの翻訳モジュールをロード アプリケーションを、それが依存するほかのアプリケーション やサービスから独⽴した形で、デプロイまたはリリースできる ‧すでに6000以上のセンテンスが登録済み ‧各センテンスが2⾔語で登録されており、3⾔語⽬の適⽤を⽬指してい る

Slide 41

Slide 41 text

© Findy Inc. 41 多⾔語化モジュールの構築 どうしてこうなった How come?

Slide 42

Slide 42 text

© Findy Inc. 42 多⾔語化モジュールの構築 しかも サポート⾔語増加予定 Global展開 拡⼤中!

Slide 43

Slide 43 text

© Findy Inc. 多⾔語化のリアーキテクチャ 43

Slide 44

Slide 44 text

© Findy Inc. 凝集度の7段階(の良い⽅) ❏ 機能的凝集(Functional Cohesion) ❏ 1つの明確な機能のみにまとまっている ❏ 逐次的凝集(Sequential Cohesion) ❏ ⼀⽅の出⼒がもう⼀⽅の⼊⼒になる。メソッドチェーン。 ❏ 通信的凝集(Communicational Cohesion) ❏ 同じデータを扱う部分を集めている 44 凝集度の設計を⾒直し 多⾔語化のリアーキテクチャ

Slide 45

Slide 45 text

© Findy Inc. 凝集度の7段階(の良い⽅) ❏ 機能的凝集(Functional Cohesion) ❏ 1つの明確な機能のみにまとまっている ❏ 逐次的凝集(Sequential Cohesion) ❏ ⼀⽅の出⼒がもう⼀⽅の⼊⼒になる。メソッドチェーン。 ❏ 通信的凝集(Communicational Cohesion) ❏ 同じデータを扱う部分を集めている 45 凝集度の設計を⾒直し 多⾔語化のリアーキテクチャ 通信(graphql)モジュールと同じデザインパターンが 適⽤できるのでは?

Slide 46

Slide 46 text

© Findy Inc. graphql モジュールの設計 ❏ 実⾏インスタンスはSingletonでmoduleにカプセル化 ❏ 各々のmoduleがDocument(静的リソース)を内包する ❏ Documentを利⽤する⼿段(Provider)をアプリケーションルートがもつ 46 凝集度の設計を⾒直し 多⾔語化のリアーキテクチャ

Slide 47

Slide 47 text

© Findy Inc. i18nモジュールの設計草案 ❏ 実⾏インスタンスはSingletonでmoduleにカプセル化 ❏ 各々のmoduleが翻訳⽂字列(静的リソース)を内包する ❏ リソースを利⽤する⼿段(Provider)をアプリケーションルートがもつ 47 凝集度の設計を⾒直し 多⾔語化のリアーキテクチャ

Slide 48

Slide 48 text

© Findy Inc. i18nモジュールの設計草案 ❏ module内に翻訳リソースが閉じるのでPull Requestの変更がmodule内に閉じる ❏ 変更が閉じることで機能moduleの増加は無関係になり、翻訳リソースも機能単位でビルドされる ❏ AppsはRouteingなど全体を知る必要があるのでもともと機能moduleには依存している ❏ リソースを利⽤する⼿段をAppsから提供できれば実現できる 48 凝集度の設計を⾒直し 多⾔語化のリアーキテクチャ いけそう 変更単位

Slide 49

Slide 49 text

© Findy Inc. 翻訳リソースを動的にロードする仕組みの作成 ❏ ライブラリにはi18n clientインスタンスを切り替える仕組み(Provider)はあった ❏ 画⾯ごとに切り替えるというより翻訳処理の設定を特定条件で切り替えるのが⽬的 ❏ ユーザーが画⾯を開くとその機能moduleのもつ辞書をロードさせたい ❏ 辞書⾃体はclientがキャッシュするのでReact Providerは不要 49 リアーキテクチャ 多⾔語化のリアーキテクチャ

Slide 50

Slide 50 text

© Findy Inc. 翻訳リソースを動的にロードする仕組みの作成 50 リアーキテクチャ 多⾔語化のリアーキテクチャ import { TeamContainer, translateResources } from '@feature/team'; export const TeamPage = () => { const { params } = useParams(); return ( ; ); }; 機能のRoot Component 翻訳リソースを動的にロードして管理する 翻訳リソースとその名前空間を受ける feature moduleで名前解決に利⽤

Slide 51

Slide 51 text

© Findy Inc. ❏ それぞれの辞書リソースを分散配置 ❏ 全ページを翻訳管理Componentでラップ ❏ 機能のRoot Componentと共に翻訳データをi18n clientに辞書登録 51 リアーキテクチャ 多⾔語化のリアーキテクチャ

Slide 52

Slide 52 text

© Findy Inc. ● keyの型をfeature moduleごとに定義しなおし ● 実体と同期しているので、keyの増減とあわせて Union型が持つ数が増減する ● module : 名前空間 : 型定義 = 1 : 1 : 1 型定義の再構築 ❏ 翻訳センテンスを識別するkeyを各featureで型に変換 ❏ コード補完 & 存在しないkeyは型エラー 52 リアーキテクチャ 多⾔語化のリアーキテクチャ /** team/i18n.d.ts */ declare module 'i18next' { interface CustomTypeOptions { resources: { team: (typeof resources)['en']; }; } } export const TeamLabel = () => { const { t } = useTranslation('team'); return {t('teamMembers')}; }; 名前空間の指定 keyの指定

Slide 53

Slide 53 text

© Findy Inc. 共通moduleの翻訳配置 ❏ UI や共有機能など、複数の機能moduleが利⽤するmoduleの辞書 ❏ これらは分散配置の対象から外し、i18n moduleに持たせる 53 リアーキテクチャ 多⾔語化のリアーキテクチャ export const SubmitButton = ( { label }: { label?: string }) => { const { t } = useTranslation('ui'); const defaultLabel = useTranslation('submit'); return {label ?? defaultLabel}; };

Slide 54

Slide 54 text

© Findy Inc. コードベースとCI時間⽐較 リアーキテクチャ 多⾔語化のリアーキテクチャ 54 Before After Now TypeScript 353,578 ⾏ 552,539 ⾏ 613,432 ⾏ JSON 51,694 ⾏ 66,888⾏ 74,210 ⾏ 対応⾔語 2 3 3 +α 平均CI時間 13分 7分 6〜7分 ❏ コードベースが1.5倍以上でCI時間が約50% ❏ その後もCI時間7分の⽔準を維持 ❏ デプロイは変更範囲によって変動。4〜15分、稀に20分弱。

Slide 55

Slide 55 text

© Findy Inc. 結論 ❏ 凝集の設計判断が間違っていた ❏ moduleがどのように利⽤されるかで凝集設計をすべきだった 55 リアーキテクチャ 多⾔語化のリアーキテクチャ 結合した要素は同じ親要素の⼦要素同⼠であるべきだ。 これが凝集(Cohesion)の1つめの意味合いだ。 凝集の2つめの意味合いは、結合していない要素は別の場所 に移すべきということだ。

Slide 56

Slide 56 text

© Findy Inc. リアーキテクチャ 多⾔語化のリアーキテクチャ 56 失速しないフロントエンド

Slide 57

Slide 57 text

© Findy Inc. そのほかの取り組みや課題 57

Slide 58

Slide 58 text

© Findy Inc. ❏ CI時間を1つの指標としてmodule 設計を⾒直し ❏ 画⾯レイアウトを共有モジュールから分離 ❏ 共有モジュールの分割 ❏ 変更頻度と成熟度 ❏ UI モジュールの分割 ❏ プリミティブとセマンティックの分割 ❏ Atomic Designではない 58 その他の取り組み その他の取り組みや課題 https://tech.findy.co.jp/entry/2025/06/30/070000

Slide 59

Slide 59 text

© Findy Inc. ❏ 巨⼤なStorybookの運⽤ ❏ すべてのUIを提供するサーバー ❏ 全⾔語の全センテンス = 全翻訳リソースが必要 ❏ VRT [Visual Regression Test] ❏ Storybookベースで実施していたが、上記理由で厳しい状況 ❏ Vitest v4の toMatchScreeenshot に期待 ❏ ESLint ❏ モノレポ対応が弱く、バージョンアップ (v10.x.x)に期待 ❏ --flag v10_config_lookup_from_file モノレポサポートが弱いエコシステムに課題 59 その他の取り組みや課題

Slide 60

Slide 60 text

© Findy Inc. まとめ 60

Slide 61

Slide 61 text

© Findy Inc. ❏ CIをショートカット ❏ 依存関係の⾃動検出 ❏ nx affected [command] で必要なテストを実⾏する ❏ モジュールの依存関係の設計と管理 ❏ Nx独⾃のeslint rule ❏ 責務をeslintのtagで表現 Nxによる効率化と管理 61 まとめ PullRequestのCI時間は10分以内

Slide 62

Slide 62 text

© Findy Inc. moduleと凝集度設計 62 まとめ ❏ 機能的凝集(Functional Cohesion) ❏ 1つの明確な機能のみにまとまっている ❏ 通信的凝集(Communicational Cohesion) ❏ 同じデータを扱う部分を集めている 機能的凝集 通信的凝集

Slide 63

Slide 63 text

© Findy Inc. 63 まとめ Nxはmoduleそれぞれが全体の与える影響度を教えてくれる CI時間を指標として定量的に凝集を評価する ❏ CIが遅い = 凝集の設計が悪い ❏ 凝集の設計が悪い = 結合の⽅法が悪い ❏ 結合の⽅法が悪い = 与えている責務に誤解がある

Slide 64

Slide 64 text

© Findy Inc. ⼤規模なフロント開発や運⽤に興味をもたれましたら、              ぜひお気軽に @shootaまで! 64 @shoota @shoota Follow me ♥ ⼤規模モノレポの秩序管理 失速しない多⾔語化フロントエンドの運⽤