GameWith Developer Blog

GameWith のエンジニア、デザイナーが技術について日々発信していきます。

ゲームをより楽しむための攻略ツール開発コンテストを開催! #GameWith #TechWith

はじめに

こんにちは。GameWith のエンジニアの tiwu です!

今回のブログは現在エントリー募集中のツール開発コンテストについて紹介していきます。

ツール開発コンテスト

f:id:tiwu:20211011120826p:plain

この度 GameWith では、ゲーム攻略のためのツール開発コンテストを開催いたします。

最優秀賞の賞金は何と100万円、18歳以上であればどなたでもご応募いただけます!

開催の背景

GameWith は“ゲームをより楽しめる世界を創る”をミッションとして掲げています。

GameWith がユーザーがよりゲームを楽しめるツールを提供するだけでなく、ユーザー自らがゲーム攻略ツールを開発し、それを世の中に公開する機会を提供することで、ミッションの実現を目指しています。

スケジュール

  • エントリー期間:2021/9/17~2021/10/21
  • 応募締切:11/15
  • 結果発表:11月末~12月初頭(予定)

※延長可能性あり

賞金

最優秀賞 100万円

優秀賞  50万円

特別賞  10万円

応募方法・詳細

応募方法や詳細はこちらの特設サイトをチェックしてください!

tool-contest.gamewith.co.jp

GameWith とツール

本ブログでも過去にいくつかツールについて執筆しています!

tech.gamewith.co.jp

tech.gamewith.co.jp

Firebase と GDS(GameWith Design System) により以前と比べ高速な開発が可能になり、現在もいくつかツールを開発をしています 👍

GDS についてはこちらを御覧ください。

tech.gamewith.co.jp

終わりに

「応募したい!」「応募するか悩んでいる…」という方は、まずはエントリーをお願いいたします!

Twitter

Twitter にてテックブログの投稿をツイートしていますので、よろしければフォローをお願いします!

twitter.com

Wanted!

一緒に働く仲間(特にサーバサイドエンジニア)を絶賛募集中です!

以下 Wantedly のページからぜひカジュアル面談へお申し込みください!

www.wantedly.com

コネクテッドシート導入時に意識したい料金について #GameWith #TechWith #ConnectedSheet

はじめに

GameWithディレクター兼分析チームの@fujii86です。 Googleのグループウェア機能の1つ、コネクテッドシートを社内で活用するにあたり、気になる料金面についてまとめていきます。

Connected Sheetとは

Googleが米国時間2020年6月30日「G Suite」ユーザーに向けて一般提供を開始した、Spreadsheet上でBigQueryデータを取り扱うことができるサービスです。
Google WorkspaceのEnterpriseサービスプランでのみ活用が可能となります。

SQLを活用せず、データをインポートするかのようにBigQueryデータを連携し、取得したデータを「グラフ」「ピボットテーブル」「関数」等の形式で集計表示する事が可能です。 ※より高度な事もできます

経緯

GameWIthはGoogle のグループウェア「Google Workspace(旧G Suite)」を活用しておりますが、最新の機能を取り入れ業務の効率化やセキュリティ強化を図る為、先日サービスプランを「Enterprise」へアップデート致しました。

アップデートに伴い多くの機能が追加されましたが、その中にある「コネクテッドシート」は、スプレッドシートでBigQueryデータを関数やグラフ、ピボットテーブルなどを使用して分析できます。ただ注意点として、BigQuery はクエリを実行する度に都度課金が発生するため、仮に全社員が毎日全データを select するといったことが起きてしまうと利用料金が跳ね上がってしまう可能性があります。

そこで、私のほうでコネクテッドシートの料金周りの調査を行う事となり、その調査結果を共有させて頂く形となりました。

結論としてビッグデータを多くの人が活用できるようにするという観点に置いて、とても素晴らしい機能である為、是非読んでる皆様においては料金の発生箇所を把握して運用に踏み出して頂けたら幸いです。

料金に関しての調査結果

