Sakiのプログラミング学習ブログ

プログラミングについて学んだことや、学習の振返りを書いています。

フィヨルドブートキャンプのシステム開発プラクティスに取り組んでいるチームメンバーの状況を、見える化するアプリをリリースしました

はじめに

本日、フィヨルドブートキャンプシステム開発ラクティスに取り組んでいるチームメンバーの状況を見える化するサービス、「Fjord Choice」をリリースしました。

  • サービスURL

fjord-choice.herokuapp.com

github.com

目次

自己紹介

 Sakiと申します。 フィヨルドブートキャンプというオンラインプログラミングスクールで、エンジニアを目指してプログラミング学習中です。この度、スクールの最終課題である自作サービスのリリースを経て卒業することができました。
前職では税理士法人で事務職として働いていました。好きなRubyのメソッドはto_iメソッドです。

Fjord Choiceとはどんなサービスか

サービスについて知っていただくために、そもそも

を説明したいと思います。

フィヨルドブートキャンプとは?

 プログラマーとして就職を目指せるだけのスキルを身につけることを目標とした、オンラインプログラミングスクールです。 学習内容の詳細はこちらで紹介されています。 受講生は、このプラクティスを上から順にこなしていき、「学習の準備」〜「Webセキュリティ」まで全て完了したら、システム開発のプラクティスに取り組むことになります。

(学習内容 | FJORD BOOT CAMP(フィヨルドブートキャンプ)より引用)

システム開発ラクティスとは、どんなことをするのか?

 フィヨルドブートキャンプ(以降、「FBC」と記載)では、bootcampというEラーニングシステムのWebサービスを使っています。このアプリでは、日報の作成・提出、質問の投稿・回答、イベントのお知らせ・申込などを行うことができます。フィヨルドブートキャンプの受講生は、システム開発のプラクティスに参加すると、それまでエンドユーザーとして利用してきたbootcampアプリに、開発者として参加することになります。

リポジトリはこちらです。

github.com

チームの構成は、

  • スクラムマスター:メンターのkomagataさん
  • プロダクトオーナー:メンターのmachidaさん
  • チームメンバー:その時システム開発に取り組んでいる受講生の方々

となっており、このメンバーでスクラム開発をしています。

FBCでは、1スプリントを1週間とし、毎週水曜日に、今回のスプリントの振返りミーティングと、次のスプリントの計画ミーティングを行っています。

issueには、完成までにかかる時間に応じてポイントが振られることになっており、自身に割り振られたissue20ポイント分のPullRequest(以降、PRと記載)がマージされれば、このプラクティスは完了となります。
自分が担当するissueは、komagataさんとmachidaさんが、各受講生の進捗状況に合わせて、その時のレベルに応じた難易度のissueを割り振ってくださいます。例えば、一番最初はGood First Issueという、初めてコントリビュートするのに適した、軽く文言を変更する程度の1ポイントのissueが振られます。進むにつれて、徐々に大きいポイントのissueが振られるようになります。

また、自分で作成したPRは、同時期にシステム開発に取り組んでいるFBC受講生の方1名とメンターのkomagataさんにレビューしていただき、2名のレビューを通ったらマージされるルールになっています。

サービスの概要

 Fjord Choiceは、システム開発に取り組んでいる受講生の方々、komagataさん、machidaさんが、GitHubでは知ることができないチームメンバーの状況を、見える化するサービスです。

使い方

 システム開発のプラクティスに入った受講生の方に、「メンバー登録をする」をクリックして、メンバー登録をしていただきます。これによって、その方の情報がメンバーの表に追加されます。

Image from Gyazo


自身に割り振られたissue20ポイント分のPRがマージされたら、こちらのプラクティスは完了になるので、その際はご自身で「メンバーからはずれる」ボタンをクリックしてしていただきます。すると、その方の情報が表から削除されます。


表への追加と削除は、受講生の方ご自身に行っていただく必要がありますが、このチームメンバーの表自体は、URLさえ知っていればどなたでも見ることができます。

この情報は登録しなくても誰でも見ることができます

主な機能

主に3つの情報を得ることができます。

1.チームメンバーのアイコンまたはユーザー名をクリックすると、そのユーザーがbootcampリポジトリで作成したPR一覧のGitHubリンクにアクセスできます。 Image from Gyazo


2.ユーザー名の下にあるゲージは、これまでアサインされたIssueの合計ポイントです。openかclosedか問わず、アサインされた全ての合計が表示されます。

3.現在メンバーレビュー依頼されているPRの数とタイトルが分かります。タイトルをクリックするとそのPRのGitHubリンクにアクセスします。

Image from Gyazo

※ 「レビュー依頼されているPR」 とは、

  • そのユーザーがまだレビューをしていないもの
  • そのユーザーがレビュー中でまだApproveしていないもの(=CommentまたはRequest Changesした状態のもの)

が表示されます。そのユーザーがApproveしたPRやマージ済のPRは表示されません。

また、メンバー登録をした状態で、右上の自分のGitHubアカウントのアイコンをクリックすると、自分がアサインされたissueだけを表示したbootcampのかんばんにアクセスできます。

Image from Gyazo

解決したかった問題

PRのレビューを、手持ちのレビューが少ないメンバーに依頼したいが、誰がどのくらいレビューを抱えているか分からない

 システム開発では、任意の受講生1名とメンターのkomagataさんの計2名にレビューしていただくルールになっていますが、どの受講生に依頼するかは、お願いする方が自由に決めていいことになっています。
