Zenn
👷

フレームワークのアップグレード作業を計画的に進めるための手順

に公開
18

Next.jsを14系から15系に、Reactを18系から19系にアップグレードする作業をしていて感じたのが「計画的に進めないと、見通しがつけられなくてしんどいな」ということ。
最初は計画もなく一気に進めようとしていましたが、状況が把握しきれなくなり、情報を整理してから別ブランチでやり直すことになりました。

原因としては、色々なことを試しているうちに、何をしたからうまくいって、逆になぜうまくいかないかが把握できなくなったこと。
そして、レビューに出す前の最終確認がとてもやりにくいと感じましたし、整理されていない状態だとレビュアーの負荷がかなり上がってしまいます。

フレームワークのアップグレード対応に関する具体的なコード例は記事がありますが、計画的な進め方に関する記事は見かけないなと思ったのが、この記事を書いているモチベーションです。
もっといいやり方や追加情報などがあればコメントいただけると嬉しいです。

これ以降に手順と具体的な方法を記載します(できてるとは言ってない)。
前半は計画を練るための作業なので検証用のローカルブランチでコミットします。情報を整理してからブランチを作り直して、後半戦として清書に移ります。

計画フェーズ

1. next buildtscなどでエラーをすべて解消する

アップグレード作業をする前に、現状のコードベースに問題がない状態にしておきます。あるいは、軽微な問題が残っていたとしても、それらをリストアップしておきたいです。
とくにメジャーバージョンには破壊的な変更が含まれているので、既存のコードを修正する必要が出てきます。そのコードに問題が残ったままアップグレードしてしまうと、問題が重なって考えにくくなってしまいます。それに問題を解消するときにも、アップグレードで発生したかどうかは重要な判断材料になります。

ですので、本番用のnext build、開発用のnext dev、TypeScriptの型チェックをするtscなどで現状の問題点を洗い出しておきます。できるなら解消しておきたいですし、軽微な問題点ならリストアップだけでもいいと思います。

2. シナリオテストで表示や動作を確認する

シナリオテストはユーザーがアプリケーションを使用する際に想定される操作や手順をもとに、期待通りに動作するかを確かめるテスト技法です。
next devを実行して、シナリオテストをしていきます。
もしシナリオテストがない場合は、主要なページや操作を想像しながら、確かめたことや、その結果を記録します。チームとしての合意がない結果だとしても、アップグレード作業後のデグレを発見することに利用できます。

あるいは、アップグレード作業前のプレビュー環境と比較できるのであれば、事前のシナリオテストは冗長かもしれません。同じ操作をして同じ結果になったら、デグレは起きていないことになるからです。

テストをするかはプロジェクトの状況に合わせてください。

3. codemodを試してパッケージが正常にアップデートできるか確認する

codemodは抽象構文木(AST)の解析によって、自動的に置換してくれるツールです。
Next.jsにもcodemodが用意されています。

Codemods are transformations that run on your codebase programmatically. This allows a large number of changes to be programmatically applied without having to manually go through every file.
Codemodsは、コードベース上でプログラム的に実行される変換です。これにより、手作業ですべてのファイルに目を通すことなく、プログラム的に多くの変更を適用することができます。

Next.js provides Codemod transformations to help upgrade your Next.js codebase when an API is updated or deprecated.
Next.jsは、APIが更新されたり廃止されたりしたときに、Next.jsのコードベースをアップグレードするためのCodemod変換機能を提供します。
Upgrading: Codemods | Next.js

Next.jsのドキュメントにはnpx @next/codemod@canary upgrade latestのようなコマンドが記載されています。

コードの置換だけでなくパッケージのアップデートもしてくれるので、すぐにアップグレード作業を始めることができます。
ここでは問題を確認したり、置換された内容を確かめるために実行します。。

4. codemodで依存パッケージに問題があった場合は、その対応方法を確認する

