Upgrade to Pro — share decks privately, control downloads, hide ads and more …

ガチャを1から作り直した話 ─規模の拡大につれて開発速度を落とさないための取り組みについて─

Genta Kamitani
August 05, 2020
4.5k

ガチャを1から作り直した話 ─規模の拡大につれて開発速度を落とさないための取り組みについて─

Genta Kamitani

August 05, 2020
Tweet

Transcript

  1. ガチャを1から作り直した話
 開発本部 CTO室 SREグループ 神谷 元太
 BIT VALLEY 2020
 プレイベント#2


    ─規模の拡大につれて開発速度を落とさないための取り組みについて─

  2. 自己紹介
 • 神谷 元太
 • 開発本部 CTO室 SREグループ
 • モンスターストライク(以下モンスト)担当


    ◦ サーバーサイドの開発 ◦ 負荷軽減 ◦ 可視化 ◦ 自動化 ◦ etc • 趣味: コーヒーを淹れること
 ◦ 最近エスプレッソマシンを購入
  3. なぜ負債を解消するのか
 • 解消しないと長期的に見て破綻するから
 ◦ 潜在的なバグのリスク ◦ 開発速度の低下 ◦ セキュリティリスク ◦

    サポート切れ ◦ etc. • 踏み倒せなくなったから
 ◦ 何年持つかわからない場合、クローズまで耐えて「踏み倒す」選択も ▪ 流行るかわからない状態と流行った後では取るべき戦略が異なる ◦ モンストは今年で7周年 ◦ 10周年を無事に迎えることを見据えて動かなければならない
  4. モンストにおけるガチャの立ち位置
 モンストにおけるガチャ
 • 売り上げの要
 • モンストをプレイする上での重要な体験の一つ
 
 
 万が一ここが壊れた場合
 •

    ユーザーに金銭的な損失
 • ユーザーからの信頼を大きく失う
 ◦ モンストだけでなく、ミクシィ全体も ◦ コラボ先やソシャゲ業界全体にも影響
  5. ガチャの現状
 度重なる改修により複雑化
 バグると大事なので、大胆な変更ができない
 ◦ 「そのときの変更量が最小になるように」の積み重ねでカオス化 ◦ 大胆なリファクタリングも行われない 
 結果
 ◦

    コードパス上は存在するが、意図して作られたのかよくわからない機能 ◦ あらゆる場所にガチャの種類に関するif文が散らばる ▪ ある種類のガチャの挙動を追うのに、ガチャ周りのコードを全部読む必要がある ◦ ある種類のガチャの挙動の変更が、他の種類のガチャにも影響しうる
  6. モンストのガチャについて
 本来はシンプルなしくみ
 重み付きのランダム抽選
 
 
 ではなぜ複雑化したのか?
 
 
 ガチャの種類が増えすぎたから
 def

    gacha items = [ {value: "はずれ", weight: 10}, {value: "あたり", weight: 1}, ] weight_sum = items.sum do |x| x[:weight] end r = Random.rand(weight_sum) items.each do |item| r -= item[:weight] return item[:value] if r < 0 end end
  7. モンストのガチャの種類
 再設計を始めた時点で12種類の抽選ロジックが存在
 ◦ 普通のガチャ ◦ ホシ玉 (レアなキャラだけが出る) ◦ 初ゲ確定ガチャ(持ってないキャラだけが出る) ◦

    アゲインガチャ(10連 × N回引けて、Nに応じて確定枠が出現) ◦ etc... この時点まで明確な設計方針や、責務の分割などは無かった
 
 
 カオスな状態に

  8. 原理・原則
 今回紹介する原理・原則
 ◦ Program Intently and Expressively ◦ Open-Closed Principle

    ◦ 関心の分離 ◦ ポリシーと実装の分離 ◦ インターフェースと実装の分離 参考文献: 『プリンシプル オブ プログラミング 3年目までに身につけたい 一生役立つ 101の原理原則』
 ◦ よく使われる原則や考え方がコンパクトにまとまっている ◦ 読みながら方針を立てたわけではないが、説明するのに便利
  9. Program Intently and Expressively (PIE)
 意図を表現できるようなコードにする
 • どう動くか
 • 何をするか


    • 何をしないか
 
 具体的に何をすべきか
 • 適切な変数名
 • 関心事に応じた責務の分離
 • シンプルなインターフェース
 参考:『プリンシプルオブプログラミング』 p.43
  10. OCP: ガチャの場合
 抽選ロジックにおけるOCP:
 新たな抽選ロジックの作成
 抽選ロジックに対応したクラスを追加する
 既存の抽選ロジックの変更
 抽選ロジックに対応したクラスだけに修正を加える
 
 解決できる問題
 •

    至るところにif文が乱立
 • あるif文の中身を変更すると、何種類かの抽選ロジックに影響する
 • コードパス上は存在するが仕様としては存在しない抽選ロジックが誕生

  11. 関心の分離
 別な関心事( = 機能 or 目的)に関係するコードは、別な場所に置く
 • 別な機能は別な場所に
 • ビジネスロジックとDB操作は別な場所に


    • ビジネスロジックとリクエストパラメーターの処理は別な場所に
 
 分離することによるメリット
 • やりたいことに対応するコードが見つけやすくなる
 • 変更が関係ない部分に及びにくくなる
 • テストもしやすい
 参考:『プリンシプルオブプログラミング』 p.89
  12. 関心の分離: ガチャの場合
 ガチャの場合の関心事
 • 抽選ロジック
 • 抽選ロジックを決める部分
 • 排出候補をどうやって取得するか
 •

    出玉をどう表示するか
 • etc...
 
 解決できる問題
 • 抽選ロジックを決める部分と抽選ロジック本体が一つに
 

  13. ポリシーと実装の分離
 コアになるロジックと、ソフトウェアの前提を分離
 
 分離されていない例:
 • adminユーザーは、全ての情報を閲覧できる
 分離されている例:
 • ユーザーは、情報に関するアクセス権を持っている
 •

    ユーザーは、アクセス権に応じた情報のみ参照できる
 • あるユーザーをadminに指定すると、そのユーザーには全ての情報の閲覧権 限が付加される
 参考:『プリンシプルオブプログラミング』 p.93
  14. インターフェースと実装の分離: ガチャの場合
 ガチャの場合のインターフェース
 • 抽選ロジック
 
 ガチャの場合の実装
 • 個々のガチャの種類ごとの抽選ロジックの実装
 


    解決できる問題
 • 12種類の抽選ロジックの全てが同じクラス、同じメソッドに
 • 似たような機能が多いが、微妙に抽象化しきれていない
 • 抽選ロジックを単体でテストするのが困難

  15. 重要視しなかった原則
 DRY (Don’t Repeat Yourself)
 同じコードを何度も書いてはいけない
 
 重要視しなかった理由:
 • OCPや関心の分離等と相反する部分は、前者を優先


    • 「見た目が同じ」からといって「変更の理由が同じ」とは限らない
 
 再設計の結果、コードは冗長になり、行数は増えた
 ※ インターフェースの統一により逆にDRYになった部分もある
 参考:『プリンシプルオブプログラミング』 p.34
  16. 構成図(after)
 Class Module (Interface) 依存 実装 (呼び出し元) 抽選FacadeのFactory 抽選ステートマシン 排出結果の表示方法

    抽選Facade 個々の 抽選ステートマシン 個々の排出結果の 表示方法 具体的な 抽選Facade
  17. 移行方針
 小さな変更をインクリメンタルに行う
 ◦ 小さい変更なら、コーナーケースの見落としの可能性は低い ◦ バグった場合の影響も小さく、影響範囲の予想も容易 常に動き続ける状態を保つ
 ◦ 先にインターフェースだけ定義 ▪

    中身は既存の実装の単なるラッパー ▪ この時点で新インターフェース経由でしかガチャは実行されないようにする ◦ 一つ一つ中身のロジックを移行 ▪ ガチャの種類ごとの仕様をテストに落とし込む ▪ インターフェースはそのままに、中身のロジックを書き換える ▪ テストが常に通る状態のまま、新しい実装に移行 仕様なのか曖昧な部分
 ◦ 都度企画サイドと確認を取りつつ、仕様も整理
  18. 結果
 リリース後から現在まで、このリファクタリングによるバグは出ていない
 
 抽選ロジックの追加や変更が格段にシンプルに
 ◦ 抽選ロジックの追加 → 新たな抽選ロジックに対応するクラスを作成 ◦ 抽選ロジックの変更

    → 既存の抽選ロジックに対応するクラスの修正 
 その後も、抽選ロジックは数ヶ月に一つのペースで増え続けている
 ◦ 再設計を行っていなければ、さらに変更の困難なコードになっていた可能性が 高い
  19. まとめ
 • モンストの重要な機能の一つであるガチャを再設計した
 • 地味ではあるけど重要な考え方
 ◦ 問題に対する深い理解 ◦ 根底にある原理原則に充実に •

    いかにして変化を恐れない状態を作るか
 ◦ 気持ちだけではだめ ◦ リスクを最小化しつつ変化を続ける • 解消しないといけない負債はまだまだある
 ◦ 今適切だと思っているものも、将来的に負債になる可能性はある ◦ その時点での最適な手段を適用し続ける戦い
  20. Further Reading
 • 『プリンシプル オブ プログラミング 3年目までに身につけたい 一生役立つ101の 原理原則』上田勲 著,

    秀和システム (2016) • 『リーダブルコード: より良いコードを書くためのシンプルで実践的なテクニック』 ダ スティン・ボズウェル、 トレバー・フーシェ 著, 角 征典 訳, オライリー・ジャパン (2012) • 『Software Architecture is Overrated, Clear and Simple Design is Underrated』 https://blog.pragmaticengineer.com/software-architecture-is-overrated/amp/