BubbleからLaravelアプリケーションにデータ移行したら想定外に大変だった件

なんと、実に6年ぶりにQiitaに投稿する。

UIも変わり果てており、もはやIT業界では老害側になってきたのであろう私は浦島太郎の気持ちである。

2024年現在は、Qiitaの他にも様々な技術関連の投稿サイトがあるものの、結局私はQiitaに立ち返ってきた。

やはり私のエンジニア人生初期を支えてくれたのがQiitaだったことは紛れもなく事実だからである。


というエモそうな冒頭はさておき、今月頭に実施したデータ移行わっしょい（？）の顛末を今後の自分に向けても記録として残しておきたく久々に筆をとった。

我々の挑戦はもはや現代なら誰にでも起こりうることである。

今やスタートアップから大企業の新規事業PJまで、幅広くプロダクト立ち上げの強い味方となった **ノーコードプラットフォーム「Bubble」で開発したサービスを、事業拡大に向けて「Laravel」や「Next.js」を用いたGCP上で稼働するフルスクラッチシステムへと移管を試みた** のだ。

※わかりやすく「フルスクラッチ」と表記しましたが、もちろんフレームワークや外部APIなども利用しています

## 前提

- 本プロジェクトが実施されたのは私が共同創業した企業での自社サービスの話です
    - セキュリティ上、実際のサービス構成から若干データをぼかして書いています
- このお話は、 **決してBubbleやノーコード批判を目的としたものではございません** 
    - むしろBubbleでの事業爆速スタートがあったからこそ実現したフルスクラッチ化です
    - リニューアルプロジェクトが多少遅延していても、Bubbleでサービスが動いているから急がなくて良い、ということの安心感は計り知れません
- ここでは **Bubbleで作られたサービスを「旧システム」、移管先を「新システム」と呼ぶことにします**
    - 旧システムと新システムはどちらも別の開発ベンダーさんへ発注していました
    - 旧システムは代表とノーコードベンダーさんが立ち上げたものです
    - 新システムへの移管プロジェクトは私がベンダー選定含めて初期から担当しました
    - 私はBubbleの知見はかなり浅いです
        - 加えてBubbleの開発者アカウントが2名までしか入れないプランだったため、旧システムの仕様は又聞きで把握した程度ですw
    - リニューアル系のデータ移行に関しては、規模の大きいシステム（以下例）で経験則がありますが、あまりに大変でプロジェクト自体が頓挫した例も多々見てきました
        - ex.ネット証券システムのリニューアル
        - ex.マスメディア顧客基盤のマイクロサービス化
- 今回のサービスは立ち上げから数ヶ月のもので、とてもシンプルなデータ構造でした
    - 幸か不幸か、テーブルは正規化されていないこともあり1つ＋AirTableを使ったほぼ静的な一覧情報のみ
    - 新システムのDB設計では今後の機能拡張計画を鑑みて正規化を施し、テーブル数は4つになりました
    - 新システムでは、LaravelのDBに加えてBigQueryにもデータを一部連動する設計としました
- 今回は旧システムが動いていたので、並行稼働（データ同期は手動）によるリリースを行いました
    - システムは止めることなく裏側でデータを移行、その後DNSを切り替えます
    - DNS浸透前に分散アクセスが生じた場合は、手動SQLでデータを同期する想定でした（結局発生しなかった）

## データ移行つまづきポイント

ここからは、今回起こった中でも、もしかしたらよくあるデータ移行のつまづきポイントを列挙します。

### 数値系カラムが文字列型（VARCHAR型）になっている

- 理想のデータ：「1」「0」
- 旧システムのデータ：「1年」「なし」

この他にも、分析のために新システムでは数値化していきたいデータなどが「500〜700万円」などの範囲指定の文字列で入っているなどの問題もありました。

これらは決めの問題で数値に変換するしかありません。

スプレッドシートの関数を使い、フォーマットの変換を反映しました。

`ex. 「1年」→「1」、「なし」→「0」、「500〜700万円」→「600」など` 

### 途中からNot Nullなカラムが増えている

前半に登録されたユーザーには存在しなかったカラムが追加開発で増えていました。

こういったことはよくありますが、データの整合性を加味すると、運用として既存ユーザーにもなんらかのデータを入れておくのが吉です。

今回は旧システムDBではデータがないままだったため、新システム移行時に「その他」のようなデフォルト値を入れることとしました。

### 月日の整合性がとれていない、DATE型で保存されていない

- 理想のデータ：2024-11-02
- 旧システムのデータ：112

バリデーションの問題です。

月と日のインプットがUI上で分かれている場合、通常であれば月日の整合性を取ることが望ましいですが、どちらかが入力されていれば必須バリデーションを通ってしまったり、月日として成り立っていない数値でも入力が可能になってしまっていたようです。

数値も1桁だったり2桁だったり、元の入力が何だったのか解析不可能なデータもありました。

