BASEプロダクトチームブログ

ネットショップ作成サービス「BASE ( https://thebase.in )」、ショッピングアプリ「BASE ( https://thebase.in/sp )」のプロダクトチームによるブログです。

社内の静的アセット管理リポジトリのNode.jsのバージョンを上げたら動作が爆速になった話

初めに

こんにちは。フロントエンドエンジニアの竹本です。 入社してそろそろ4ヶ月が経とうとしています。だいぶBASEの開発にも慣れてきました。

この記事では私が社内の静的アセットを管理しているリポジトリ(以降は便宜上static-repositoryと呼びます)のNode.jsのバージョンを12から16にあげたら、webpack dev serverの立ち上がりが約12分から約5秒に短縮できた話を紹介したいと思います。

この作業は業務の隙間時間でやったのですが、どのように取り組んでリリースまで持っていったかをお伝えできればと思います。

結論3行

  • webpack dev serverの立ち上がりが遅かったのはApple シリコン搭載のMacでNode.js 12を動かしていたから。
  • Apple シリコン搭載のMacでNode.js 15未満を動かすと、rosetta経由になり非常に遅くなる。
  • Node.jsのバージョンを上げようとしたら、それなりに大変だった。

話の始まり

事の発端は私がBASEに入社して約2ヶ月ほど経ったときの事でした。その頃私はBASE10周年プロジェクトにアサインされ、10周記念特設サイトの開発・リリースを担当することになりました。と言ってもWebサイト自体の制作自体は別の方が担当されるので、やることといえば成果物に対して既存のビルド処理を通してコンポーネント等を差し込み、リリースに備えて準備を行う程度のものでした。

※ 余談ですが10周記念特設サイトはリリース後、Web Design Clipさんにも取り上げられました。とても素敵な仕上がりになっているので、よければ一度ご覧になってください。

特設ページは基本的にstatic-repositoryで管理されており、静的ページにビルド処理を通して独自のコンポーネントを差し込んだりmetaタグを追加することができるようになっています。

特設サイトのソースコードを受け取った私はそれをstatic-repositoryに移して、webpack dev server起動コマンドのyarn run devを実行、、、しますがローカルサーバーが立ち上がりません。あれ???と思いながら何度か同じコマンドを叩きますが、どうやらローカルサーバーが立ち上がらないわけではなく、立ち上がりが非常に遅くなっているようでした。

static-repositoryは静的アセットを管理するだけのリポジトリなので、メンテナンス頻度はあまり高くなく考えられる原因はいくつかありましたが、この時の私は納期が迫っていましたし、立ち上がりが遅いだけでエラー等は起こってなかったのでとりあえず作業を完了させることにしました。10周記念特設サイトは無事にリリースされ反響も大きく、プロジェクトは大成功でした。

それから2ヶ月ほど経って、BASEのフロントエンドエンジニアが有志で集まって負債解消に取り組むfrontend-yatteikiという集まりに参加させていただくことになり、そこでずっと気になっていたstatic-repository遅い問題に取り組ませてもらうことになりました。

やったこと

まずは現状の調査から始めました。yarn run devを実行してからローカルサーバーが立ち上がるまでの時間を測定したところ平均して12分くらいかかっていました。遅くなっている原因をざっとコードを眺めて探ってみましたが、.node-versionで指定されていたNode.jsのバージョンが12.14.1と非常に低いことが気になりました。

なので雑にNode.jsのバージョンをLTSである16まで上げるところから始めることにしました。.node-versionを16.18.0に書き換えてyarn installを実行すると以下のようなエラーが出力されました。

1 warning and 1 error generated.
make: *** [Release/obj.target/binding/src/binding.o] Error 1
gyp ERR! build error 
gyp ERR! stack Error: `make` failed with exit code: 2
gyp ERR! stack     at ChildProcess.onExit (/Users/XXXXXX/XXX/XXXXXXX/XXXXXX/static-repository/node_modules/node-gyp/lib/build.js:262:23)
gyp ERR! stack     at ChildProcess.emit (node:events:513:28)
gyp ERR! stack     at Process.ChildProcess._handle.onexit (node:internal/child_process:293:12)
gyp ERR! System Darwin 21.6.0
gyp ERR! command "/Users/XXXXXX/.nodenv/versions/16.18.0/bin/node" "/Users/XXXXXX/XXX/XXXXXX/XXXXXX/static-repository/node_modules/node-gyp/bin/node-gyp.js" "rebuild" "--verbose" "--libsass_ext=" "--libsass_cflags=" "--libsass_ldflags=" "--libsass_library="
gyp ERR! cwd /Users/XXXXXX/XXX/XXXXXX/XXXXXX/static-repository/node_modules/node-sass
gyp ERR! node -v v16.18.0

node-sassのビルドでコケているようです。node-sassのバージョンは実行環境のNode.jsのバージョンに大きく影響を受けます。なるほど、これがNode.jsのバージョンを上げにくくなっていた原因かもと思いました。しかしstatic-repositoryは直接node-sassに依存してなかったのでyarn why node-sassでどのライブラリがnode-sassに依存を持っているか調べます。

❯ yarn why node-sass
yarn why v1.22.19
[1/4] 🤔  Why do we have the module "node-sass"...?
[2/4] 🚚  Initialising dependency graph...
[3/4] 🔍  Finding dependency...
[4/4] 🚡  Calculating file sizes...
=> Found "node-sass@4.12.0"
info Reasons this module exists
   - "base-kit#gulp-sass" depends on it
   - Hoisted from "base-kit#gulp-sass#node-sass"

base-kitが依存しているgulp-sassnode-sassに依存しているようです。base-kitはBASEの社内ライブラリのようですが、今まで見かけた記憶がありません。調べたところbase-kitはフロントエンド開発の便利ツールを集めたライブラリとして過去には存在していましたが、数年前にメジャーバージョンが上がる際にbbqとしてUIライブラリに生まれ変わっていました。困ったことにstatic-repositorybase-kitの依存がそこそこあり、その中にはbase-kitをアップデートしてbbqにすると削除されてしまうモジュールもありました。どうして...

ということで、頑張ってbase-kitへの依存を剥がしていきます。base-kitをアップデートしてbbqの最新版にし、node-sassを使っているところはdart-sassに置き換えていきます。この移行はビルド設定ファイルの変更はほとんどありませんでしたが、webpackのビルド時にSCSSのコンパイルで新しくエラーが出るようになったので、そこも一通り対応していきます。bbqにアップデートすると消えてしまうモジュールに関してはコード量がそこまで大きくなかったので、コピペでそのまま持ってきます。

その他、軽微な修正をいくつか行なってyarn installを実行するとNode.js 16でインストールに成功しました!yarn install自体もかなり速くなってました。そのまま続けてyarn run devを実行するとwebpack dev serverが数秒で立ち上がり、表示崩れ等もない状態でした。

static-repositoryはdevelop・masterブランチに新しい変更が加わればCIでビルド処理を行なって、成果物を自動的にS3に上げることが役割なので、ビルドが通ることとビルドの成果物が期待通りの状態になっていれば問題ないという考えのもと、ステージング環境で再ビルドされたものを入念に動作確認をして本番リリースしました。

リリースから数日の間はCIでエラーにならないか気を配っていましたが、2回ほどCIが落ちることがありヒヤリとしました。しかし、いずれもNode.jsのバージョンを上げたことが直接の原因ではなく、すぐに安定稼働してくれるようになったのでそこでようやく一安心することができました。

なぜstatic-repositoryが遅くなっていたのか

たまたまNode.jsのバージョンアップをしたら動作が爆速になったので、てっきりNode.jsのバージョンが古かったことが遅くなっている原因だと思っていました。しかし、PRを作り始めた時にCTOの川口さんから以下のようなコメントを頂きました。

なんと!思っていたこととちょっと違っていました。 私は業務でしかM1 Macを使っておらず、プライベートでは基本的に最新版のNode.jsしか使っていないため全く気付きませんでした。他の方がIntel Macで試してみたところ、Node.js 12でも快適に動作したとのことだったのでstatic-repositoryが遅くなっていた直接の原因はApple シリコン搭載のMacでNode.js 15未満を動かしていたからでした。

後になってから知ったのですが、元々BASEではM1 Macbookを導入する際にCTOや基盤チームの方がM1でも開発できるよう開発環境を整備して下さっていました。BASEの主要リポジトリはその時にNode.jsのバージョンが引き上げられていましたが、更新頻度の低いstatic-repositoryは取り残されていたというのがこの話のオチになります。

やってみて得られた知見やBASEのいいところ

バージョンアップ対応は溜め込むと大変

今回の事例でいうとstatic-repositorybase-kitに依存しており、base-kitnode-sassに依存し、node-sassがNode.jsのバージョンに依存しているという構造になっていました。そのため、Node.jsのバージョンを上げるためにはコードを読み解いて依存関係を理解しつつ、一つ一つエラーを潰していく作業が必要でした。ライブラリ等のバージョンアップをしないでいると古い依存関係の上に新しい依存が作られていくことになるので、改めて日頃から依存ソフトウェアのバージョンを上げていくことの大切さを実感しました。

やらないといけないことはやっていく必要がある

Webサービスを提供している事業会社はどうしても新機能の開発や既存機能の改善をしていくことが優先されます。それはユーザーに新しい価値を届けるためにとても大切なことですが、一方でプロダクトをより堅牢に, より速く変えていけるようにすることも同じくらい重要です。ライブラリのバージョンアップはまさにそういった作業の第一歩だと思うので、積極的に上げていきたいなと思いました。

BASEは「いつかやらないといけないこと」にも取り組めている

BASEは創業10周年を迎え、会社もサービスも大きくなりましたが同時にやり残し・積み残し作業も数多く存在しています。ですがBASEはサービスを成長させていくのと同時に「いつかやらないといけないこと」についても今取り組んでいこうという空気を強く感じていますし、実際かなり取り組めていると思います。

今回私が参加したfrontend-yatteikiではプレイヤーとしてのフロントエンドエンジニアの他にフロントエンド領域に明るいセクションマネージャーの方や基盤チームの方も参加されており、BASEの負債や開発上の課題について活発に議論が行われていたりプロジェクトとは関係のないPRが出されていたりしています。BASEで責任ある立場にいる方がこういう問題に積極的に取り組んでいることは、開発チームとしてとても良い状態だと思いますし、私自身も何かしら貢献がしたいと思えるようになりました。私が入社する前の事例を見ても入社歴が長くないフロントエンジニアの方がVue.jsのバージョンを上げたりビルドパイプラインを改善したりと自発的に課題解決に取り組んでいる様子がslackから伝わってきて、非常にいい刺激をもらいました。

終わりに

今回は社内リポジトリのNode.jsのバージョンを上げた事例を紹介させていただきました。依存ソフトウェアのバージョンを上げていくことはとても大切です。社内にはたくさんのリポジトリがあり、中にはあまり頻繁にはメンテナンスされていないものもありますが、自分が関わったリポジトリに関しては来た時より綺麗な状態にしていけるよう努めていきたいですね。