Skilltypescript
Performance Diff Skill
変更前後のパフォーマンスを比較し、リグレッションを検出
SKILL.md
---
name: performance-diff
description: 変更前後のパフォーマンスを比較し、リグレッションを検出
user-invocable: true
argument-hint: [PRでは自動、手動では--before/--after指定]
allowed-tools: Read, Grep, Bash
context: fork
---
# civicship-api パフォーマンス比較
コード変更前後の**パフォーマンスを比較**し、リグレッション(性能劣化)を検出します。ベンチマーク結果、クエリ数、レスポンスタイムを分析します。
## 使用方法
```bash
# PRのパフォーマンス比較(自動)
/performance-diff --pr 123
# ブランチ間の比較
/performance-diff --before main --after feature/point-expiration
# 特定ドメインの比較
/performance-diff wallet --before HEAD~1 --after HEAD
```
**引数:**
- `$ARGUMENTS`: ドメイン名、PRでは番号、または `--before`/`--after` オプション
---
## パフォーマンス比較プロセス
### ステップ1: 変更内容の取得
GitでのDiffを取得:
```bash
# PRの差分取得
gh pr diff 123
# ブランチ間の差分
git diff main..feature/point-expiration
# 変更されたファイルのみ
git diff --name-only main..feature/point-expiration
```
**変更サマリー:**
```markdown
## 変更概要
### 変更されたファイル(10ファイル)
**Application Layer:**
- `src/application/domain/account/wallet/service.ts` (+50, -10)
- `src/application/domain/account/wallet/usecase.ts` (+20, -5)
- `src/application/domain/account/wallet/data/repository.ts` (+30, -8)
**Infrastructure Layer:**
- `prisma/schema.prisma` (+5, -0)
**Presentation Layer:**
- `src/application/domain/account/wallet/schema/type.graphql` (+10, -0)
---
### 変更の種類
- [ ] データベーススキーマ変更(カラム追加)
- [ ] 新しいクエリ追加
- [ ] ビジネスロジック変更
- [ ] GraphQL API拡張
```
---
### ステップ2: パフォーマンステストの実行
変更前後でベンチマークを実行:
```bash
# 変更前(main)のパフォーマンステスト
git checkout main
pnpm test:performance > performance-before.txt
# 変更後(feature branch)のパフォーマンステスト
git checkout feature/point-expiration
pnpm test:performance > performance-after.txt
# 差分比較
diff performance-before.txt performance-after.txt
```
**パフォーマンステスト結果:**
```markdown
## パフォーマンステスト結果
### Test: GraphQL Query `wallet(id: "...")`
#### 変更前(main)
\`\`\`
Requests: 1000
Success: 1000 (100%)
Duration: 5.2s
Avg: 5.2ms
P50: 4.8ms
P95: 8.5ms
P99: 12.3ms
\`\`\`
#### 変更後(feature/point-expiration)
\`\`\`
Requests: 1000
Success: 1000 (100%)
Duration: 5.5s
Avg: 5.5ms (+5.8%)
P50: 5.0ms (+4.2%)
P95: 9.0ms (+5.9%)
P99: 13.1ms (+6.5%)
\`\`\`
**評価:** 🟡 軽微な劣化(+5.8%)
**原因:** 有効期限チェックの追加(日付比較)
**許容範囲:** ✅ Yes(+10%以内)
---
### Test: GraphQL Mutation `walletCreate`
#### 変更前
\`\`\`
Requests: 100
Duration: 2.1s
Avg: 21ms
\`\`\`
#### 変更後
\`\`\`
Requests: 100
Duration: 2.3s
Avg: 23ms (+9.5%)
\`\`\`
**評価:** 🟡 軽微な劣化(+9.5%)
**原因:** expiresAt カラムの書き込み追加
**許容範囲:** ✅ Yes(+10%以内)
---
### Test: Batch Process `expirePoints`
#### 変更前
\`\`\`
該当なし(新規処理)
\`\`\`
#### 変更後
\`\`\`
Wallets Processed: 1000
Duration: 8.5s
Avg per wallet: 8.5ms
\`\`\`
**評価:** 🟢 新規処理(ベースラインとして記録)
```
---
### ステップ3: クエリ数の比較
SQLクエリ数の変化を分析:
```markdown
## クエリ数の比較
### GraphQL Query `wallet(id: "...")`
#### 変更前
\`\`\`
SELECT * FROM t_wallets WHERE id = '...' -- 1クエリ
\`\`\`
**Total:** 1クエリ
---
#### 変更後
\`\`\`
SELECT * FROM t_wallets WHERE id = '...' -- 1クエリ
\`\`\`
**Total:** 1クエリ(変更なし)
**評価:** ✅ クエリ数増加なし
---
### GraphQL Mutation `pointTransfer`
#### 変更前
\`\`\`
1. SELECT * FROM t_wallets WHERE userId = 'from-user'
2. SELECT * FROM t_wallets WHERE userId = 'to-user'
3. UPDATE t_wallets SET balance = ... WHERE id = 'from-wallet'
4. UPDATE t_wallets SET balance = ... WHERE id = 'to-wallet'
5. INSERT INTO t_point_transactions ...
\`\`\`
**Total:** 5クエリ
---
#### 変更後
\`\`\`
1. SELECT * FROM t_wallets WHERE userId = 'from-user'
2. SELECT * FROM t_wallets WHERE userId = 'to-user'
(有効期限チェック追加: メモリ内で実行、クエリ不要)
3. UPDATE t_wallets SET balance = ... WHERE id = 'from-wallet'
4. UPDATE t_wallets SET balance = ... WHERE id = 'to-wallet'
5. INSERT INTO t_point_transactions ...
\`\`\`
**Total:** 5クエリ(変更なし)
**評価:** ✅ クエリ数増加なし
```
---
### ステップ4: N+1問題の検出
新たにN+1問題が導入されていないか確認:
```markdown
## N+1問題の検出
### 検証: User.wallets
**変更内容:**
\`\`\`diff
User: {
wallet: (parent, _, ctx) => {
- return prisma.t_wallets.findUnique({ where: { userId: parent.id } });
+ return ctx.loaders.wallet.load(parent.id);
}
}
\`\`\`
**評価:**
- 変更前: N+1問題あり
- 変更後: DataLoader使用、N+1問題解消 ✅
**パフォーマンス改善:**
- 100ユーザー取得時のクエリ数: 100回 → 1回
- レスポンスタイム: 5秒 → 50ms(100倍改善)
**評価:** 🟢 大幅改善
```
---
### ステップ5: データベースインデックスの影響
インデックス追加によるパフォーマンス改善を確認:
```markdown
## インデックスの影響
### 追加されたインデックス
\`\`\`prisma
model t_wallets {
// ...
+ @@index([expiresAt])
}
\`\`\`
---
### クエリパフォーマンス比較
#### Query: `SELECT * FROM t_wallets WHERE expiresAt < NOW()`
**変更前(インデックスなし):**
\`\`\`
Seq Scan on t_wallets (cost=0.00..25.50 rows=10 width=100)
Execution Time: 45.2 ms
\`\`\`
**変更後(インデックスあり):**
\`\`\`
Index Scan using idx_wallets_expires_at on t_wallets (cost=0.15..8.17 rows=10 width=100)
Execution Time: 2.1 ms
\`\`\`
**評価:** 🟢 大幅改善(21倍高速化)
```
---
### ステップ6: メモリ使用量の比較
メモリフットプリントの変化:
```markdown
## メモリ使用量
### ヒープメモリ
**変更前:**
- Used Heap: 120 MB
- Heap Limit: 512 MB
- Usage: 23%
**変更後:**
- Used Heap: 125 MB (+4.2%)
- Heap Limit: 512 MB
- Usage: 24%
**評価:** 🟢 許容範囲内(+5%以下)
---
### オブジェクト数
**変更前:**
- Total Objects: 150,000
**変更後:**
- Total Objects: 152,000 (+1.3%)
**評価:** 🟢 微増(問題なし)
```
---
### ステップ7: リグレッション判定
総合的なパフォーマンス評価:
```markdown
## リグレッション判定
### 判定基準
| メトリクス | 閾値 | 変更前 | 変更後 | 差分 | 判定 |
|-----------|------|--------|--------|------|------|
| Avg Response Time | +10% | 5.2ms | 5.5ms | +5.8% | ✅ Pass |
| P95 Response Time | +15% | 8.5ms | 9.0ms | +5.9% | ✅ Pass |
| P99 Response Time | +20% | 12.3ms | 13.1ms | +6.5% | ✅ Pass |
| Query Count | +0 | 5 | 5 | 0 | ✅ Pass |
| Memory Usage | +10% | 120MB | 125MB | +4.2% | ✅ Pass |
| Error Rate | +0% | 0% | 0% | 0% | ✅ Pass |
---
### 総合評価
**スコア:** 95 / 100 🟢 Excellent
**判定:** ✅ **パフォーマンスリグレッションなし**
**コメント:**
- 軽微な劣化はあるが、全て許容範囲内
- 有効期限チェックの追加によるオーバーヘッドは予想通り
- インデックス追加により一部クエリは大幅改善
- N+1問題の解消により全体的なパフォーマンス向上
**推奨アクション:**
- マージ可能
- 本番環境での監視継続
```
---
### ステップ8: パフォーマンス改善の提案
さらなる最適化の提案:
```markdown
## パフォーマンス改善提案
### 提案1: キャッシュ導入
**現状:**
- 有効期限チェックを毎回実行
**提案:**
\`\`\`typescript
// Redis キャッシュ(TTL: 60秒)
const cacheKey = \`wallet:expiration:\${walletId}\`;
const cached = await redis.get(cacheKey);
if (cached) return JSON.parse(cached);
const wallet = await this.repo.findById(ctx, walletId, tx);
await redis.setex(cacheKey, 60, JSON.stringify(wallet));
return wallet;
\`\`\`
**効果:**
- レスポンスタイム: 5.5ms → 0.5ms(11倍改善)
- データベース負荷: 90%削減
---
### 提案2: SELECT句の最適化
**現状:**
\`\`\`typescript
return tx.t_wallets.findUnique({ where: { id } });
\`\`\`
**提案:**
\`\`\`typescript
return tx.t_wallets.findUnique({
where: { id },
select: {
id: true,
balance: true,
expiresAt: true,
// 必要なフィールドのみ
}
});
\`\`\`
**効果:**
- データ転送量: 80%削減
- メモリ使用量: 削減
```
---
### ステップ9: 比較レポート生成
```markdown
# パフォーマンス比較レポート
**PR:** #123
**ブランチ:** feature/point-expiration
**比較:** main vs feature/point-expiration
**実施日:** 2026-01-15
---
## エグゼクティブサマリー
### 総合評価
**判定:** ✅ **マージ可能**
**スコア:** 95 / 100
**リグレッション:** なし(軽微な劣化は許容範囲内)
---
## 詳細比較
### レスポンスタイム
| エンドポイント | 変更前 | 変更後 | 差分 | 判定 |
|---------------|--------|--------|------|------|
| wallet(id) | 5.2ms | 5.5ms | +5.8% | ✅ |
| walletCreate | 21ms | 23ms | +9.5% | ✅ |
| pointTransfer | 45ms | 46ms | +2.2% | ✅ |
---
### クエリ数
| エンドポイント | 変更前 | 変更後 | 差分 | 判定 |
|---------------|--------|--------|------|------|
| wallet(id) | 1 | 1 | 0 | ✅ |
| pointTransfer | 5 | 5 | 0 | ✅ |
---
### パフォーマンス改善
- **N+1問題解消:** User.wallets(100倍改善)
- **インデックス追加:** expiresAt クエリ(21倍改善)
---
## 推奨アクション
### 即座に実施
- ✅ PR をマージ可能
- 🔔 本番環境で監視継続(24時間)
### 将来的に検討
- キャッシュ導入(さらなる最適化)
- SELECT句の最適化
---
## 承認
- [ ] テックリード
- [ ] DevOpsリード
```
---
## 活用例
### 例1: PRのパフォーマンス比較
```bash
/performance-diff --pr 123
```
**出力:**
- 変更前後の比較
- リグレッション判定
- マージ可否の判断
---
### 例2: リリース前の検証
```bash
/performance-diff --before v1.0.0 --after main
```
**出力:**
- バージョン間の比較
- パフォーマンス推移
---
## 参考資料
- [Performance Testing Best Practices](https://k6.io/docs/testing-guides/performance-testing/)
- [Database Query Performance](https://www.postgresql.org/docs/current/performance-tips.html)
- [GraphQL Performance](https://www.apollographql.com/docs/apollo-server/performance/caching/)