Studyplus Engineering Blog

スタディプラスの開発者が発信するブログ

フロントエンドのCircle CI実行時間を1/3にしました

こんにちは。ForSchool事業部の石上です。先日、コンビニでサクレの梨味を買ってきたところ妻に絶賛されました。今年一番家庭で貢献した日かもしれません。

今回はフロントエンドのCircle CI実行時間を短くしたことについて書きます。

3行で

  • フロントエンドのCI実行時間が長く困っていました
  • 無駄なものを消したり並列化したことで、実行時間が9分(改善前)から3分(改善後)になりました
  • 今回サクッとできたのは改善がしやすい仕組みのおかげ

Studyplus for School フロントエンドのテスト

私が現在携わっているStudyplus for School(塾向けのSaaS)のフロントエンドでどのようにテストを書いているかについては、以前@okuparaさんが書いてくれました。

tech.studyplus.co.jp

この方針が定まる前はテストコードが少なかったのですが、方針を定めて開発メンバー全員で少しずつ書いていった結果、現在はだいぶテストコードが増えました。

Jestの実行結果に出力されるテスト数。Test Suites: 77, Tests: 403, Snapshots: 4 passed

テストコードが増えたため、実行時間も長くなりました。今回の改善をする前には、CIの実行時間がおよそ9分かかっていました。1ページに対するテスト実行時間は複雑な仕様のページだと1分を超えます。このままいくと、既存画面へのテストコード追加や新規画面の追加によって、10分、11分...と増えていくことは明らかでした。

CIの実行はGitHubへのpushの度に行われ、リリースの際は必ずCI上のテストが通ってから行うことになっています。CIに10分かかるということはリリース作業の度に、10分も手が止まってしまうことになります。

Circle CIの実行時間は9分ほどだった

やったこと

それに対してやったことは以下の2つです。

  • 無駄なものを消す
  • 並列化する

無駄なものを消す

CIで実行する必要があるものを見直しました。改善前は以下のような設定になっていました。

Circle CIの実行画面。tsc, prettier, eslint, jest, build-storybook, post test coverage

この中で、Storybookのビルドは不要と判断しました。というのも、ビルドサーバーは別にあり(Jenkins)、Storybookのビルドはそちらでも行っていたからです。

また、Jestでカバレッジの出力をしていましたが、これもCI上では行わないようにしました。実行時間がそこそこかかるのと、この次に行うテストの並列実行の設定も難しそうだったからです。テストが全然書かれていない状態から増やしていくときには良いモチベーションになっていましたが、現在テストカバレッジは70%を超えています。実行時間を長くしたり設定を複雑にしてまで、この値を毎回確認する必要もないだろうという判断です。カバレッジを確認したいときはローカルで実行すれば確認できます。

並列化する

Circle CIジョブの実行と、テストを分割して並列実行する設定をしました。これはCircle CIのドキュメント通りです。

workflows:
  build:
    jobs:
      - build
      - lint:
          requires:
            - build
      - tsc:
          requires:
            - build
      - test:
          requires:
            - build

設定後、以下のようになりました。

build でnode_modulesのインストールなどを行い、ワークスペースを作ります。その後の各ジョブはそのワークスペースでタスクを実行しますので、かかる時間はほぼその個別のコマンドの時間のみとなります。

workflowの分割と並列実行

テスト分割は、Circle CIのコマンドを利用してファイル名を分割して、各コンテナでテストランナーに渡します。並列数は、今回は8にしました。ざっくりと2,4,8,16と増やして試して16で速くならなかったので8に留めました。ここはもう少し最適化の余地がありそうです。また、Circle CIではtiming dataというテスト実行時間の記録を使って最適に分割する機能もあります。今回はこれも使わず、単純にファイル数での分割にしました。今後さらに速くしたいとなれば、その辺の設定も検討したいと思います。

- run:
    name: test
    command: circleci tests glob 'src/**/*.test.{ts,tsx}' 'tests/**/*.test.{ts,tsx}' | circleci tests split | xargs yarn test

テストの分割と並列実行

これで、ジョブを並列にしつつ、テストも分割して並列実行できるようになりました。実行時間は3分ほどなので、修正前のおよそ1/3となりました。

並列になって速くなった様子(CircleCIの画面)。実行時間は2m59s。

今回サクッとできたのは改善がしやすい仕組みのおかげ

このような改善は、それ自体が難しくなくても後回しになりがちです。私のこれまでの経験でも、こういった作業は特に計画に入れず手が空いたときにやるということが多かったです。

Studyplus for Schoolの開発チームでは、毎スプリント2割はこういった技術的改善に当てて良いということになっています。そのため事業に直接影響がない(けど開発効率に地味に影響がある)ような改善を、エンジニアが優先度を決めてできるようになっています。この仕組みを利用して、今後も開発効率をじわじわと上げていけたら良いなと考えています。