- サービスの概要
- 開発理由
- 使用技術
- 技術選定
- 使用方法
- 工夫した点
- 苦労した点
- なぜFirebase Authenticationを導入したのか
- なぜidTokenを検証する必要があるのか
- なぜスキーマ駆動開発を導入したのか
- 作り直したい部分
- 自信のあるコード
- フィードバックをどう改善したのか
- どんな学びを得たのか
サービスの概要
Duet Codeは、①プライベートで書いたコードをレビューしたり、されたりする機会が無い問題②他の人が書いたコードを読んだり、自分の書いたコードに意見を貰うことで、技術力を上げたいニーズを解決するサービスです。レビューしてほしいプルリクエストを投稿し、レビュアーを募集することができます。ログインすることでレビュイーにもレビュアーにもなることができます。
開発理由
プライベートで書いたコードをレビューしたり、されたりする機会が無い問題を解決したかったからです。現状、プライベートだとコードをレビューする機会が無く、プライベートで書いたコードをレビューしてもらえるサービスはMENTAくらいしか無く、契約制(有料)ということもあり、ハードルが高く、もっと手軽にコードレビューが出来たらいいなと思い開発しました。
使用技術
バックエンド
フロントエンド
※API関連のコードはOpenAPI Generator TypeScript Axiosで自動生成しています。
インフラ
テスト
- APIテスト
- E2Eテスト
CI/CD
※Lefthookを使用してコミット前にESLint、Prettier、RuboCopを実行し、コードの品質を保っています。
環境構築
外部サービス
技術選定
Ruby on Rails
選定理由 :
- 実務でRuby on Railsを使用しているので、Laravel, Djangoに比べて早く開発できるから。
Next.js
他選択肢 : React, Angular, Nuxt.js
選定理由 :
- CtoCサービスなのでSEOを意識する必要があり、Pre-renderingをサポートしているから。(React単体ではPre-renderingをサポートしていない)
- 実務でReactを使用しているので、Angular, Nuxt.jsに比べて少ない学習コストで習得できると思ったから。
TypeScript
他選択肢 : JavaScript, Flow
選定理由 :
- 型を付与することでプログラムの可読性が向上したり、エディターの補完機能が効いたり、タイポやバグの早期発見が可能だから。
- Googleトレンドで定常的にFlowよりTypeScriptが優位だから。
Tailwind CSS
他選択肢 : Bootstrap, Chakra UI, styled-components
選定理由 :
- デザインに可能な限り合わせたかったから。
- インラインで疑似クラスやレスポンシブ対応できるから。
- クラス名を考える必要がないから。
MUI
他選択肢 : Bootstrap, Chakra UI
選定理由 :
- MUIに用意されているコンポーネントがデザインしたコンポーネントに似ていて、カスタマイズをTailwind CSSで行うことが可能だから。
Fly.io
他選択肢 : Heroku, Railway, Render.com
選定理由 :
- 無料枠があるから。
- 一定時間アクセスが無くてもスリープしないから。(Render.comの無料プランだと15分間アクセスが無いとスリープする)
- 一定期間後にデータベースが初期化されないから。(Render.comの無料プランだと90日後にデータベースが初期化される)
- 日本語のドキュメントが充実していて、導入が簡単だから。
Vercel
選定理由 :
- Next.jsとの相性が良く、導入が簡単だから。
Rspec
他選択肢 : minitest
選定理由 :
- 実務でRSpecを使用しているので、minitestに比べて早く開発できるから。
- APIテストの便利メソッドが格納されているcommittee-railsのREADMEがRSpecで書かれているから。
Cypress
他選択肢 : Selenium
選定理由 :
GitHub Actions
他選択肢 : CircleCI
選定理由 :
- Publicリポジトリだと上限なく無料で利用可能だから。
- .github/workflowsのYAMLファイルに設定を書くだけで導入が簡単だから。
Docker
他選択肢 : VirtualBox
選定理由 :
- 同じ環境を再現することが容易だから。
- VirtualBoxはホストOS上で完全な仮想化を行い、ゲストOSを起動するためオーバーヘッドが発生し重たいですが、DockerはホストOSのカーネルを共有するため、ゲストOSを起動する必要が無く軽量だから。
Firebase Authentication
他選択肢 : Auth0
選定理由 :
- Firebase AuthenticationのデザインがAuth0に比べて好みだったから。
- Firebase Authenticationを使用したJWT認証の記事が多かったから。
使用方法
レビュイー
①「ログイン」or「無料で始める」をクリックして、GitHub認証でログインします。
②ヘッダーの「募集する」をクリックします。
③「タイトル」「プルリクエスト」「使用言語」「プルリクエストの説明」「レビューしてほしい点」を入力して「保存する」をクリックします。(GitHub APIを使用してPublicリポジトリのプルリクエストを取得しています)
④レビュアーが「レビューする」をクリックすると通知が届きます。また、レビュアーが「レビューする」をクリックすると、GitHub APIを使用して自動でリポジトリのコラボレーターに追加され、レビュアーにレビューリクエストが届きます。
⑤レビュアーがプルクエストをApproveして、フィードバックを送信すると通知が届きます。通知をクリックし「お礼する」をクリックして、お礼を送信します。お礼を送信するとレビューは完了になります。
レビュアー
①「ログイン」or「無料で始める」をクリックして、GitHub認証でログインします。
②レビュー募集中からレビューしたい投稿を探します。レビューする場合「レビューする」をクリックします。「レビューする」をクリックすると、GitHub APIを使用して自動でリポジトリのコラボレーターに追加され、レビューリクエストが届きます。プルリクエスト上でレビューを行い、問題が無ければApproveしてください。プルリクエストをApproveしたら「フィードバックする」をクリックして、フィードバックを送信します。
③レビュイーがお礼を送信すると通知が届き、レビューは完了になります。
工夫した点
スキーマ駆動開発を導入しました
スキーマ駆動開発とは、OpenAPIの仕様書を最初に定義し、その仕様書をもとにバックエンドとフロントエンドの開発を同時に進める開発手法です。スキーマ駆動開発を導入したことでフロントエンド側、バックエンド側にメリットがありました。フロントエンド側は、openapi.ymlからAPIクライアントのコードを自動生成できるので、余分なコードを書く必要が無くなったり、リクエストとレスポンスに型が付与されているので安全に開発を行えたり、モックサーバーを立てることでバックエンドの実装を待つことなく、フロントエンドの実装を進められたり、ドキュメントを見れば、どんなリクエストを送れば、どんなレスポンスが返ってくるのか分かるので、バックエンドのコードを読みに行く必要がなく楽でした。バックエンド側は、OpenAPIの仕様書を最初に定義しているので、実装すべきことが明確で迷うことなく実装できました。
GitHub APIを導入することでユーザーの手間を減らし、UI/UXを改善しました
レビュー自体はプルリクエスト上で行うことを構想していたのですが、①レビュイーがレビュアーをリポジトリのコラボレーターに追加する②レビュアーが招待を承認する③レビュイーがレビュアーにレビューリクエストを送るという手順が必要でした。これを手動で行うとレビューをするまでに数日かかってしまいます。この手間を省くためにGitHub APIを導入し、レビュアーが「レビューする」をクリックするだけで手順を自動化し、UI/UXを改善しました。
苦労した点
Firebase AuthenticationのidTokenを用いてバックエンドで検証し、UIDをDBに保存するまでのJWT認証の実装に苦労しました
①フロントエンドからFirebase Authenticationに認証情報を送る②認証に成功するとidTokenが返却される③フロントエンドからidTokenをバックエンドに送り、idTokenを検証するメソッドが格納されているfirebase-auth-railsを使用して、改竄されていないか検証する④検証に成功するとpayloadが返却されるので、payloadからuidを取得してDBに保存するまでのJWT認証の流れを理解し、ミニアプリを作って試しながら、理解を深めて実装していきました。
なぜFirebase Authenticationを導入したのか
- 様々な認証プロバイダをサポートしていて、GitHubログインを簡単に実現できるから。
- OAuth/OpenID Connectなどの認証プロトコルをサポートしていてセキュリティの高い認証フローを実装出来るので、自前で認証を構築し、GitHubのメールアドレス、パスワードをDBに保管するより安全に管理できるから。
なぜidTokenを検証する必要があるのか
idTokenはBase64で暗号化されており、第三者でも簡単に復号できてしまうため、改竄される恐れがあるから。
なぜスキーマ駆動開発を導入したのか
使用したことのない技術を多く使用していて、実装前に技術をキャッチアップしたのですが、バックエンドの実装が完了してからフロントエンドの実装を進める手法だと、余分なキャッチアップコストが掛かってしまうと思ったので、同時並行で両方の実装を進めたかったから。
作り直したい部分
バックエンドのインフラをFly.ioからAWSやGCPなどのパブリッククラウドに作り直したいです。理由は2点あります。1点目は、実務ではAWSやGCPなどのパブリッククラウドが一般的に使用されているため、AWSやGCPなどのパブリッククラウドに作り直すこと自体が勉強になるからです。2点目は、Fly.ioのダッシュボードは見づらく、カスタマイズに制限があり、スロークエリの特定やトレースの確認、CPUの使用率、メモリの使用率、リクエスト数、レイテンシなどのアプリケーションのパフォーマンスに関する指標が把握しづらいため、サービスを運用していく上で不便に感じているからです。
自信のあるコード
routesとcontrollerです。DHHはどのようにRailsのコントローラを書くのかという記事を参考にデフォルトのCRUDアクションであるindex
show
new
edit
create
update
destroy
のみを使うようにしました。
フィードバックをどう改善したのか
RubyKaigi 2018のデザインを担当されたmachidaさんにフィードバックをいただき、キャッチコピーを変更したり、デザインを改善しました。他にも面接で「パフォーマンスは推測するのではなく、計測した方がいい」というフィードバックをいただき、APMツールを導入し、パフォーマンスチューニングをしました。
どんな学びを得たのか
- 個人開発は長期戦になるので、モチベーションを管理するのが一番難しい。最初から完璧を目指すのではなく、まずは終わらせることを意識する。
- 記事は参考になるが誤植もあるので、一次情報を見る癖を付けた方がいい。
- 1ヶ月前に書いたコードが何をしたいのか分からないことがあるので、出来る限りコードは綺麗に書く。
- エラーの大半はログを見れば解決する。
- 技術はアウトプットすることで理解が深まる。
- 要件定義、プロトタイピング、リソース設計、DB設計、実装、テスト、リリースをやってみてサービス開発の全体像が分かった。
- フロントエンド、バックエンド、インフラをやってみてバックエンドの開発をしている時が一番楽しいと感じることに気付いた。