この 記事は Next.js 版の 内容です。 現在は Astro で 構築し直した ため、 情報が 古い 可能性が あります。 当時の リポジトリは こちら に あるので 参考に してください。
この ブログを 公開してから 約 1 年が 経過した。 それに 伴い、 リファクタリングを 実施した。
UI は ほとんど 変更していない ものの、 各処理を 最適化した ことで UX の 向上が 期待される (要検証)。 さらに、 内部で 使用する ライブラリを 見直す ことで ビルド時間を 短縮し、 開発体験の 改善も 図った。
v1 は 参照用と して 別の ブランチに 保存している
今回の リファクタリングに おいて 意識した点は 以下の 4 つである。
ライブラリの バージョンを 最新版に する 開発体験を 向上させる 必要な ライブラリのみ 使う 最適な 実装を 行う ライブラリの バージョン管理には Renovate を 使用している。 アップグレード対象の ライブラリが 見つかれば、 自動で PR を 作成し、 PR が 作成されると 同時に CI が 起動。 ビルドスクリプトや 静的コード解析を 実行し、 CI が 通れば 即マージと いう 運用だ。
しかしながら、 メジャーアップデートが 絡むと、 当然の ごとく CI が エラーを 吐く。 CI が 通らない 場合は ローカル環境で コードを pull して 修正する 必要が あるのだが、 その 手間を 後回しに するのが 人間の サガと いう もの。 結果、 気づけば アップグレード待ちの ライブラリが 10 個以上 たむろすると いう、 面倒くさい 状況が 生まれてしまった。
ライブラリの アップグレードを 放置するのは セキュリティ的に よろしくないし、 新機能を 享受できないのも 何とも 言えず 心理的にも やもやする。 と いうわけで、 時間を とって、 全ライブラリを 最新バージ ョンに アップグレードした。
作業自体は 概ねスムーズだったのだが、 一筋縄では いかなかったのが Contentlayer と いう ライブラリだ。 MDX ファイルを 型安全な JS オブジェクトへ 変換する 便利ツールであるが、 残念な ことに 資金不足で メンテナンスが ストップしてしまっていた。 この 問題には 少々頭を 悩ませたが、 最終的には Contentlayer2 と いう フォーク版を 採用する ことにした。
Contentlayer2 は 新機能の 追加が 期待できない ものの、 必要最低限の 機能は 揃っている ため実用上の 問題は なさそうだ。 必要な ものを 手に 入れるだけなら これで 十分。 今後の 安定稼働を 願いつつ、 新しい 環境での 運用を 進めていく。
この 1 年の 間に、 JavaScript 界隈の ツールチェーンは 激動の 時代を 迎えつつある。 その 波に 乗り、 より 快適な 開発環境を 求めて 以下の ツールを 切り替えた。
用途 移行前 移行後 コードフォーマッタ Prettier Biome リンタ ESLint Biome パッケージマネージャ pnpm Bun ランタイム Node.js (Bun)
これに より、 4 つの ツールが 2 つに 集約された。 使う ツールは 少ない ほど 管理も 楽に なる。
Biome に 切り替えた 最大の 理由は、 その 圧倒的な 実行速度である。 例えば、 以前は Prettier と ESLint を 組み合わせて コードの 整形と Lint を 実行していたが、 処理時間は それぞれ 3.9 秒 + 10.3 秒 = 合計 14.2秒 を 要していた。
一方で、 Biome は その 両方の 機能を 兼ね備えており、 処理時間は わずか 0.045 秒。 その差は なんと 約 315 倍である。 まさに 「瞬きする 間に 終わる」と いう レベルだ。
しかし、 Biome には 課題も ある。 例えば、 言語サポートが 限定的である 1 ほか、 ESLint の 豊富な プラグインが 利用できないと いった 制約が ある。
ただし、 筆者の 用途では 特に 問題なく、 必要十分な 機能を 発揮している。
他に 特筆すべきは、 追加の プラグインを インストールしたり、 煩雑な 設定ファイルを 書く 必要が ない点だ。
実際の 設定ファイルは これだけで 済む ↓
" $schema " : " https://biomejs.dev/schemas/1.9.4/schema.json " ,
シンプルで ありながら 強力、 そしてとにかく 速い。 Biome は、 コード規約を 厳格に 管理する 必要が ない 環境に おいて、 非常に 有用な 選択肢と いえる。
次に Bun だが、 こちらも Biome に 負けず劣らずの 速度を 誇る。 まず、 パッケージマネージャと しての 性能を 見てみる。
node_modules
を 削除した 状態で ライブラリを 再インストールする 場合、 pnpm では 23.1 秒を 要した。 これでも npm や Yarn より 速いが、 Bun で 実行すると 5.6 秒で 完了した。 その差は 約 4 倍。 ここまで 速いと、 インストール待ちの 時間が ほぼ気に ならない。
また、 Bun は ランタイムと しても 使用可能だ。 公式ドキュメントに よれば、 Next.js の ローカル開発環境で Bun を ランタイムと して 使うことも できると ある2 。 しかし、 実際に 試してみた ところ、[custom formatter threw an exception]
と いう エラーが 表示され、 不安要素が 残った ため、 結局 ランタイムと しては Node.js を 使用している。
デプロイに 関しては、 Vercel が Bun を パッケージマネージャと して サポートしている ため、 問題なく 動作している。 ランタイムと しての 完全な 移行は 今後の アップデートに 期待したい ところだが、 現時点では パッケージマネージャと して 活用するだけでも 十分に 恩恵を 受けられる。
リファクタリングの 過程で、 「本当に この ライブラリは 必要か?」と 自問 自答した 結果、 いく つかの ライブラリを 廃止する 決断を 下した。 その 主な 対象が 以下の 2 つである。
tRPC は 「型安全な API 通信」を 実現できる 強力な ツールである。 しかし、 導入に あたり 多くの 設定が 必要で、 その 初期ハードルの 高さが 以前から 気に なっていた。 また、 v11 の 安定版が いつまで 経っても リリースされないと いう 状況も 不安要素の 一つだった。 現時点での tRPC は、 長期的に 採用するには 少し 懸念の 残る 選択肢だったと いえる。
一方、 この ブログは Next.js v15 上で 動作しており、 Server Actions を 活用する ことで、 型安全に API リクエストを 送信できるようになった。
tRPC を 導入した 背景には、 純粋に 「技術的な 興味」と いう 動機が あった。 しかし、 リファクタリングを 進める 中で、 「tRPC を 使い続ける 理由」が 薄れていった ことを 感じた。
もちろん、 Server Actions を 採用した ことで Next.js に ロックインされる リスクは 意識しておく 必要が ある。 しかし、 それを 差し引いても、 今の ブログの 規模では tRPC を 維持する コストに 見合う メリットは 感じられなかった。
次に Motion (旧 Framer Motion)も 剥が すことにした。 Motion は、 React 環境での アニメーション実装を 簡単かつ 直感的に 行える 素晴らしい ツールだ。 軽微な モーションから 高度な インタラクションまで 幅広く 対応できる ため、 一時は この ブログでも 多用していた
だが、 クライアントバンドルの 肥大化の 問題や ブログと いう 特性上、 「魅せる アニメーション」よりも 「軽快な 読み込み体験」の 方が 重要だと 考え直した。 必要な アニメーションは CSS の transition
や keyframes
で 実装する ことにした。 この 変更に より、 クライアントサイドの コード量が 削減され、 バンドルサイズが 小さくなった。 結果と して、 ページの 初回読み込み速度も 向上し、 全体 的な パフォーマンス改善に 寄与した。
お問い 合わせフォームの 実装を 見直し、 これまで 使っていた React Hook Form から、 Conform に 切り替える ことにした。 この 変更の 背景には、 Server Actions への 対応状況が 大きく 影響している。 React Hook Form は 便利だが、 現時点では Server Actions と 組み合わせるには まだ 課題が 残っている。 一方、 Conform は すでに Server Actions に 対応しており、 移行後の 実装は 驚く ほど スムーズだった。
また、 メール送信や Google スプレッドシートへの 書き込みと いった 処理も、 すべて 1 つの Server Action に 集約した ことで、 管理の 手間が 大幅に 軽減されている。
フォームの 見直しと 並行して、 JavaScript の バンドルサイズ削減にも 注力した。 クライアントサイドでの 負荷を 減ら すため、 可能な 限り Server Components を 利用するように 実装を 変更した。 この 結果、 初回描画速度の 向上と、 クライアントサイドの JavaScript コード削減に よる パフォーマンスの 改善を 達成した。
ただし、 すべてを Server Components に 移行する ことが 最適とは 限らない。 RSC Payload が 増える ことで、 かえって パフォーマンスが 悪化する 場合も ある ため、 試しながら 進めている。
最後に、 過去の ハックな 実装方 法を 一掃し、 ベストプラクティスに 従う ことを 目標に コードベースを 根本から 見直した (ここで 書くべきことではないのかもしれないが)。 動作優先で 無理やり 書かれた コードや、 なぜ動いているのか 理解が 難しい 箇所を 改善した。
リファクタリングとは、 過去の 自分と 対峙する 作業だ。
その コードが なぜ生まれ、 どのように 育ち、 どれだけの 時間を 共に したのか。 そして、 なぜ それを 手放さなければならなかったのか── その 答えを 見つける 旅でもある。
リファクタリングに 終わりは ない。
リリースした 瞬間から、 その ソースコードは レガシーコードと なってしまう ことを 忘れずに 開発していきたい。