この辺りは細かい調整ができないノーコードだから起きたのか、当時のテストが甘かったのかは定かではありませんが…

これらは関数での一発処理、というわけにはいかなかったため、GoogleスプレッドシートのフィルターでDATE型になっていない形式を絞り込み、手動で書き換えていきました。

正直、「112」が「何年の」「01-12」なのか「11-02」なのか、は今となってはわからないため、後日ユーザーから問い合わせが来た時に対応できるよう、一律で年数を範囲外の「1900」にすることなどで対処しています。

`ex. 「112」→「1900-11-02」` 

### Bubbleのタイムスタンプが私の知ってるタイムスタンプ型じゃない

- 理想のデータ：2023-07-16 18:43:00
- 旧システムのデータ：Jun 16, 2023 6:43 PM

地味にこれが厄介でした。旧システムのデータそのままでは、BigQueryやLaravelのDBにインポートした時にTIMESTAMP型にならずString型になったりエラーになってしまったりするのです。

その上、Googleスプレッドシートの日付表示変換には対応していない形式のため、関数などでの変換も難しい…

ここはChatGPTに変換スクリプトを通して返却してもらうことで解決しました。AIさまさまです。

もちろん個人情報はChatGPTには渡せませんので、変換したい日付データだけを別出ししています。

### 改行コードの違いでインポートエラーになる

複数行あるVARCHAR型のカラムで、BigQueryにだけインポートできないケースもありました。これはBubbleとBigQueryでの改行コードの差異によるものだったようです。

これはGoogleスプレッドシートの正規表現置換 `\r\n|\n|\r` で対応しました。

## どうやってデータ移行を進めたか？

データ移行に限らず、 **システム開発をする上で大切な手順を踏んだまで** 、ではありますが、特に事前準備には時間がかかりました。

今回、旧システムがとてもシンプルなデータ構造だったこともあり、このデータ移行準備の工数見積もりを見誤ってしまったと反省しています。結論からいうと、想定の倍以上の日数を要してしまいました。

### 事前準備

1. 新旧のデータ形式を照らし合わせて調査する
2. データ移行計画を立てる
3. データ移行手順書をつくる（この時点では大雑把な叩き台）
4. データ移行の予行練習を実施
    - 個人情報をマスクした本番データにて、限りなく本番と同様の環境を実現
5. データ移行手順書を改善する
    - 失敗点を踏まえて、必要なデータ加工などの手順を追加していく
6. 4と5を繰り返す（今回は完全成功するまで3-4回かかりました）
7. 本番リリース手順の中に、データ移行を組み込む

特にこの中で、正しくデータがインポートされるようデータの加工方法を工夫するのが大変でした。

前述のとおり、単純なスプレッドシートでの変換ではうまくいかない形式のデータもあり、ChatGPT用のプロンプトを使って加工スクリプトを通したりなどの手順も加えています。

また、あちらがうまくいけばこちらがエラー、といった形でインポートして、動作するかを検証して初めて判明する不備もあったりと、データが多ければ多いほど何度も挑戦が続いていきます…

500番前のユーザーまでは発生しなかった問題が、501番目のユーザーで発生する、といったこともありました。

そのためリリース当日まで全く気が抜けない状況だったのです。

### 本番リリース

1. リリース作業直前に旧システムのデータをもらう
2. データ移行手順書の通りにデータを加工する
3. BigQueryにインポートする
4. Laravel DBにインポートする
5. 他リリース手順が続く…

補足として、今回に限らず、 **顧客の認証情報の扱いをどうするか、についても深く検討しておく必要がある** でしょう。

今回は新旧で認証サービスやパスワードのハッシュロジックも異なるため、ユーザーにはデータ移行時に一律ランダムなパスワードを再設定し、パスワードリセットを促すフローとしました。

## 事の顛末

今回の件が最終的にどうなったかというと、大変だった事前準備が功を奏し、本番リリースはあっさり成功、無事にデータ移行もDNS切り替えも完了しました。

おかげさまで、現在に至るまで安定稼働しています。

これも全てはご協力いただいたノーコード開発、フルスクラッチ開発の各ベンダー様のおかげです。

今後も、Bubbleなどのノーコードプラットフォームを使った初期開発は加速していく動きがあるかと思います。

順調にビジネスが成長して、フルスクラッチの開発に切り替えるぞ！というフェーズに来た時、今回のデータ移行プロジェクトの知見がまたどこかで活かされることを願っています。

### おまけ（Xでのぼやき）

https://x.com/maidol_28/status/1750528312798257597?s=20

https://x.com/maidol_28/status/1752263882012573801?s=20

https://x.com/maidol_28/status/1752931617914650865?s=20

そんなこんなで、大変なリリース作業を無事に1発成功で乗り越えた我々は、次なるデータの泉を探しに、アマゾンの奥地へと向かうのであった…