🧶

27のプロジェクトでYarn 1系から4系にバージョンアップした話

に公開

背景

AIの力を借りてLancersの27のフロントエンドプロジェクトをYarn v1からv4に一気にバージョンアップしたお話。バージョンアップ手順やAIを使って何をしたのか、直面した課題と対処、良かった点をまとめた。

なぜ今Yarn v4に?

数年前からYarn v1という遺物をバージョンアップしたいという動きはあった。しかし、明確に何かに困っているというわけではなく、規模の大きなフロントエンド基盤の変更になることから先送りにされてきた。弊社のフロントエンド賢者たちも「やりたいし、いつかやらなきゃだけど今それに見合った効果得られないよね」という判断だった。
しかし、AIの時代がやってきて状況は大きく変化した。コストの部分がほぼ0になった今ならできるかもしれない。Yarnは、v1(Classic)からv2以降(Berry)のバージョンアップで多くの新機能や改善が行われているため得られるものは大きい。そう思い、週2~3時間の空いた時間で少しずつ進めたところ最終的に 1ヶ月半で完了することとなった。

初期の状態

Lancers のフロントエンドは、単一の巨大なモノレポプロジェクトではなく、frontend/ 配下に 27 のフロントエンドプロジェクト(機能ごとの独立した React 等アプリ)が並ぶ構成になっている。そのため、それぞれで別々のパッケージファイル(package.jsonnode_modules)を持ち、バラバラのバージョン管理がされている。
また、社内デザインシステムのリポジトリが存在し、それぞれのモジュールから取得している。

移行プロセス

1. 社内デザインシステム(別リポジトリ)を先に Yarn 4 に
プロダクト側が社内デザインシステムを Git 参照で取得しているため、バージョンや、依存の取り方・ビルドの仕方・インストール直後の処理は、使うパッケージマネージャに依存している。
それらに対応できるようAIに壁打ちを行い、デザインシステム内の複数のモジュール(コンポーネント・デザイントークン・Lintカスタムルール等)のパス構成やプロダクト側での利用方法を検討した。
その後、最新リリースのみYarn v4にし、この時点ではプロダクト側から呼び出さない状態にした。

2. 優先順位を決める
プロダクト側の27のモジュールのバージョンアップ優先順位を決めた。
開発時期も7年前のものから最近のものまで、構造や利用パッケージも様々。Reactプロジェクトか、viteを利用しているか、デザインシステムを利用しているか、リスクの大きさ、などの視点でバージョンアップ優先順位を決定。この時、基準だけは人間が決め、残り作業(優先度決め、issue化)はAIに任せた。

3. 最初の1プロジェクトバージョンアップで型を作る
まずは一つのプロジェクトでバージョンアップのPullRequestをAIに作成してもらった。その結果から必要な作業や懸念事項を人間がチェックする。
ここで、CIworkflowなど共通で利用しているものは、Yarn v1, v4どちらにも対応できるようにしておく。

4. 型が固まったあと、残りを並行で進めた
ここまでできたら一気に10個程度のプロジェクトのバージョンアップPullRequestを作成してもらう。この時worktree上で作業してもらい並行で進める。問題が起きた際には基本的にAIに自己解決してもらい、その後仕様書の書き換えまでするように指示した。
しかし作られたPullRequestを見てみると、CIの失敗を解決できていなかったり、局所的に対応が必要だったりなものも数回発生し、そこは人間が介入して修正指示をした。

課題と解決策

パスやexportの仕様変更・厳密化

デザインシステムのバージョンアップを行なっていた時の話。Yarn v1 利用時には、プロダクト側でデザインシステムを取得する際、デザインシステムリポジトリ全体を取得し、ファイルパスを直接指定して利用していた。

// Yarn v1 では exports がなくてもファイルシステムを直接たどって動いていた
@use '@lancers/design_guideline/src/styles/forward.scss' as *;

しかし、Yarn v4 は Node.js の仕様に厳密に従うモジュール解決を行うようになっており、サブパスの解決には package.jsonexports フィールドが必要になった。exports がない場合、./styles./lint/... といったサブパスが解決できずエラーが発生してしまった。

そのため package.json に以下の2つのフィールドを追加した。

exports(公開エントリポイントの定義)

パッケージ利用側がどのパスで import できるかを明示する。ESM / CJS の条件分岐(import / require)や型定義(types)も同時に定義できる。

"exports": {
  ".": {
    "types": "./dist/index.d.ts",
    "import": "./dist/es.js",
    "require": "./dist/umd.js"
  },
  "./styles": "./dist/styles/forward.scss",
  "./lint/eslint-plugin-design-guideline": "./lint/eslint-plugin-design-guideline/dist/index.js",
  "..."
}