私はこのプラクティスに取り組んでいた当時、PRのレビューをお願いする際、「各メンバーが抱えているレビューの数や重さを把握した上で、今負担が少ない方にお願いしたい」と思っていました。理由は、一名の方にレビュー依頼が集中せず、チームメンバー全員がまんべんなくレビューを行った方が良いと思っていたためです。
メンターさんからは日々のミーティングで、「すでにレビュー依頼されているPRの数が多かったり、忙しい時は断って大丈夫ですよ〜」と仰っていたので、すでに2-3件持っている時はレビューをお断りすることもありました。その際も、あらかじめ誰がどのくらい手持ちのレビューを持っているか分かれば、このようなやり取りが減り、よりスムーズにレビューが進むのではないかと感じていました。

しかし、GitHubで「現在レビュー依頼されているユーザー」を確認するには、一つ一つPRにアクセスして、Reviewersを見ていくしか方法がありません。 GitHubが用意しているリポジトリのPR一覧の検索機能では、AssigneeやAuthor(PRの作成者)を指定したり、レビューの進捗状況によって検索結果を絞ることができますが、Reviewerを指定して検索することはできません。

実際にbootcampアプリでPRを検索する時の画面

このため、各チームメンバーにレビュー依頼されているPRを一目で分かるようにしたいと思い、このサービスでは、「チームメンバーが現在レビュー依頼されているPR」が分かるようにしました。 レビューが進行中のものだけ知りたいので、

そのチームメンバーが、

  • まだ一度もレビューしていないもの
  • レビューでComment または Request Changes をしたもの

を表示するようにし、Approveしたものは、そのユーザーのレビューは終わっているので、表示されないようにしました。

GitHubの「自動アサイン機能」では解決できないのか

 GitHubには元々、「自動アサイン機能」という、PRのレビュワーとしてチームをアサインした時に、そのチーム内の数名を自動アサインする機能があります。
(参考:Managing code review settings for your team - GitHub Docs)
この機能のアルゴリズムで 「ロードバランス」を選択すると、最近レビュー活動を行っていない人を優先的にレビュワーとして割り当てます。
この機能をFBCシステム開発に導入したら解決できないかを検討しましたが、レビュー活動を行っていなければissueの重さ・難易度に関係なく自動的にレビュワーとして割り当てられてしまうので、最近システム開発に入ったばかりで、レビューすること自体に慣れていない方に突然5ptレベルの重たいPRのレビューが割り振られてしまう、という問題が生じることが考えられるため、導入しない方が良いという結論に至りました。

komagataさんとmachidaさんが受講生に新しいissueを割り振る時に必要な情報が、すぐに分かるようにしたい

必要な情報1. 受講生がこれまでアサインされたIssueの合計ポイント

 FBCシステム開発では、自分の担当のissueがレビュー依頼まで行った等、きりの良いところまで終わったら、komagataさんとmachidaさんが新しいissueを割り振ってくださいます。
そして、issueを割り振る際、必ず「これまで割り振られたissueは合計で何ポイントですか?」と聞かれます。これは、komagataさんとmachidaさんが、(担当のissueが20ポイント分マージされることをゴールとした時)どれくらい進んでいるかに応じて、難易度や重さを考慮してissueを決めてくださるためです。

例えば、私が取り組んでいた際は、「まだ合計5ポイントだからRailsを使っている箇所の機能追加をお願いしよう」、「10ポイントを超えているからそろそろVue.jsを書くissueにしよう」「15ポイントでもうすぐ20ポイントに到達するから、最後に重ための5ポイント以上のissueをお願いしよう」という感じで振ってくださっていました。
(※bootcampアプリは現在、サーバーサイドはRuby on Rails、フロントエンドはVue.jsを使用しています。)

このように、FBCシステム開発では、「issueの合計ポイント」は、よく使われる情報なのですが、GitHubには、これを計算して教えてくれる機能は無いので、受講生が自身で数えるしかありません。
ポイントは、1,2,3,5,8,13,21 と7種類あり、各issueにポイントに応じた数字のラベルを貼ることで管理しています。

bootcampリポジトリで使っているポイントのラベル

そのため、今回リリースしたFjord Choiceで、各チームメンバーのissueの合計ポイントが一目で分かるようにしました。

必要な情報2. 受講生がこれまでどんなPRを作成してきたか

 また、komagataさんとmachidaさんから、「どのissueを割り振るか決める際、その方が過去に作成したPRも見て決めている」と伺ったので、チームメンバーのアイコンまたはユーザー名をクリックすると、そのチームメンバーがこれまでbootcampリポジトリで作成したPR一覧のGitHubリンクにアクセスできるようにしました。

Image from Gyazo

GitHub上のリポジトリのPull Requestsから、各ユーザーが作成したPRの一覧のみを、一から検索して表示させるには、以下のような手間がかかります。

  • Assigneeを選択する時に、選択できるユーザーの数が多いので、いちいちユーザー名を手入力して選択しないといけない
  • デフォルトでis:openというフィルターがかかっており、オープンな状態のPRしか表示されないので、マージされた等でclosedな状態のPRも表示させたい場合、Filtersのis:openを手入力で削除しないといけない
  • Assigneeを指定しても、純粋にそのユーザーが作成したPRのみが表示されるわけではなく、そのユーザーのPRがマージされたリリースブランチも検索結果に入ってくる。フィヨルドブートキャンプでは毎週木曜日と、短い頻度でリリースを行っているので、多くのリリースブランチが検索結果に入ってきてしまい、見にくくなる。これを削除するにはFiltersに-label:releaseと手入力し、releaseというラベルの付いたPRが表示されないようにしないといけない

リリースブランチも検索結果に入ってきてしまうので見にくい

実際に一から検索した時のスクリーンショットです。

Image from Gyazo