料金が発生する場所

  • 連携データから「グラフ」「ピボットテーブル」「関数」「計算された列」「列の統計情報」の適応(作成)をする時
  • 作成した「グラフ」「ピボットテーブル」「関数」「計算された列」「列の統計情報」の更新をする時
  • カスタムクエリでbigqueryと連携をする時

料金が発生しなかった場所

  • データ連携時
  • 異なるアカウントでの接続時
  • Spreadsheet開閉時

料金を軽減する為に意識したい事

  • 更新タイミングをデフォルトの手動同期で活用していくこと
  • 適応又は更新時には表示されている処理量を確認すること
  • カスタムコスト管理の作成を実施すること

補足

  • 自動同期を活用したダッシュボードを作成する場合は、管理が行いやすいデータポータル活用を推奨

コネクテッドシートが有効なケース

  • SQL知識が無くてもビッグデータの分析が行える為、企画職等が直接分析を行いやすい
  • 手動更新が行える為、今後継続して活用するかわからない分析のテスト運用を行いやすい

機能概要

コネクテッドシートの具体的なイメージが沸かないかと思うので、機能の紹介です。
コネクテッドシートは、スプレッドシートから活用することができます。

①データコネクタでbigqueryと連携を行う
指定するテーブルの情報は別途活用者向けにまとめておく必要があります。 f:id:fujii86:20211006190528p:plain

②連携シートから活用したい形式を選択
主にはグラフ、ピボットテーブル、関数を活用していきます。 f:id:fujii86:20211006190935p:plain

③グラフの設定を埋めていく
ピボットテーブルは設定項目が異なり、関数は設定方法が異なります。
下図はグラフの一例となります。 f:id:fujii86:20211006191430p:plain

④データの更新時間の設定を確認する
手動更新を推奨してますが、自動更新についても触れておきます。 f:id:fujii86:20211005161828p:plain

最後に

ここまでお読み頂きありがとうございました。
活用次第では素敵な機能だと感じておりますので、より多くの企業に導入され、より多くの人が活用できることを願っております。

Twitter

Twitter にてテックブログの投稿をツイートしていますので、よろしければフォローをお願いします!

twitter.com

Wanted!

一緒に働く仲間(特にサーバサイドエンジニア)を絶賛募集中です!

以下 Wantedly のページからぜひカジュアル面談へお申し込みください!

www.wantedly.com

GDS のビルド方式をパッケージビルド方式に変更しました #GameWith #TechWith

はじめに

こんにちは!スケールアーキテクトチームの @53able, @inosy22, @nog です!

今回は GDS のビルドの構成を変更したので、紹介していきたいと思います!

GDS についてはこちらのブログで紹介していますので、御覧ください。

tech.gamewith.co.jp

課題

GDS での開発も1年半が経ち、いくつか課題が見えてきました。

ビルド成果物が一つにまとまっているため、下記のような課題があります。

  • 開発/リリースの度に他の機能への影響を及ぼす可能性がある
  • 不要な箇所で不要な機能まで読み込まれていることが多く、無駄な通信が発生している
  • また、リリースの度にバージョンが変わるため変更をしていない箇所の JS のキャッシュが消え再読み込みが走り、無駄な通信が発生している
  • 開発時にバージョンがよくコンフリクトする
  • ビルドにかかる時間が長くなってきた

これらの課題を解決するためにビルド成果物を1つにまとめず、パッケージ単位(幾つかのコンポーネントをバンドルした単位)で分割してビルドする方式に変更をしました。

今までのビルド方式

ビルドターゲットを全てのコンポーネントにしており、ビルド成果物を1つにまとめていました(ビルドは下記コマンドを叩くイメージです)

$ yarn build

生成されるパスは以下のようになっており、ひとつのバージョンに対してビルドする度に全てのコンポーネントが更新されていました。

gds/${version}/gds.min.js
gds/${version}/gds.0.min.js
gds/${version}/gds.1.min.js
gds/${version}/gds.2.min.js
gds/${version}/gds.3.min.js
...

パッケージビルド方式

変更後のビルド方式はパッケージ単位でビルドし、更新するコンポーネントを減らしました。

