Backbone.js(モデル・ビュー・ルーターだけを提供する軽量な JavaScript フレームワーク)で構築された業務システムを、React へ段階的に移行しようとしているエンジニアに向けた内容です。移行計画を立てる際に見落とされがちな落とし穴を整理しました。
長期間保守されてきたBackboneアプリのリプレイス案件に関わったことがある方なら、見積もりが大きく外れた経験があるかもしれません。その原因の多くは、Backbone本体ではなく「Backboneの上に誰かが積み上げた独自基盤」にあります。
何が起きるか:移行コストが見積もりを超える
Backboneは意図的に機能を絞ったライブラリです。モデル・コレクション・ビュー・ルーター・イベントの仕組みは提供しますが、それ以外は提供しません。
そのため実際の業務アプリでは、チームが独自にルールを継ぎ足しています。グローバルなシングルトン(アプリ全体から参照できる単一のオブジェクト)、イベントバス(離れたモジュール同士が直接依存せずに通信する仕組み)、画面遷移を管理するナビゲーションコントローラ、ビューの後始末コード、テンプレートのビルド手順、そしてjQueryプラグイン群です。
これらは設計書に残っていないことがほとんどです。退職したメンバーが数年前に決めたルールを、今のチームが暗黙知として引き継いでいるケースも珍しくありません。React移行の見積もりがBackbone本体のAPIだけを見て作られると、この「名前のない基盤」の分だけ工数が膨らみます。
影響範囲は画面単体では収まりません。イベントバスを介した画面間連携、グローバルAJAXフック(全リクエスト共通のローディング表示や認証ヘッダー付与)、jQuery Mobileのようなページ遷移の仕組みまで、見えない依存が横断的に広がっています。
なぜ起きるか:原因を段階的に分解する
原因1: Backboneが「未定義領域」を大量に残す設計だったこと
Backboneはビューの破棄方法もコンポーネント間通信の方法もナビゲーションの管理方法も規定していません。Angular や Vue のような後発フレームワークと違い、この部分をチームの裁量に委ねています。結果として、プロジェクトごとに全く異なる「暗黙のフレームワーク」が育ちます。
原因2: 周辺ライブラリの陳腐化が気づかれにくいこと
RequireJS(JavaScriptモジュールの非同期読み込みを行うローダー)やHandlebars 1.x(テンプレートエンジン)、古いjQueryプラグインは更新が止まっていることがあります。jQuery Mobileは2021年に開発元から正式に非推奨(deprecated)と発表されています。アプリ自体は動いているため、この陳腐化は障害が起きるまで表面化しません。
原因3: 「ゾンビビュー」がメモリリークと不具合の温床になること
Backboneのビューは、画面から取り除いても明示的にイベント購読を解除しない限りイベントを受け取り続けます。画面上には存在しないのにイベントに反応し続ける「ゾンビビュー」は、多くのBackboneチームが一度は遭遇する典型的なバグパターンです。この後始末コード(close()のようなメソッド)が各プロジェクトで独自実装されているため、React移行時に「どこまでが業務ロジックで、どこからがBackboneの延命コードか」の切り分けが難しくなります。
自分のプロジェクトが該当するか確認する方法
本格的な移行計画を立てる前に、コードベースに独自基盤がどれだけ積み上がっているか棚卸しします。以下のキーワードでコード検索を行い、ヒット数を数えてみてください。
grep -r "\.extend(" src/ | wc -l
grep -r "listenTo(\|\.on(\|trigger(" src/ | wc -l
grep -r "\$(" src/ | wc -l.extend( のヒット数はモデルとビューの総量を示します。listenTo・on・trigger のヒット数は、画面間の隠れた配線がどれだけあるかを示す指標です。ここが多いほど、イベントバス経由の依存関係を洗い出す工数がかかります。
合わせて以下も確認しておくと、独自基盤の全体像が見えてきます。
- グローバルなシングルトンオブジェクト(
AppやGlobalsのような名前で、コントローラや現在ユーザー情報を保持しているもの)の有無 - ビュー破棄用の共通メソッド(
close()やremove()をオーバーライドした独自実装)があるか - ルーティングが
Backbone.Routerだけで完結しているか、別のコントローラファイルが画面遷移を管理しているか - テンプレートのビルドが
package.jsonのスクリプトで自動化されているか、READMEに手順が書かれた手動ビルドか $.ajaxSetupのようなグローバルAJAXフックで、ローディング表示や認証ヘッダーを一括設定していないか
併せてpackage.jsonやbower.jsonを開き、BackboneとjQuery、RequireJSのバージョンを確認してください。古いバージョンで固定されている場合、移行前にモジュールバンドラの入れ替えが必要になることがあります。
対策の手順
手順1: 独自基盤の一覧表を作る
前述のgrep結果をもとに、「チームが作ったもの」「何のためか」「Reactで何に置き換わるか」を一覧表にします。イベントバスはReactのpropsやContext API(コンポーネント間でデータを受け渡す標準の仕組み)に、ビュー破棄コードはReactのアンマウント処理に、それぞれ自然に置き換わる部分です。
手順2: モジュールバンドラを先に現代化する
RequireJSや<script>タグの羅列が残っている場合、React導入より前にwebpackなどのバンドラへ切り替えます。このとき既存のBackboneモジュールを書き直す必要はありません。バンドラ移行とフレームワーク移行を同時にやると切り分けが難しくなるため、順番を分けることが安全です。
手順3: BackboneモデルをReactから読めるようにする
画面を一気に書き換えるのではなく、Backboneのモデルやコレクションをそのまま残し、Reactコンポーネントから購読できる薄いアダプタ層を用意します。これにより、ビュー単位で段階的にReact化を進められます。
手順4: 小さい画面から移行してテストを後付けする
テストが存在しないBackboneアプリは珍しくありません。移行対象の画面単位でテストを先に書き、挙動を固定してからReactコンポーネントへの置き換えに着手します。
手順5: 置き換え完了後に削除できるコードを確認する
イベントバス、ビュー後始末コード、ナビゲーションコントローラは、対応するReact側の実装が揃った時点で削除対象になります。棚卸し表のチェック欄として「削除済みか」を追加しておくと、移行の進捗が可視化できます。
移行判断と確認事項のまとめ
Backboneからの移行は、ライブラリの置き換えというより「チームが過去に積み上げた暗黙知を可視化する作業」に近いものです。
- アプリが安定して変更頻度も低く、保守担当者が困っていなければ、無理に移行しない選択も現実的です
- 変更のたびにコストが増え続けていると感じたら、それが移行検討のタイミングです
- 移行前にgrepでの棚卸しとpackage.jsonのバージョン確認を行い、独自基盤の規模を数値で把握しておくと見積もり精度が上がります
まずは手元のリポジトリで.extend(とlistenTo(の数を数えるところから始めてみてください。