# Docstring Guidelines (Tiered Approach)

**Status**: Active (as of 2025-10-26)
**Enforcement**: Tiered by layer (Domain strict, others relaxed)

## Quick Reference

### Which tier am I in?

| Path | Tier | Policy | Requirements |
|------|------|--------|---|
| `src/noveler/domain/**` | 1 (STRICT) | ⭐⭐⭐⭐⭐ | Purpose, Args, Returns, Raises, Side Effects |
| `src/noveler/application/**` | 2 (MODERATE) | ⭐⭐⭐ | Purpose only (others optional) |
| `src/noveler/infrastructure/**` | 3 (RELAXED) | ⭐⭐ | One-liner or Purpose if complex |
| `src/noveler/presentation/**` | 4 (MINIMAL) | ⭐ | Module header only (functions optional) |
| `tests/**` | 5 (EXEMPT) | ✓ | Test names must be self-documenting |

---

## Rationale

**Why tiered standards?**

- **50,474 existing docstring violations** across 1,400 files → one-size-fits-all enforcement is impractical
- **LLM era**: Type hints + good naming reduce docstring necessity significantly
- **Layer-specific value**:
  - Domain layer (business logic) = specification foundation → strict documentation needed
  - Application layer (use cases) = type hints often sufficient
  - Infrastructure layer (I/O) = implementation is the specification
  - Presentation/Tests = high change frequency makes docs quickly stale

**Result**: Development velocity + code quality balance

---

## Tier 1: Domain Layer (STRICT)

**Path**: `src/noveler/domain/**`

**Required docstring sections** for **all classes and functions**:
- **Purpose**: What and why (2-3 sentences, motivate the design)
- **Args**: All parameters (unless type hints are perfectly clear)
- **Returns**: Return value meaning (if function returns)
- **Raises**: Intentional exceptions (if function raises)
- **Side Effects**: State changes, I/O, events, or explicit "None"

**Why strict?** Business logic is the foundation for:
- Automated specification generation
- Domain model validation
- Design review discussions

### Tier 1: Good Examples ✅

#### Example 1: Entity method with business logic
```python
def calculate_royalty(sales: Decimal, rate: Decimal) -> Decimal:
    """
    印税を計算する（消費税込み、最小単位1円）。

    Purpose:
        売上金額と印税率から著者への支払額を算出。
        端数は切り捨て（出版業界慣習）。

    Args:
        sales: 税込売上金額（負数チェックは呼び出し側の責務）
        rate: 印税率（0.0-1.0範囲、範囲外はValueErrorで検証）

    Returns:
        計算された印税額（円単位、Decimalで精度保証）

    Raises:
        ValueError: rateが0.0-1.0範囲外の場合

    Side Effects:
        None - 純粋関数（状態変更なし）
    """
    if not (0.0 <= rate <= 1.0):
        raise ValueError(f"rate must be 0.0-1.0, got {rate}")

    result = sales * rate
    return Decimal(int(result))  # 円単位に切り捨て
```

#### Example 2: Aggregate method with side effects
```python
def initialize_episode(
    number: int,
    title: str,
    draft_manuscript: Manuscript
) -> None:
    """
    新規エピソードの初期化と永続化。

    Purpose:
        エピソード番号、タイトル、原稿から新規エピソードを
        構築し、リポジトリに保存。プロジェクト検証も実施。

    Args:
        number: エピソード番号（1以上、プロジェクト最大値以下）
        title: エピソードタイトル（1-200文字）
        draft_manuscript: 初期原稿（検証済みManuscriptインスタンス）

    Returns:
        None - サイドエフェクト経由で状態更新

    Raises:
        ValueError: titleが1-200文字範囲外
        ProjectIntegrityError: プロジェクト検証失敗時

    Side Effects:
        - self._episodes リストに新規エピソード追加
        - episode_repository への永続化実施
        - 変更履歴ファイルに初期化イベント記録
    """
    if not (1 <= len(title) <= 200):
        raise ValueError(f"title length must be 1-200, got {len(title)}")

    episode = Episode(number=number, title=title, manuscript=draft_manuscript)
    self._episodes.append(episode)
    self._repository.save(episode)
    self._history.record("episode_initialized", episode_id=episode.id)
```