$ PKG=Sample1 yarn pkg
gds-packages/sample1/${version}/gds-sample1.min.js
gds-packages/sample1/${version}/gds-sample1.0.min.js
gds-packages/sample1/${version}/gds-sample1.1.min.js
gds-packages/sample1/${version}/gds-sample1.2.min.js
gds-packages/sample1/${version}/gds-sample1.3.min.js
...

$ PKG=Sample2 yarn pkg
gds-packages/sample2/${version}/gds-sample2.min.js
gds-packages/sample2/${version}/gds-sample2.0.min.js
gds-packages/sample2/${version}/gds-sample2.1.min.js
gds-packages/sample2/${version}/gds-sample2.2.min.js
gds-packages/sample2/${version}/gds-sample2.3.min.js
...

パッケージビルド方式では、パッケージ単位でバージョンを管理しているため、上記のようにパッケージ名がパスに含まれています。

CI/CD 環境

今までのバージョンの指定は package.jsonversion を利用していました。

パッケージビルド方式ではパッケージ名ごとにバージョンが違うため、各パッケージのディレクトリに .version ファイルを設置し利用しています。

今までの CI/CD では差分などはチェックせず毎回ビルドを行っていました。

パッケージビルド方式では開発中の CI/CD では git diff を行い master ブランチの差分を元にデプロイの対象を選定します。また、 .version ファイルが設置されていない場合はリリース対象になりません。

本番環境の CI/CD では S3 にデプロイされているバージョンと比較し、大きい場合のみデプロイされます。

苦労した点

複数のパッケージを同じページで読み込むと、一部のパッケージの WebComponents が使えないケースがありました。

この原因を調査して解決するには、 vue-cli によってビルドされた成果物が、WebComponents を使えるようにするための、内部の構造を理解する必要があったので、紹介させていただきます。

新しいパスを見て気づいた人もいるかもしれませんが、ファイル名にもパッケージ名を入れています。

すでに ${package-name}/${version} のようにパスにパッケージ名を入れているので、ファイル名はどのパッケージ名でも gds.min.js にしてもよいはずです(わざわざパッケージ名を入れる必要はありません)。

しかし、ファイル名にパッケージ名を入れているのは理由があります。

vue-cli からビルドされた成果物の内部コードでは、ファイル名をキーにして window にオブジェクトが新しく作られます。

そのため、ファイル名を gds.min.js で共通にしてしまうと、window["gds_jsonp"] に対して上書きし合うため、バグの温床になってしまいます。

ファイル名にパッケージ名を入れることで gds-xxx.min.js になりますが window["gds-xxx_json"] になるため、上書きを回避できます。

$ vue-cli-service build --target wc-async --inline-vue --name gds ...
(window["gds_jsonp"] = window["gds_jsonp"] || []).push([[0], ... }]);

$ vue-cli-service build --target wc-async --inline-vue --name gds-sample1 ...
(window["gds-sample1_jsonp"] = window["gds-sample1_jsonp"] || []).push([[0], ... }]);

http:// https://cli.vuejs.org/guide/build-targets.html#async-web-component

最後に

ブログの冒頭であげていたこれらの課題は、パッケージビルド方式にすることで解決されました!

開発/リリースの度に他の機能への影響を及ぼす可能性がある

👉 パッケージごとにビルドされるため、他のパッケージに影響を及ぼすことはありません。

不要な箇所で不要な機能まで読み込まれていることが多く、無駄な通信が発生している

👉 必要なページで、必要なパッケージを読み込むようにすることで、通信量を削減できるようになりました。

また、リリースの度にバージョンが変わるため修正をしていない箇所の JS のキャッシュが消え再読み込みが走り、無駄な通信が発生している

👉 リリースが行われても対象外のパッケージのバージョンは変わらないため、引き続きがキャシュが有効になり無駄な通信は発生しなくなりました。

開発時にバージョンがよくコンフリクトする

👉 パッケージ単位で .version が定義されるため、コンフリクトがほぼ発生しなくなり分担して作業が行いやすくなりました。

ビルドにかかる時間が長くなってきた

