こんにちは、YOUTRUSTの今井(YOUTRUST / X)です。
今回は、先日僕が実施したデータ移行タスクについて紹介しようと思います。
何をしたのか
YOUTRUSTのコミュニティ機能に関するテーブルを作成し直し、既存テーブルにあるデータを全て新しいテーブルに移行しました。僕自身、初めてデータ移行タスクを担当したのですが、その過程を振り返りたいと思います。
今回のテーマに関連するので、まずは簡単にYOUTRUSTのコミュニティ機能について紹介させていただきます。
補足:YOUTRUSTのコミュニティ機能について
YOUTRUSTには共通の趣味や興味関心のある話題でユーザーさん同士が交流をすることができるコミュニティ機能があります。 このYOUTRUSTのコミュニティでは、複数のトピックが並び、各トピックごとにコメントで会話ができます。
コミュニティに関連する主な機能が以下になります。
- コミュニティ内にトピックを立ち上げる(例:「最近オススメの本を紹介してください!」)
- トピック内にコメントを投稿する(例:「最近読んだ「XXXX」という本はオススメです!」)
- コメントにいいねをする
- 参加中のコミュニティに投稿されたコメントの未読・既読判定
- コメント内でのメンション機能
以下のリンクから、僕が好きな「野球好き集まれコミュニティ」に参加できます!
前提として、なぜデータ移行する必要があったのか
まずデータ移行前、YOUTRUSTには以下のようなテーブルが存在していました。
postテーブル:YOUTRUSTのタイムラインでの投稿用テーブル(以下通常投稿とします)community_postテーブル:コミュニティ内のトピック用テーブル(以下コミュニティトピックとします)post_commentテーブル:通常投稿やコミュニティトピックへのコメント用テーブル

上記の通り、community_postテーブルがpostテーブルに対して1対1で紐づく設計となっていました。そのため、どうしてもcommunity_postテーブルがpostテーブルに依存する形になっていました。YOUTRUSTにおいて、タイムラインでの投稿(post)とコミュニティ内でのトピック(community_post)は全く別物として扱いたかったので、上記のような依存関係があると、開発しづらい状況が生まれていました。
そこで、postテーブルとの依存関係を無くすために、community_postテーブルおよび関連するテーブルを作成し直し、データを移行することになりました。
↓データ移行完了後のテーブル設計は以下のようになります
※このタイミングでサービス上コミュニティトピックと読んでいるので、community_postではなくcommunity_topicに命名を変更。
postテーブル:通常投稿post_commentテーブル:通常投稿へのコメント用テーブルcommunity_topicテーブル:コミュニティトピックcommunity_topic_commentテーブル:コミュニティトピックへのコメント用テーブル

どのように移行を進めたか
データ移行は以下の手順で進めました。
※旧テーブル:データ移行前のテーブル、新テーブル:データ移行後のテーブル
- データ移行が必要なテーブルの洗い出し
- 新テーブルの実装
- 同期処理の実装
- 後方互換性を担保するため、既存APIの中で旧テーブルと新テーブルの両方にデータを登録・更新するように改修
- データ移行用のRakeタスクの実装および、Rakeタスクの実行
- 旧テーブルに保存されている過去のデータを、新テーブルに移行
- このタイミングで、旧テーブルと新テーブルの内容が完全一致する
- 新テーブルのデータを参照・更新する用の新APIの実装
- 後方互換性を担保するため、新APIでも旧テーブルと新テーブルの両方にデータを登録・更新
- フロント側の新画面の実装
- リソース表現が変わったことに伴い、以下のようにパスも変更されました
- 旧画面:
https://youtrust.jp/community_posts - 新画面:
https://youtrust.jp/community_topics
- 旧画面:
- リソース表現が変わったことに伴い、以下のようにパスも変更されました
- 旧画面から新画面へのリダイレクト処理を実装
- 旧画面へのルーティングを削除
データ移行をする中で、大変だったこと
複雑な設計をした結果、コードの可読性が低下してしまった
当初、新テーブルに旧テーブルへの参照用カラムを用意しない設計にした結果、新旧テーブル同士の同期状況を確認をする際のコードの可読性が低下してしまいました。

旧テーブルにあるデータが新テーブルにも同期されているかを確認する際に、以下のような困り事が発生しました。
- 参照用カラムを用意しなかった結果
- テーブル同士のリレーションを辿りながら、データの存在を確認する必要があった
- リレーションが長くなる場合、コードの可読性が下がってしまいました
最終的には、参照用カラムを用意した方がコードがシンプルになると判断し、テーブルに参照用カラムを追加し、コードを書き直すことになりました。
「なるべくテーブルに不要なカラムは追加したくないなぁ」と思い、テーブル設計をシンプルにすることを優先した結果、コードの可読性が低下してしまいました。この経験から時には実装の複雑さを軽減するために、テーブル設計のシンプルさを妥協することも選択肢の一つだと学びました。(参照用カラムは、データ移行完了後に削除するカラムなので、なおさら最初から追加して良かったと思います)
設計段階での考慮漏れが多く、手戻りが発生してしまった
- ユーザー同士でアプリのバージョンが違う場合の考慮はできているか?
- 具体例
- 新バージョンのアプリユーザーがコミュニティにトピックを立ち上げる(新バージョンでは、新テーブルにデータを保存する)
- この時、旧テーブルを参照している旧バージョンのアプリユーザーが、新テーブルに保存されているトピックの閲覧やコメントが可能かどうか
- 具体例
- ユーザーさんの退会処理など、一見コミュニティに関連しない処理でも、コミュニティのデータ更新処理が発生していないか?
- 退会処理の中で、退会されたユーザーさんに紐づくコミュニティのデータにも更新処理が発生していました
- なので、退会処理についても対応が必要なのですが、設計段階では考慮から漏れていました
上記のような観点を設計段階で考慮できておらず、実装の中で追加対応が発生し、デリバリーが遅れてしまいました。
振り返り:エッジケースをどれだけ網羅できるかが大事
データ移行を進める中で、何度も手戻りが発生してしまいました。この手戻りを減らすためには、設計段階でどれだけエッジケースを網羅できるかが重要ということを学びました。
YOUTRUSTの創業エンジニアであるやまでぃさんから、「見聞色を磨こう」とよくアドバイスをくださるのですが、これからも様々な成功パターンや失敗パターンを経験し、事前にさまざまなケースを予測し、対策を練れるように勉強していきたいです。
(最後に)採用強化中です!
Webエンジニア、アプリエンジニア、SREなど、様々なポジションで求人を出しておりますので、ぜひご応募ください!