ソロ開発者のためのドッグフーディング問題
「自分で飼っている犬のエサ(=自社のドッグフード)を食べろ」は良い助言です。自分の製品を使いましょう。ユーザーが見つけるバグを探しましょう。ユーザーより先に痛みを味わいましょう。
実際には、何が起きているかというと:
- リリース直後、かなり頻繁に使う
- 開発が優先され、触らなくなる
- ユーザーとしてではなく、開発者として確認する — 正しい動線は全部知っている
- 「動くこと」が基準になり、雑なUXがすり抜ける
私は一人でウェブアプリを作り、保守しています。ある時気づきました。ユーザーとして実際に使っていなかったのは、もう何週間も前からだったのです。機能は出荷していましたが、製品そのものを体験していなかった。
そこで、少しばかげていると思えることをやりました。AIエージェントに仕事を渡したのです。
3時間ごとにAIエージェントが私のプロダクトを開き、動作確認を行い、壊れているものを見つけたらPRを開きます。
それがどう動くのか、何を見つけたのか、そしてできないことは何かを紹介します。
セットアップ
3つのコンポーネントがあります:
| コンポーネント | 何をするか |
|---|---|
| AIエージェント(Claude) | 何をチェックするかを決め、結果を解釈し、修正を書きます |
| MCPサーバー | アプリのAPIをAIから呼び出せる関数として公開します |
| Playwright | AIが実際のブラウザを操作してUIをチェックできるようにします |
エージェントはスケジュールされたハートビートで動作します。チェックする内容はマークダウンファイルで定義します:
# チェックするもの(順番に回します):
## APIチェック
- list_projects、get_canvas、get_verification_status を呼び出す
- データの整合性とレスポンス形式を検証する
## UIチェック
- 実ブラウザでライブサイトを開く
- モバイルビューポート(375px)をチェック
- ダークモードをチェック
- 空の状態(新規ユーザーには何が見える?)をチェック
- 異常があればスクリーンショットを撮る
## コードの品質
- tsc --noEmit を実行し、TypeScript のエラーを報告
- 最近変更したファイルで未使用のimportがないか確認
## 壊れているものを見つけたら:
- ブランチを作成する
- 修正する
- vitest を実行してテストが通ることを確認
- PRを開く
- Discord に報告する
これが唯一の指示セットです。あとはAIが処理します。
API連携がどのように動くか
デフォルトでは、AIはアプリの内部に直接アクセスできません。そこで私は、自分のAPIをMCP(Model Context Protocol)サーバーとして包みました — つまり、AIが呼び出せる関数のリストです。
// AIはツール呼び出しのようにこれらを呼び出せます
const tools = {
list_projects: {
description: "Get all projects",
handler: async () => await db.project.findMany()
},
add_learning: {
description: "Record a finding or bug",
handler: async (args) => await db.learning.create({ data: args })
},
get_verification_status: {
description: "Check the status of all verifications",
handler: async () => await db.verification.findMany()
}
};
これによりAIは、人間ユーザーが行うこと — レコードの作成、データの読み取り、状態の確認 — を、クリック操作ではなくAPI経由で実行できるようになります。
見つかったこと
以下は、エージェントが見つけた「本物の」バグ3つで、これを自分ではおそらく見つけられなかったものです:
バグ1:APIとUIが同期していなかった
API経由でデータを作成すると、APIのレスポンスにはデータが正しく表示されました。ところが、そのデータがUIには現れなかった。
原因: データは2つの別々のデータベーステーブルに保存されていました。APIは一方を書き込み、UIはもう一方を読み取っていたのです。
人間が見逃した理由: 人間は常にUIを使います。ブラウザで「作成」をクリックすれば、両方のテーブルに書き込まれます。このバグは、API経由で作成したときだけ発生しました — 人間はそれを決してしませんが、AIは毎回のチェックでやってくれました。
バグ2:モバイルレイアウトが壊れていた
デスクトップでは問題ありませんでした。モバイル(375px)では、入力欄が水平方向にオーバーフローしていました。
修正: CSSの1か所を変更するだけ: grid-cols-2 → grid-cols-1 md:grid-cols-2。
バグ3:空の状態が真っ白だった
新規ユーザーが最初のプロジェクトを開くと、…何も表示されませんでした。エラーはなく、ただ真っ白。案内もなく、「最初のアイテムを作成」ボタンもありません。
これは技術的に壊れているわけではありませんでしたが、新規ユーザーにとって製品が分かりにくくなるだけでした。エージェントはこれをUXの問題としてフラグを立て、空の状態コンポーネントを提案しました。
ドッグフーディングだけでは不十分だった
ドッグフーディングは多くのことを見つけてくれます — とくに壊れたフロー、レイアウトの問題、雑なUXです。
しかし、すべては見つけられません。
特定の条件下でのみ起きるバグがあります:
- まれなユーザー操作のあとでだけ、コンポーネントがクラッシュする
- importの不一致が、手動テストでは踏まないルートを壊す
- 例外が実データ、実際のタイミング、実際のブラウザ状態でのみ現れる
そういったバグは、数時間ごとに手動でプロダクトを使って探すのは難しいです。
そこで私は、2つ目のループを追加することにしました:エラーモニタリングです。
ドッグフーディングのエージェントは、製品がユーザー体験として機能しているかをチェックします。
エラーモニタリングは、製品が現実の場で失敗していないかを確認します。
この組み合わせは、どちらか一方だけよりもずっと強力でした。
Sentryを2つ目のフィードバックループとして追加する
これでシステムには、相補的な2つのループがあります:
返却形式: {"translated": "翻訳されたHTML"}| ループ | 何を検知するか |
|---|---|
| 3時間ごとのドッグフーディング | 壊れたフロー、見た目の不具合、空の状態、モバイル回帰、荒いUX |
| Sentryの監視 | 実行時例外、本番限定のバグ、再現しづらいクラッシュ |
ドッグフーディングのループは次に答えます:
- ユーザーは実際に製品を使って前に進めるのか?
- UIは意味のあるものになっているか?
- 何か見た目が壊れていないか?
Sentryのループは次に答えます:
- 本番で何かがクラッシュしたのか?
- スタックトレースと、そのときの状況の文脈は何だったのか?
- 低頻度の失敗の裏に、直せるバグは隠れていないか?
これは重要です。品質上の問題はすべて同じ見え方をしないからです。
いくつかの問題は目に見えます。
他は、スタックトレースとしてだけ現れます。
ドッグフーディングだけに頼っていると、本番限定の失敗を見落とします。
Sentryだけに頼っていると、気まずいUXや、クラッシュはしないが壊れているフローを見落とします。
この2つを組み合わせることで、はるかに完全な品質ループが形成されます。
検知から自動修正へ
Sentryを追加したことで、エージェントの仕事は拡張されました。
もはや、製品を使うことで問題を探すだけではありません。
製品自身から報告された問題にも、反応できるようになりました。
現在のフローは次のようになっています:
- 3時間ごとに、エージェントがアプリをドッグフーディングする
- 別のスケジュールで、解決されていない課題がないかSentryを確認する
- 本当のバグを見つけたら、スタックトレースとソースコードを分析する
- ブランチを作成し、修正を書き、テストを実行してPRを開く
- 小さな安全な修正は、チェックが通った後に自動でマージできる
特に良い例の1つは、間違ったi18nフックのimportによってページがクラッシュしていたケースでした。
エラーメッセージ自体は曖昧でした。手動テストでも一貫して見つけられませんでした。
しかしSentryは、エージェントが問題を「不適切なimport」まで追跡して、小さな修正を生成するのに十分な文脈を提供してくれました。
その瞬間から、これは「自動テスト」のようなものではなくなり、自動メンテナンスのループのように感じられるようになりました。
AIにできること/できないこと
| AIが得意なこと | AIができないこと |
|---|---|
| 何かが動くかどうかの確認 | 何かが気持ちよく感じるかの判断 |
| 回帰を自動で検知すること | 「このやり取りはイライラする」 |
| 人間が見落としがちな例外ケースをカバーすること | 主観的なUXの評価 |
| バグを見つけたら即座にPRを開くこと | 機能が欠けていると判断すること |
| 疲れずに3時間ごとに実行すること | 実際のユーザーからのフィードバックの代替 |
「できないこと」の列が重要です。AIは代替ではなく補完です。
エージェントがチェックを終えた後でも、私は自分で製品を使ってユーザーと話す必要があります。エージェントは客観的で、繰り返し可能なチェックを担当します。私は、主観的で、体験に基づくものを担当します。
もう1つの正直なメモ
約30%の時間、エージェントは「直った」と報告しますが、実際には完全に修正できていないことがあります。これは、ある厳格な要件を組み込むまで悩ましい問題でした:完了扱いにする前にテストが通っている必要がある。
ルール:PRを開く前に `npx vitest run` を実行する。
テストが失敗した場合はPRを開かない。
代わりに失敗を報告する。
これにより、誤った完了(false completions)が大幅に減りました。エージェントの自信は信頼できません——信頼できるのはテスト結果です。
これを作る方法
あなたは私と同じセットアップをする必要はありません。最小限の実用的なバージョン:
- スケジュール実行するランナーを選ぶ — GitHub Actionsのcron、crontab、またはスケジュールされたタスクを扱える任意のエージェントプラットフォーム
- AIが呼び出せるAPIエンドポイントを1つ公開する — 最初はヘルスチェックだけで始める
- シンプルなチェック指示を書く — 「このエンドポイントを呼び出して失敗するかどうか報告する」
- 後からPlaywrightを追加する — ブラウザのチェックは必須ではありませんが、見た目の回帰を見つけるのに非常に強力です
本質的な洞察は技術スタックではありません。ドッグフーディングは能力の問題ではなく、規律(ディシプリン)の問題です。 あなたは自分の製品をテストする方法を知っています。ただし、それを一貫してやっていないだけです。
それを自動化すれば、規律の要求を取り除けます。
あなたはサイドプロジェクトに、自動化された品質ループを何か組み込んだことはありますか? それともテストは「手元の環境で動いた」で始まり、「手元の環境で動いた」で終わってしまっていますか? 他の人が試したことをコメントで知りたいです。
エージェントがテストし続ける製品は、KaizenLabです。仮説検証とプロダクト学習のための私のアプリです。
このセットアップが特に役立っているのは、プロダクトの意思決定を整理するために私が使っているのと同じシステムが、エージェントによるチェック、ストレステスト、そして改善支援にも使われ続けているからです。