👉 概ね30秒程度短縮されて、1分以内でビルドが完了するようになりました。

パッケージビルド方式での課題としては、以下があります。

  • 引き続きバージョンは手動で書き換える必要がある
  • 実は全てのコンポーネントをパッケージビルド方式に変更しておらず、新旧のビルド方式が混ざっている
  • 全パッケージで利用しているファイルを修正した際に、全パッケージのバージョンを変更する必要がある
    • パッケージ毎にビルドできるメリットの裏側的な
  • パッケージを横断する修正を行った場合に、複数パッケージをリリースする必要がある

今回の改修でコンポーネント実装がよりスケールできるようになりました!

今後もより良いコンポーネントを作り、GameWith を改善していきます!

Twitter

Twitter にてテックブログの投稿をツイートしていますので、よろしければフォローをお願いします!

twitter.com

Wanted!

一緒に働く仲間(特にサーバサイドエンジニア)を絶賛募集中です!

以下 Wantedly のページからぜひカジュアル面談へお申し込みください!

www.wantedly.com

GameWith の フロントエンド を TypeScript へマイグレーションする #GameWith #TechWith

はじめに

こんにちは!Incremental Stream Team の @53able です!

今回は現在自分が着手中の TypeScript マイグレーション PJ について書いていきたいと思います!

GameWith の JavaScript

まず GamewWith の JavaScript について紹介していきたいと思います。

JavaScript のボリューム

Cloc というツールを利用し JavaScript のコード行をカウントしてみました。

github.com

brew 経由でインストールし、cloc ディレクトリ名 を実行すれば、カウントしてくれます。

brew install cloc
cloc /Gamewith

結果は3万6千行でした!

JavaScript のビルドフロー

次に、ビルドフローについて紹介します。

GameWith は Gulp を利用して JavaScript を連結・圧縮しています。

gulpjs.com

GameWith メインリポジトリでは Vue, React といったフレームワークは導入しておらず、jQuery を利用しています。

※GameWithDesignSystem のリポジトリでは Vue を利用しています

tech.gamewith.co.jp

TypeScript 化

今回 GameWith の3万6千行を一度に TypeScript 化し、リリースするのは現実的ではないと判断しました。

そのため、ファイル単位で TypeScript 化して修正をするという影響範囲を小さくしたマイグレーションのフローにしました。

新しいビルドフロー

TypeScript 化の際に長年積み上げてきた Gulp で行っているビルドフローの改修はとてもリスクが大きいため、Gulp は引き続き利用することにしました。

f:id:tiwu:20210830150127p:plain

新しいビルドフローは Gulp の前に TypeScript を割り込ませ、既存のフローをできるだけ変更せず導入を考えています。

JavaScript を中間コードとして扱う発想でこの新しいビルドフローを考えました。

マイグレーションの作業フロー

JavaScript -> TypeScript は ts-migration というツールを利用しました。

github.com

今回マイグレーションの作業フローを作るにあたって、以下の作業を行いました。

  1. ts-migration をいくつか適当なファイルに実行
  2. 生成された TypeScript のファイルを ESLint で整形し既存の JavaScript のファイルと目で差分のチェック
  3. 次に、自動化したいフローを洗い出すため手動でファイル操作を行いました
    1. ts-migration はフォルダをターゲットに動作するため、変換用フォルダへ JavaScript をコピーする
    2. ts-migration をフォルダ指定で実行し TypeScript へマイグレーション
    3. マイグレーションで変換後の TypeScript を元の JavaScript と同じ場所へコピーする
    4. TypeScript を tsc でコンパイルし、オリジナルの JavaScript が上書きされる ( ここで JavaScript 👉 TypeScript 👉 JavaScript の差分がわかる)
    5. TypeScript 化された JavaScript は ESLint 対象外、ファイルを削除し、.gitignore に追加する

f:id:tiwu:20210830150147p:plain

ts-migration は、ディレクトリをターゲットに TypeScript へマイグレーションするので、一旦変換用のディレクトリに作業ファイルをコピーしてマイグレーションを実行するようにしています。