このように、GitHub上でユーザーが自力で欲しい情報に辿り着くまでに、検索条件の入力がたくさん必要で手間だと思ったので、リンクを貼ることにしました。 上記の手間をなくせるように、アクセスした時点で、

  • クリックしたチームメンバーがAssigneeとして登録されている
  • openかclosedか問わずすべてのPRが表示されている
  • releaseブランチが表示されないようになっている

状態のリンクにとぶようにしました。

私のユーザー名をクリックした時の検索結果

なぜFjord Choiceというサービス名にしたのか

 FBCのチーム開発に取り組まれている方々に使っていただくので、Fjord BootcampからFjordを頂戴して入れることにしました。 また、このサービスを使うのは、

  • 自分が作成したPRのレビュー依頼を誰にするか
  • 受講生に割り振るissueをどれにするか

を選ぶ(choice)時だと思ったので、Choiceを入れて、「Fjord Choice」というサービス名にしました。

技術スタック

開発の過程

ペーパープロトタイプの作成

どんな機能をつけるかで悩む

 作るものが決まったら、まずペーパープロトタイプを作成することになります。自分がシステム開発のプラクティスを終わらせてしまうと、一ユーザーでなくなってしまうので、「自分だったらどのような機能があったら嬉しいか」「どういう画面なら見やすいか」という視点で考えるために、システム開発に取り組んでいる最中に考えました。
作る画面を考えるために、まずどのような機能をつけるか考えました。「これが分かったら嬉しい」「これができたら便利そう」と思う機能を一通り書き出しました。 その後、「それがあるとなぜ便利か」という、その機能がなぜ必要かをじっくり検討しましたが、どこまでをファーストリリースに含めて、どこからは含めないか決めるのが難しかったです。

具体的にどのような機能をつけるかの検討過程は、以下のissueやNotionに詳しくメモしていました。

ペーパープロトタイプの作成 · Issue #6 · Saki-htr/fjord-choice · GitHub

www.notion.so

また、このサービスは、FBCのチーム開発に取り組む方々に使っていただくので、komagataさんとmachidaさんや、システム開発を経験したことがある受講生の方に、「こういう情報があったら便利だと思いますか?」等ヒアリングもしました。

最終的に、以下のプロトタイプが完成し、こちらを元に開発を進めていくことにしました。

メンバーの登録・退会は、メンバーの方自身にやっていただくことにした

 最初、極力ユーザーの手間を減らしたかったので、ユーザーが何も操作しなくても、サービスにアクセスしたら、システム開発に取り組んでいるメンバーの情報がすぐに見れるようにしたいと考えていました。
これを実現するには、プログラムを書いて「システム開発に取り組んでいるユーザー」だけを取得できなければいけません。bootcampリポジトリには、システム開発に取り組んでいる受講生の他に、FBCのメンターの方々等がコラボレーターとして登録されているので、GitHubが持っている情報では判別できません。

そこで、komagataさんにFBCのbootcampアプリのAPIを使えば取得可能か相談したところ、

  • bootcampアプリのAPIを使えば、システム開発ラクティスに入っているユーザーの名前は取れるし、ユーザーにはGitHubアカウントを登録してもらっているので、それを元にGitHubに取得しにいくことはできる
  • しかし、イレギュラーなケースがいっぱいある
  • などなどイレギュラーなケースがたくさん有るので、プログラムでの自動化はできたとしても、イレギュラーが多くなってしまうことが予想される。
  • イレギュラーが多いと「こういう場合はこうしてね」という、サービスを使う上でのルールがたくさん必要になってしまい、使い勝手が悪いので、最終的に使われなくなってしまう可能性がある

と教えていただきました。
ユーザーの手間を全面的に排除しようとすると、逆に使い勝手が悪くなってしまうのは本末転倒だと思ったため、「システム開発に入ったら、受講生の方ご自身に『メンバー登録』ボタンをクリックしていただいて、GitHub認証によってその方のGitHubアカウントの情報を取得し、その方の情報を表にさ追加する」、という仕様にしました。

チームメンバーの状況は、誰でも見られるようにした

 チームメンバーの状況を、システム開発ラクティスに取り組んでいる人だけ見られるようにするか、誰でも見られるようにするかで悩みましたが、

  • システム開発に入る前の受講生やメンターなどFBC関係の方々
  • FBCの紹介先企業で卒業生を採用したいと考えていらっしゃる方々

など、システム開発に取り組んでいないが、今誰がどのくらいの進捗か知りたい方がいるだろうと予想したため、この表自体は誰、URLさえ知っていれば誰でもアクセスできるようにしました。

ただ、誰でも見られると、FBCを受講していることをバレたくない方にとって迷惑になってしまうかもしれないので、誰でも見られる状態にして大丈夫かどうか検討しました。

FAQ | FJORD BOOT CAMP(フィヨルドブートキャンプ)より

komagataさんとmachidaさんに相談した結果、

  • フィヨルドブートキャンプ生であることが公になる=転職したいことが周囲にばれる。それは嫌な方もいるので、避けなければならない。
  • しかし、bootcampリポジトリのコラボレーター一覧は誰でも見られるようになっているので、その時点でFBC生であることがバレてしまう。つまりバレたくない場合、そもそもGitHubアカウントを別に作ってコラボレーターとして参加する必要がある。
  • なので、このサービスを使うにあたって、特定の人だけが見られるようにするための認証機能は必要ないのでは。

と仰っていただいたので、私も必要ないと判断し、誰でも見られるようにしました。

技術検証

技術検証とは、自分が考えたwebサービスが実現可能かどうか調査することです。
FBCの技術検証のプラクティスでは、以下のように説明されています。