パッケージのアップデート作業でつきものなのが、パッケージ同士の依存関係によるエラーです。
codemodを実行すると、多くの場合で依存するパッケージの警告やエラーが出てくると思います。
対応方法は依存しているパッケージもアップデートすることなので、それも調査をしたり、コードを修正する対象になるということです。

まずはログをよく確認して、どのパッケージにどのような問題が起こっているかを記録します。そして、@latestを付けてパッケージをアップデートして、依存関係が解消されることを確認します。

具体的には次のような手順になります。

  1. codemodを実行する
  2. ログを確認して、アップデートが必要なパッケージをリストアップする
  3. package.jsonなどのローカルの変更をリセットする
  4. node_modulesを削除してnpm ciを実行する(不要かもしれないですが、不要な問題を踏まないために)
  5. リストアップしたパッケージを@latestを付けてnpm installする
  6. もう一度codemodを実行する

ここでは、パッケージのアップデートが正常に完了するためのコマンドと、調査や修正が必要になるかもしれないパッケージを一覧が手に入りました。
パッケージの差分はまとめてコミットして、情報をマークダウン形式でまとめておきます。

ちなみにモノレポの場合は、マイクロサービス(それぞれのルート)で一気にパッケージをインストールしたほうが安全です。日を跨いだりすると、バージョンが微妙に更新されてしまい、それがエラーの原因になる可能性があるからです。

5. codemodで発生した差分をリストアップする

codemodを実行すると自動的にコードが置換されます。
置換されたコードは基本的に信じてコミットしてもいいと思います。ただし、'use client';'use client';;のように置換された例もあったので、目視確認は必要そうです。

次に、出てきた差分に対応する仕様の変更を公式ドキュメントのアップグレードガイドから探します。
資格を取得するための勉強で、先に問題集を解いて、わからなかったことを調べるみたいな方法ですね。
変更内容の理解ができたら、それをマークダウン形式でまとめておきます。

あとで参照しやすいように、変更内容ごとにコミットしておきます。

6. tscを実行する

型チェックのエラーが起きていないか確認します。
このあとnext buildも実行するのですが、途中でビルドが止まるとエラーがどれくらい発生しているか把握できなくなります。
tscnext buildのエラー内容はイコールではないですが、大まかな見当をつけることはできると思います。エラー内容やエラーが起きたパスを記録しておきます。簡単にいくつか解消してみるのもいいかもしれません。

7. next buildを実行する

本番用ビルドを実行して、具体的にどのようなエラーが出るか確認します。
アップグレードガイドを見ながら、いくつか解消してもいいと思います。変更内容の理解ができたら、それをマークダウン形式でまとめておきます。

8. その他パッケージの対応状況を検索する

codemodによって依存関係のあるパッケージの問題は解消されましたが、問題として検知されないパッケージがあるかもしれません。
たとえば、RecoilはReact19に対応していませんが、問題として検知されませんでした。最終更新が2023年4月12日で2025年1月2日にアーカイブされたのですが、検知するための仕組みが更新されていなかったのかもしれません(詳しくないので分かりませんが)。

アップデートしていないパッケージを対象に「パッケージ名 React19」のように対応状況を軽く確かめるだけでも、あとで問題が発覚するリスクを軽減できると思います。

9. アップグレードガイドやアップデートしたパッケージのチェンジログを通読する

ここまででアップグレード作業の見通しがついてきたと思います。いくつかの仕様変更にも対応できるようになったかもしれません。
アップグレードガイドでまだ読んでいないところがあるかもしれないので、一通り目を通しておきます。とくに、エラーには出てきていないけれど使っているAPIに関しては重点的に確認をして、ある程度の想定ができると理想的です。

アップデートしたパッケージに関しても、アップグレードガイドやチェンジログを確認しておきます。クリティカルな変更だけでも確認しておきたいですね。

10. ここまでの内容と手順を整理する

ここまでで結構な情報量が手に入ったと思いますが、アップグレード作業の最終チェックに使うことを想定すると、ドキュメントとして整理しておく必要があります。
たとえば、次のような内容になると思います。

  • 基本的な手順
  • 手順ごとの具体的な対応方法や注意点
  • 関連リンク