手動で作業を確認後、同等の動作を Node で動作するスクリプトを書き、自動化をしました。

※スクリプトを書く際に js-fire を利用しました

github.com

最終的に完成した自動化のスクリプトは下記のように3つになりました。

// 1. 変換させるたのフォルダへ JavaScript をコピーする
npm run migrate -- start /GameWith/xxx.js

// 2. `ts-migration` をフォルダ指定で実行し TypeScript へマイグレーション
// 3. マイグレーションで変換後の TypeScript を元の JavaScript と同じ場所へコピーする
// 4. TypeScript を `tsc` でコンパイルし、オリジナルの JavaScript が上書きされる
npm run migrate:sync

// 5. TypeScript 化された JavaScript は ESLint 対象外、ファイルを削除し、`.gitignore` に追加する
npm run migrate -- end /GameWith/xxx.js

スクリプトは下記のようなイメージです。

const migration = {
    start: async (jsFile) => {
        // 1. 変換させるたのフォルダへ JavaScript をコピーする
        return jsFile;
    },
    end: async (jsFile) => {
        // 5. TypeScript 化された JavaScript は ESLint 対象外、ファイルを削除し、`.gitignore` に追加する
        return jsFile;
    },
};

fire(migration);

おわりに

破壊的なビルドフローの変更ではなく、既存ファイル以前のソースコードを設けることにより、マイグレーションコストを格段に減らしました。

TypeScript へマイグレーションしますが、中間コードが JavaScript なので、既存ファイルと共存は問題ないようにしています。

また、短期間ですべての JavaScript をマイグレーションしてしまうと既存の動いている他案件とコンフリクトし、解消するコストがかなり高くなってしまうため、修正の際に1ファイルずつ案件を担当するエンジニアがマイグレーションできるフローにしました!

まだマイグレーションは始まったばかりですが、完走したいと思います!

Twitter

Twitter にてテックブログの投稿をツイートしていますので、よろしければフォローをお願いします!

twitter.com

Wanted!

一緒に働く仲間(特にサーバサイドエンジニア)を絶賛募集中です!

以下 Wantedly のページからぜひカジュアル面談へお申し込みください!

www.wantedly.com

ペア・モブ作業(見積り・設計・プログラミングなど)の紹介 #GameWith #TechWith

はじめに

こんにちは。GameWith のエンジニアの tiwu です!

以前モブ設計・モブ見積りについて紹介しました。

tech.gamewith.co.jp

今回はチームや開発サイクルを紹介しつつ、いろいろなペア・モブ作業について紹介していこうと思います!

チーム・開発サイクルの紹介

自分が所属しているチームはエンジニア2人とディレクター1人の合計3人で構成されています。

チームは1週間のスプリント開発を行っており、ざっくり下記のようなサイクルで動いています。

※作業 = 設計・開発・コードレビュー・テスト・リリースなどが含まれます

  • 月曜日
    • 作業
    • 新規案件共有会・見積り会
  • 火曜日
    • 作業
    • スプリントの終了・開始
  • 水曜日
    • 作業
  • 木曜日
    • 作業
  • 金曜日
    • 作業

モブ見積り

新規案件共有会で新しい案件の共有を受けた後、モブ見積りを行っています。

自分が所属している開発チームのエンジニアとディレクター、他チームのエンジニアがモブ見積りに参加しています。

Google Meet で通話しながら Hatjitsu というツールを利用して見積りを行っています。

hatjitsu.toolforge.org

以前書いたモブ見積りでのブログでも紹介しましたが、とても重宝しています!ありがとうございます!

f:id:tiwu:20210719134903p:plain

見積りは1案件毎に下記フローで行っており、1案件 2,3 分で終わるスピードで進めています。

  • 案件の共有
  • 議論
  • 見積り
  • (ズレていたら)議論
  • 決定

見積もりの場に起票者はいないため、要件などに関する質問は新規案件共有会で起票者にしています。

見積りがズレた際は、3, 5 などの隣り合っている数値の場合は大きい方を採用しています。

