🤖

【Devin に任せる CI 改善】 Flaky Test 自動修正で得た開発体験の進化

に公開

こんにちは!
Hubble でバックエンドエンジニアをしている @koyakota です!

Hubble Advent Calendar 2025 の 15 日目の記事です。

はじめに

皆さんは 不安定なテスト(Flaky Test) によって、以下のような問題に直面した経験はないでしょうか?

  • すみません、現在 develop で CI が落ちていてテストがグリーンにならないのですが、以下はこのままレビュー再開してもよろしいでしょうか?

  • 恐らく Flakyのテストが落ちていると思うので、何回か Re-Run してもらえますか 😇

  • ぐぬぬぬ…(通らねぇ 😭)

Hubbleでも、この不安定なテストが原因でCIの信頼性が低下し、エンジニアの貴重な時間を奪ってしまうという課題を抱えていました。

テストが失敗するたびにCIを再実行しなければならない状態に陥っていたのです…

この記事では、Flaky Test問題を解決するために、Devinを活用して修正プルリクエストの作成を自動化し、CIの安定化と開発体験の向上を実現した取り組みについてご紹介します。

読んで欲しい方

  • Flaky Test がデプロイの妨げになることで開発効率が悪化していると感じている方
  • AI エージェント「Devin」などを活用し、自動化できないかなと考えたりしている方
  • CI を安定させ、開発者 本来の業務に集中できるような開発体験ができないのかを検討している方

そもそも Flaky Test とは

Flaky Test の定義と問題点

不安定なテスト、いわゆるFlaky Testとは、テストコードやテスト対象のコード自体に変更がないにもかかわらず、実行するたびに成功したり失敗したりしてしまうテストのことを指します。

本来は常に同じ結果を返すはずのテストですが、実行する環境やタイミング、並列でテストを行うかどうかなどによって振る舞いが変わってしまい、その結果として発生します。

Flaky Test を放置するとどうなるか

結論から言うと 「Flaky Test は単なるテストの不具合ではなく、チーム全体の生産性と、CI に対する心理的安全性をじわじわと削る継続的な負債になるかもしれない」 ということです。

Flaky Testを放置すると、テストが本来担うはずの「安心して継続的に変更できる」という状態が少しずつ崩れていきます。
最初は「たまに落ちるな」程度でも、チームの開発人数や変更量が増えていくほど影響は雪だるま式に大きくなります。
開発体験、リリース速度、そして品質保証の信頼性を同時に蝕んでいくことになります。

開発効率の悪化

いちばん大きな悪影響は、開発者の時間が奪われてしまうことです。
問題ないはずのPull Request (PR)のCIが失敗すると、エンジニアは本来不要だった「なぜテストが落ちたのか」の原因調査や、CIの再実行に時間を費やすことになってしまいます。

しかも厄介なのは、調査した結果、「特に何も問題がなかった」で終わることが多い点です。
ログを追って、直近の変更を追って、関連しそうな箇所を探してみる。
でも最後は「再実行したら通ってしまった」で片付く…

デプロイの遅延

リリースの速度にも直接影響します。
PR の段階であれば、「いったん後回しでいいや」とできる場面もありますが、masterブランチの CI が失敗してしまうと、デプロイ担当者はそれがFlaky Testによるものかどうかを切り分ける作業を強いられることになります。

⸻

再実行でたまたま通っても、それは解決ではなく先送りです。
リリースのたびに同じ不安を抱えて、同じ手段を繰り返すことになってしまいます。
そしてデプロイが遅れるということは、単なる待ち時間が増えるという話ではありません。
リリースが遅れることは、ユーザーに届く価値が遅れるということでもあります。
さまざまな関係者の判断が遅れる可能性もありますし、それが積み重なってビジネス的にも良くない影響が起こり得ます。
Flaky Testは一見すると開発の内部問題に見えますが、最終的には開発のスピードに深く影響する問題です。

⸻

だからこそ、早期発見と修正の自動化が必要になる。

ここまで挙げた課題は、いずれも「人が頑張って運用でカバーする」という方向に流れがちです。
再実行でしのぐ、担当者が勘で判断する、落ちたら誰かが調べる。短期的には回ることもありますが、長期的には確実にチームの体力を奪います。

そこでこれらの課題を解決し、エンジニア全員が安心して高速に開発を進められる環境を作るために、Flaky Testの早期発見と修正を自動化する仕組みの構築に乗り出しました。

解決策

全体像: 4 ステップで自動化のフローを構築

