Slide 1

Slide 1 text

2021.9.17 Ryo Iida (@aviciida)

Slide 2

Slide 2 text

はじめに - 自己紹介 飯田 諒(@aviciida) ● mikanのiOSエンジニア ○ 🍊 mikan歴 3年(since 2018/9) ○ 🍎 iOSエンジニア歴 2年 (since 2019/5) ● PM/分析もやってます! ● ユーザー目線でプロダクト作るの好き ● Twitter活発です!@aviciida 2

Slide 3

Slide 3 text

3 mikanについて Mission 本質的なテクノロジー活用で あらゆる人の英語学習によりそい 人生の可能性を広げる 「英語を扱えるようになりたい、得意になりたい」人に効果的な手法を提供し、 語学が人生のボトルネックでは無くなる未来を作るのが私たちmikanです。

Slide 4

Slide 4 text

アプリダウンロード NO.1 コアバリューが支持され国内最大級に成長 4

Slide 5

Slide 5 text

● iOSエンジニア ● Androidエンジニア ● サーバーサイド・インフラ エンジニア ● デザイナー ( 時期を見て、週1回程度の出社を想定) 社員 (まずは副業からでもOK) ※基本リモート勤務 ご連絡はこちらから (Twitter DMでも!󰢏) https://mikan.link/carrers mikanは絶賛採用中です! 5

Slide 6

Slide 6 text

今日話すこと mikanのiOSアプリのデータベースを 組み込み型のSQLiteからCloud Firestoreに 9ヶ月ほどかけて移行した時の話をします 6

Slide 7

Slide 7 text

大前提: レガシーについて 本セッションは「レガシーを置き換えていく話」ですが、mikan では「レガシー」を前向きに捉えています。 まだ知識がない中で、スピード感持って事業を進めていくための 当時は最善の手段であり、そのおかげで今の500万DLがありま す。(mikanではレガシーではなく「ビンテージ」と読んでます) セッション内でもできるだけ配慮した表現を使いますが、大前提 として先人の方々には尊敬と感謝の念でいっぱいであることを共 有しておきます🙏(特に創業者の宇佐美さんと高岡さん) 7

Slide 8

Slide 8 text

アジェンダ ① なぜSQLiteからFirestoreに移行したのか? ・なぜ組み込み型SQLiteが採用されていた? ・当時のツラミ ・Firestoreを採用した理由 ・移行によって変わること ② 実際の移行の話 ・全体観 ・ダブルライト ・マイグレーション ・クライアント側のreadロジック移行 ③振り返って ・うまくいったこと / やればよかったこと ・実際Firestore使ってみてどう? ・今回の学び 8

Slide 9

Slide 9 text

アジェンダ ① なぜSQLiteからFirestoreに移行したのか? ・なぜ組み込み型SQLiteが採用されていた? ・当時のツラミ ・Firestoreを採用した理由 ・移行によって変わること ② 実際の移行の話 ・全体観 ・ダブルライト ・マイグレーション ・クライアント側のreadロジック移行 ③振り返って ・うまくいったこと / やればよかったこと ・実際Firestore使ってみてどう? ・今回の学び 9

Slide 10

Slide 10 text

組み込み型SQLite? 10 ● DBを移行する前は、ユーザーの学習データは各端末のSQLite データベースに保存されており、サーバーと同期はしない形式 ● バックアップのために、1日の最初の起動時に、DBファイルを AWS S3の各ユーザーのバケットにアップロード

Slide 11

Slide 11 text

なぜか? (創業者の宇佐美さん(@usatie)に聞きました!) ● 当時はまだ経験不足で、サーバーにDBを置く選択肢がなかった ● クライアントサイドで完結する作りにしたかった ● (結果的に、オフラインでも使える実装に) なぜ組み込み型SQLiteが採用されていた? 11

Slide 12

Slide 12 text

データの同期・移行が不可能、もしくはかなり大変 ● 異OS間では不可能 ○ AndroidはRealmを使っている && スキーマも違うため ● 同OS間では”一応”可能(だがめちゃ大変) ○ まず問い合わせいただく→CSチームがS3に入る→旧端末のバケッ トから新端末のバケットにDBファイルをドラッグ&ドロップする →ユーザーさんがアプリ内でデータの復元をする その他 ● 生SQLを書く時に、エラーがランタイムにしかわからない ● マスターがないため、あらゆるバージョンを想定する必要あり 当時のツラミ - 組み込み型SQLite 12

Slide 13