3,5,8 など3パターン以上の場合は最小・最大の意見を聞いて、再度見積もりをしたり数値を変えたりして収束させています。

ペア設計

自分が所属しているチームはエンジニアが2人なので、スプリントの開始後は Google Meet で通話しながらペア設計をしています。

水曜日・木曜日・金曜日は基本的に 15:00 - 19:00 でペア作業をしているので、1時間毎に休憩を入れつつ作業を進めています。

ペアプログラミング

実装もエンジニア2人でペアプロしています。

Google Meet で通話しながら VSCode Live Share を利用してペアプロしています。

marketplace.visualstudio.com

ドライバー・ナビゲーターという役割分担はしていないですが、実際に docker を立ち上げている側が比較的ドライバーよりになっています。

Google Meet + VSCode Live Share + docker などを組み合わせると Mac の動作が結構重くなったり、一部言語はシンタックスハイライトが効かなかったりしますが、かなり重宝しています!

VSCode Live Share について詳しくはこちらを御覧ください!

tech.gamewith.co.jp

コードレビュー

普段1人で設計やプログラミングをした際は、チームメンバーにレビューをしてもらっています。

ペア設計・ペアプロをしている案件についてはメンバーと一緒に作業をしているので、レビューというフローは飛ばしています。

(PR を立てた後に簡単にチェック的なことはしたりしています)

テスト

テストケースもエンジニア2人で設計したりテスト作業も2人で分担して行っています。

案件によってはQAエンジニアと協力をしてケース作成、作業を行っています。

終わりに

チームのエンジニアが2人というのを最大限に活かし、ほとんどのタスクをペア・モブで作業をしています!

1人で作業をしているとレビュー待ちなどで案件がストップしたりしますが、基本的に止まること無く進んでいきます。

また、「案件 vs 自分」ではなく「案件 vs チーム」になり知恵を出し合いつつ、知見の共有を行うことができていると感じます。

作業側のメリットを考えペア・モブ作業を開始しましたが、雑談が発生するという副次的なメリットがあり、リモートワーク環境でのスタンダードになっていくのでは?と思ったりもしています!

チームのパフォーマンスは下記で計測しているので、ペア・モブ作業による変化を観測し、より良くしていきたいと思います!

tech.gamewith.co.jp

Twitter

Twitter にてテックブログの投稿をツイートしていますので、よろしければフォローをお願いします!

twitter.com

Wanted!

一緒に働く仲間(特にサーバサイドエンジニア)を絶賛募集中です!

以下 Wantedly のページからぜひカジュアル面談へお申し込みください!

www.wantedly.com

開発速度・パフォーマンスを可視化する #GameWith #TechWith

はじめに

こんにちは。GameWith のエンジニアの tiwu です!

今回は自分が所属する開発チームの開発速度・パフォーマンスを可視化してみたので、利用技術など解説していこうと思います!

開発チームの紹介

まず、簡単にチームの紹介をしたいと思います。

自分が所属しているチームは1週間のスプリント開発を、GitHub + ZenHub を利用して行っています。

起票された Issue は下記の図のように、起票から完了まで流れていきます。

※IceBox, Sprint Backlog, Close は ZenHub の Pipeline 名です

※途中に In Progress などがありますが省略しています

f:id:tiwu:20210702192112p:plain

月曜日に新規案件共有会があり、Issue が起票され IceBox に移動します。

f:id:tiwu:20210702192155p:plain

火曜日がスプリントの開始なので、IceBox から Sprint Backlog に移動させます。

f:id:tiwu:20210702192244p:plain

スプリント中に対応が終わった Issue は Sprint Backlog から Close に移動させます。

f:id:tiwu:20210702192323p:plain

計測

「LeanとDevOpsの科学」を参考に下記2つを計測していきたいと思います。

  • デプロイ頻度
  • 変更のリードタイム

デプロイ頻度

スプリント中に対応が完了した Issue の数を計測しようと思います。

Issue != デプロイ数ではありますが、いったん Issue 数で定義しました。