自分のWebサービスのアイデアに必要な技術が可能かどうか検証しましょう。(もちろんやる前にIssueを作ってカンバンに登録しましょう)

  • 必要なデータを提供してくれる外部APIがあるかの調査。
  • 自分のやりたいことを助けてくれるgemがあるかの調査。
  • 使う予定のライブラリが本当に自分のアプリにマッチするのかのプロトタイプ作成。

自分のやりたいサービスが違法ではないかの調査。 調査した結果、そもそも「このサービスは実現不可能だった」となったらまた別のサービスのエレベーターピッチを考えましょう。
(技術検証をする | FBCより)

APIをたたくのが初めてで分からないことばかり

 Issueの合計ポイントやレビュー依頼されているPRを表示させるには、GitHubから必要なデータを取得してくる必要があります。なのでまず、サービスで使いたいデータをGitHubが持っているかを調査をしました。
これまで行ってきたFBCのプラクティスでは、ユーザーによってCRUD操作が行われるアプリケーションしか作ったことがなく、APIといえば、FBCシステム開発RailsからVue.jsに必要なデータを渡す時のRailsAPIくらいしか知りませんでした。
外部サービスのAPIをたたくことが初めてだったので、

  • そもそもどのようにすればGitHubAPIをたたくことができるのか
  • 自分がほしい情報はどうすれば取得できるのか

が最初全く分かりませんでした。

そんな中、GitHubの公式ドキュメントを読み進めていく中で色んな情報に触れ、

  • GitHubAPIにはRESTだけでなくGraphQLというAPIもあるらしい→GraphQLとは何だろう?それを使うかの検証もすべき?
  • アプリケーションからAPIにリクエストするにはOAuthアプリケーションとして登録して認証する必要があるらしい→ターミナル上でcurlコマンドでたたく方法もよく分かっていないのに、認証やOAuthが絡んでくるとさらに分からない...
  • ラッパーライブラリというものがあるらしい→それを使うとどういうメリットがあるか分からない。私のサービスでも使った方がいいのか?

と、「調べれば調べるほど分からないことが出てきて、何から検証していけばいいか分からない」という混乱状態に陥りました。
ちょうど、FBCの卒業生や自作サービスのリリースを終えた先輩が集まる場があったので、そこで技術検証の進め方について相談しました。
良いアドバイスをたくさんいただいたのですが、中でも

認証の仕組みと、「APIから欲しい情報を取得できるか」は分けて考えたほうが良い。
認証の理解は難しいので、まず欲しいデータを取れるかどうかを、認証を行うOAuthアプリケーションではなく、ターミナル上でcurlコマンドを使って試すといい。

というお話がとても参考になりました。
このアドバイスをもとに、認証やラッパーライブラリのことは一旦置いて、まずcurlコマンドで、GitHubAPIが必要な情報を持っているかを調べました。そして、欲しい情報が取得できると分かった後に、ラッパーライブラリを試したりOAuthアプリからAPIをたたいたら、外部サービスのAPIに関して何も知らなかった時よりもとてもスムーズに使い方を理解できました。

当時学んだことを別の記事にまとめたので、気になった方は読んでくださると嬉しいです。これからFBCの自作サービスで外部サービスのAPIを使う予定の方、GitHubAPIを使いたい方にとって参考になる記事かなと思います。

saki-htr.hatenablog.com

saki-htr.hatenablog.com

検証用のリポジトリを作って、色んなパターンのissueとPRのデータを作成して試した

技術検証を始めた当初、bootcampリポジトリの、実際のissueとPRを元にして検証を進めていましたが、検証する時によって存在するデータがばらばらなので、最初「このプログラムを書けば、これが実現できそう」と思っていても、その少し後に検証してみると、網羅していなかったパターンに遭遇することがありました。
そのため、bootcampリポジトリをForkして自分のアカウントに、検証用のリポジトリを作りました。

github.com

こちらに、実際に起こり得そうなパターンのissueやPRを作って、これに対してAPIリクエストして、「issueに誰かがアサインされている時はこのデータの中身はどうなるか」や「PRがcloseされた場合はこのデータはどう変わるのか」など、GitHubAPIの動作確認をしていました。

実際に作成して動かしていたissueとPRはこちらです。

PRは、自分が作成者だと、レビュワーに自分を登録できないので、当時同じく自作サービスの開発を進めていたFBC生の方々をコラボレーターとして追加し、PRを作成していただくなどご協力いただきました。

開発

GitHubAPIが、自作サービスで使うデータを持っていることが分かったので、フレームワークなどの技術検証を終えた後は、rails newをしてCIなどの環境構築を終え、開発に取り掛かりました。

一番大変だったこと:GitHubの最新のデータを、常にRailsのDBに保存する仕組みを作る

 GitHubAPIが、自作サービスで使いたいデータを持っていることが分かったので、後は開発に入って、検証→開発→また検証というサイクルを短く繰り返していけばいいかと思い、rails newしました。
しかし、実際に機能を作り始めたら、GitHubの最新のデータを、常にRailsのDBに保存させる仕組みを作ることが非常に難しく、この仕組みの実装に一番時間がかかりました。

このサービスは、リアルタイム性の高さが重要になってきます。例えば、GitHubで、とあるPRでチームメンバーの誰かがレビュワーとして登録されたら、すぐに私のサービスでもそのPRがチームメンバーにレビュー依頼されていることが分かるようにPRの情報を表示しなければなりません。GitHubでPRにレビュワーが登録されてから、私のサービスに反映されるまでに時間がかかると、ユーザーにとって使いにくいものになってしまいます。そのため、bootcampリポジトリのissueとPRの最新のデータをRailsのDBに保存する仕組みが必要です。この仕組みをどうやって実現するかが、一番大変でした。具体的にどんな方法を考えて検証してきたかを以下にまとめました。

