電通総研 テックブログ

電通総研が運営する技術ブログ

フロントエンド依存ライブラリのバージョンアップ戦略

こんにちは、ISID デジタルビジネス推進室の浦本です。好きな食べ物は生八ツ橋です。
本記事は 電通国際情報サービス Advent Calendar 2021 の24日目の記事です。

はじめに

皆さんは、npmでインストールした依存パッケージを定期的に更新されていますか?

「面倒だから更新していない。。。」
「フロントエンドはそのままでも大丈夫でしょ?」

と思って放置されている方もいらっしゃるかもしれません。

しかーし!!

更新せずに放置していると、古い依存パッケージの脆弱性を突かれてしまったり、アプリのコード体系が古くなり開発に支障が出たりします。

そこで本日は、面倒になりがちな依存パッケージのバージョン更新を円滑に行うための戦略を書きたいと思います。

【用語について】 本記事では、依存パッケージを利用する開発プロジェクトを単に「アプリ」と呼称します。

戦略

以下より詳細をご説明します。

dependencies と devDependencies の違いを意識する

お悩み:「色々とパッケージを追加したけれど、どれが重要か分からなくなってきた。」

package.json内の dependencies にはプロダクション・コードに影響する依存パッケージ名を列挙し、 devDependencies にはプロダクション・コードに影響しない依存パッケージ名(ビルドツールなど)を列挙します。

【例】

{
  "name": "my-app",
  "version": "1.0.0",
  "private": true,
  "dependencies": {
    "hogehoge": "^2.1.0",
    "fugafuga": "^3.4.2"
  },
  "devDependencies": {
    "piyopiyo": "^6.7.0"
  }
}

依存パッケージ名を両者に振り分けておくことで、更新作業の際に、どのパッケージがより重要であるか容易に把握できます。 また、脆弱性情報の有無を確認するための npm audit コマンドでは --production オプションを指定することで dependencies のみを調査対象とすることもできます。

【振り分け方法】
npm install <パッケージ名> コマンドにて依存パッケージを追加する際に、下記のオプションで指定します。

## dependenciesに追加 (オプション指定不要)
$ npm intall hogehoge

## devDependenciesに追加 (--devまたは-Dオプションを付与)
$ npm install --dev hogehoge

既に依存パッケージを追加済みである場合は、package.jsonを手動で編集して対象パッケージ名の行を移動した後に、引数なしの npm install コマンドを実行すれば大丈夫です。

更新サイクルをチーム内で明確に決める

お悩み:「依存パッケージ、最後に更新したのはいつだろう(遠い目)。」

フロントエンドの依存パッケージは非常に速いペースで機能拡張されていきますので、定期的に更新するという意識を持たないと、ついつい利用バージョンが古いままになってしまいます。

そこで下記のように、いつ(どのような頻度で)更新を行うかを明確化することが大切です。

  1. 臨時のセキュリティ更新
    • 週に1回など短い間隔で依存パッケージの脆弱性の有無をチェックし、脆弱性があればピンポイントで更新する。これによりアプリをセキュアに保つ。
    • npm audit --production コマンドにて dependencies の依存パッケージに対し既知の脆弱性がないかチェックし、影響度に応じてパッケージを更新する。または、 GitHub Dependabot などの自動チェックサービスを用いる。
  2. 定期的な一括更新
    • 3カ月に1回など具体的な頻度を決めて、依存パッケージを可能な範囲で全て更新する。これによりコードの陳腐化を防ぐ。
    • npm outdated コマンドにて新しいバージョンの存在チェックを一括で行い、影響調査・更新を実施する。

動作に影響を与える可能性が高い依存パッケージを把握する

お悩み:「package.json内の記載順にパッケージ更新可否を調査し始めたが、何だか効率が悪い。」

以下に属する依存パッケージは芋づる式で他のパッケージの動作に影響を与えるため、優先的に更新の検討を行う事をオススメします。

  • TypeScript
    • 最近はTypeScriptの型定義を提供するライブラリが多くありますが、型定義よりもTypeScriptのバージョンの方が古いとコンパイルエラーになる場合があるため、真っ先に更新可否を調査したいところです。
  • webpackなどのモジュール・バンドラ、およびプラグイン
    • 例えばwebpackの場合、基本的に複数のloader系プラグインを導入して使うことになるため、webpackとプラグイン間のバージョン相性が悪いとビルドに失敗します。当然、各loader系プラグインの作者はバラバラなので、最適なバージョンの組み合わせは自分で探る必要が出てきます。まさにパズルであり、場合によっては更新を断念する事もあります。こちらも優先的に調査したいところです。
  • VueやReactなどのフレームワーク、およびプラグイン
    • こちらは説明不要ですね!フレームワークの更新内容はアプリの実装コード全体に影響してしまうので、早めに調査しましょう。