#### Example 3: Service with complex return value
```python
def suggest_next_writing_step(
    current_state: WriteState,
    quality_metrics: QualityMetrics
) -> NextStepSuggestion:
    """
    次の執筆ステップを品質スコアに基づいて提案。

    Purpose:
        現在の執筆状況と品質メトリクスから、推奨される
        次のステップと理由を算出。スコア計算は機械学習
        モデルで動的調整。

    Args:
        current_state: 現在の執筆状況（完了ステップ、最終スコア含む）
        quality_metrics: 最新の品質メトリクス（構文/可読性/リズム等）

    Returns:
        NextStepSuggestion: 推奨ステップ、理由、信頼度スコア
        - step: 次のステップID
        - reason: 推奨理由（日本語、ユーザ向け）
        - confidence: 0.0-1.0の信頼度（ML精度指標）

    Raises:
        InvalidStateError: current_state が内部的に矛盾している場合
        MLModelError: 学習モデルロード失敗時

    Side Effects:
        - ML推論実行（CPU/GPU使用）
        - 推奨理由を一時ログに記録（後続分析用）
    """
```

### Tier 1: Bad Examples ❌

```python
# Missing: Purpose, Args, Returns, Side Effects
def validate_episode(ep):
    return True

# Missing: Side Effects section
def save_to_db(data):
    """Save data to database.

    Purpose:
        Store episode data in PostgreSQL

    Args:
        data: Episode object

    Returns:
        bool: Success indicator
    """
    # Actually persists to disk AND sends webhook!

# Misleading: Args/Returns not documented
def process_manuscript(txt, cfg, opts):
    """
    Process manuscript with advanced options.

    Purpose:
        Analyze manuscript quality and generate report

    Side Effects:
        None
    """
    # But actually has Args and Returns!
```

---

## Tier 2: Application Layer (MODERATE)

**Path**: `src/noveler/application/**`

**Required**: **Purpose** section only
- **Args/Returns/Raises**: Only if **not obvious** from type hints
- **Side Effects**: Only if **non-trivial** (e.g., DB writes, file I/O)

**Why moderate?** Use cases + type hints are often self-explanatory

### Tier 2: Good Examples ✅

```python
# Clear from types → Purpose only is fine
def create_episode(
    title: str,
    manuscript: Manuscript
) -> Episode:
    """新規エピソードを作成し、永続化する。"""
    # No Args/Returns/Side Effects needed (types are explicit)
```

```python
# Complex use case → Purpose + Side Effects
def export_episode_to_multiple_formats(
    episode: Episode,
    formats: list[FileFormat]
) -> ExportResult:
    """
    エピソードを複数フォーマットにエクスポート。

    Purpose:
        ZIP内に複数フォーマットを書き込む。
        既存ファイルは上書き。失敗時は一部ロールバック。

    Side Effects:
        - ローカルディスクに一時ファイル作成
        - ZIP作成・書き込み実施
        - 一時ファイルは処理後に削除
    """
```

### Tier 2: Bad Examples ❌

```python
# Missing Purpose
def process_episode(ep):
    """Process an episode."""
    # What does "process" mean?

# Verbose when types say it all
def get_user(user_id: int) -> User | None:
    """
    Get user by ID.

    Purpose:
        Retrieve user record from database by user_id.
        Returns None if not found.

    Args:
        user_id: The user's ID (must be positive integer)

    Returns:
        User object if found, None otherwise

    Raises:
        DatabaseError: On connection failure

    Side Effects:
        Queries database
    """
    # All of this is obvious from the type signature!
```

---

## Tier 3: Infrastructure Layer (RELAXED)

**Path**: `src/noveler/infrastructure/**`

**Required**: One-line summary only

- One-liner sufficient for trivial I/O
- Detailed docstring only for:
  - Complex error handling patterns
  - Non-obvious side effects (caching, retry logic, retry limits)
  - Performance considerations

**Why relaxed?** Implementation IS the specification

### Tier 3: Good Examples ✅

```python
# One-liner sufficient for simple I/O
def read_yaml(path: Path) -> dict[str, Any]:
    """YAMLファイルを読み込む"""

def write_json(path: Path, data: dict) -> None:
    """JSON形式でファイルに保存"""
```

```python
# Complex side effects → document them
def save_with_backup(path: Path, data: str) -> None:
    """
    ファイルを保存し、既存ファイルをバックアップ。

    Side Effects:
        - .bak ファイルに既存データバックアップ
        - リトライ3回（ファイルロック時）
        - 失敗時は .bak から自動復旧
    """
```

### Tier 3: Bad Examples ❌

```python
# Over-documented for simple I/O
def load_config(path: Path) -> Config:
    """
    Configuration file loader implementation.

    Purpose:
        Load configuration from YAML file at path.
        Parse YAML, validate schema, return Config object.

    Args:
        path: Path to config file (must exist and be readable)

    Returns:
        Parsed Config object with validated fields

    Side Effects:
        Reads filesystem
    """
    # Too much documentation for something so simple!
```

---

## Tier 4: Presentation Layer (MINIMAL)

**Path**: `src/noveler/presentation/**`

**Required**: Module header only
- Function docstrings: Optional (UI code changes too frequently)

### Tier 4: Good Examples ✅