検証1. GitHub上でデータに変化があったタイミングでAPIをたたく方法がないか調査

 GitHubのデータが変わったタイミングでAPIをたたくことができれば、効率的にAPIリクエストさせることができるので、GitHubAPIにそういったことができる機能が備わってないか公式ドキュメントをくまなく探しましたが、ありませんでした。

検証2. バッチ処理で定期的にAPIリクエストさせる

 データが変わったタイミングでAPIリクエストさせることができないため、バッチ処理によって、定期的に2分間に1回、APIリクエストさせることにしました。
しかし、実際にこの方法でissue/PRの情報を表示する機能を作ったところ、常に最新のGitHubの値をDBに保存することは実現できましたが、非常に無駄が多いことに気づきました。

バッチ処理でDBを更新する時のイメージ図

具体的には以下2つの問題がありました。

問題1. 実際にGitHubの値が変わる頻度よりもずっと高く、APIリクエストを行うので、アプリに負荷がかかり、ユーザーがアプリにアクセスした時の表示に時間がかかる

issueに誰かがアサインされたり、誰かがレビュワーとして登録されたり、レビューでApproveしたりといった、実際のbootcampリポジトリでのissue/PRのデータの値が変わる頻度を大きく上回った大量の回数をAPIリクエストさせるので、アプリの処理が重たくなり、ユーザーとしてアクセスする際のデータの表示にも時間がかかりました。
リアルタイム性の高さを重視しているため、「2分間に1回」というリクエストの頻度を落とすわけにもいかないので、バッチ処理ではこの問題を解決することはできませんでした。

問題2. 集計に必要ないissueやPRのデータもDBに保存してしまう

 GitHubから欲しい情報は大きく分けて以下の2つです。
1つめは、ユーザーにアサインされたissueのデータ。
2つめは、ユーザーがレビュー依頼されているPRのデータです。
GitHubが用意しているエンドポイントでは、クエリパラメーターを使ってassigneeを指定すれば、ユーザーにアサインされたissueだけを取得することができます。
例えば、私がアサインされたissueかつ、openもclosedも合わせた全てのステータスの状態のissueのリストが欲しい場合、クエリパラメーターでassigneestateというパラメーターを使って、以下のようなエンドポイントでアクセスすると取得することができます。

https://api.github.com/repos/fjordllc/bootcamp/issues?assignee=Saki-htr&state=all

しかしPRでは、とあるユーザーがレビュワーとして登録されているPRだけを取得するエンドポイントが用意されていませんでした。そのため、以下のURLのように、openな状態のPRのリストを取得し、その中のrequested_reviewerキーに入っているデータがレビュワーの情報なので、これを元にDBにPRのデータを保存するしか方法がありませんでした。

https://api.github.com/repos/fjordllc/bootcamp/pulls?state=open

APIについて調べる前に考えていたアプリのデータ作成の流れとして、

  1. ユーザーが私のサービスにGitHub認証することで、GitHubアカウントの情報を取得し、Userモデルのレコードを作成する
  2. Userモデルのレコードが作成されたら、そのユーザーがアサインされたissue、そのユーザーがレビュー依頼されているPRを取得しにいく

というのを考えていました。
しかし、レビュワーを指定してPRを取得することができないので、これは実現不可能になりました。そのため、アプリのUserモデルのレコードとは関係なく、issueとPRのデータをあらかじめダムのように貯めておいて、ユーザーが登録されたら、そのユーザーがアサインされたissueとレビュー依頼されているPRを表示することにしました。

これをバッチ処理で実現させようとすると、issueとPRのレコードの数がどんどん増え、作成されたレコードの値が変わっていないか常にバッチ処理でリクエストさせて見に行かせることになるので、どんどん処理が重たくなってしまいます。

検証3. GitHub Actionsを使って、イベントが起きたタイミングでissueとPRのデータをアプリにPOSTしてもらう

バッチ処理だと上記のような問題があったので、そのことを自作サービス進捗報告会でkomagataさんに報告したところ、

GitHub Actionsを使えば、「こういうイベントが起こったら、こういう処理を実行して」ということができるので、issueやPRに変化が起こった時に、issue/PRのデータをRailsアプリに送信させることができるのでは

とアドバイスをいただきました。
GitHub Actionsがどのようなイベントを補足してくれるかは、公式ドキュメントを読めば比較的容易に知ることができました。

しかし、

  • イベントが起きたissueとPRのデータはどうやって取得するのか
  • issue/PRのデータをどうやってRailsアプリに送るのか

を調べるのが大変でした。

GitHub Actionsというのは、そもそもGitHubでの開発をより効率的に行うためのツールなので、ネットでの記事は、「PRが作成されたら自動的にpackage.jsonファイルを更新する」「pushされたら自動的にテストが実行されるようにする」というような自動化の記事が圧倒的に多く、自分が実現したいことをどんぴしゃで行っている記事は見つかりませんでした。しかし、GitHub Actions を用いて issue が更新されたら LINE に通知する方法 - Qiitaという記事を見つけ、「GitHubリポジトリから別のアプリに、イベントが起こったissueの情報を通知できるなら、自分がやりたいことを実現する方法はあるはずだ」と考え、GitHub Actionsの公式ドキュメントをじっくり読んだり、参考になりそうな記事をくまなく探したりしました。
その結果、イベントが起きたタイミングでissueとPRのデータをRailsアプリに送信する仕組みを作ることができました。

GitHubActionsからイベントが発生したタイミングで、issue/PRのデータをRailsアプリに送信させることで、仕組みを実現