files(パッケージングの対象ファイル)

files フィールドは、パッケージ化する際に含めるファイルを明示するための設定である。今回のように Git 参照で利用する構成では、files を定義しておくことで dist/lint/ など必要な配布物だけを含め、src/ や設定ファイルを含めないようにしやすくなる。

"files": ["dist", "README.md", "lint", "scripts"]
フィールド 役割 Yarn v1 Yarn v4
exports 「どう使わせるか」を定義 なくてもファイルシステムで直接解決できた サブパスの解決に必須
files 「何を配布するか」を定義 Git参照でも問題が表面化しにくかった files を明示すると配布対象を意図どおりに固定しやすい

インストール後のビルドスクリプト実行制御

Yarn v1 から Berry系 へ移行すると、依存パッケージのビルドスクリプト(postinstall など)の扱いが変わる。今回利用していた社内デザインシステムは、すでにビルド済みの成果物を含んでいたため、インストール時に改めてビルドが走るとかえってエラーの原因になるケースがあった。

この問題は dependenciesMetabuilt: false を指定することで解決した。

"dependenciesMeta": {
  "@lancers/design_guideline": {
    "built": false
  }
}

built: false を設定することで、そのパッケージに対して不要なビルド処理を避けられる。今回のように、すでに成果物を含んで配布しているパッケージに対して指定しておくことで、余計な処理をスキップでき、インストールの安定性と速度が向上した。

テストがないことによる動作確認負荷増

ほとんどのプロジェクトにおいて、フロントエンドテスト(E2Eテスト結合テストVRTコンポーネントテストUTなど)が書かれておらず、動作確認を手動でせざるを得なかった。

AIの進歩により Playwright を使ったテストやスクリーンショット撮影の自動化などもしやすくなっている。
しかしこれを活用すると同時に、開発する上で大事なのは、何をどこまで保証するかを決め、適切にテストを書くことだと強く感じた。レビューコストが高まる中、テストの価値は上がっていると思う。

良かった点

圧倒的低コストでのバージョンアップ

人間が関わったのは方針決定と問題が発生した時の解決指示、動作確認程度。1行もコードを書かないのは今の時代当たり前になりつつあるが、コードだけでなくissuePullRequest作成、進捗管理、手順書作成などなど大部分をAIに任せて進められたのは、非常に良かった。乗るしかない、このビッグウェーブに。
自分のメインのプロジェクトを持ちつつ、隙間時間での作業だったが、認知負荷でパンクすることも一切なく気づいたらできている状態で進めることができた。

Yarn v4化以外の改善も同時に進めることができた

AI主軸で進めていたため余力すらあり、なかなか普段の開発で改善するには負荷の高いフロントエンド基盤の改善も同時に進めることができた。
というのも、AIに指示する際「今回のバージョンアップに関連して改善できる部分があれば提案してね」と添えていた。さらに自分からも普段なんとなく困ってるけど改善まで手が回っていない部分を伝えて可能なら修正のPullRequestを作成するように指示したからである。正直開発組織にとってはこちらの方が恩恵が大きかったかもしれない。以下に今回一緒に改善した内容の一部を記載する。

  • TypeScriptのバージョンアップ
  • スタイルファイル群のビルド速度を20~30倍高速化
  • デザイントークン利用時のパス改善
  • Yarn本体の一本化による約70MBの軽量化
  • 不要ファイルの削除

組織に「AIでできる」を示せた

終わってから思ったのは、基盤の改善や負債解消のような後回しにしがちなタスクも意外とできることを組織に示すことができたことである。この流れの中で弊開発組織ではNode.jsViteTypeScriptStorybookなどさまざまなバージョンアップが動いている。
AIがあれば、そのハードルは実は思ったよりも低いかもよということを皆さんにも伝えたい。

まとめ

Yarn v1からv4への移行は、社内デザインシステム対応やプロダクト側の多様なケースに応じた手順が必要な作業だった。優先順位の整理や調査、issue/PullRequest作成、コーディングといった作業の大部分をAIに任せることで、本業の合間でも現実的な期間で負荷なく完走でき、人間は方針決定と最終判断に集中できた。結果としてYarn v4化以外の改善も同時に前進させることができ、組織に対して「基盤改善や負債解消もAIを使って負荷なく進められる」という実例を残せたことが、技術面以上の収穫だったと感じている。

ランサーズ株式会社

Discussion