はじめに
個人開発でタスク管理系のWebアプリ(Next.js + PostgreSQL + Prisma、AWS上にサーバーレス構成でホスティング)を作っています。開発は自分(人間)1人ではなく、Claude CodeやOpenAI Codex CLIなど複数のAIコーディングエージェントを並列に起動して進めるスタイルを取っています。
この記事では、「エージェントを並列で走らせて開発速度は上がったが、成果物の確認が追いつかない」という課題に対して、GitHubのPRごとに独立したプレビュー環境をAWS上に自動構築する仕組みを作った話を紹介します。ポイントは「アーキテクチャは複製せず、データベースだけスキーマで論理分離してコストを抑えた」ところです。
課題:エージェントを並列に動かすと、成果物の確認が追いつかない
複数のAIエージェントに機能実装を並列で任せると、開発のスループット自体は上がります。しかし、次のような問題が出てきました。
- 各エージェントがそれぞれ別ブランチ・別PRで作業するため、「動いているところを見る」までのハードルが高い(ローカルでブランチを切り替えて起動し直す必要がある)
- 複数PRを同時にレビューしようとすると、ローカル環境の切り替えコストがボトルネックになる
- 「コードは読んだが実機で動かしていない」状態でマージしてしまうリスクがある
つまり、律速しているのは実装スピードではなく「人間がどれだけ早く・楽に成果物を確認できるか」でした。
対策:PRごとに独立したプレビュー環境を自動で立てる
そこで、PRを作成すると自動でそのPR専用の検証環境がAWS上に立ち上がり、URLがPRにコメントされる仕組みを作りました。
- URL:
https://pr-<PR番号>.preview.dev.example.com - ライフサイクル: PRの
opened/synchronize/reopenedでデプロイ・更新、closed(マージ含む)で自動破棄 - レビュアーはローカルで何も準備せず、PRのリンクを開くだけでそのPRのコードが動いている状態を触れる
これにより、複数エージェントが同時に出してくるPRを、ブラウザのタブを切り替えるだけで見比べられるようになりました。
全体アーキテクチャ
本番環境はシンプルなAWSサーバーレス構成です。
- フロントは Next.js の静的エクスポートを S3 + CloudFront で配信
- API は API Gateway + Lambda
- CloudFront が
/api/*を API Gateway にルーティングする同一オリジン構成なので、CORS設定が不要でCookieベースのセッションもそのまま使える - DBは Aurora Serverless v2(PostgreSQL互換)
プレビュー環境は、この本番構成と同型のスタック(CloudFront + S3 / API Gateway + Lambda)をPRごとに作る一方で、以下は本番と共有します。
| 共有するもの | 理由 |
|---|---|
| VPC・サブネット | プレビューのためだけにネットワークを増やさない |
| Aurora クラスタ本体 | PRごとにDBを新規作成するとコスト・起動時間の両方で割に合わない |
| Lambda用セキュリティグループ | Aurora への ingress 許可をPRごとに増やさない |
ワイルドカードTLS証明書(*.preview.example.com) |
証明書発行をPRごとに待たない |
| Route53 ホストゾーン | 既存の権威DNSをそのまま使う |
共有リソースの情報(VPC ID、DBエンドポイント、SecretのARNなど)は、本番スタックの CloudFormation Outputs から aws cloudformation describe-stacks で取得し、プレビュー用スタックへ CDK の context(-c prNumber=... -c vpcId=...)として渡しています。CDKのクロススタックExport依存やsynth時のルックアップに頼らないことで、PRごとのスタックを本体から完全に独立してデプロイ・破棄できるようにしています。
押しポイント:DBはアーキテクチャを分離せず、スキーマで分離してコストを削減
一番こだわったのはここです。素直にやるなら「PRごとにAuroraクラスタを新規作成する」という選択肢もありますが、それだと
- クラスタ起動に数分〜十数分かかり、PRを開いてもすぐ確認できない
- Aurora Serverless v2でも最低ACUのぶんクラスタ数に比例してコストがかかる
- 複数PRが同時に動くと、無視できない金額になる
という問題があります。今回は 単一のAuroraクラスタを全PRで共有し、PostgreSQLの「スキーマ」でデータだけを論理分離する 方式にしました。
- PRごとに
pr_<PR番号>というスキーマを作成 - そのPRのプレビュー用Lambda(API・マイグレーション)は
DATABASE_URLに?schema=pr_<番号>を付与し、Prismaの search_path をこのスキーマに切り替える(アプリケーションコード側の変更は一切不要) - マイグレーション用Lambdaが
CREATE SCHEMA IF NOT EXISTS "pr_<番号>"→prisma migrate deployの順に実行し、そのスキーマにだけテーブルを作る - PRクローズ時は、共有のLambda(
drop-schema)を{"schema":"pr_<番号>"}というペイロードでinvokeし、DROP SCHEMA ... CASCADEで片付ける- スキーマ名は
pr_<数字>の形式のみ許可するようにして、誤削除やインジェクションを防止
- スキーマ名は
この結果、
- PR作成時に新たに課金対象が増えるのは Lambda / API Gateway / CloudFront / S3 の従量分だけ(Aurora自体は増えない)
- アイドル時(誰もアクセスしていないPR)のコストはほぼゼロ
- Prisma のスキーマ設計・マイグレーションはそのまま使い回せる
というバランスの良い落とし所になりました。「環境を丸ごと複製する」のではなく「データだけ論理分離する」という発想が、今回のコスト削減の一番のポイントです。
PR作成時のフロー
GitHub Actions(pull_request イベント)でPRの状態に応じて次のように動きます。
認証は GitHub OIDC を使い、長期のAWSクレデンシャルをGitHub Secretsに置かずに AssumeRole しています(本番デプロイと同じロールを、必要な権限だけ追加して使い回しています)。
新規PR作成時はCloudFrontのディストリビューション作成が入るため反映までに数分かかりますが、同一PRへの追加pushはS3・Lambdaの更新だけなので短時間で反映されます。
今後やりたいこと:プレビュー環境ごとのデフォルトデータ投入
現状、プレビュー環境はスキーマを作ってマイグレーションを流すだけなので、中身は空の状態です。動作確認のたびに手でユーザーやタスクを作る必要があり、地味に手間がかかっています。
次にやりたいのは、プレビュー環境の作成フローに「デフォルトのシードデータ投入」を組み込むことです。イメージとしては、
- マイグレーションLambdaと同じタイミングで、テスト用ユーザー・組織・サンプルタスクなどをシードするステップを追加
- Prismaの seed スクリプトをプレビュー用に拡張し、
pr_<番号>スキーマに対して実行する
あたりを想定しています。これができると、PRを開いた瞬間から「ある程度データが入った状態のアプリ」をすぐ触れるようになり、レビューのハードルがさらに下がるはずです。
まとめ
- 複数のAIエージェントを並列で走らせる開発スタイルでは、実装速度よりも「成果物をどれだけ早く確認できるか」がボトルネックになりやすい
- GitHubのPRごとに独立したプレビュー環境をAWS上に自動構築することで、レビュアーはURLを開くだけで各エージェントの成果物を確認できるようになった
- インフラ(CloudFront/S3/API Gateway/Lambda)はPRごとに複製しつつ、DBだけは単一のAuroraをスキーマで論理分離することで、環境を丸ごと複製するよりも大幅にコストを抑えられた
- 次はプレビュー環境へのデフォルトデータ投入をやっていきたい
同じように複数エージェント・複数PRを並行して回している方の参考になれば幸いです。