スプリントを GitHub のマイルストーンを利用し管理しているので、マイルストーンに紐づくクローズされた Issue の数がデプロイ頻度となります。

変更のリードタイム

これは2段階に分けて計測をしていきます。

IceBox ~ Sprint Backlog

まずは IceBox ~ Sprint Backlog の移動時間です。

f:id:tiwu:20210702193846p:plain

この時間が長ければ長いほど起票されてから開発着手まで時間がかかったことがわかります。

起票の日時は GitHub API を利用することで取得することが出来ます。

Sprint Backlog への移動日時は ZenHub API を利用することで取得することが出来ます。

Sprint Backlog ~ Close

次に Sprint Backlog ~ Close の移動時間です。

f:id:tiwu:20210702193903p:plain

1週間スプリントで動いているため、この時間が1週間以上のタスクはスプリント内に終わらなかったことがわかります。

Close された日時は GitHub API を利用することで取得することが出来ます。

実装

GAS 経由で GitHub API, ZenHub API を利用しデータを取得し、SpreadSheet に保存します。

データの可視化は DataStudio を利用します。

f:id:tiwu:20210705181912p:plain

GitHub API

まず、マイルストーンに紐づくクローズされた Issue を取得します。

docs.github.com

Issue 取得 API のmilestonestate パラメーターを利用して取得します。

const response = await fetch('https://api.github.com/repos/${owner}/${repo}/issues?milestone=${milestone_number}&state=closed', {
  headers: {
    'Authorization': 'token ${token}'
  },
});

次に Issue の起票日とクローズ日ですが、これは Issue 取得 API のレスポンスに created_at, closed_at が含まれているためこれを利用します。

ZenHub API

ZenHub の Sprint Backlog への移動は、Events API を利用することで取得できます。

github.com

const response = await fetch('https://api.zenhub.io/p1/repositories/${repo_id}/issues/${issue_number}/events?access_token=${token}');

レスポンスは下記のようになっており(公式から)、type = transferIssue イベントの to_pipeline = Sprint Backlog となっている日時が今回計測に使う日時になります。

[
  {
    "user_id": 16717,
    "type": "estimateIssue",
    "created_at": "2015-12-11T19:43:22.296Z",
    "from_estimate": {
      "value": 8
    }
  },
  {
    "user_id": 16717,
    "type": "estimateIssue",
    "created_at": "2015-12-11T18:43:22.296Z",
    "from_estimate": {
      "value": 4
    },
    "to_estimate": {
      "value": 8
    }
  },
  {
    "user_id": 16717,
    "type": "estimateIssue",
    "created_at": "2015-12-11T13:43:22.296Z",
    "to_estimate": {
      "value": 4
    }
  },
  {
    "user_id": 16717,
    "type": "transferIssue",
    "created_at": "2015-12-11T12:43:22.296Z",
    "from_pipeline": {
      "name": "Backlog"
    },
    "to_pipeline": {
      "name": "In progress"
    },
    "workspace_id": "5d0a7a9741fd098f6b7f58ac"
  },
  {
    "user_id": 16717,
    "type": "transferIssue",
    "created_at": "2015-12-11T11:43:22.296Z",
    "to_pipeline": {
      "name": "Backlog"
    }
  }
]

GAS

GAS では下記のような関数を作りデータを取得し、Spreadsheet に書き込みをします。

