けものみち

まったりと、きのむくままに。

アカツキ社のインターンに参加してきた

f:id:WhiteFox-Lugh:20210207205552p:plain

2/1~2/15 の平日10日間、株式会社アカツキ(以下、アカツキ)さんのインターンに参加させていただきました。このインターンにおいて、得た知見や経験、感じたことなどを本記事に記載しようと思います。

忖度などは一切なく、思ったことを素直に書いています。

今回行った業務の関係上、抽象的に書いてあり理解がしづらい部分があるかと思われますが、ご了承ください。

参加経緯

22卒新卒採用の選考過程で、インターン参加が決定しました。

自分はこれまでインターン経験がゼロであったため、働くイメージをもたないまま就職するとなると、万が一ミスマッチがあった場合、双方にとって不利益になるため、実際に働いてみて、業務内容や働き方、会社の雰囲気などをつかんでから意思決定をする方がお互いにとって良いだろうとのことでした。

アカツキさん自体は、大変失礼ながらサポーターズさんにご紹介いただくまで存じ上げておりませんでした。 そんな自分が、なぜアカツキさんに深く興味を持つようになったかというと、アカツキさんについて自分から進んで調べていき、ゲーム開発そのものや、制度面、企業の雰囲気に魅力を感じたこと、 面談や面接を通して、一緒にどういうエンジニアを目指すと良いか考えてくださったことが大きかったと思います。インターン参加が決定したときは純粋にうれしかったです。

面談

インターン実施1か月前ほどに、メンターさんとインターン内容について面談を行いました。ここでは、

  • インターンの日程
  • 機材の手配
  • 触りたい技術内容
  • 事前に勉強しておいたほうがよいこと

あたりの話をしました。

インターンの実装内容

タスク自体は3つで

  • ゲームのバージョンアップに伴って追加された要素を、既存の要素の仕様に合うように変更する
  • ユーザデータのキャッシュについて、不必要なアクセスを減らす
  • 内部ツールの改善

です。以下、それぞれについて簡単に記述します。

既存仕様に合わせる改善

インターンで最初にいただいたタスクです。

近年のゲームは、ゲームをリリースした後も新たに機能が追加されたり、アイテムなどの新要素が追加されることがよくあります。 この際に、先行でリリースされていた要素と、後発でリリースされた要素に関連性がある場合は、コード内で実装に一貫性をもたせておくことにより、メンテナンス性を高めることができます。

抽象的な書き方をしてしまったので、簡単に例を挙げておくと、ゲーム進行によりもらえる報酬やゲーム内通貨、アイテムなどの量を1単位としたときに、特定条件を満たしたときは多くもらえるように報酬の量を調整したり、アイテムを獲得する確率を通常より高くなるように調整したりします。この調整を、個々の要素ごとに倍率を一つ一つ値を定めて調整するのではなく、条件を満たした場合、すべての要素に一律の倍率補正をかけるようにすると、一貫性がとれてメンテナンスがしやすくなります。

今回、ある時期のアップデートで追加された要素が、その一律の倍率補正から外れた実装になっているので、 その部分を探し出し、修正を行ってほしいというのが最初のタスクの内容です。

はじめに、コードの調査を行いました。具体的には、GitHub のレポジトリ内を関連しそうなキーワードで検索したり、過去のコミットやプルリクからどういう変更があったかを読みました。 コードのどこかに原因があるからと、この課題の序盤ではほとんどソースコードを読んでいましたが、複雑な部分は社内 wiki に仕様書がおいてあるのでそれもあわせて読むと理解が早くなるとアドバイスいただきました。

この調査を進めていった結果、

  • Rails の settings.yml ファイルに定数が定義されており、特定条件を満たすと報酬がその定数倍になる
  • ゲーム内の抽選で、1つもらえるか、もらえないかが確率的に決定する要素が、特定条件を満たした場合、その定数に関わらず規定回数抽選、それ以外の場合は1回だけ抽選するようにコード内で固定されている

ということがわかりました。

この部分を settings.yml で定義された定数( n とする)を用いて、 n 回抽選するように書き換えます。 実装としては以下のコード(実際のコードと多少異なる)のような感じで、flag で特定条件を満たしているかどうかを判定し、 flagtrue なら n 回、false なら1回抽選という風にします。lottery は抽選を行い確率 ptrue (当たり)、確率 1-pfalse を返す関数です。

if (flag ? n : 1).times.any? { lottery(p) }
  (報酬がもらえる)
end

また、これが正しく動作しているかをテストするために、lottery の当選確率を0にして、lottery がぴったり n 回呼び出されるかというテストも rspec を用いて記載しました。

導入課題ということもあるので、タスクとしては取り組みやすかったのですが、エンジニアが積み重ねてきた数万コミットあるコードの中から、問題の原因となる部分を効率よく探す方法や、 実装に関連する部分を素早く的確に読むことが求められたと感じています。