まとめると、以下の優先順位で影響を調査していけば、手戻りを防ぐことが出来ます。

  1. npm audit --production コマンドにて脆弱性が表示されたパッケージ
  2. 本節で触れた、動作に影響を与える可能性が高い依存パッケージ
  3. 残りの dependencies
  4. 残りの devDependencies

【Tips】
プラグイン系パッケージが想定している基幹パッケージ(例えば wepack-hogehoge-plugin の作者が想定している webpack)のバージョンを知りたいときは、GitHubリポジトリのリリースノートの確認に加えて、対象パッケージ直下にあるpackage.json内の devDependenciespeerDependencies に書いてある基幹パッケージのバージョンをチェックしましょう。

peerDependencies の方は馴染みがないかもしれませんが、ここには、プラグイン側がアプリの dependencies または devDependencies に要求する基幹パッケージのバージョンが記載されています。 ただし、緩い範囲(例えばメジャーバージョンの指定のみ)でバージョン指定されていることもあるので、あくまで指標として捉えてください。

ビルド結果の差分を確認する

お悩み:「従来まで取得できていた値がundefinedになっちゃった!」

TypeScriptなどのトランスパイラや、webpackなどのモジュール・バンドラのバージョンを更新すると、生成されるJavaScriptコードに差分が出ることがあります。 それが僅かな差分であっても、アプリの一部が機能しなくなる可能性があります。

生成されたコードの差分を確認するためには、以下の手順を踏んでください。

  1. 旧環境と新環境において、モジュール・バンドラのコード圧縮をOFFにしてそれぞれビルドを行う。
    • ※圧縮したままだと比較が困難であるため
  2. JavaScriptコードの差分をdiffツールで確認する。明らかに影響がなければここで終了。
  3. 影響が有りそうな差分を発見した場合、その周辺のキーワードを元に、TypeScriptやwebpackのGitHubソースコードを確認し、コードを生成している箇所を突き止める。その上で差分の意味を理解し、自分のアプリに影響があるかを判断する。

なお、開発環境のNode.jsをメジャーバージョンアップしたときも、念のため、同様の手順で確認しておいた方が良いでしょう。

バージョン選定の理由や注意点を表にまとめる

お悩み:「何らかの理由でこのバージョンに固定したんだけど、思い出せない。」

依存パッケージの定期的な一括更新作業を行うとき、前回の実施時から日が空いてしまうと、前回採用したバージョンの選定理由を忘れてしまう事があります。 それを防ぐために、バージョン選定の理由や注意点を表にまとめておきましょう。未来の自分・自チームへの助けになります。

下表に例を示します。

No. 分類 パッケージ名 バージョン 選定理由や注意点
1 dependencies hogehoge 2.1.0 動作確認済みであるv2系の最新版を利用。v3系はアイコンの見た目が変わるので利用不可。
2 dependencies fugafuga 3.4.2 IE11向けのpolyfillなのでバージョンアップ不要。IE11のサポートを切ったら消す。
...
70 devDependencies piyopiyo 6.7.0 極力最新版を利用するが、あくまでTypeScriptのバージョンに依存。

このような情報さえまとめておけば、更新作業担当者が変わっても差し支えないため、柔軟な作業アサインも可能となります。

コマンド一発で実行可能な単体テストを用意しておく

お悩み:「影響範囲の確認が終わらないよ~!」

依存パッケージの更新時は、アプリの動作に影響がない事を確認する必要があります。

このテスト作業の負荷を少しでも減らすために、フロントエンドの各種実装コードに対して、普段からコマンド一発で実行可能な単体テストを書いておく事が非常に重要です。

最近ではVueやReactなどの普及によりUIコンポーネントを主眼に置いた開発が浸透してきているため、コードの部品化が促進され、比較的テストが作りやすい状況にあると言えます。

ご利用のフレームワークで推奨されているテストライブラリを用い、UIコンポーネントやクラスの単体テストを実装しておきましょう。何千というテストケースが一気に通る様を眺めるのは爽快ですよ!

【テスト実装方法の参考リンク】

おわりに

いかがでしたでしょうか。 本記事の内容が、少しでも読者の方のお役に立つことができたなら幸いです。

最後に戦略図を再掲します。

電通国際情報サービス Advent Calendar 2021 の他の記事もぜひ、お楽しみください。

執筆:@uramoto.kazuhiro、レビュー:@sato.taichiShodoで執筆されました