具体的にどのようにして仕組みを作ったか、説明していきます。

送信する側(GitHub Actions)の処理

以下のGitHubActionsのワークフローファイルを作成し、bootcampリポジトリに置いていただきました。

  • issueのデータを送信するワークフローファイル

github.com

issueは、

  • issueに誰かがアサインされた
  • アサインをはずされた
  • ラベルが貼られた
  • ラベルが外された

タイミングで、Railsアプリに、イベントが起こったissueのid、ラベル、アサインされたユーザーのGitHubのidをPOSTします。

  • PRのデータを送信するワークフローファイル

github.com

PRは、

  • 誰かがレビュワーとして登録された
  • レビュワーから外された
  • PRが編集された
  • PRがclosed状態になった(マージされるとclosedになります)

タイミングで、RailsアプリにPRのid、タイトル、ステータス、レビュワーとして登録されたユーザーのGitHubのidをPOSTします。

受信する側(Rails)の処理

POSTされたデータを受け取る側の処理が必要なので、issueとPRのデータを受信する用のルーティングを作成しました。

  • ルーティング
# config/routes.rb

Rails.application.routes.draw do
  namespace :api do
    resources :issues, only: [:create]
    resources :pulls, only: [:create]
  end
end

issue/PRのidをもとに、DBに同じidのレコードがないか探し、あればそのレコードを、なければ新しくインスタンスを作成した後、POSTされてきたパラメーターを元にissue/PRのレコードを作成または更新しています。

値が変わったらリポジトリ側からデータ送信してくれるので効率が良くなった

GitHubActionsからのPOSTを待ってDBを更新する時のイメージ図

このように、GitHubActionsを使うことで、Railsアプリから値が変わってないか常に見に行かせるのではなく、必要なタイミングでbootcampリポジトリからPOSTさせたのを受信する形式にしたことで、効率的かつリアルタイム性を高く保った状態で、issueとPRのデータをRailsのDBに保存することができました。
また、GitHubREST APIは、APIリクエストのレート制限が1時間に5000回までなので、それを超えないように考慮してプログラムを作る必要がなくなったのもありがたかったです。
GitHubActionsの技術検証はこちらのNotionに整理しながら行っていました。

www.notion.so

bootcampリポジトリからのみ、POSTを受け取る仕組みを作った

 Railsは、CSRF対策として、POST,DELETEリクエストなど副作用があるリクエストに対してはあらかじめセキュリティトークンを発行しておき、その発行されたトークンをリクエスト時に送信しています。そして送信されたトークンを検証することで、第三者からのリクエストではなく自身のアプリケーションからのリクエストであるので実行しても問題ないと判定する、という手法をとっています。
つまり、元々第三者からのPOST受信はできないようになっているため、ワークフローファイルの設置とルーティングの追加をしただけでは、POSTを受け取ることができません。
受信するためにどのような設定が必要か調べたところ、「protect_from_forgery(セキュリティトークンの検証を行わない設定)と書いてCSRF対策を外せばいいだけ」など、セキュリティ面から見て危険そうな記事がいくつか出てきたので、メンターさんに正しい方法を聞いた方が良いと思い、質問しました。

結果、以下の処理を追加することで、bootcampリポジトリからのPOSTのみを受信できるようにしました。

1.bootcampリポジトリとFjord Choice、お互いしか知らないトークンを環境変数FJORD_CHOICE_TOKENとして設定する。

2.bootcampリポジトリのワークフローファイルで、データをPOSTする際に、Authorizationヘッダーにトークンを含めて送信するようにする。

curl -X POST https://fjord-choice.herokuapp.com/api/issues \
             -H 'Content-Type: application/json' \
             -H 'Authorization: Token ${{ secrets.FJORD_CHOICE_TOKEN }}' \ # トークンも含めてPOSTする
             -d '{"number": ${{ github.event.issue.number }},
                  "point": ${{ toJson(github.event.issue.labels.*.name) }},
                  "assignee_uids": ${{ toJson(github.event.issue.assignees.*.id) }}
                  }'


3.Railsの受信するコントローラに以下の設定を追加する。

class API::IssuesController < APIController
  protect_from_forgery except: :create #  createアクションでのみCSRF対策を外す
  before_action :authenticate, only: [:create]

  def create
    (省略)
  end

  private

  def fjord_choice_token
    ENV['FJORD_CHOICE_TOKEN']
  end

  def authenticate # 渡されたトークンとFjordChocieのトークンが同じか検証する
    authenticate_or_request_with_http_token do |token, _options|
      ActiveSupport::SecurityUtils.secure_compare(token, fjord_choice_token)
    end
  end
  


これで、bootcampリポジトリからのみPOSTを受信できるようになります。

※ちなみに、authenticateメソッドは、

  def authenticate
    authenticate_or_request_with_http_token do |token, _options|
      token == ENV['FJORD_CHOICE_TOKEN']
    end
  end

のように直接比較すると、タイミング攻撃によってトークンが分かってしまうリスクがあるので、secure_compareメソッドを使いました。

これまで、アプリケーション内でHTTPリクエストするアプリケーションを主に扱ってきたので、初めてのことばかりでしたが、セキュリティの勉強にもなり面白かったです。

とにかくGitHubの仕様に左右される

 サービスの開発では、とにかくGitHubの仕様に左右されるので、「こういう状態のissueでは、APIのこの値はどうなっている?」「これが起こったら、APIのこの値はどう変わる?」と調査して、それに応じてプログラムを書くのが大変でした。
GitHub Actionsでどういうイベントを使えるかはこちらのドキュメントにあるのですが、具体的にどういう時にそのイベントが発生する/しないか細かくは載っていないので、調査する必要がありました。

