$30 off During Our Annual Pro Sale. View Details »

小さなバグが生んだ悲劇、そこから学ぶ耐障害性の高いアプリ設計

Yosuke Imairi
September 02, 2023

 小さなバグが生んだ悲劇、そこから学ぶ耐障害性の高いアプリ設計

iOSDC Japan 2023 にて登壇した内容となります。
https://fortee.jp/iosdc-japan-2023/proposal/eb9d4449-4ff8-421d-9ffb-691179245d14

登壇のアーカイブ
https://www.youtube.com/watch?v=9GbG13-jMVM

Yosuke Imairi

September 02, 2023
Tweet

More Decks by Yosuke Imairi

Other Decks in Technology

Transcript

  1. © GO Inc. 自己紹介 2 プロフィール写真 GO株式会社 ユーザーシステム1グループ / 今入庸介

    タクシーアプリ『GO』のiOSアプリ開発 • 新機能開発 • アーキテクチャの選定・導入 • アプリ全体の設計、新機能開発 • CI 環境の構築・整備、自動化の促進 • チーミング、エンジニアの育成 @kamekiti
  2. © GO Inc. 〜 iOS アプリのリリースの翌日 〜 5 小さなバグが生んだ悲劇(タクシーアプリ『GO』での障害事例) iOS

    アプリ起動後に異常な数のリクエストが送られていることが判明 💥 (通常の数百倍のリクエスト…!ローディング状態が続き次に進めない) 社内で「一部のサービスへのリクエストが異常に減っている」との連絡 (どうやらタクシー配車依頼までユーザが辿り着けていない 🤔)
  3. © GO Inc. 8 障害時の対応がよくなかった 1. メンテナンス状態なのにアプリ側で適切な対応ができなかった ◦ メンテナンスモードに切り替わらなかった ▪

    アプリの初期段階からメンテナンスモード自体は実装されていた ◦ ユーザを混乱させてしまった ▪ これは障害?一時的に負荷が高い状態? ▪ ユーザ側の環境の問題?(電波が悪い etc..) 2. バグを含んだバージョンの利用を抑えることに時間を要した ◦ 修正版のアプリに更新してもらうことをただ祈るのみ…
  4. © GO Inc. • メンテナンス状態のときにユーザに現状の報告ができる場 ◦ アプリ内でユーザに現状を明確に伝える ▪ 障害なのか定期メンテナンスなのか ▪

    いつ復旧するのか • 復旧の目処が立たないという情報だけでも有益 ◦ WebサイトやSNSなどで障害情報を発信できるがあくまで補助的 • ユーザはアプリを使いたいのに使えない状態であることを肝に銘じる ◦ これはユーザにとって最悪の体験の1つとなる 11 メンテナンスモードで何を伝えられるとよいか
  5. © GO Inc. • 根本的な原因は、API に依存した設計になっていたこと ◦ メンテナンス状態かどうかを API 経由で取得していた

    12 なぜ『GO』はメンテナンスモードに切り替わらなかったのか iOSアプリから通常の数百倍のリクエストが送られる(バグ) GO のサーバや DB に負荷がかかり捌けなくなる アプリは API からメンテナンス状態を受け取りたいが、API のレスポンスが返ってこない アプリ上は通信中状態のまま(ユーザには何が起こっているか分からない)
  6. © GO Inc. • JSONファイルの取得に成功した場合のみ、その状態を適用させる • iOS / Android で別々のファイルで管理

    ◦ どちらか一方だけで障害が起きる可能性がある • サーバ・DB に依存しない場所でメンテナンス状態を管理する ◦ 『GO』の場合は GCS に JSONファイルを配置 ◦ メンテナンス状態かどうかを Bool 値で管理 13 メンテナンス状態かどうかの判断をどうやるか【障害時】 {"is_maintenance":false}
  7. © GO Inc. • 障害時と同様でもよいが、APIが相応の状態を返してもよい ◦ 定期メンテナンスを判断する API を用意 ◦

    機能の一部として利用している API の状態を利用 ▪ API ごとにハンドリングできるので、細かな制御がしやすい 14 メンテナンス状態かどうかの判断をどうやるか【定期メンテナンス】
  8. © GO Inc. • ユーザに対してアプリのバージョンアップを促す機能 ◦ ユーザはアプリを更新するまでアプリ内の機能が利用できない • 想定される利用シーン ◦

    アプリ内部にバグがあるバージョンの利用を抑制したい ◦ 古い機能のサポートを終了したい ◦ ユーザに届ける体験を統一したい ▪ ユーザによって利用できる機能に差がないようにする 16 強制アップデート機能とは?
  9. © GO Inc. ◦ needs_update_versions: 指定されたバージョンが対象 ◦ required_app_version: 指定されたバージョン未満すべてが対象 •

    『GO』の場合は、GCS に iOS / Android で個別の JSON ファイルを配置 ◦ メンテナンス状態の管理と同じ思想 • 強制アップデートを要求するアプリバージョンを記載する 18 強制アップデートが必要かどうかの判断をどうやるか { "needs_update_versions": ["6.7.0”, “6.7.1"] "required_app_version": "6.5.0" }
  10. © GO Inc. • アプリを起動した直後が理想 ◦ 可能な限り早い段階で判断したい ▪ アプリ内の機能が利用される前 ◦

    アプリ内部のバグはどこに潜んでいるか分からない 19 強制アップデートを要求するタイミング
  11. © GO Inc. • 今回の障害時のタイミングでは『GO』には強制アップデート機能がなかった ◦ 修正版をリリース後、アプリが更新されることを祈るしかなかった • Karte の接客テンプレートを使い、バグがあるバージョンに対して

    起動後にアプリのバージョンアップを促すようにした ◦ 告知は閉じられたので、やや強制力は弱め 20 余談:今回の事例で『GO』はどのようにアップデートを促進したのか?
  12. © GO Inc. • 同じ過ちを繰り返さないようにしたい ◦ なぜ今回の事象が発生したのかをチームメンバーと振り返る ◦ 根本的な原因は何だったのか? ◦

    どうすれば防ぐことができたのか? • 『GO』では今回の事例を受けて以下の対応をした ◦ メンテナンスモードの改善( サーバ / DB に依存しない設計) ◦ 強制アップデート機能の追加 ◦ ”目に見えないバグ” への対処(後述) 22 障害が落ち着いたら終わりではない
  13. © GO Inc. • 今回の障害の原因 ◦ コード誤り(ケアレスミス) • 今回の障害の詳細 ◦

    地図を動かしたり GPS の精度が変わったタイミングで、 意図しない通信処理が実行された ◦ GPS の精度はかなり頻繁に更新される ▪ 通常の数百倍のリクエストにつながった 23 『GO』の事例: ”目に見えないバグ” がリリースされた
  14. © GO Inc. 24 『GO』の事例: ”目に見えないバグ” がリリースされた • コードレビューで気づけなかったのか? ◦

    指摘がされていたが、修正されないまま Approve されマージされた • QA で気づけなかったのか? ◦ 現状の QA メンバーの規模ではサーバへの負荷にはならなかった ◦ 通信回数の確認までは行っていなかった • そもそも開発段階で気づけなかったのか? ◦ シミュレータでは再現しづらいバグで気づけなかった バグは様々な工程をすり抜けてリリースされてしまう…!
  15. © GO Inc. • 「目に見えない」ということが発見の障壁となった ◦ 目に見えるようにすれば気づきやすくなるのでは? • どれくらい通信されているかを可視化するデバッグモードを開発した ◦

    各 API がどれくらいリクエストされているかを一覧表示させる ◦ 前回のバージョンと比較して大幅な変化がないかを確認 25 『GO』の事例: ”目に見えないバグ” に対する再発防止策
  16. © GO Inc. 28 まとめ • 『GO』で起きた障害をもとに耐障害性の高いアプリ設計を検討した • メンテナンスモードの設計 ◦

    サーバ / DB に依存しない状態管理 ◦ 障害時と定期メンテナンス時では設計手法に差がある • 強制アップデート機能の設計 ◦ アップデートの要求は起動直後が理想 ◦ ユーザにアップデートを促すためにうまく誘導する • 根本的な原因の追究と対策 ◦ 同じ過ちを繰り返さぬようチームメンバーと対策を検討 & すぐ実行