キャッシュの不要なアクセスを減らす

2つ目のタスクです。

ユーザのアカウントやそれに紐づいた情報を取得したい場合、いちいちデータベースにアクセスするのは効率が悪く、ユーザの情報をキャッシュに保存しておくということをします。

キャッシュに置いてある情報は、ログイン日時の記録をする場合、ユーザが名前を変えた場合、ゲーム内で報酬を得てステータスが変更された場合、チートなどを発見して運営側がアカウントの凍結を行った場合など、さまざまな場面で更新する必要があります。しかし、データベースからレコードそのものを引っ張ってきてキャッシュに置くということをしている以上、キャッシュにおいてある情報自体もすべて利用するわけではありません。

既存のコードでは、キャッシュにおいてある、ユーザ関連の情報のうち、どれか1つの要素が書き換わったときにキャッシュにアクセスするという風にしていましたが、今回はキャッシュにおいてある情報のうち、実際に利用されるデータに変化があった場合のみキャッシュにアクセスし更新する、という実装を行いました。

実装としては、Rails の callback を利用し「特定のデータに変更があった場合、キャッシュを更新するフラグを立てて、キャッシュ更新処理を行う」という感じになりました。 擬似コードを書いておくと、

after_save do
  (キャッシュ関連のクラスのキャッシュ書き込みフラグを立てる) if (特定のカラムに変更があったかどうか)
end

after_commit { (キャッシュ書き込み関連の関数を呼ぶ) }

というイメージです。 キャッシュ書き込みフラグが立っているかどうかは、after_commit で呼び出される「キャッシュ書き込み関連の関数」内でチェックされます。

実装自体は簡潔ですが、キャッシュ周りの挙動を読むのがなかなか難しく、調査にかなり時間がかかりました。

次に、rspec を用いたテストを記述しました。

キャッシュの書き換えが行われる部分については、expect { subject }.to change { 対象の値 }.from(変更前の値).to(変更後の値) というコードで確認しました。 また、キャッシュに不必要な書き込みがないかのテストは expect { subject }.not_to change { 対象の値 }.from(元の値) というコードで確認しました。

自分は、最初 change を用いず、eq (変更後の値) と書いていました。コードレビューでは、何をテスト対象とし、どういう目的のテストなのかをしっかりコードで書き示すことを意識するようにご指導いただきました。

内部ツールの改善

3つ目のタスクです。

ゲームをつくる人はエンジニアだけではありません。プランナーさん、デザイナーさん、他社のエンジニアさんなど多くの人が関わってきます。 そういったときに、ゲーム内で利用されるデータを、開発者側で扱いやすい形で保存し、検索や取り出しができるようにすると、情報の共有がスムーズに行えるようになります。

今回は、ゲーム内のキャラクターに関する情報を、キャラクターID、名前など、さまざまな条件から検索できる内部ツールの改善を行いました。

実装した内容としては、

  • キャラ名の完全一致検索(部分一致は実装済み)
  • ID範囲検索
  • 指定したデータ、カラムだけ CSV 出力
  • キャラクターに関連する情報の表示

です。

キャラ名の完全一致検索は、Rails のデータベース検索における、where 句の基本的な使い方をそのまま利用すれば実装ができました。

ID範囲検索は、クエリでは 1-10 といったようにハイフンをつかって範囲を指定する形式で書くようにし、そのような形のクエリが来た場合は、 データベース検索で使えるように 1..10 といった形に変換処理を行った後でデータベース検索を行う、というように実装しました。

指定したデータだけ CSV 出力する機能については、今までの検索結果表示画面において、各キャラクターの情報の一番左側にチェックボックス☑を追加し、チェックが入ればそのデータを出力する、という風に実装を行いました。また、検索結果画面の最下部に、どのカラムを出力するか選択できるオプションを設置しました。

キャラクターに関連する情報の表示については、同じキャラクターでも、条件を満たすとより強いキャラクターに変化することがあるので、 現キャラクターの変化前、変化後の情報を表示してほしいという要望でした。

自分は、最初キャラクターのデータベーススキーマ定義からリレーションをたどり、変化前、変化後の情報などを定義したスキーマの情報をどう取り出すか考え、愚直に実装を行っていました。 メンターさんにコードや表示結果を見ていただいたところ、キャラクターの検索後、各キャラクターの詳細表示をするページがあるのですが、そこに現キャラクターの変化前、変化後の情報を表示する部分があり、そこの実装を参考にしてみるといいかも、というヒントをいただきました。このヒントをもとにコードを調査すると、現キャラクターのレコードを入れると、変化前、変化後の情報をとってきてくれるヘルパーメソッドが定義されていました。 その既存の実装を参考にすることによって、簡潔に実装を行うことができました。

時間の関係上、追加した機能におけるパフォーマンス面の改善までは手が届きませんでしたが、 ツールを利用する方々に Zoom ミーティングで UI、UX 面を確認していただきながら、要件を満たす機能を実装することはできたと思います。