なので実際に検証用リポジトリに様々な状態のPRを作って、イベントが発生するか、発生した時GitHubの値はどうなるかを確認しました。

レビュー状況のパターンの表を作って確認しながら記録していました

また、はじめFBCシステム開発のルールに合わせてプログラムを書くか、ルール上はほぼ起こり得ないけれどGitHub上では起こり得る動作も考慮して書くか迷いました。
例えば、FBCシステム開発では、まず受講生の方1名にレビューをお願いし、その方がApproveした後にkomagataさんにレビューをお願いすることになっています。なので、基本的には複数名が同時にレビュワーでいることはほぼ起こり得ないです。
しかし、ルールは今後変わるかもしれないですし、GitHubの仕様上は同時に複数名をレビュワーとして登録することが可能なので、最終的にGitHubの仕様に合わせて書くようにしました。

テスト

FBC内で開催されたEverydayRails輪読会合宿に参加

多くの開発現場でテストはRSpecが使われていると聞いたので、自分のサービスでもRSpecを書きたいと思っていたので、ゴールデンウィークに開催された、現FBC卒業生の@Paruさん@トミーさんご主催のEverydayRails輪読会合宿(1週間で合計約40時間)に参加しました。


こちらは合宿内で各々が学んだことを振り返る時に記入したHackMDノートです。

hackmd.io


RSpecを書くのは初めてでしたが、この合宿に参加したおかげでスムーズにテストを書き始めることができました。主催のお2人には本当に感謝しています。

beforeをなるべく使わないようにした

 合宿には、FBCのメンターさんやエンジニアとして働かれている卒業生の方もいらっしゃり、「現場ではbeforeは極力使いたくないので、letを使う」と教えていただいたので、なるべくbeforeを避けてletを使うようにしました。

デザイン

見やすさにこだわった

デザインは、メンバーの表の見やすさにこだわりました。machidaさんから「彩度の高いものは避けた方がいい」「Gmailアプリなど参考にすると良い」と教えていただき、全体の背景色を薄いグレーにし、明るい文字やボタンが目に入るとユーザーにとって鬱陶しいと思ったので、メンバー登録のボタン以外は目立たせないようにしました。

レスポンシブ対応がとても難しい

 今回、初めてレスポンシブ対応にチャレンジしたのですが、まさかこんなに難しいとは思っていませんでした。自分が使っているMacBookでは良い感じの幅・空白にできていても、スマホで開くと幅が足りなくてヘッダーに配置したボタンが表示できなかったり、逆に大きいディスプレイだと幅に伸びてしまうので最大幅の制限が必要だったりと、どの端末で開いても綺麗に表示させることの大変さを痛感しました。

とくに、スマホは画面が小さいので、テキストによる説明ではなく、ボタンの配置や色などによってユーザーに使い方を伝えないといけないので、自分が普段スマホでサービスを使っていて、迷いなく操作できているのはデザイナーさんが使いやすさを考えてくださったり、CSSを当ててくださっているおかげなのだと学びました。

イデア出し〜リリースまでをやってみての感想

一通り全部経験できてよかった

 アイデア出しからリリースまで全部行うのは本当に大変でしたが、一通りの工程を経験したことで、世の中のサービスと、サービスに携わっている方々への感じ方も変わりました。
「この規模でこんなに大変なのに、世の中には利用していただくことでお金をいただくレベルの、もっともっと大規模なサービスがいっぱいある。そのアイデアを考えだしたり、作っている方々はなんてすごいんだ!」と思うようになりました。

私はエンジニアとして働きたいのでこれから就職活動に入りますが、働くことになったら、違う部署・職種の方々と協力しあってお仕事していくことになるので、デザイナーさんなど他の職種の方がお仕事でなさっていることを少しですが経験できて良かったです。 やったことがない上で想像だけで「大変なのだろうな」と思って一緒にお仕事をするのと、経験をしたことがあって多少大変さが分かっている上でお仕事をするのとでは全然違うと思います。

こちらに関してはmachidaさんもブログ記事で、以下のように仰っていました。

いろいろな役割を経験することで、その役割の人の気持ちや出会う問題について知れることが重要なポイントだと考えます。この役割の人にはこう動いてもらえると、この役割の人にとってはこういう場合にすごく助かる、というのがわかっていることで、実際チームで開発をする際にいい動きができるようになります。
一人でサービスを一回作っただけで知れる範囲というのは限られてはいますが、それでもやったことがない人に比べて大きな差はあると思います。また、自分がその役割をやってみたことががきっかけで、開発者以外の役割のことにも興味が持てれば、チームで開発がさらに楽しくなれるかもしれません。
(フィヨルドブートキャンプに「自作サービスを作る」という課題がある理由とフィヨルドブートキャンプの取り組み | FJORD BOOT CAMP(フィヨルドブートキャンプ)より)

自作サービスは質問が難しい

 これまでのFBCでの学習では、プラクティスの課題について質問する時は、作るもの・ゴールが決まっていて、質問する自分と質問されるメンターさんがお互いに共通認識ができた状態で質問することができました。システム開発は、bootcampアプリについて、issueを元に質問させていただくので、「こういう機能を作りたい」「現状のbootcampのコードはこうなっている」という共通認識がお互いにできた状態で質問ができました。
しかし自作サービスは、「こういう機能を作りたい」「調べてみたらこういうことが分かった」「実装する上でこういう問題がある」等について、一番分かっているのは自分だけど、分かっていないこともたくさんある状態なので、質問する時に、欲しい答えを得るために必要な情報を正確に伝えることが非常に難しかったです。
開発当初は、「自分のサービスなのだから自分の力でやらねば」という気持ちがあり、なかなか質問に踏み切れない時がありましたが、それでは進みませんし、仕事することになったら、分からないことがあったら一人で抱え込まないで質問して進めることが大切になると思ったので、途中からはFBCのQ&Aやdiscordの#wakaranチャンネルでどんどん質問するようになりました。