1. テスト実行と検出

  • CI: 日頃のCIテスト結果を Datadog に送信し、Datadog CI Visibility(テスト結果の可視化)を利用する
  • Datadog: Flaky Test を自動検出し、Monitor でアラートを発報する

2. 情報収集とトリガー

  • Workflow Automation が、Flaky Test の修正に必要な情報を収集する
    • Flaky Test が発生したテストファイル名
    • 直前の Commit SHA
    • Branch 名
    • Author 情報
    • 失敗内容(エラーメッセージ、スタックトレース)

3. 自動修正

  • Workflow Automation が収集した情報をもとに Devin API を呼び出し、修正 PR を自動作成する
  • GitHub に PR が作成される

4. 担当者が PR をレビュー

  • Merge または Close を判断する

Devin は本当に Flaky Test を直せるのか?

「Devin が実際に Flaky Test を修正できるのか?」という疑問を持たれるのは当然かもしれません。

自分自身も 「ホンマにできるんかいなww」 と思っていました。
しかし、結論から言うと、Devinは当初の予想を遥かに上回る、非常に的確な修正提案を提示してくれています!

この精度の高さの鍵はなにか。
Devinに与えるプロンプト設計にあります。
以下は、Devinに渡しているプロンプトの一部を抜粋したものです。
単に「直して」と依頼するのではなく、以下の点について具体的な指示を出しています。

---------------- ■ テスト情報 ----------------
テストファイル: テストファイル名

---------------- ■ 実行環境情報 ----------------
Branch   : git_branch
Commit   : git_sha
Author   : git_author
Error    : error_message
Stack    : error_stack

---------------- ■ 作業内容 ----------------
1. 該当テストコードを修正し、Flaky(不安定)状態を解消してください。

2. 修正後、ローカル環境で当該テストを5回連続実行し、すべて成功することを確認します。
   - Seed値を変更しながら実行してください。
   - 複数のテストファイルを同時に実行し、並列実行環境に近い条件で検証します。

3. 以下の条件で Pull Request を作成します。
   - タイトル: flaky: (原因と修正内容を簡潔に記載)

---------------- ■ Pull Request の description に記載する内容 ----------------
- 発生していた問題の概要
- 修正内容と Flaky が解消されたと判断できる理由

---------------- ■ 注意事項・制約 ----------------
- 修正内容は最小限にし、テストの意図を変更しないこと。
- 他の spec ファイルと一貫性のある修正パターンを採用すること。
- 失敗を再現する際は、並列実行環境で自然に発生しうる条件を使用すること。
  (特定データを手動作成するなど人工的な操作は禁止)
- 修正前に再現実験を行い、失敗時の実行コマンド・再現手順を明記してください。
- 修正によって Flaky が解消されると判断した理由を明確に説明してください。

---------------- ■ 参考情報 ----------------
- テストはファイル単位で分割され、並列実行されています。

このプロンプトに基づき、Devinは様々なパターンの Flaky Testを修正してくれました。

ここでは、その中から代表的な例をご紹介します。

修正例: 不安定性の解消

↓↓↓ 実際に作成してくれたPR概要

## Pull Request の概要

対象ファイル名 のテストが不安定に失敗していた問題を修正します。

### 発生していた問題
テスト内で `2.days.ago` が2回呼び出されていました:
1. `subject { Model.update!(value: 2.days.ago.to_s) }` - 更新時
2. `expect(Model.reload.value).to eq(Time.zone.parse(2.days.ago.to_s).utc.to_s)` - アサーション時

これらの呼び出しが秒境界をまたぐと、値が異なりテストが失敗していました。

### 修正内容
`let(:updated_value) { 2.days.ago.to_s }` を追加し、`subject` と assertion で同じ時刻値を使用するように修正しました。これは同ファイル内の他のテスト(line 255等)と同じパターンに従っています。

### Flaky が解消されたと判断できる理由
時刻値を `let` で固定することで、テスト実行中に時刻が変化しても同じ値が使用されるため、秒境界をまたぐ問題が発生しなくなります。

before

原因は、subjectとexpectの異なる行で動的な時刻(2.days.ago.to_s)が再計算され、秒単位のズレが生じる可能性があったようです。

# 変更前
let!(:model) { create(:model, value: 1.week.ago.to_s) }

# subject行で1回目の時刻計算
subject { model.update!(value: 2.days.ago.to_s) }

# it行で2回目の時刻計算
it "hogefuga" do
  subject
  # 失敗の原因: 実行中に時刻がズレると、subjectで使われた値と異なる
  expect(model.reload.value).to eq(Time.zone.parse(2.days.ago.to_s).utc.to_s)