```python
# Optional - function name is sufficient
def render_episode_card(episode: Episode) -> str:
    """エピソードをHTMLカードで表示"""

# Can omit docstring entirely if name is clear
def get_user_avatar_url(user: User) -> str:
    return user.avatar_url
```

---

## Tier 5: Tests (EXEMPT)

**Path**: `tests/**`

**Required**: None (test function names should be self-documenting)

**Good example**:
```python
def test_create_episode_with_invalid_title_raises_validation_error():
    # Test name IS the documentation
    with pytest.raises(ValueError):
        create_episode(title="", manuscript=...)
```

---

## Tools & Commands

### Check Compliance

#### All layers (STRICT mode):
```bash
# Show all violations (strict)
python scripts/comment_header_audit.py

# Show violations in specific file(s)
python scripts/comment_header_audit.py src/noveler/domain/services/my_service.py
```

#### Tiered mode:
```bash
# Check with tier-specific requirements
python scripts/comment_header_audit.py --tiered

# Check specific directory with tier awareness
python scripts/comment_header_audit.py --tiered src/noveler/domain/services/
```

### Generate Templates

#### STRICT mode (default):
```bash
python scripts/generate_docstring_templates.py src/noveler/domain/services/my_service.py
```

#### Tiered mode:
```bash
# Generate tier-appropriate templates
python scripts/generate_docstring_templates.py --tiered src/noveler/infrastructure/services/my_service.py
```

---

## Enforcement

### Pre-commit Hook
- Runs automatically before `git commit`
- **Blocks commit if Domain layer violations detected**
- **Warns** (but allows) for other layers
- Only checks **staged files** (changed code)

**Manual trigger**:
```bash
python scripts/pre_commit_docstring_check.py
```

### CI Integration
- **Phase 1** (Week 1-2): Domain layer warnings only
- **Phase 2** (Week 3+): Domain layer failures block PR
- **Phase 3** (Month 2+): Application layer also enforced
- **Ongoing**: Infrastructure/Presentation remain relaxed

---

## FAQ

### Q: My Application layer function needs detailed docstrings too?

**A**: Only if the type hints are **not obvious**. Compare:

```python
# Type hints are CLEAR → Purpose only
def create_user(email: str, password: str) -> User:
    """新規ユーザーを作成"""

# Type hints are UNCLEAR → Add detail
def process_data(config: dict, options: Any) -> tuple:
    """
    設定とオプションからデータを処理。

    Purpose:
        複雑な前処理ロジックを実行。

    Args:
        config: 設定辞書（キー: 'mode', 'threshold', etc.）
        options: 可変オプション（型チェックなし）

    Returns:
        (processed_data, metadata) のタプル
    """
```

### Q: Can I skip docstrings in Infrastructure layer?

**A**: Yes, one-liners are optional for trivial code. But document **non-obvious side effects**:

```python
# Trivial → no docstring needed
def exists(path: Path) -> bool:
    return path.exists()

# Non-obvious side effects → document them
def save_with_retries(path: Path, data: str) -> None:
    """
    保存失敗時は最大3回リトライ。既存ファイルはバックアップ。
    """
```

### Q: My test function has a complex scenario?

**A**: Test **names** should be descriptive; docstrings are not needed:

```python
# Good: Name describes the scenario
def test_episode_validation_fails_when_title_exceeds_200_chars_and_contains_invalid_unicode():
    # Test body explains the assertion
    with pytest.raises(ValueError) as exc_info:
        Episode(title="a" * 201, ...)
    assert "title" in str(exc_info.value).lower()
```

### Q: How do I handle "Side Effects: None"?

**A**: Write it explicitly (especially in Domain layer):

```python
def calculate_score(x: int, y: int) -> int:
    """
    スコアを計算する。

    Purpose:
        2つの値を使用して加重スコアを算出。
        副作用なし。

    Args:
        x: 第1値
        y: 第2値

    Returns:
        計算スコア (0-100)

    Side Effects:
        None - 純粋関数（状態変更、I/O、外部呼び出しなし）
    """
```

---

## Related Documents

- **AGENTS.md**: Tiered standards definition (implementation contracts)
- **Implementation Plan**: `reports/docstring_compliance_implementation_plan.md`
- **Baseline Metrics**: `reports/docstring_violations_baseline_2025_10_26.json`
- **Audit Tool**: `scripts/comment_header_audit.py --tiered`
- **Template Generator**: `scripts/generate_docstring_templates.py --tiered`

---

## Feedback & Questions

- Unclear tier classification? → See AGENTS.md § "Comment & Docstring Standards (Tiered Approach)"
- Tool not working? → Check `reports/docstring_compliance_implementation_plan.md` for troubleshooting
- Disagree with tier? → Raise issue or discuss in team sync

**Last updated**: 2025-10-26
