初めに
こんにちは。フロントエンドエンジニアの竹本です。 入社してそろそろ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-sass
がnode-sass
に依存しているようです。base-kit
はBASEの社内ライブラリのようですが、今まで見かけた記憶がありません。調べたところbase-kit
はフロントエンド開発の便利ツールを集めたライブラリとして過去には存在していましたが、数年前にメジャーバージョンが上がる際にbbq
としてUIライブラリに生まれ変わっていました。困ったことにstatic-repository
はbase-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-repository
がbase-kit
に依存しており、base-kit
がnode-sass
に依存し、node-sass
がNode.jsのバージョンに依存しているという構造になっていました。そのため、Node.jsのバージョンを上げるためにはコードを読み解いて依存関係を理解しつつ、一つ一つエラーを潰していく作業が必要でした。ライブラリ等のバージョンアップをしないでいると古い依存関係の上に新しい依存が作られていくことになるので、改めて日頃から依存ソフトウェアのバージョンを上げていくことの大切さを実感しました。
やらないといけないことはやっていく必要がある
Webサービスを提供している事業会社はどうしても新機能の開発や既存機能の改善をしていくことが優先されます。それはユーザーに新しい価値を届けるためにとても大切なことですが、一方でプロダクトをより堅牢に, より速く変えていけるようにすることも同じくらい重要です。ライブラリのバージョンアップはまさにそういった作業の第一歩だと思うので、積極的に上げていきたいなと思いました。
BASEは「いつかやらないといけないこと」にも取り組めている
BASEは創業10周年を迎え、会社もサービスも大きくなりましたが同時にやり残し・積み残し作業も数多く存在しています。ですがBASEはサービスを成長させていくのと同時に「いつかやらないといけないこと」についても今取り組んでいこうという空気を強く感じていますし、実際かなり取り組めていると思います。
今回私が参加したfrontend-yatteiki
ではプレイヤーとしてのフロントエンドエンジニアの他にフロントエンド領域に明るいセクションマネージャーの方や基盤チームの方も参加されており、BASEの負債や開発上の課題について活発に議論が行われていたりプロジェクトとは関係のないPRが出されていたりしています。BASEで責任ある立場にいる方がこういう問題に積極的に取り組んでいることは、開発チームとしてとても良い状態だと思いますし、私自身も何かしら貢献がしたいと思えるようになりました。私が入社する前の事例を見ても入社歴が長くないフロントエンジニアの方がVue.jsのバージョンを上げたりビルドパイプラインを改善したりと自発的に課題解決に取り組んでいる様子がslackから伝わってきて、非常にいい刺激をもらいました。
終わりに
今回は社内リポジトリのNode.jsのバージョンを上げた事例を紹介させていただきました。依存ソフトウェアのバージョンを上げていくことはとても大切です。社内にはたくさんのリポジトリがあり、中にはあまり頻繁にはメンテナンスされていないものもありますが、自分が関わったリポジトリに関しては来た時より綺麗な状態にしていけるよう努めていきたいですね。