---
name: intent-based-dedup
description: >-
  字面の同一性ではなく意図（目的）の同一性に基づいてコードの共通化を判断するスキル。
  DRY原則の誤適用（字面が同じだが意図が異なるコードの共通化）を検出し、正しい共通化判定を
  支援する。コードレビュー、リファクタリング、新規実装時に重複コードの扱いを判断する場合に使用。
  対象言語: 言語非依存（Rust, Java, TypeScript, Go, Python, Kotlin, Scala等すべて）。
  トリガー：「重複コードを共通化したい」「DRYに従いたい」「似たコードがある」
  「この2つの関数をまとめたい」「コードの重複を減らしたい」「共通化すべきか」
  「リファクタリングで共通関数にしたい」といった重複コード・共通化関連リクエストで起動。
---

# 意図ベースの共通化判定

字面が同じかどうかより、意図（目的）が同じかどうかで共通化する。

## 核心原則

**「字面が同じなら共通化」という単純思考は危険。意図・目的の一致を最優先に判断する。**

コード表現が同一でも、ビジネスロジック上の目的が異なれば共通化してはならない。
逆に、表現が異なっていても目的が同じなら統一すべきである。

## 判断マトリックス

| 字面 | 意図 | 判定 | アクション |
|------|------|------|-----------|
| 同じ | 同じ | 共通化 ◎ | DRY原則を適用し共通関数に抽出 |
| 異なる | 同じ | 統一 ◎ | どちらかの実装に統一 |
| 同じ | 異なる | **共通化 ✗** | **絶対に共通化禁止**（最重要） |
| 異なる | 異なる | 共通化 ✗ | 検討不要 |

## 判断フロー

```
重複コードを発見した
    ↓
2つのコードの「目的」は同じか？
    ├─ YES → 字面は同じか？
    │         ├─ YES → 共通化する（標準的DRY）
    │         └─ NO → 実装方式を統一する
    └─ NO → 字面は同じか？
              ├─ YES → ⚠ 共通化禁止（最も危険なケース）
              └─ NO → 何もしない
```

## アンチパターン検出

以下のパターンを見つけたらDRY誤適用の兆候：

```
❌ 異なるドメイン概念に同じユーティリティ関数を使い回す
❌ "たまたま同じ計算式" を共通関数に抽出
❌ 共通化した関数に if (type == A) / else if (type == B) の分岐が増える
❌ 共通関数名が汎用的すぎる（calculate, process, transform等）
❌ 一方の仕様変更時に「もう一方も壊れないか」を心配する
❌ 共通関数のパラメータが増殖し続ける
```

## 4パターンの詳細

### 1. 字面が同じ × 意図が同じ → 共通化する

DRY原則が正しく適用されるケース。

```rust
// ❌ 同じ目的の処理が2箇所に重複
fn report_even_squares(numbers: &[i32]) -> Vec<i32> {
    numbers.iter()
        .filter(|&&x| x % 2 == 0)
        .map(|&x| x * x)
        .collect()
}

fn display_even_squares(numbers: &[i32]) -> Vec<i32> {
    numbers.iter()
        .filter(|&&x| x % 2 == 0)
        .map(|&x| x * x)
        .collect()
}

// ✅ 共通化: 同じ目的なので1つにまとめる
fn even_squares(numbers: &[i32]) -> Vec<i32> {
    numbers.iter()
        .filter(|&&x| x % 2 == 0)
        .map(|&x| x * x)
        .collect()
}
```

### 2. 字面が異なる × 意図が同じ → 統一する

同じ目的だが異なる実装スタイルで書かれているケース。

```rust
// パターンA: 関数型アプローチ
fn total_a(values: &[i32]) -> i32 {
    values.iter().fold(0, |acc, &x| acc + x)
}

// パターンB: 命令型アプローチ
fn total_b(values: &[i32]) -> i32 {
    let mut sum = 0;
    for &v in values { sum += v; }
    sum
}

// ✅ どちらか一方に統一（チーム規約に従う）
fn total(values: &[i32]) -> i32 {
    values.iter().sum()
}
```

### 3. 字面が同じ × 意図が異なる → 共通化禁止（最重要）

**最も危険なケース。** 形式上同じコードでも、ビジネス上の目的が異なる。

```rust
// ケース1: 攻撃力計算（地形倍率を適用）
fn adjusted_attack_points(weapon_points: &[i32]) -> Vec<i32> {
    weapon_points.iter()
        .map(|&x| x * 2)
        .filter(|&x| x % 2 == 0)
        .map(|x| x * x)
        .collect()
}

// ケース2: 武器加工費用計算
fn weighted_crafting_costs(amounts: &[i32]) -> Vec<i32> {
    amounts.iter()
        .map(|&x| x * 2)
        .filter(|&x| x % 2 == 0)
        .map(|x| x * x)
        .collect()
}
```

**字面は同じだが共通化してはならない。** 理由：

- 攻撃力の仕様変更（地形倍率が3倍に変わる等）が費用計算を壊す
- 費用計算の変更（税率適用等）が攻撃力計算を壊す
- 変更理由が異なる = 共通化すると結合度が不正に上がる

```rust
// ❌ 危険な共通化
fn apply_formula(values: &[i32]) -> Vec<i32> { /* ... */ }

let attack = apply_formula(&weapon_points);  // 攻撃力？
let cost = apply_formula(&amounts);          // 費用？
// → 一方を変更すると他方が壊れる
```

### 4. 字面が異なる × 意図が異なる → 何もしない

検討不要。それぞれ独立したコードとして維持する。

## 「意図の同一性」の判定基準

目的が同じかどうかを見極めるための質問：

1. **変更理由テスト**: 一方の仕様が変わったとき、もう一方も同じ理由で変わるか？
   - YES → 意図が同じ（共通化可）
   - NO → 意図が異なる（共通化不可）

2. **命名テスト**: 共通化した関数に、両方の文脈で意味の通る名前を付けられるか？
   - YES → 意図が同じ
   - NO（汎用的な名前しか付けられない）→ 意図が異なる

3. **ドメインテスト**: 2つのコードは同じドメイン概念を表しているか？
   - YES → 意図が同じ
   - NO → 意図が異なる

## 適用指針

### 推奨

- 「似たコードがある」と報告されたコードレビュー
- リファクタリングで共通関数を抽出しようとしている場面
- ユーティリティ関数が複数のドメイン概念に使い回されている場合
- 共通化した関数のパラメータが増殖し始めた場合

### 過剰適用を避ける

- 明らかに同一目的のボイラープレート（設定初期化、ログ出力等）
- フレームワーク/ライブラリ提供のユーティリティ
- 数学的に同一の演算（三角関数等、目的に依存しない純粋な計算）

## レビュー観点

コードレビュー時の確認ポイント：

1. **意図の確認**: 共通化対象の2つのコードは同じ「目的」を持つか
2. **変更理由の分離**: 一方の仕様変更が他方に波及しないか
3. **命名の自然さ**: 共通化した名前は両方の文脈で意味が通るか
4. **パラメータ増殖**: 共通関数に条件分岐やフラグが増えていないか
5. **ドメイン境界**: 異なるドメイン概念を1つの関数に混ぜていないか

## 関連スキル（併読推奨）
このスキルを使用する際は、以下のスキルも併せて参照すること：
- `domain-building-blocks`: 異なるドメインの同形コードを共通化しない判断
- `first-class-collection`: 同じ構造のコレクションでも意図が異なれば別型とする判断
- `package-design`: 同じコードでも変更理由が異なればパッケージを分ける判断
