cocos2dx V2 から V3 への道のりとその先のMetalを見据えて 「大規模」リファクタリングでやっておくべきこと
大規模リファクタリング 岡田大介
View Slide
自己紹介 大規模リファクタリング
職務経歴 ・株式会社リコー (約10年) ・ココネ株式会社 最近の技術経歴 ・cocos2dx,Unity,ReactNative ・C++,C#,TypeScript 最近の趣味 ・卓球 ・ゴーストオブツシマ ・JustDance 岡田大介 クライアント開発/ポケコロ開発リーダー
大規模リファクタリングについて 「やってよかったこと/やるべきこと」 を中心に話します。
は じ ま り/ 背 景 大規模リファクタリング
はじまり ・10年ほど走ってきたスマホアプリで超スパゲッティコード え、100万行?? (ステップ数ではなく行数。。。とはいえ) ・何か機能を作るとすぐにどこか関係ない所でバグが出る。 ・結果開発コストが弊社の他アプリと比べても1.5〜2倍はコストがかかる 今の状況闇が深い
はじまり ・10年ほど走ってきたスマホアプリで超スパゲッティコード え、100万行?? (ステップ数ではなく行数。。。とはいえ) ・何か機能を作るとすぐにどこか関係ない所でバグが出る。 ・結果開発コストが弊社の他アプリと比べても1.5〜2倍はコストがかかる なんとかしたいと思いつつ、 サービス最優先 直そうとチャレンジしようとした人は なぜかいつも他の部署へ異動。。 今の状況ビルド時間が長すぎ!! 技術力とは別のほぼ職人芸 影響度読めない 闇 闇が深い
はじまり ・2018/6 Apple、ios/macで グラフィックスAPI「OpenGL」非推奨を宣言 Apple社の「Metal」への対応が必須へ
はじまり cocos2dx version2ベース (cocosの最新は4でMetal対応されている) アイテム、UIに弊社独自のファイル形式を使用、描画処理にダイレクトにOpenGLを使用 アプリの状況Metal対応するしかない!
はじまり 開発リーダーしつつ、Metal対応へ向けてリファクタリングも行うことに 「このアプリの開発リーダーとMetalへの対応よろしくね」 「はい。。。(まじか)」
やるべきこと やってよかったこと 大規模リファクタリング
やるべきこと/やってよかったこと 計画について
やるべきこと/やってよかったこと① 関係者へ理解を求めること 関係者 = 事業の責任者(事業部長、社長、各職種リーダーなど)、CTO、サービスへの決定権を持つ人 リファクタリング「する」ことへのリスク リファクタリング「しない」ことへのリスク 計画の共有 このあたりをちゃんと説明すること 開発以外の人からも計画や 人員計画などのアイディアが出る 運用中のサービスの大規模リファクタリングとなると影響度が尋常じゃない!! が、やらないと将来的にまずい!!!
やるべきこと/やってよかったこと② 計画は絶えず見直す 当初の計画 年末から開発者全員(10人以上)でサービス開発を止めて cocos2dxV2→cocos2dxV4(Metal)へ変更 4ヶ月 大きい 計画変更 cocos2dxV2→V3へ変更次の計画変更 次の年の4月から次の次の計画 サービス開発を止めずに対応次の次の次の計画 対応メンバーは少数精鋭約4人で
やるべきこと/やってよかったこと② 計画は絶えず見直す 目標 期間 チーム体制 全部見直しが必要 見直し項目特に期間は関係者への共有は必須 (いいづらいけども) 最初にとても見積もりきれない 絶えず見直しと関係者への共有を忘れない 場合によってはゴールの見直しも必要
やるべきこと/やってよかったこと③ 目標をたてていくこと 小さい ex. ・技術課題の管理 ・TableViewの差し替え、37/149 達成など ・Popup画面の対応 50/85 細かくタスクを管理し、達成具合を記載していくこと ・普通のプロジェクト以上に見える化が大事、周りから見ると何やってるか分からない ・モチベーションの持続 正直リファクタリングを長時間ひたらすらやるのはツライ
やるべきこと/やってよかったこと④ チーム体制 リファクタリングということで中々別チームを作るのは難しいですが リファクタリングに集中できる環境を作ること ・チームをサービス側とリファクタリング側で分割 ・互いにリーダーを立てた サービス開発を止めずに対応するということで。。 リファクタリングリーダー(V3) サービスリーダー(V2)
開発について
やるべきこと/やってよかったこと⑤ Debug機能の充実化/開発環境の充実化 ・ログ機能 ・画面レイヤー構造の確認ツール ・アカウントの切り替え機能 ・アバター確認 ・UI確認機能 ・Androidバックキー などなど マシンを差し替え ex.Rizen9マシンにしたらAndroidは2倍 ex.M1Macもビルド時間の向上に ・バグは大量に出るので、原因究明できるようにしておく。今後の役にも立つ ・開発速度を早くする工夫。cocos2dxは。。 ・ビルドマシンは状況によりけりですが、良くできるならすべき
やるべきこと/やってよかったこと⑥ 断捨離 ・機能の削除 ・ソースコード(クラス、関数)の削除 対応箇所をいかに減らすか ビルド時間の向上にもつながる
やるべきこと/やってよかったこと⑥ 常にマージ/最新の状態を保つこと ・Gitで管理 ・サービス側がリリースされる度にマージ ・一度乖離すると非常に難しい ・ソースコードは意外となんとかなる ・Xcodeのプロジェクトファイルが結構厄介 マージツールとしてmergepbxを使用 XcodeGenも今後検討したいところ
やるべきこと/やってよかったこと⑦ テストできる設計を目指す ・シングルトンの嵐 ・依存関係がすごい ・SOLID原則に反しまくっている ・色々な所でベースのLayerクラスをdynamic_castして処理を判断している ・せめてプラットフォーム部分はinterfaceを分離。 ・シングルトンではなくサービスロケータに。 ・DI的にするのは中途半端に終わりさらなるカオスになりそうと判断 ・画面遷移の仕組みも修正 全部は直せない、何年かかるか分からん!!
やるべきこと/やってよかったこと⑦ テストできる設計を目指す とりあえずinterfaceは分離しよう、具象クラスであるシングルトンも自然に減るだろう。。その上に変更もしていくCP1AssetManagerCP2AssetManagerIAssetManagerLocatorSingleton
やるべきこと/やってよかったこと⑦ テストできる設計を目指す CP1AssetManagerSingletonClient::funciton{ CP1AssetManager::getInstance().download("main_asset");}
やるべきこと/やってよかったこと⑦ テストできる設計を目指す CP1AssetManagerIAssetManagerLocatorSingleton注入Client::funciton{ CPLocator::AssetManager()->donwload("main_asset");}Init { IAssetManager assetManager = CP1AssetManager::getInstance() CPLocator::provide(assetManager );}開発途中ではシングルトン状態とサービスロケータを同居Client2::funciton{ CP1AssetManager::getInstance().download("main_asset");}開発途中利用者1 利用者2
やるべきこと/やってよかったこと⑦ テストできる設計を目指す CP1AssetManagerIAssetManagerLocatorSingleton注入Client::funciton{ CPLocator::AssetManager()->donwload("main_asset");}Init { IAssetManager assetManager = new CP1AssetManager() CPLocator::provide(assetManager );}Client2::funciton{ CPLocator::AssetManager()->donwload("main_asset");}全部置き換えたらSingletonなくす開発途中
やるべきこと/やってよかったこと⑦ テストできる設計を目指す CP2AssetManagerIAssetManagerLocator注入Client::funciton{ CPLocator::AssetManager()->donwload("main_asset");}Init { IAssetManager assetManager = new CP2AssetManager() CPLocator::provide(assetManager );}Client2::funciton{ CPLocator::AssetManager()->donwload("main_asset");}CP1AssetManager(cocosv2用)をCP2AssetManager(cocosV3用)に差し替えCP1AssetManager
やるべきこと/やってよかったこと⑦ テストできる設計を目指す BaseLayer+restoreMyAvatar()+nativeEventReceive()+block()+unblock()+onMovePlanet()+startAvatarIdleAction()+stopAvatarIdleAction()+show()・・・Fatなクラス ベースのクラスの割に色々と機能が 詰め込まれている状態
やるべきこと/やってよかったこと⑦ テストできる設計を目指す BaseLayer+show()・・・LayerAvatarInterface+restoreMyAvatar()+startAvatarIdleAction()+stopAvatarIdleAction()BlockInterface+block()+unblock()PlanetInterface+onMovePlanetまずはとにかく分割
やるべきこと/やってよかったこと⑦ テストできる設計を目指す BaseLayer+show()・・・LayerAvatarInterface+restoreMyAvatar()+startAvatarIdleAction()+stopAvatarIdleAction()BlockInterface+block()+unblock()PlanetInterface+onMovePlanet一部しか使っていないものは具象クラスの方へ移動PlanetLayer・・・
やるべきこと/やってよかったこと⑦ テストできる設計を目指す BaseLayer+show()・・・LayerAvatarInterface+restoreMyAvatar()+startAvatarIdleAction()+stopAvatarIdleAction()BlockInterface+block()+unblock()PlanetInterface+onMovePlanet使わなくなったものは削除PlanetLayer・・・
やるべきこと/やってよかったこと⑧ いじらせない ・interface分離 いっそのこと ・ライブラリ化 (モジュール化) ソースコードを書く以上だんだんおかしくなっていく
まとめ 大規模リファクタリング
一番重要なこと ・大規模リファクタリングの場合、関係者への理解は必須 痛感したこと ・最初からリファクタリングは定期的にしておくべき ・新規アプリで余裕なくても2年すぎたらやるべき #2年すぎてサービスが続いているならば、まだサービスは続くと思うので ・プラットフォームは定期的にバージョンアップすべき(cocosにしろunityにしろndkでも) まとめ
Q & A
ご静聴ありがとうございました!!