end

after

時刻値を固定し、subjectとexpectの両方で固定された同じ値を使用することで、時刻のズレによる競合を解消しました。

# 変更後
let!(:model) { create(:model, value: 1.week.ago.to_s) }

# 時刻値を固定するletを追加
let(:updated_value) { 2.days.ago.to_s }

# subjectとitで同じ固定値を使用
subject { model.update!(value: updated_value) }

it "successfully updates value" do
  subject
  # 固定値 updated_value を使用
  expect(model.reload.value).to eq(Time.zone.parse(updated_value).utc.to_s)
end

特に重要なのは、以下の 2 点です。

  • セルフチェックを伴う厳格な検証指示(作業内容 2)
  • 思考プロセスの明示要求(Pull Request description)

いわゆる 見えるように汗をかく ということをさせています。

(見えるように汗をかく については、最後に少し記述しています)

導入後の成果と今後やりたいこと

直近で4件のFlaky Testを修正するPRが、Devinによって作成されましたが、すべてエンジニアが何も変更することなくマージされました。
(同時に存在するFlaky Testを感知し、同じ内容のPRは作成されていましたが😇)

導入後の成果

導入前

  • CIが失敗するたびにエンジニアが、SlackとCIログなどを行き来しながら、これは本当のテストのバグなのか、それともFlakyなのかを整理する時間が必ず発生
  • Flaky Testだと判断できた場合でも、再現手順の特定や修正案の検討、PR作成までにそれなりの時間をとられる
  • さらに、誰が対応するのかが曖昧になり、対応が後回しになるといった課題も起きていた

導入後

  • 検知されると同時に、Devinが原因推論を含む 修正案PRを自動で提示
  • そのままMergeフローをたどる
  • その結果、CIは安定し、エンジニアはFlaky Testによる手間取りや不要な調査から解放され、より価値のある機能開発に注力できる

今後やりたいこと

レビューもAIを活用し、自動化へ展開していきたいと考えています。
現在、Devinが作成した PRをマージするかどうかの最終判断は人間が行っていますが、CursorやCopilot、その他のツールなども併用し、Devinが作成したコード自体を自分で修正してもらい、品質を高めて運用を進めていく予定です。

まとめ

どうやって見えるような汗をかいてもらうか

そもそも「見えない汗をかく」とはチャッピー🤖 さんいわく、

比喩としての「見えない努力」
表からは見えないところで気を配ったり、準備したり、工夫したりしていること。
例:目立たない改善を積み上げてる
「陰で努力している」「裏方仕事をしている」に近いニュアンスです。

らしいのですが…

DevinのようなAIエージェントにFlaky Testの修正を任せた場合、最終的な修正コードだけを見ても、「なぜこの変更になったのか」「裏で何を考え、どう試行したのか」が分かりません。
運用初期は、成果物のMerge判断は人が最終決定を行っていたため この「見えない汗」をあえて「見える汗」に変えることが、アウトプット(PR)を信頼するためには必要だと考えました。

「見える汗」がもたらす効果

  • レビューの迅速化
    • 「なぜこの修正が必要なのか?」という根本的な疑問が解消され、レビューにかかる時間が短縮されます。
  • ナレッジの蓄積
    • Flaky Testの原因と修正方法に関するAIの推論がPull Requestに記録されることで、それはチーム共通の貴重なデバッグノウハウとして蓄積されます。特に人間が見逃しがちな原因をAIが特定した場合、その記録は大きな価値を持ちます。

まとめると

以下のような情報があると、レビュワーも背景情報をすっとインプットでき、即 Mergeできるわけです。

  • 原因の推測(なぜテストが落ちたのか)
  • 修正方針の決定(どういうアプローチで直すことにしたか)
  • 実行した具体的な変更(最終的に何をしたか)

さいごに

今回は、Devinを使ってFlaky Test修正を自動化し、CIの安定化と開発体験の改善につなげた取り組みをご紹介しました!

AIを開発フローに取り入れることで、これまで人手で抱えていた切り分けや調査、修正といった負荷の高い作業を減らし、より価値のある開発に集中できる状態を作ることができました!

これは小さいかもしれませんが、開発体験の進化だと思っています。

この記事が、Flaky Test対応に課題を感じている方にとって、何か1つでもヒントになれば嬉しいです。

最後までお読みいただき、ありがとうございました!

明日は @nohata さんです!

Discussion