function setData(milestone, sheetName) {
  // シートの取得
  const sheet = SpreadsheetApp.openById('XXX').getSheetByName(sheetName);
  const lastRow = sheet.getLastRow();

  // マイルストーンに紐づく Issue を取得
  const response = UrlFetchApp.fetch('https://api.github.com/repos/${owner}/${repo}/issues?milestone=${milestone}&state=closed', {
    headers: {
      'Authorization': 'token XXX'
    },
  });
  const issues = JSON.parse(response.getContentText());

  issues.forEach((issue, index) => {
    // issue 毎にイベント情報を取得
    const eventsResponse = UrlFetchApp.fetch('https://api.zenhub.io/p1/repositories/${repo_id}/issues/${issue.number}/events?access_token=XXX');
    const events = JSON.parse(eventsResponse.getContentText());
  
    let springBacklogDate = null;
    // 最新順に取得できるので、古い順からみる
    for (const event of events.reverse()) {
      if (event.type === 'transferIssue' && event.to_pipeline.name === 'Sprint Backlog') {
        springBacklogDate = new Date(event.created_at);
        break;
      }
    }

    created_at = new Date(issue.created_at);
    closed_at = new Date(issue.closed_at);

    const row = lastRow + index + 1;
    sheet.getRange(row, 1).setValue(issue.title);
    // 起票日
    sheet.getRange(row, 2).setValue(Utilities.formatDate(created_at, 'Asia/Tokyo', 'yyyy-MM-dd HH:mm:ss'));
    // Sprint Backlog 移動日
    sheet.getRange(row, 3).setValue(Utilities.formatDate(springBacklogDate, 'Asia/Tokyo', 'yyyy-MM-dd HH:mm:ss'));
    // クローズ日
    sheet.getRange(row, 4).setValue(Utilities.formatDate(closed_at, 'Asia/Tokyo', 'yyyy-MM-dd HH:mm:ss'));
    // IceBox ~ Sprint Backlog
    sheet.getRange(row, 5).setValue(parseInt((springBacklogDate - created_at) / 1000)); // 秒
    // Sprint Backlog ~ Close
    sheet.getRange(row, 6).setValue(parseInt((closed_at - springBacklogDate) / 1000)); // 秒
  });
}

GAS には JS のように fetch 関数はないため UrlFetchApp を利用して、外部の API を叩きます。

developers.google.com

また、JSON で取得することは出来ないため、getContentText 後に JSON.parse をする必要があるます。

developers.google.com

DataStudio

DataStudio ではシンプルに棒グラフと折れ線グラフを作りました。

Issue 数は安定して 7.5 ほど対応できています(たまに20を超えることも)

f:id:tiwu:20210705183757p:plain

案件の速度は平均を取ってみました。

IceBox ~ Sprint Backlog はたまに平均 20オーバーの時もあり、起票後着手まで時間がかかった案件があることがわかります。

Sprint Backlog ~ Close に関しては、7日を超えたり越えなかったり。

f:id:tiwu:20210705183829p:plain

終わりに

今回は平均を取ってみましたが、中央値を取るべきかなどなど改善すべき箇所が他にもありそうです。

また、計測して終わりではなく BML ループのように学び・次に活かすループを回していこうと思います!

Twitter

Twitter にてテックブログの投稿をツイートしていますので、よろしければフォローをお願いします!

twitter.com

Wanted!

一緒に働く仲間(特にサーバサイドエンジニア)を絶賛募集中です! 以下 Wantedly のページからぜひカジュアル面談へお申し込みください!

www.wantedly.com

GameWith サービス開発だけではない話(1) #GameWith #TechWith

こんにちは!Incremental Stream Team の @53able です!

今回は、GameWith 開発するエンジニア採用強化のため、我々開発組織固有の雰囲気をカジュアルな形でより知っていただくように、

一緒に働くことをイメージできるきっかけになればと考えて GameWith の開発に参画する三人で談義した収録を発信しています。

スピーカー

柴山 (シバヤマ)

エンジニアマネージャー。GameWith歴4年。

竹内 (タケウチ)

ディレクター。新卒1期生。

只野 (タダノ)

フロントエンドエンジニア。GameWith歴2年。

どんなテーマなのか?

事業貢献するために、GameWith の開発組織として特別な取り組みの事例をふりかえっています。

この取り組みを行ってきた影響や結果を通して、当時感じていたことを外部はもちろんのこと、GameWith 内部に対しても関係強化をアピールできることを期待しています。

では、お聴きください!

Twitter

Twitter にてテックブログの投稿をツイートしていますので、よろしければフォローをお願いします!

twitter.com

Wanted!

一緒に働く仲間(特にサーバサイドエンジニア)を絶賛募集中です! 以下 Wantedly のページからぜひカジュアル面談へお申し込みください!

www.wantedly.com