Slide 13 text

データ構造の一部が負債化 ● 従来は「Book(本)」の中に「Chapter(章)」 ● Chapterごとに単語を学んでいく方式。 (1教材 = 1 Book) 当時のツラミ - その他、旧DBについて 13 Book Chapter

Slide 14

Slide 14 text

データ構造の一部が負債化 ● ある日、本としては1冊だけど、複数のBookを持 つ教材が登場 ○ 1教材 = 複数 Books があり得る状態に ● 単体Bookと複数Booksに対応しなければならず、 コードにif文が大量発生し、負債に 14 before 1教材 = 1book after 1教材 = 複数books 当時のツラミ - その他、旧DBについて book table

Slide 15

Slide 15 text

mikanの求めていた要件 / 当時の状況にマッチしていた ● オフライン対応 ● データの移行・同期 ● サーバーサイドが薄く、クライアントサイドが厚いチーム体制 ○ 上記要件を、サーバーレスに実現できるのが魅力的 当時の候補 ● API作る: サーバーサイドのリソースの関係で厳しい...! ● Realm: サーバーサイドで扱える言語がmikan(Go)とマッチしない Firestoreを採用した理由 15

Slide 16

Slide 16 text

① 組み込み型→ネットワーク型へ ● 同じIDでログインすれば、簡単にデータ移行可能に ● 基本はオフライン対応可能だけど、最初は通信必要に ② RDB→KVSへ ● カラムの追加が簡単に ● 集計が弱いため、サマリーデータを持つ必要が出てくる ○ 各単語の記憶度を元にした、各教材の進捗度 ○ モデルのそのまま移行というわけにはいかなくなる! Firestoreへの移行によって変わること整理 16 サマリーデータを持っている ドキュメント

Slide 17

Slide 17 text

アジェンダ ① なぜSQLiteからFirestoreに移行したのか? ・なぜ組み込み型SQLiteが採用されていた? ・当時のツラミ ・Firestoreを採用した理由 ・移行によって変わること ② 実際の移行の話 ・全体観 ・ダブルライト ・マイグレーション ・クライアント側のreadロジック移行 ③振り返って ・うまくいったこと / やればよかったこと ・実際Firestore使ってみてどう? ・今回の学び 17

Slide 18

Slide 18 text

移行の全体観 18 ローカル SQLite app Phase⓪ 従来 write read app Phase① ダブルライト write read Firestore write Phase② マイグレーション Firestore ゴリっと 移行 app Phase③ readロジック移行 Firestore write read 👋 ローカル SQLite GCS ローカル SQLite DBファイルを upload app

Slide 19

Slide 19 text

Phase① ダブルライト ● 基本はクライアントサイドの仕事 ● データの書き込みを、SQLiteだけでなく、Firestoreにも ● ★ ダブルライト開始日をユーザーごとに保持しておく ○ マイグレーションの際、ダブルライト以降のデータ は対象にしないようにするため ○ ユーザーのアップデートに依存してしまい、開始タ イミングがバラバラなため 19 app write read Firestore write ローカル SQLite

Slide 20

Slide 20 text

Phase① ダブルライト 20 SQLiteへの書き込み Firestoreへの書き込み Firestoreへの書き込み

Slide 21

Slide 21 text

Phase② マイグレーション ● ダブルライト前のデータをFirestoreに移す作業 ● サーバーサイドの仕事 ● 非同期で行う ○ ユーザーによっては大変な量になるので (多ければ数年分) 21 Firestore ゴリっと 移行 GCS DBファイルを upload app

Slide 22

Slide 22 text

1. client: マイグレーション対象かどうかを判断 2. client: 自分のSQLiteファイルをGoogle Cloud Storageにアップロード 3. client: 自分のステータスを「マイグレーション待ち」に変更 4. server: ステータスが「待ち」のユーザーに対してマイグレーションを実行 22 DBファイルをuploadするコード Phase② マイグレーション

Slide 23

Slide 23 text

Phase③ readロジック移行 ● クライアント側の最後の大仕事 ● 読み込みロジックをSQLiteからFirestoreに移行 ○ これ以降はFirestoreで書き込み/読み込みが成立し、 SQLiteの依存からは脱却 ● 大変なところ ○ 同期処理→非同期処理への書き換え ○ RDB→KVSという変更によって、データの持ち方も変更 ■ 合わせてコアの学習機能のロジックも少し変更 ○ DBの書き込み・読み取りロジックが元々クライアント サイドに寄っていたため、対象ファイルが膨大... 23 app Firestore write read 👋 ローカル SQLite

