Optimization convention-rule-of-three

Rule of Three. 코드가 3번 반복되면 리팩토링하라. 한 번은 괜찮고, 두 번은 주의, 세 번이면 추출.

install
source · Clone the upstream repo
git clone https://github.com/sunLeee/optimization
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/sunLeee/optimization "$T" && mkdir -p ~/.claude/skills && cp -r "$T/.claude/skills/reference/philosophy/design/rule-of-three" ~/.claude/skills/sunleee-optimization-convention-rule-of-three && rm -rf "$T"
manifest: .claude/skills/reference/philosophy/design/rule-of-three/SKILL.md
source content

convention-rule-of-three

@AW-038 | @docs/design/ref/team-operations.md § AW-038

Rule of Three: 같은 코드를 처음 쓸 때는 그냥 써라. 두 번째는 중복을 인식하라. 세 번째에는 반드시 리팩토링하라.

VIOLATION 1: 세 곳에 같은 전처리 로직

# VIOLATION: 동일한 전처리 로직이 3곳에 반복

# agents/demand_analyst/tools.py
def load_demand_data(tool_context: ToolContext) -> pd.DataFrame:
    df = pd.read_csv(tool_context.state["app:demand_file"])
    df = df.dropna()                          # 동일한 전처리
    df = df[df["demand"] >= 0]                # 동일한 필터링
    df = df.rename(columns={"zone": "zone_id"})  # 동일한 정규화
    return df

# agents/regional_analyst/tools.py
def load_regional_data(tool_context: ToolContext) -> pd.DataFrame:
    df = pd.read_csv(tool_context.state["app:regional_file"])
    df = df.dropna()                          # 반복
    df = df[df["demand"] >= 0]                # 반복
    df = df.rename(columns={"zone": "zone_id"})  # 반복
    return df

# agents/format_agent/tools.py
def load_format_data(tool_context: ToolContext) -> pd.DataFrame:
    df = pd.read_csv(tool_context.state["app:format_file"])
    df = df.dropna()                          # 3번째 반복 → 리팩토링 필요
    df = df[df["demand"] >= 0]
    df = df.rename(columns={"zone": "zone_id"})
    return df
# CORRECT: 공통 전처리 함수로 추출 (libs/utils에 위치)
def preprocess_demand_dataframe(df: pd.DataFrame) -> pd.DataFrame:
    """수요 DataFrame을 전처리한다.

    Logics:
        1. 결측값 제거
        2. 음수 수요 필터링
        3. 컬럼 정규화 (zone → zone_id)
    """
    return (
        df.dropna()
          .loc[lambda x: x["demand"] >= 0]
          .rename(columns={"zone": "zone_id"})
    )

# 각 agent에서 재사용
def load_demand_data(tool_context: ToolContext) -> pd.DataFrame:
    df = pd.read_csv(tool_context.state["app:demand_file"])
    return preprocess_demand_dataframe(df)

VIOLATION 2: 조건부 로직 3번 반복

# VIOLATION: zone 유효성 검사 3곳에 반복
# create_zone.py
if zone_id < 0 or zone_id > 99999:
    raise ValueError(f"Invalid zone_id: {zone_id}")

# update_zone.py
if zone_id < 0 or zone_id > 99999:   # 반복
    raise ValueError(f"Invalid zone_id: {zone_id}")

# delete_zone.py
if zone_id < 0 or zone_id > 99999:   # 3번째 → 추출 필요
    raise ValueError(f"Invalid zone_id: {zone_id}")
# CORRECT: 검증 함수로 추출 + @AW-018 DRY 적용
def validate_zone_id(zone_id: int) -> None:
    """zone_id 유효성을 검증한다."""
    if zone_id < 0 or zone_id > 99999:
        raise ValueError(f"Invalid zone_id: {zone_id}")

# 재사용
validate_zone_id(zone_id)  # 어디서든 동일하게

관련 CLAUDE.md 규칙

규칙위치내용
@AW-038@docs/design/ref/team-operations.md § AW-038Rule of Three
@AW-018@docs/design/ref/team-operations.md § AW-018DRY — 중복 제거 원칙과 연계
@AW-035@docs/design/ref/team-operations.md § AW-035Boy Scout Rule — 발견 시 개선

참조

  • @docs/design/ref/team-operations.md § AW-038
  • @.claude/skills/convention-dry/SKILL.md
  • @.claude/skills/convention-boy-scout-rule/SKILL.md