フェードアウトしないようにFBCの方と関わる場に積極的に行った&作った

 「自作サービスは長期戦」と聞いていたので、開発に詰まってしんどい時も出てくるだろうなと予想していました。なので、FBC内でパーフェクトRuby on Railsの輪読会を主催して、平日毎日FBCの方と強制的にお話する機会を作りました。

自作サービスはシステム開発ラクティスと比べると、一人で黙々とやる時間が増えます。はじめは自作サービスと主催を両立できるか不安でしたが、やってみたらそこまで大変ではありませんでした。この輪読会はまだ続いていて、今週10章(全12章)に入りました。主催をしてみて、参加してくださる方がいないと輪読会はできないので、参加者のありがたみが増しました。(いつも来てくださる皆様ありがとうございます...🙏✨) もちろん自作サービスのためだけに主催したわけではないのですは、人と話すことで元気が出るタイプなので、平日毎日話す場を作ってよかったなと思います。また、Railsを使って開発をしていたので、Railsでやりたいことを実現する方法を調べる時に、「そういえばこの前読んだところに方法がのっていたな」と役立つことも多々ありました。

また、トミーさん主催の、同じ時期に自作サービスに取り組んでいたFBC受講生で集まり、週に一度各々のGitHubのかんばんを使って、今週取り組むissueについて説明し、ポイントを付けていただく会に参加していました。ここで自分が詰まっていることに関してアドバイスをいただいたり、励ましあったりしたので、それが心の支えとなり、なんとかリリースまで辿り着くことができました。

ユーザーの使いやすさをとことん追求するのが楽しかった

 私のサービスは、FBC受講生の方々に使っていただくことを想定して開発していたので、「この機能があったら嬉しいかな」「こういう仕様の方が使いやすそう」とあれこれ考えながら、プロトタイプを作ったり、機能を作っていくのはとっても楽しかったです。サービスはリリースしたら終わりではないので、これからも受講生の方々やkomagataさんとmachidaさんの声をお聞きして、便利な機能を作っていきたいです。
エンジニアとして働けることになったら、ただ指示された通りにコードを書くのではなく、使う方が本当に求めているものは何か、どういう仕様だと使いやすいか、をとことん追求して考えて作れるエンジニアになりたいです。

何を作るかのアイデアは一人で考えこんでボツにする前に、人に聞いてもらう

 実はこのサービスのエレベーターピッチを提出する前にボツにするか悩んだことがありました。しかし、一人で抱え込んで悩むよりも、他の方の意見を聞いた方が良いと思い、思い切って提出しました。
このサービスのエレベーターピッチを考えていた当初は、「今誰が手持ちのレビューが多いか知りたいのは私だけかもしれない」と思ったりもしましたが、提出時にmachidaさんに「すごく欲しい」と仰っていただけたり、ミートアップでアイデアを話した時に「自分も欲しい」と仰っていただけたりしました。なので、自作サービスのアイデア出しをしていらっしゃる方で悩んでいる方がいらしたら、ぜひ人に話したり、一旦エレベーターピッチを提出してみることを強くおすすめしたいです!

今後追加したい機能

リロードしなくても、表示されるデータが自動的に更新されるようにしたい

 現状、表示されるデータを最新の状態にするには、ユーザーが自分でリロードする必要があります。週一のミーティングの時など、bootcampのGitHubのかんばんを見ながら、このサービスの画面を開きっぱなしにして見ることがあると思います。ミーティングでは新しくissueが振られる = 私のサービスで表示するデータが変わるので、リロードしなくても自動的に値が変われば、ユーザーは「今は最新の状態なのだろうか」と心配してリロードする必要がなくなるので、表示されるデータが自動的に更新されるようにしたいです。

さいごに

前職での経験から、「システムにできることは人がやるのではなく、システムにやってもらうようにして、その人にしかできないことに集中できる環境を作っていきたい」という思いが強かったので、このサービスによって、komagataさんとmachidaさん、チーム開発に取り組まれる受講生の方々が少しでも、コードを書いたりレビューしたりすることに専念できるように、チーム開発の助けになればとても嬉しいです。


自作サービス進捗報告会でいつも的確なアドバイスをくださったkomagataさん、machidaさん、何度も相談にのってペアプロしてくださったcafedomancerさん、サポートしてくださったメンターの方々、ポイント付け会で一緒だった、eatplaynapさん、Paruさん、ガラムマサラさん、yocajiiさん、グループコーチングで一緒だったharunaさん、いっしーさん、niikzさん、チェリー本輪読会で一緒だった皆さん、パRails輪読会に参加してくださった皆さん、分報でリアクションくださった皆さん、本当に本当にありがとうございました。自作サービスの開発は、チーム開発と比べると、自分がプロダクトオーナー、開発、デザイナー全てを自分一人で行うので、孤独に感じることもありました。そんな中たくさんの方々が助言をくださったり、弱音を聞いてくださったりして、なんとかリリースまで辿り着くことができました。皆さんには感謝してもしきれません。
これからついに最後のプラクティスである就職活動が本格的に始まり、不安な気持ちもありますが、自分が働きたいと思う会社さんから、"一緒に働きたい"と思っていただけるように全力を尽くしたいと思っています。自分が内定をいただけるかは、選考に進むまで分からないことですが、落ちた時に「もっと努力をしておけばよかった」と悔いが残らないようにがんばりたいです。