Slide 24

Slide 24 text

24 Phase③ readロジック移行 同期処理で、呼び出すだけ 非同期で単語取得 処理順を変更 before after

Slide 25

Slide 25 text

25 Phase③ readロジック移行

Slide 26

Slide 26 text

アジェンダ ① なぜSQLiteからFirestoreに移行したのか? ・なぜ組み込み型SQLiteが採用されていた? ・当時のツラミ ・Firestoreを採用した理由 ・移行によって変わること ② 実際の移行の話 ・全体観 ・ダブルライト ・マイグレーション ・クライアント側のreadロジック移行 ③振り返って ・うまくいったこと / やればよかったこと ・実際Firestore使ってみてどう? ・今回の学び 26

Slide 27

Slide 27 text

自前で集客して、パブリックベータを実施できた。 うまくいったこと / やってよかったこと 1/3 27 ● Why: iOSの段階的リリースは、実際は段階 的ではない問題 ○ 少数に実際に使ってほしい ● 集客はLine@やpush通知 ● 社内で潰せなかった不具合がたくさん潰せた ○ 色んなユーザーのデータで触ってもらえ たおかげ ● ただ、600人以上はなかなか集まらず...

Slide 28

Slide 28 text

うまくいったこと / やってよかったこと 2/3 問い合わせてくれたユーザーさんの多くに個別対応 ● コンソールで自ら返信対応 ○ 不具合の再現手順などを詳しくヒアリングして不具合修正 ● +α 素敵なフィードバックもいただけて、やる気が出る💪 28

Slide 29

Slide 29 text

うまくいったこと / やってよかったこと 3/3 最小単位のログを別のところで貯めておく ● mikanでいうと「1単語ごとの学習ログ」 ○ 2021/9/17 18:50 に、appleという単語の問題を解いて、不正解 ○ みたいな ● もし不具合などで「学習履歴」や「学習進捗」のデータが消えても、 そのログがあればサマライズして、復元できる 29

Slide 30

Slide 30 text

難しかったところ・後悔 1/4 ダブルライトの不具合を見つけるのが困難 ● アプリ内で「Firestoreへの書き込みがうまくいっているか」を確認で きない ○ readをFirestoreから行うように書き換えるまでは、SQLiteの データを参照しているため ○ Firestoreへの書き込みがうまく行っていなくても検知できない ○ リリース前のQAは、Firestoreのコンソールを目視で確認 30

Slide 31

Slide 31 text

難しかったところ・後悔 1/4 ダブルライトの不具合を見つけるのが困難 ダブルライト期間中に起きた、本当にあった怖い話... 1. 新教材をFirestoreに入稿 2. 入稿の際に、field名のスペルミス 3. クライアントからFSにも学習データを書き込みたいが、クライアント で指定しているキー名と異なるので、Decode Error 4. ダブルライト失敗しているが、SQLiteには書き込みできているので、 気づかない 31

Slide 32

Slide 32 text

難しかったところ・後悔 2/4 強制アップデートとセキュリティルールのどちらも、後になって導入 セキュリティルールを後から導入したけど、強制アップデートの仕組みがな いため、めちゃくちゃ苦労しました...! 以下のサイクルを4周くらいしました。 1. しっかりしたセキュリティルールを適用 2. むかーしのバージョンで、ある違法な値が投げられることが判明 3. セキュリティを2に合わせて、少し緩める(アップデートさせられないので) 4. 1に戻る 32

Slide 33

Slide 33 text

難しかったところ・後悔 3/4 ABテストをできるならやるべきだった やるべき理由 ● 数字が上がったのか、下がったのかの判断を正しくするため ○ 継続率は時期要因にも左右されるので、同時期にABしてないと、 正しく分析できない ● パブリックベータの人数確保も簡単だった 方法 ● DBを触っているVCを全部複製して、片方をDB刷新版にする ○ 入口から分けてあげると「従来の世界」と「DB刷新の世界」を分 けられる。 33

Slide 34

Slide 34 text

一度に多くの問題を解決しない方がいい ● 一度に多くの問題を解決しようとすると、開発項目が大きくなる ● 開発項目が大きくなると、シンプルに大変で、価値を届けるのが遅く なり、検証も遅くなってしまう ● 解決したい課題は、個別で対応できるかも 難しかったところ・後悔 4/4 34

Slide 35

Slide 35 text