学びなど

ゲーム開発について

ゲーム開発自体に興味はあったものの、実際に大規模なゲーム開発自体に携わったことはなく、いまいちイメージがつかめない状態でした。

今回のインターンでは、

  • どういう風にデータベースやコードの仕様が決定されているのかを仕様書やコードから把握すること
  • ユーザ数が多いサービスで、パフォーマンスをよくするためにどのような工夫がされているか

あたりは短期間ながら大まかにつかむことができたのではないかと思います。

今回のタスクでは、数日かけて調査して、書いたコードがたった数行というものがあります。それだけ、大規模なコードの中から不具合の原因や、改善点を調査するのに時間がかかるということです。また、個人開発と異なり、すべての仕様やコードを把握することはできないので、自分が担当する箇所のコードの挙動、やり取りされるデータの中身や型をしっかり把握することや、わからないことがあればほかの詳しい人に尋ねるというのも大事な行動の一つでありました。

スクラム開発

よく、「スクラムが流行りだ」とは最近耳にしていましたし、頭でもスクラム開発はどんなものかはふわふわとしたイメージながらもっていました。

実際にチームに所属してみると、円滑に開発を進めていくための様々な工夫として、

  • ツールを使ってタスクや進捗状況(未着手、着手、レビュー中、完了など)を可視化して管理している
  • 自身の進捗についてうまくいっている点や難航している点を正直に言語化し、チームで共有する
  • ペアプロやモブプロなどを適宜導入し、一人で悩む時間を減らすことや知見を共有し、開発効率を上げる
  • 業務のことだけでなく、朝会やランチ、夕会などで雑談をしてチームの交流を増やす

あたりは体験することができました。

コンプライアンス関連の話

開発そのものについて学ぶことも多かったのですが、インターン序盤で行われたオリエンテーションにおいて、開発を通じて知りえた情報の取り扱いについてのお話も個人的には大変勉強になりました。

エンジニアだとついつい自分が関連するプロダクトについて「俺はこんなことを作ってるぞ!」とか「このプロダクトのここが魅力的だ!」と語りたくなることもあるかと思いますが、そういった安易な書き込みが会社の発言ととられ、思わぬトラブルに発展してしまうおそれがあることや、リリース前の情報をネタバレしてしまい、企業の利益や信頼を損失する恐れがあるなど、コンプライアンス的な観点で学んだことも多かったです。

関わる人の多さ

たった10日間のインターンをとっても、Slack のチャンネルや 1on1 の Zoom ミーティングで手厚くサポートしてくださったメンターさんや、チーム内のエンジニアの方々、ほかの開発チームメンバーや、プロダクトマネージャー、自身の勤務を管理してくださった総務の方々、といったように関わった人はとても多かったです。

リモートワークという形態だったため、オフィスという物理的空間制約がなく、Slack のチャンネルを切り替えさえすればたくさんの人の会話や議論が見えたのも、人の多さを実感できた要因だと考えられます。そのためか、困ったときにメンションを飛ばすと、すぐに誰かが駆けつけてくれるという点はリモートワークの良い点だったのではないかと感じています。

最後に

インターン自体初めてで、刺激的な10日間を過ごせそうとワクワクしていた部分もありながら、自分の実装力不足や能力不足がひたすら露呈する10日間になるのではないか、と懸念もしていました。どちらかというと後者の方が気持ちとして強かったかもしれません。

参加して数日経った段階で開発そのものや、いろんな人とコミュニケーションをとっていくのが楽しいと感じていました。ものづくりをすることが好きなこと、人に喜んでもらうのが好きなこと、一日の大半をゲームに費やすことがあって依存症レベルでゲームが好きなことなど、様々な思いを巡らせ、自分自身ってこういうカラーを持っていたなと改めて考えることができたと思います。同時に、わからないことだらけで、自分の力不足も毎日痛感していました。メンターさんからは、できることは少しずつ増えていくものだから、焦らなくても大丈夫だよ、とアドバイスをいただきましたが、やっぱり力不足を感じると悔しいもので、空き時間で次の日は同じミスをしないように復習するというようなことをしていました。

また、ツイッター上で「お仕事いきたくない」など、自分自身のお仕事やライフスタイルに対して不満ばかりいう人を多く見かけていて、自分も将来こうなるのかな、と就活を通して懸念していた部分がありました。面談や面接だけだと、会社の雰囲気やライフスタイルってなかなかわからないもので、長期的に楽しくはたらくことができるか、そしてプライベートとお仕事を両立できるかについては、不安材料としてずっと残っていました。インターンを通してこの点は具体的なイメージをもつことができたので、大きな収穫だったのではないかと感じています。

最後に、インターン生として温かく迎えていただき、サポートしていただきました、すべての方々に感謝を申し上げたいです。本当にありがとうございました。