計画はこれで完了です。今まで作業したブランチは残したまま、新しい清書用のブランチを作成してアップグレード作業を最初から始めましょう。

実作業フェーズ

1. マークダウンで整理した情報をリポジトリ内にコミットする

Cursorを使っている場合も、ClineやDevinのような自律的なAIエージェントを使う場合も、参照する情報で精度に違いが出てきます。
今まで調べて整理をしてきた情報も生成AIに渡せばアップグレード作業の役に立つはずです。

これ以降に出てくる新しい情報も、このファイルに追記していくことで、コンテキストを適切に引き継ぐこともできるようになります。

2. 依存関係のパッケージをアップデートする

ここから本格的にアップグレード作業を始めます。
まずはcodemodを使用する前の依存関係の解消です。アップデートしたらコミットしておきます。

3. codemodを実行する

ここも事前の計画通りに進められます。パッケージの差分はコミットしておきます。

4. codemodで発生したコードの差分をコミットする

事前に差分は確認済みなので、前回と同じように変更内容ごとにコミットしていきます。
目視確認も忘れずに。

5. next buildを実行して、エラーを1つずつ解消する

codemodでは修正できないエラーが複数起きている可能性があるので、1つずつ解消していきます。
エラーの原因が同じファイルがあれば、一緒に検討すると適切な修正方法が見つかりやすいです。コミットもまとめてできるので、レビューをする人からも理解しやすくなります。

そのほか、自動テストなどがあればnext buildと同様に進めます。

6. tscを実行する

next buildのエラーをすべて解消できていたら、ここではエラーが起きないかもしれません。念のため問題が起きていないか確認します。

7. next devを実行して、表示や挙動に問題がないか最終確認する

画面上で操作をしたときを中心に、まだエラーが残っている可能性があります。
「2. シナリオテストで表示や動作を確認する」で使ったシナリオテストを使用して、表示や挙動に問題がないか確認していきます。

8. プルリクエストに情報を整理して、レビュー依頼をする

アップグレード作業のコミット数は多くなりがちで、レビュアーに負担をかけやすいです。
次のような情報を記載しておくと、レビュアーの負荷も下がりそうです。

  • 変更したファイルパスやパッケージ
  • 発生した問題
  • 解決方法
  • 何が確認できれば正常か
  • 補足情報(URLなど)

ここまでしたくない場合は、コミットの粒度を適切に保ちつつ、コミットメッセージを充実させるとよさそうです。

9. Project Rulesを更新する

Cursorであれば.cursor/rulesディレクトリにプロジェクト固有のルールを設定できます
アップデートされてまだ日が経っていない場合、古いAPIの仕様で実装されてしまうことがあります。
たとえば、forwardRefはReact19から非推奨になりましたが、Project Rulesを設定していないと普通に提案されていました。
それ以外にも、アップグレードに伴うプロジェクト固有の暫定対応があるかもしれません。生成AIを使っている場合は、ルールの充実度合いが最終的な生成結果の精度に影響を与えやすいと感じています。

パッケージをアップデートする時に知っておきたいこと

ここからはおまけです。

@latestを指定する理由

依存関係のパッケージをアップデートするときに@latestをバージョン指定の代わりに付けていました。
package.jsonで次のような指定がされている場合にnpm installをすると、最新のバージョンにアップデートできないからです。

  • x.y.z:特定の完全なバージョンに固定する
  • ^x.y.z:メジャーバージョンを固定する
  • ~x.y.z:マイナーバージョンを固定する

パッケージのアップデート時に@latestを指定していないと、1つ前のメジャーバージョンで更新されてしまうといったことが起こる可能性があります。最新のメジャーバージョンがいつでも適切なわけではありませんが、この仕様を把握していないと「最新バージョンにアップデートしたつもりだった」というような勘違いが起きる可能性があるので注意が必要です。。

@latest@canaryなどの意味

