見出し画像

スピフルで得たDevExへの挑戦

こんにちは、プログリットでエンジニアをしているKokiです。
先日リリースした「スピフル」で開発リードを担当したのですが、今回は、迅速に開発していくためにDevEx(Developer Experience)観点で挑戦して良かったものをご紹介したいと思います。

怒涛の開発期間

スピフルのキックオフは2023年1月24日にありました(もう一年も経つなんて早い…)
キックオフメンバーは、4人(PdM、デザイナー、エンジニア2)でジャストアイディアからペルソナ選定まで全員で議論しながら行いました。
その後のタイムラインとしては

  • 1月:キックオフ、要件ディスカッション

  • 2月:技術選定

  • 3月〜5月:第1開発期(口頭英作文の実装)

  • 6月〜7月:第2開発期(1分間スピーチの実装)

  • 8月〜11月:ベータ期間

  • 12月:リリース

という怒涛の日々を送りました。第2開発期で実装した1分間スピーチについては先日記事を書いたのでぜひ読んでみてください!

さて、スピフルは長年一緒に仕事し僕が尊敬している業務委託の方との2人3脚で開発を行いました。この間、他プロダクトと兼任体制だったため、技術選定だけでなく効率よく開発を行えるかを非常に重視していました。工夫した点としては以下になります

  • フロント、バックエンドのモノレポ構成

  • PRレビューのスキップ

  • いつでもリリース出来る環境

プログリット初?のモノレポ構成

基本的にプログリットではフロントとバックエンドでチームが分かれており、Githubリポジトリもそれに合わせて分かれています。
なぜ今回モノレポにしたかというとエンジニア2人ともフルスタックに開発経験があるため、技術領域でリポジトリを分ける必要もなく、むしろREADMEやDockerなどの初期セットアップやリポジトリの細かい設定(ブランチの保護など)などを省きスピードを優先したかった。というのが背景にあります。
また、E2Eは初期段階から導入しようと考えていたので、リポジトリが別れているだけでGithub Actions上でAPIを立ち上げる手間がかかるのは目に見えていました(Github Actionsは別リポジトリのアクセス権をデフォルトで持っていないので、PATなどを別途用意&管理する必要があります)。

モノレポにして良かったのか?

結論、良かったと思います。というのも例えばバックエンドとフロントはOpenAPIを元に生成したクライアントコードでやり取りしていたのですが、この生成をコミット時に自動生成してPRに含めてしまう。など細かいですが、別レポであればコミットPushする手間だけでなく、PR作成→CIチェック→マージとモノレポと比べると作業が2倍以上かかっただろうと思います。

STAGED_OPENAPI_FILES=$(git diff --cached --name-only | (grep "^openapi.*.yml$" || :))

if [ "$STAGED_OPENAPI_FILES" != "" ]; then
  echo "start bundle openapi yml"
  yarn build:openapi
  npx --yes @redocly/cli bundle ./openapi/index.yml --output ./openapi/index.json --lint
  echo "bundle success"

  echo "generate api definitions"
  yarn workspace frontend generate-api-client
  git add frontend/src/api/aspida
fi

↑実際のhuskyのpre-commitファイルに書いていたものです。

PRレビューを辞めてみる

僕ら二人は長年一緒に仕事をしていて、2年ほどお互いのコードをレビューしてきた仲で2年も経つとPRで大きな指摘もなく、相手がどんなコードを書いてくるか想像内に収まるという状態が続いていました。
そこで、スピフルではPRレビューをスキップする施策を取ってみました(開発スピードとコード品質がどのようなバランスに落ち着くのか個人的な興味もありました。)
結果としてはこの1年間デグレや不具合は起きず、むしろGithubのNotificationsがチラつかず非常に集中しやすい職場環境だった気がします。

もちろん、PRレビューをスキップするのですから、特殊なコードだなと感じたらコメントを書いたりFYIメンションをPR内でしたりとコミュニケーションを十分に取ることには気を遣ったり、月1の振り返りでお互いにFBをしています。

月イチ振り返りなどについてはShimaさんが過去に記事書いてくれたのでご参照ください

コミュニケーション以外にも二人ともテストを手厚く書いていく意識がとてもあったと感じています。テストは

  • 単体テスト(フロント&バックエンド)

    • 難しい文字列計算など

  • 統合テスト(バックエンド)

    • DBを含めた統合テスト。ほとんどのAPIのテストを書いています

  • E2Eテスト

    • UI含めたテスト。録音機能など一部機能を除き、ほぼ全ての画面をテストしています。

これらをGitHub Actionsで実行して、それぞれGitHubのブランチ保護ルールでテストがパスしないとPRマージ出来ないようにしています。
フロントチームで大事にしているテストについては下記の記事書いているので良ければご覧ください!

Enable auto-mergeもめちゃくちゃ良かった

開発効率を上げるために、もう一つ便利だったのがGitHubの「Enable auto-merge」です。
自動マージ機能はGitHub Actionsで動かしているLinterや単体テスト、E2Eなど必須としたものが成功すると自動的にマージされる機能です。
僕らの性格なのか、イシューやPRを細かく作る特性があり、今見ると累計1,200PRくらいマージしていました。ざっくり計算で1日6PRマージしてきたことになります(1200PR÷(月20日稼働×10ヶ月)=6PR/日)。
PR作成コストがその分必要になりそうですが、この自動マージ機能がそれを軽減してくれ次の作業、次の作業に移りやすかったと感じました。