一度に多くの問題を解決しない方がいい 今回解決しようとした課題は大きく分けると以下 ● OS間のデータ移行ができない ○ SQLite ⇔ Realmのconverterを作る ● 同OSでデータ同期ができない ○ 今のデータ構造・スキーマのままFirestoreに移行する ● アプリのデータ構造がしんどい ○ SQLite、Realmのままデータ構造だけ変える 難しかったところ・後悔 4/4 35

Slide 36

Slide 36 text

● 「オフライン対応」と「データ移行」が特に何もせずに実現されてる のすごい ○ オフラインの永続性はデフォルトでオンになっている* ○ データ移行は、Firebase Authでログインすれば完了 Firestore実際使ってみて - 良いところ 1/2 * https://firebase.google.com/docs/firestore/manage-data/enable-offline?hl=ja#swift 36

Slide 37

Slide 37 text

● スケールを気にしなくていい ● セキュリティルール!バリデーションのコードをDBに持たせられる ○ 型だけじゃなく、その型の中でどんな値を許容するかを決められ る ○ 誰が書き込めるか、読み取れるか、も決められる ● クライアントサイドだけで、自由にカラムを追加できる Firestore実際使ってみて - 良いところ 2/2 37

Slide 38

Slide 38 text

ドキュメントの取得速度 ● データは綺麗に階層構造にしてしまうとし んどくなる 例: あるbookに属するwordsを全部取りたいと きに、chapterでfor文回してwordsを全部取っ てこないといけない(これが時間かかる) Firestore実際使ってみて - 難しいところ 1/3 38 実際のFirestoreの階層構造 books > chapters > words の構造になっている

Slide 39

Slide 39 text

● 集計弱いので、サマリーデータを持たないといけなくなる ○ サマリーデータと詳細データが食い違う不整合が発生し得る Firestore実際使ってみて - 難しいところ 2/3 39

Slide 40

Slide 40 text

Firestore実際使ってみて - 難しいところ 3/3 40 ● 不整合なくデータを入れていくために必要な学習コストが高い ○ security rules, transaction, batch, increment valuesなど ● 値段が高い ○ 今はFirestoreだけで25万円 ○ AWSのRDS使っていた時は、EC2やロードバランサー含め15万ほど

Slide 41

Slide 41 text

なんの課題を解決したいのかを明確化しよう ● 「OS間のデータ移行もできるようにしたいし、データ構造変だし、デー タの同期もできるようにしたいし...!よし!データ構造変えながら SQLiteからFirestoreに移行しちゃおう!」 ○ ふわっとした課題に対しては、ふわっとした解決策になりがち。 ● 「我々は、今なんの課題を解決しようとしているのか?」を問い続けよ う。 ○ 課題を常にシャープにしよう プロジェクトでの学び・アドバイス 1/4 41

Slide 42

Slide 42 text

ダブルライトは要らなかった説 ● readが旧DBのときに、新DBへのダブルライトのバグを見つけるのは至 難の業 ● ダブルライトやらずに、readロジックの移行の📱アップデートのタイミ ングでマイグレーションを走らせて、非同期でゴリっとデータを移行さ せるだけでよかったかも。 ○ 今選択している教材だけは同期的にクライアント側で移行してあげ れば、起動直後の学習体験は最低限担保できる プロジェクトでの学び・アドバイス 2/4 42

Slide 43

Slide 43 text

データの構造は最初にしっかり設計しよう! ● book > chapter > word の構造じゃなくて book > chapter, wordの構造に ○ chapterとwordを同じ階層に置く ○ 👉 book内の全wordを取得するのがもっと高 速化してた。 ● 後から変更しづらいので、最初の設計が大事。 プロジェクトでの学び・アドバイス 3/4 43 こんな感じにしておけば...!

Slide 44

Slide 44 text

セキュリティルール・強制アップデートは早めに入れておこう ● DBの移行関係ないですが、強制アップデートは少なくとも早めに入れて おくと便利です...! プロジェクトでの学び・アドバイス 4/4 44

Slide 45

Slide 45 text

改めて: mikanは絶賛採用中です! ● iOSエンジニア ● Androidエンジニア ● サーバーサイド・インフラ エンジニア ● デザイナー ( 時期を見て、週1回程度の出社を想定) 社員 (まずは副業からでもOK) ※基本リモート勤務 ご連絡はこちらから (Twitter DMでも!󰢏) https://mikan.link/carrers 45

Slide 46

Slide 46 text

ありがとうございました!! 46