distribution tags(dist-tags)と呼ばれる、公開しているパッケージのエイリアスを提供する機能です。
npm-dist-tag | npm Docs

Other than latest, no tag has any special significance to npm itself.
latest以外には、npm自身にとって特別な意味を持つタグはない。

と記載されているように、latest以外のタグはパッケージごとに使用状況が変わるようです。

distribution tagsは次のように考えておくとよさそうです。

  • 本番環境で使う場合
    • @latest:最新の安定バージョンでStableと同義。バージョン指定なしで優先的にインストールされるバージョン。
    • @latest-1のように1つ前の安定版のような指定もできる。
  • 次期リリースのテストをする場合
    • @rc:Release Candidateの略で正式リリース前の最終段階バージョン。問題がなければlatestになる。
    • @next:正式リリース予定のバージョン。新機能を試したいけれど@rcがなかったり、確かめたいバージョンではない場合に使う。
    • @canary:実験的な最新のビルド版。将来的にstableでリリースされる機能を含んでいる。
  • 試験的なバージョンをテストする場合
    • @beta:テスト中のベータ版
    • @alpha:初期段階のアルファ版(@betaより不安定)
    • @experimental:新機能開発のための実験的バージョン。

npm dist-tag ls nextのように実行すると、そのパッケージのdistribution tagsを確認できます。

canary: 15.3.0-canary.10
latest: 15.2.2
next-11: 11.1.4
next-12-2-6: 12.2.6
next-12-3-2: 12.3.4
next-13: 13.5.8
next-14-1: 14.1.1
next-14: 14.2.24
rc: 15.0.0-rc.1

npm view nextのように実行すると、distribution tagsや依存関係にあるパッケージなどが確認できます。

next@15.2.4 | MIT | deps: 7 | versions: 3055
The React Framework
https://nextjs.org

keywords: react, framework, nextjs, web, server, node, front-end, backend, cli, vercel

bin: next

dist
.tarball: https://registry.npmjs.org/next/-/next-15.2.4.tgz
.shasum: e05225e9511df98e3b2edc713e17f4c970bff961
.integrity: sha512-VwL+LAaPSxEkd3lU2xWbgEOtrM8oedmyhBqaVNmgKB+GvZlCy9rgaEc+y2on0wv+l0oSFqLtYD6dcC1eAedUaQ==
.unpackedSize: 111.0 MB

dependencies:
@next/env: 15.2.4           @swc/helpers: 0.5.15        caniuse-lite: ^1.0.30001579 styled-jsx: 5.1.6           
@swc/counter: 0.1.3         busboy: 1.6.0               postcss: 8.4.31             

maintainers:
- rauchg <rauchg@gmail.com>
- timneutkens <timneutkens@icloud.com>
- vercel-release-bot <infra+release@vercel.com>

dist-tags:
canary: 15.3.0-canary.24  next-11: 11.1.4           next-12-3-2: 12.3.7       next-14-1: 14.1.1         rc: 15.0.0-rc.1           
latest: 15.2.4            next-12-2-6: 12.2.6       next-13: 13.5.11          next-14: 14.2.26          

published 5 days ago by vercel-release-bot <infra+release@vercel.com>

npm-view | npm Docs

パッケージの脆弱性の深刻度を判断する方法

こちらの記事がとてもわかりやすかったです。

【npm audit】npm package の脆弱性対応してますか?

ざっくりと次のような理解をしています(ちゃんと検証はできていません)。

  • npm auditを実行するとnpmが管理している脆弱性情報を確認できる
  • 次のように修正方法が提示される
    • fix available via npm audit fix
    • fix available via npm audit fix --force
    • No fix available
  • npm audit fixはnode_modulesを変更せずに対応するもの
  • npm audit fix --forceはメジャーアップデートなどの破壊的な変更が入る可能性がある
  • npm audit fixnpm audit fix --forceで解消できない場合はpackage.jsonoverridesを追加して対応する
    • Next.js15のcodemodでも使用されていた
18
chot Inc. tech blog

Discussion

ログインするとコメントできます