とはいえ、モノレポだとCIが色々動いちゃう

PR作成してから自動マージされるまでの流れ

通常、変更に関係あるCIのみを動かしたいものです。バックエンドだけの変更であればフロント側のLintや単体テストの実行は不要です。しかし、ブランチ保護ルールと自動マージ機能を上手く噛み合わせるのに一工夫が必要でした。
ブランチ保護ルールはベースブランチごとに変えることは出来ますが、PRの内容によって成功必須とするJobを変更することは出来ません。
PRを細かく作る性格上、Github Actionsの実行コストが膨れ上がるコストが膨れ上がる懸念もあり、保護ルールを外すかで悩みました。かといって開発効率を上げるためにも自動マージ使いたい、というジレンマがありました。

PHPUnitがwaitingで止まっている様子

そこで色々調べている中で、GitHubの保護ルールはJob名で判断していそうというのが判明し、ダミーのJobを用意してみることにしました。

# 必須ではないCIジョブでもprotection ruleの恩恵を受けられるようにする
name: Dummy job

on:
  pull_request:

jobs:
  Jest:
    runs-on: ubuntu-latest
    steps:
      - run: echo "dummy job"
  PHPUnit:
    runs-on: ubuntu-latest
    steps:
      - run: echo "dummy job"

ただechoするだけのジョブですが、ジョブ名 「PHPUnit」の成功実績を作れるので保護ルールも通り抜けること出来ます。
そして、この実績は後勝ちのようで、本当のPHPUnitが動いて失敗した場合は、失敗実績が残り保護ルールでブロックされます。
変更に関連するCIだけを動かしながら網羅的に保護ルールを適用することが可能になりました。

いつでもリリース出来る環境

プログリットの他プロダクトは月1くらいのペースでマイルストーンを設けており、それに向けて各チーム仕様議論、実装、QAなどを行っています。
スピフルは新規事業ということもあって、ベータ期間(3ヶ月)中できるだけお客様の声を聞き、機能追加や改善をリリースして反応を伺いたい!という気持ちがあったので、他プロダクトのマイルストーンとは関係なくほぼ毎日リリースを行える環境を目指し下記を用意しました。

  • リリースPRの自動生成するGitHub Actions

  • リリースしてOKチェックボックスの設置とチェック

リリースPRの自動生成するGitHub Actions

name: Create PR from staging to master
on:
  push:
    branches:
      - develop
jobs:
  gitPrRelease:
    name: git-pr-release
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - name: git-pr-release
      uses: bakunyo/git-pr-release-action@master
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        GIT_PR_RELEASE_BRANCH_PRODUCTION: main
        GIT_PR_RELEASE_BRANCH_STAGING: develop
        GIT_PR_RELEASE_LABELS: release,ignore-for-release-note

リリースPRのGitHub Actionsは非常に単純で下記のActionを使わせて頂きました。
bakunyo/git-pr-release-action

developにPRをマージすると上記のようなPRが自動作成されます

このチェックリストはマージしたPRごとに行が追記されます。
QAで問題がなければエンジニアがチェックを入れるという流れです。
このチェックボックスは、「このPRリリースしてOK!」という意味合いがあり、全てチェックが入って初めてこのリリースPRはマージ出来るようになります。
全てにチェックが入っているかは Shopify/task-list-checker を利用したGitHub ActionsをリリースPR更新時に走らせるようにしています。

name: Relase Task Cheker
on:
  pull_request:
    types: [opened, edited, synchronize, reopened]
    branches:
      - main

jobs:
  relase-task-cheker:
    runs-on: ubuntu-latest
    steps:
      - name: Check for incomplete task list items
        uses: Shopify/task-list-checker@main
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}

このWorkflowはチェックが全て入っていないと成功ステータスにならないので、ブランチ保護ルールを使い誤リリースを防ぐ事が出来ます。
リリースPRの自動生成とチェックリストを設けることで、リリースして良いのか悪いのかをコミュニケーション取ることなく判断するのが容易になりました。
他にも前述のテストを実行したりとコード品質も担保していましたが、こういった人を介在しない開発効率にも力を入れました。

まとめ

コード品質を下げることなく開発やリリースのサイクルを早める事例を紹介しました。不具合なく開発出来たのは新規機能追加がほとんどだったので、PRレビュースキップは常套手段とはならないと思いますが、新規事業ならではな大胆施策を出来たのは、とても楽しく学びの濃い期間でした。

現在、フロントエンドチームでは複数のプロダクトをモノレポに移行しています。一つ一つのPRがより広範囲に影響を及ぼすコード環境になるため、PRレビュースキップによるデグレリスクは楽観視出来ないなと考えています。ただ、これからもエンジニアが増え多用な開発状況になっても生産性の向上と何よりコード書いていて楽しいと思える職場環境をみんなと一緒に作っていきたいです!
ここまで読んでくれてありがとうございました。

プログリットの成長を加速させる開発のお仕事を担う仲間を募集しています!

プログリットでは、プロダクト開発のメンバーを募集しています!
「世界で自由に活躍できる人を増やす」というミッションに共感してくださる方、組織の中でお互いに切磋琢磨しながら成長していきたいという方は、ぜひカジュアル面談でお話しましょう!


この記事が気に入ったらサポートをしてみませんか?